const osc = require('osc') const midi = require('midi') const Quat = require('quaternion'); // CC kanali za midi mapping (kanal, CC kanal) const midiKanal = 176; // CC kanali senzorjev const midiKanalSenzor = { 1: 20, 2: 21, 3: 22 } const senzorCCKanal = { 1: 75, 2: 76, 3: 77 } const senzorCCMute = { 1: 78, 2: 79, 3: 80 } const senzorCCkalibracija = { 1: 81, 2: 82, 3: 83 } const DEBUG = { midi: false, osc: false } const batMin = 0.09 const batMax = 0.133 const procentBaterije = baterija => Math.round((baterija - batMin) / (batMax - batMin) * 100) // Kalibracijsko izhodisce const izhodisceQ = { 1: new Quat(), 2: new Quat(), 3: new Quat() } const prejsnjiQ = { 1: new Quat(), 2: new Quat(), 3: new Quat() } const eulerRotacija = { 1: [0, 0, 0], 2: [0, 0, 0], 3: [0, 0, 0] } const senzorCC = { 1: 64, 2: 64, 3: 64 } let staroVrtenjeZMidi = { 1: 0, 2: 0, 3: 0 }; const muteMidi = { 1: 0, 2: 0, 3: 0 } // Stetje prejetih paketov (na sekundo) let cas = 0; let baterija = 0; let stPaketov = 0; let stPaketovNaSekundo = 0; // Midi controller input const mi = new midi.Input() mi.openVirtualPort("danijela-midi") mi.on('message', (deltaTime, msg) => { if (msg[0] === midiKanal) { if (Object.values(senzorCCKanal).includes(msg[1])) { const s = Object.values(senzorCCKanal).indexOf(msg[1]) + 1 if (!muteMidi[s]) { senzorCC[s] = msg[2] console.log(`senzor ${s}: ${msg[2]}`) } } else if (Object.values(senzorCCMute).includes(msg[1])) { const s = Object.values(senzorCCMute).indexOf(msg[1]) + 1 if (msg[2]) { console.log(`senzor UNMUTE ${s}`) muteMidi[s] = false; } else { muteMidi[s] = true; console.log(`senzor MUTE ${s}`) } } else if (Object.values(senzorCCkalibracija).includes(msg[1])) { const s = Object.values(senzorCCkalibracija).indexOf(msg[1]) + 1 // resetiraj izhodisce quaterniona izhodisceQ[s] = prejsnjiQ[s].conjugate() // nastavi CC vrednost na sredino - 64 senzorCC[s] = 64; console.log(`senzor KALIBRACIJA ${s}`) } } //console.log(`midi in: ${msg} d: ${deltaTime}`); }) // Razpon za pospeskomerje const accMin = -8000 const accMax = 8000 // Povezava na wavey wind const ip = process.argv[2] const oscWS = new osc.WebSocketPort({ url: `ws://${ip}:6676`, metadata: false }) oscWS.on('ready', () => { console.log(`Povezan sem na ${ip}`) // PING test! if (DEBUG.ping) { setInterval(() => { console.log('ping') oscWS.send({ address: '/ping', args: [true]}) }, 500) } }) function posljiMidi(kanal, vrednost, jakost) { DEBUG.midi && console.log('MIDI', kanal, vrednost, jakost) oscWS.send({ address: '/midi', args: [kanal, vrednost, jakost] }) } function posljiMidiCC(kanal, vrednost) { posljiMidi(midiKanal, kanal, vrednost); } //const norm = (val, min, max) => Math.min(1, (val - min) / (max - min)) const norm = (val, min, max) => { const d = 0 - min; const r = max - min return Math.min(1, (Math.max(min, val) + d) / r); } const normSin = val => { return norm(Math.sin(val), -1, 1); } const normMidi = (val, min, max) => Math.round(norm(val, min, max) * 127) const abs = Math.abs; let cakaj = false; const izpisiStanje = (vrtenje, baterija, signali) => { const vrstica = `| vrtenje: ${String(vrtenje).padStart(3, ' ')} | baterija: ${String(procentBaterije(baterija)).padStart(3, ' ')}% | signali/sekundo: ${signali}` process.stdout.write(`${vrstica}\r`); //process.stdout.moveCursor(vrstica.length + 1) //process.stdout.clearLine(1) } oscWS.on('message', ({ address, args }) => { DEBUG.osc && console.log('MSG OSC', address, args.map(x => x.toFixed(4))) // Wavey Wind sporočila const re = /\/ww\/0\/ww\/(\d+)\/([a-zA-Z]+)/ const m = address.match(re) if (m) { const index = parseInt(m[1]) const addr = m[2] if (addr == 'bat') { baterija = args[0] } if (addr == 'accel') { const acX = args[0] const acY = args[1] const acZ = args[2] //console.log(acX, acY, acZ); // CC sporocila! /* posljiMidiCC(20, normMidi(acX, accMin, accMax)); posljiMidiCC(21, normMidi(acY, accMin, accMax)); posljiMidiCC(22, normMidi(acZ, accMin, accMax)); */ } if (addr == 'quaternion') { //console.log('IDX', index) // Izracunaj spremembo rotacije const novQ = new Quat({w: args[0], x: args[1], y: args[2], z: args[3]}) const novQC = novQ.mul(izhodisceQ[index]) const qWd = novQC.div(prejsnjiQ[index]) prejsnjiQ[index] = novQC; const eulerD = qWd.toEuler() //console.log(index, eulerD) eulerRotacija[index][0] += eulerD.roll * (senzorCC[index] / 64); eulerRotacija[index][1] += eulerD.pitch * (senzorCC[index] / 64); eulerRotacija[index][2] += eulerD.yaw * (senzorCC[index] / 64); const vrtenjeX = Math.abs(Math.sin(eulerRotacija[index][0])); /* posljiMidi(180, 20, Math.round(vrtenjeX * 127)); */ // Roka dol/gor const vrtenjeY = Math.abs(Math.sin(eulerRotacija[index][1])); //posljiMidi(180, 20, Math.round(vrtenjeY * 127)); //posljiMidi(181, 20, Math.round(vrtenjeY * 127)); const vrtenjeZ = normSin(eulerRotacija[index][2]) const vrtenjeZmidi = Math.round(vrtenjeZ * 127) if (vrtenjeZmidi !== staroVrtenjeZMidi[index] && !muteMidi[index]) { posljiMidi(182, midiKanalSenzor[index], vrtenjeZmidi) staroVrtenjeZMidi[index] = vrtenjeZmidi } // Vsako sekundo shranimo stevec stPaketov += 1 if (Date.now() - cas > 1000) { cas = Date.now() stPaketovNaSekundo = stPaketov; stPaketov = 0; // 0.086 - 0.133 je razpon baterije } izpisiStanje(vrtenjeZmidi, baterija, stPaketovNaSekundo) } } }) oscWS.open()