diff --git a/hvcc/generators/c2js/c2js.py b/hvcc/generators/c2js/c2js.py index 34617c4..0bd7762 100644 --- a/hvcc/generators/c2js/c2js.py +++ b/hvcc/generators/c2js/c2js.py @@ -204,20 +204,11 @@ def compile( externs=externs, pool_sizes_kb=externs["memoryPoolSizesKb"])) - pre_js_path = os.path.join(out_dir, "midi_utils.js") - with open(pre_js_path, "w") as f: - f.write(env.get_template("midi_utils.js").render( - name=patch_name, - copyright=copyright_js, - externs=externs, - pool_sizes_kb=externs["memoryPoolSizesKb"])) - js_path = cls.run_emscripten(c_src_dir=c_src_dir, out_dir=out_dir, patch_name=patch_name, output_name=patch_name, post_js_path=post_js_path, - pre_js_path=pre_js_path, should_modularize=1, environment="web") @@ -250,6 +241,14 @@ def compile( externs=externs, pool_sizes_kb=externs["memoryPoolSizesKb"])) + pre_js_path = os.path.join(out_dir, "hv_worklet_start.js") + with open(pre_js_path, "w") as f: + f.write(env.get_template("hv_worklet_start.js").render( + name=patch_name, + copyright=copyright_js, + externs=externs, + pool_sizes_kb=externs["memoryPoolSizesKb"])) + js_path = cls.run_emscripten(c_src_dir=c_src_dir, out_dir=out_dir, patch_name=patch_name, @@ -296,4 +295,4 @@ def compile( "out_dir": out_dir, "out_file": "", "compile_time": time.time() - tick - } + } \ No newline at end of file diff --git a/hvcc/generators/c2js/template/hv_worklet.js b/hvcc/generators/c2js/template/hv_worklet.js index eec2471..ae26736 100644 --- a/hvcc/generators/c2js/template/hv_worklet.js +++ b/hvcc/generators/c2js/template/hv_worklet.js @@ -109,7 +109,7 @@ class {{name}}_AudioLibWorklet extends AudioWorkletProcessor { // typedef void (HvSendHook_t) (HeavyContextInterface *context, const char *sendName, hv_uint32_t sendHash, const HvMessage *msg); var sendHook = addFunction(function(context, sendName, sendHash, msg) { // Filter out MIDI messages - const midiMessage = midiOutMsg(UTF8ToString(sendName), msg); + const midiMessage = sendMidiOut(UTF8ToString(sendName), msg); if (midiMessage.length > 0) { self.port.postMessage({ type: 'midiOut', @@ -125,7 +125,7 @@ class {{name}}_AudioLibWorklet extends AudioWorkletProcessor { }, "viiii" ); - _hv_setSendHook(this.heavyContext, sendHook); + _hv_setSendHook(this.heavyContext, sendHook); } sendEvent(name) { @@ -206,3 +206,155 @@ var tableHashes = { }; registerProcessor("{{name}}_AudioLibWorklet", {{name}}_AudioLibWorklet); + + +// midi_utils + +function sendMidiIn(hv_context, message) { + if (hv_context) { + var command = message[0] & 0xF0; + var channel = message[0] & 0x0F; + var data1 = message[1]; + var data2 = message[2]; + + // all events to [midiin] + for (var i = 1; i <= 2; i++) { + _hv_sendMessageToReceiverFF(hv_context, HV_HASH_MIDIIN, 0, + message[i], + channel + ); + } + + // realtime events to [midirealtimein] + if (MIDI_REALTIME.includes(message[0])) { + _hv_sendMessageToReceiverFF(hv_context, HV_HASH_MIDIREALTIMEIN, 0, + message[0] + ); + } + + switch(command) { + case 0x80: // note off + _hv_sendMessageToReceiverFFF(hv_context, HV_HASH_NOTEIN, 0, + data1, + 0, + channel); + break; + case 0x90: // note on + _hv_sendMessageToReceiverFFF(hv_context, HV_HASH_NOTEIN, 0, + data1, + data2, + channel); + break; + case 0xA0: // polyphonic aftertouch + _hv_sendMessageToReceiverFFF(hv_context, HV_HASH_POLYTOUCHIN, 0, + data2, // pressure + data1, // note + channel); + break; + case 0xB0: // control change + _hv_sendMessageToReceiverFFF(hv_context, HV_HASH_CTLIN, 0, + data2, // value + data1, // cc number + channel); + break; + case 0xC0: // program change + _hv_sendMessageToReceiverFF(hv_context, HV_HASH_PGMIN, 0, + data1, + channel); + break; + case 0xD0: // aftertouch + _hv_sendMessageToReceiverFF(hv_context, HV_HASH_TOUCHIN, 0, + data1, + channel); + break; + case 0xE0: // pitch bend + // combine 7bit lsb and msb into 32bit int + var value = (data2 << 7) | data1; + _hv_sendMessageToReceiverFF(hv_context, HV_HASH_BENDIN, 0, + value, + channel); + break; + default: + // console.error('No handler for midi message: ', message); + } + } + } + +function sendMidiOut(sendName, msg) { + switch (sendName) { + case "__hv_noteout": + var note = _hv_msg_getFloat(msg, 0); + var velocity = _hv_msg_getFloat(msg, 1); + var channel = (_hv_msg_getFloat(msg, 2) - 1) % 16; // no pd midi ports + return [ + ((velocity > 0) ? 144 : 128) | channel, + note, + velocity + ] + case "__hv_ctlout": + var value = _hv_msg_getFloat(msg, 0); + var cc = _hv_msg_getFloat(msg, 1); + var channel = _hv_msg_getFloat(msg, 2) % 16; // no pd midi ports + return [ + 176 | channel, + cc, + value + ] + case "__hv_pgmout": + var program = _hv_msg_getFloat(msg, 0); + var channel = _hv_msg_getFloat(msg, 1) % 16; // no pd midi ports + return [ + 192 | channel, + program + ] + case "__hv_touchout": + var pressure = _hv_msg_getFloat(msg, 0); + var channel = _hv_msg_getFloat(msg, 1) % 16; // no pd midi ports + return [ + 208 | channel, + pressure, + ] + case "__hv_polytouchout": + var value = _hv_msg_getFloat(msg, 0); + var note = _hv_msg_getFloat(msg, 1); + var channel = _hv_msg_getFloat(msg, 2) % 16; // no pd midi ports + return[ + 160 | channel, + note, + value + ] + case "__hv_bendout": + var value = _hv_msg_getFloat(msg, 0); + let lsb = value & 0x7F; + let msb = (value >> 7) & 0x7F; + var channel = _hv_msg_getFloat(msg, 1) % 16; // no pd midi ports + return [ + 224 | channel, + lsb, + msb + ] + case "__hv_midiout": + let firstByte = _hv_msg_getFloat(msg, 0); + return (firstByte === 192 || firstByte === 208) ? + [_hv_msg_getFloat(msg, 0), _hv_msg_getFloat(msg, 1)] : + [_hv_msg_getFloat(msg, 0), _hv_msg_getFloat(msg, 1), _hv_msg_getFloat(msg, 2)]; + default: + console.warn(`Unhandled sendName: ${sendName}`); + return []; + } +} + +/* +* MIDI Constants +*/ + +const HV_HASH_NOTEIN = 0x67E37CA3; +const HV_HASH_CTLIN = 0x41BE0f9C; +const HV_HASH_POLYTOUCHIN = 0xBC530F59; +const HV_HASH_PGMIN = 0x2E1EA03D; +const HV_HASH_TOUCHIN = 0x553925BD; +const HV_HASH_BENDIN = 0x3083F0F7; +const HV_HASH_MIDIIN = 0x149631bE; +const HV_HASH_MIDIREALTIMEIN = 0x6FFF0BCF; + +const MIDI_REALTIME = [0xF8, 0xFA, 0xFB, 0xFC, 0xFE, 0xFF]; diff --git a/hvcc/generators/c2js/template/hv_wrapper.js b/hvcc/generators/c2js/template/hv_wrapper.js index 13a6324..ca09e3c 100644 --- a/hvcc/generators/c2js/template/hv_wrapper.js +++ b/hvcc/generators/c2js/template/hv_wrapper.js @@ -57,7 +57,7 @@ AudioLibLoader.prototype.init = function(options) { sampleRate: this.webAudioContext.sampleRate, blockSize: blockSize, printHook: options.printHook, - sendHook: options.sendHook, + sendHook: options.sendHook }); this.audiolib = instance; this.webAudioProcessor = this.webAudioContext.createScriptProcessor(blockSize, instance.getNumInputChannels(), Math.max(instance.getNumOutputChannels(), 1)); @@ -247,8 +247,7 @@ var tableHashes = { if (hook) { // typedef void (HvSendHook_t) (HeavyContextInterface *context, const char *sendName, hv_uint32_t sendHash, const HvMessage *msg); var sendHook = addFunction(function(context, sendName, sendHash, msg) { - // Filter out MIDI messages - const midiMessage = midiOutMsg(UTF8ToString(sendName), msg); + const midiMessage = sendMidiOut(UTF8ToString(sendName), msg); if (midiMessage.length > 0) { hook("midiOutMessage", midiMessage); } else { @@ -309,3 +308,156 @@ var tableHashes = { } Module.{{name}}_AudioLib = {{name}}_AudioLib; + + + +// midi_utils + +function sendMidiIn(hv_context, message) { + if (hv_context) { + var command = message[0] & 0xF0; + var channel = message[0] & 0x0F; + var data1 = message[1]; + var data2 = message[2]; + + // all events to [midiin] + for (var i = 1; i <= 2; i++) { + _hv_sendMessageToReceiverFF(hv_context, HV_HASH_MIDIIN, 0, + message[i], + channel + ); + } + + // realtime events to [midirealtimein] + if (MIDI_REALTIME.includes(message[0])) { + _hv_sendMessageToReceiverFF(hv_context, HV_HASH_MIDIREALTIMEIN, 0, + message[0] + ); + } + + switch(command) { + case 0x80: // note off + _hv_sendMessageToReceiverFFF(hv_context, HV_HASH_NOTEIN, 0, + data1, + 0, + channel); + break; + case 0x90: // note on + _hv_sendMessageToReceiverFFF(hv_context, HV_HASH_NOTEIN, 0, + data1, + data2, + channel); + break; + case 0xA0: // polyphonic aftertouch + _hv_sendMessageToReceiverFFF(hv_context, HV_HASH_POLYTOUCHIN, 0, + data2, // pressure + data1, // note + channel); + break; + case 0xB0: // control change + _hv_sendMessageToReceiverFFF(hv_context, HV_HASH_CTLIN, 0, + data2, // value + data1, // cc number + channel); + break; + case 0xC0: // program change + _hv_sendMessageToReceiverFF(hv_context, HV_HASH_PGMIN, 0, + data1, + channel); + break; + case 0xD0: // aftertouch + _hv_sendMessageToReceiverFF(hv_context, HV_HASH_TOUCHIN, 0, + data1, + channel); + break; + case 0xE0: // pitch bend + // combine 7bit lsb and msb into 32bit int + var value = (data2 << 7) | data1; + _hv_sendMessageToReceiverFF(hv_context, HV_HASH_BENDIN, 0, + value, + channel); + break; + default: + // console.error('No handler for midi message: ', message); + } + } + } + +function sendMidiOut(sendName, msg) { + switch (sendName) { + case "__hv_noteout": + var note = _hv_msg_getFloat(msg, 0); + var velocity = _hv_msg_getFloat(msg, 1); + var channel = (_hv_msg_getFloat(msg, 2) - 1) % 16; // no pd midi ports + return [ + ((velocity > 0) ? 144 : 128) | channel, + note, + velocity + ] + case "__hv_ctlout": + var value = _hv_msg_getFloat(msg, 0); + var cc = _hv_msg_getFloat(msg, 1); + var channel = _hv_msg_getFloat(msg, 2) % 16; // no pd midi ports + return [ + 176 | channel, + cc, + value + ] + case "__hv_pgmout": + var program = _hv_msg_getFloat(msg, 0); + var channel = _hv_msg_getFloat(msg, 1) % 16; // no pd midi ports + return [ + 192 | channel, + program + ] + case "__hv_touchout": + var pressure = _hv_msg_getFloat(msg, 0); + var channel = _hv_msg_getFloat(msg, 1) % 16; // no pd midi ports + return [ + 208 | channel, + pressure, + ] + case "__hv_polytouchout": + var value = _hv_msg_getFloat(msg, 0); + var note = _hv_msg_getFloat(msg, 1); + var channel = _hv_msg_getFloat(msg, 2) % 16; // no pd midi ports + return[ + 160 | channel, + note, + value + ] + case "__hv_bendout": + var value = _hv_msg_getFloat(msg, 0); + let lsb = value & 0x7F; + let msb = (value >> 7) & 0x7F; + var channel = _hv_msg_getFloat(msg, 1) % 16; // no pd midi ports + return [ + 224 | channel, + lsb, + msb + ] + case "__hv_midiout": + let firstByte = _hv_msg_getFloat(msg, 0); + return (firstByte === 192 || firstByte === 208) ? + [_hv_msg_getFloat(msg, 0), _hv_msg_getFloat(msg, 1)] : + [_hv_msg_getFloat(msg, 0), _hv_msg_getFloat(msg, 1), _hv_msg_getFloat(msg, 2)]; + default: + console.warn(`Unhandled sendName: ${sendName}`); + return []; + } +} + +/* +* MIDI Constants +*/ + +const HV_HASH_NOTEIN = 0x67E37CA3; +const HV_HASH_CTLIN = 0x41BE0f9C; +const HV_HASH_POLYTOUCHIN = 0xBC530F59; +const HV_HASH_PGMIN = 0x2E1EA03D; +const HV_HASH_TOUCHIN = 0x553925BD; +const HV_HASH_BENDIN = 0x3083F0F7; +const HV_HASH_MIDIIN = 0x149631bE; +const HV_HASH_MIDIREALTIMEIN = 0x6FFF0BCF; + +const MIDI_REALTIME = [0xF8, 0xFA, 0xFB, 0xFC, 0xFE, 0xFF]; diff --git a/hvcc/generators/c2js/template/index.html b/hvcc/generators/c2js/template/index.html index 0ba40e3..aadcfc4 100644 --- a/hvcc/generators/c2js/template/index.html +++ b/hvcc/generators/c2js/template/index.html @@ -52,7 +52,7 @@ printHook: onPrint, // optional: provide a callback handler for [s {sendName} @hv_param] messages // sendName "midiOutMessage" is reserved for MIDI output messages! - sendHook: onSentMessage, + sendHook: onSendMessage, // optional: pass an existing web audio context, otherwise a new one // will be constructed. webAudioContext: null @@ -160,7 +160,7 @@ } } - function onSentMessage(sendName, message) { + function onSendMessage(sendName, message) { switch (sendName) { {%- if parameters_out | length %} diff --git a/hvcc/generators/c2js/template/midi_utils.js b/hvcc/generators/c2js/template/midi_utils.js deleted file mode 100644 index 63e38e8..0000000 --- a/hvcc/generators/c2js/template/midi_utils.js +++ /dev/null @@ -1,150 +0,0 @@ -// midi_utils.js - -function sendMidiIn(hv_context, message) { - if (hv_context) { - var command = message[0] & 0xF0; - var channel = message[0] & 0x0F; - var data1 = message[1]; - var data2 = message[2]; - - // all events to [midiin] - for (var i = 1; i <= 2; i++) { - _hv_sendMessageToReceiverFF(hv_context, HV_HASH_MIDIIN, 0, - message[i], - channel - ); - } - - // realtime events to [midirealtimein] - if (MIDI_REALTIME.includes(message[0])) { - _hv_sendMessageToReceiverFF(hv_context, HV_HASH_MIDIREALTIMEIN, 0, - message[0] - ); - } - - switch(command) { - case 0x80: // note off - _hv_sendMessageToReceiverFFF(hv_context, HV_HASH_NOTEIN, 0, - data1, - 0, - channel); - break; - case 0x90: // note on - _hv_sendMessageToReceiverFFF(hv_context, HV_HASH_NOTEIN, 0, - data1, - data2, - channel); - break; - case 0xA0: // polyphonic aftertouch - _hv_sendMessageToReceiverFFF(hv_context, HV_HASH_POLYTOUCHIN, 0, - data2, // pressure - data1, // note - channel); - break; - case 0xB0: // control change - _hv_sendMessageToReceiverFFF(hv_context, HV_HASH_CTLIN, 0, - data2, // value - data1, // cc number - channel); - break; - case 0xC0: // program change - _hv_sendMessageToReceiverFF(hv_context, HV_HASH_PGMIN, 0, - data1, - channel); - break; - case 0xD0: // aftertouch - _hv_sendMessageToReceiverFF(hv_context, HV_HASH_TOUCHIN, 0, - data1, - channel); - break; - case 0xE0: // pitch bend - // combine 7bit lsb and msb into 32bit int - var value = (data2 << 7) | data1; - _hv_sendMessageToReceiverFF(hv_context, HV_HASH_BENDIN, 0, - value, - channel); - break; - default: - // console.error('No handler for midi message: ', message); - } - } - } - -function midiOutMsg(sendName, msg) { - switch (sendName) { - case "__hv_noteout": - var note = _hv_msg_getFloat(msg, 0); - var velocity = _hv_msg_getFloat(msg, 1); - var channel = _hv_msg_getFloat(msg, 2) % 16; // no pd midi ports - return [ - ((velocity > 0) ? 144 : 128) | channel, - note, - velocity - ] - case "__hv_ctlout": - var value = _hv_msg_getFloat(msg, 0); - var cc = _hv_msg_getFloat(msg, 1); - var channel = _hv_msg_getFloat(msg, 2) % 16; // no pd midi ports - return [ - 176 | channel, - cc, - value - ] - case "__hv_pgmout": - var program = _hv_msg_getFloat(msg, 0); - var channel = _hv_msg_getFloat(msg, 1) % 16; // no pd midi ports - return [ - 192 | channel, - program - ] - case "__hv_touchout": - var pressure = _hv_msg_getFloat(msg, 0); - var channel = _hv_msg_getFloat(msg, 1) % 16; // no pd midi ports - return [ - 208 | channel, - pressure, - ] - case "__hv_polytouchout": - var value = _hv_msg_getFloat(msg, 0); - var note = _hv_msg_getFloat(msg, 1); - var channel = _hv_msg_getFloat(msg, 2) % 16; // no pd midiports - return[ - 160 | channel, - note, - value - ] - case "__hv_bendout": - var value = _hv_msg_getFloat(msg, 0); - let lsb = value & 0x7F; - let msb = (value >> 7) & 0x7F; - var channel = _hv_msg_getFloat(msg, 2) % 16; // no pd midi ports - return [ - 224 | channel, - lsb, - msb - ] - case "__hv_midiout": - let firstByte = _hv_msg_getFloat(msg, 0); - return (firstByte === 192 || firstByte === 208) ? - [_hv_msg_getFloat(msg, 0), _hv_msg_getFloat(msg, 1)] : - [_hv_msg_getFloat(msg, 0), _hv_msg_getFloat(msg, 1), _hv_msg_getFloat(msg, 2)]; - default: - console.warn(`Unhandled sendName: ${sendName}`); - return []; - } -} - -/* - * MIDI Constants - */ - -const HV_HASH_NOTEIN = 0x67E37CA3; -const HV_HASH_CTLIN = 0x41BE0f9C; -const HV_HASH_POLYTOUCHIN = 0xBC530F59; -const HV_HASH_PGMIN = 0x2E1EA03D; -const HV_HASH_TOUCHIN = 0x553925BD; -const HV_HASH_BENDIN = 0x3083F0F7; -const HV_HASH_MIDIIN = 0x149631bE; -const HV_HASH_MIDIREALTIMEIN = 0x6FFF0BCF; - -const MIDI_REALTIME = [0xF8, 0xFA, 0xFB, 0xFC, 0xFE, 0xFF];