web midi
This commit is contained in:
parent
b79381e85f
commit
a001c8c9a2
2
TODO
2
TODO
@ -1,3 +1,5 @@
|
|||||||
|
* mod wheel
|
||||||
|
|
||||||
* recursive object merge in tibia
|
* recursive object merge in tibia
|
||||||
* copyrights
|
* copyrights
|
||||||
* dotjs error on undefined or JSON schema
|
* dotjs error on undefined or JSON schema
|
||||||
|
@ -7,8 +7,9 @@ import * as demo from "./{{=it.product.bundleName}}.js";
|
|||||||
window.demo = demo;
|
window.demo = demo;
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
var audioCtx;
|
var audioCtx, midi;
|
||||||
var module, node;
|
var module, node;
|
||||||
|
var hasAudioInput, hasMidiInput;
|
||||||
|
|
||||||
var Player = {
|
var Player = {
|
||||||
sourceBuffer: null,
|
sourceBuffer: null,
|
||||||
@ -68,10 +69,19 @@ window.addEventListener("load", function (e) {
|
|||||||
var start = document.getElementById("start");
|
var start = document.getElementById("start");
|
||||||
var starting = document.getElementById("starting");
|
var starting = document.getElementById("starting");
|
||||||
var main = document.getElementById("main");
|
var main = document.getElementById("main");
|
||||||
|
var player = document.getElementById("player");
|
||||||
var file = document.getElementById("file");
|
var file = document.getElementById("file");
|
||||||
var playPause = document.getElementById("playPause");
|
var playPause = document.getElementById("playPause");
|
||||||
var controls = document.getElementById("controls");
|
var controls = document.getElementById("controls");
|
||||||
|
|
||||||
|
hasAudioInput = demo.Module.data.product.buses.filter(x => x.type == "audio" && x.direction == "input").length > 0;
|
||||||
|
hasMidiInput = demo.Module.data.product.buses.filter(x => x.type == "midi" && x.direction == "input").length > 0;
|
||||||
|
|
||||||
|
if (hasMidiInput && !navigator.requestMIDIAccess)
|
||||||
|
alert("Your browser doesn't support the Web MIDI API");
|
||||||
|
|
||||||
|
player.hidden = !hasAudioInput;
|
||||||
|
|
||||||
// reset on refresh
|
// reset on refresh
|
||||||
file.value = "";
|
file.value = "";
|
||||||
playPause.disabled = true;
|
playPause.disabled = true;
|
||||||
@ -131,6 +141,8 @@ window.addEventListener("load", function (e) {
|
|||||||
audioCtx = new AudioContext();
|
audioCtx = new AudioContext();
|
||||||
if (!module)
|
if (!module)
|
||||||
module = new demo.Module();
|
module = new demo.Module();
|
||||||
|
if (!midi && hasMidiInput)
|
||||||
|
midi = await navigator.requestMIDIAccess();
|
||||||
await module.init(audioCtx, "{{=it.product.bundleName}}_processor.js", "{{=it.product.bundleName}}.wasm");
|
await module.init(audioCtx, "{{=it.product.bundleName}}_processor.js", "{{=it.product.bundleName}}.wasm");
|
||||||
node = new demo.Node(module);
|
node = new demo.Node(module);
|
||||||
node.connect(audioCtx.destination);
|
node.connect(audioCtx.destination);
|
||||||
@ -151,6 +163,52 @@ window.addEventListener("load", function (e) {
|
|||||||
document.getElementById("v" + e.data.index).innerText = e.data.value;
|
document.getElementById("v" + e.data.index).innerText = e.data.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (midi) {
|
||||||
|
function forwardMIDIMessage(msg) {
|
||||||
|
for (var i = 0; i < demo.Module.data.product.buses.length; i++) {
|
||||||
|
var b = demo.Module.data.product.buses[i];
|
||||||
|
if (b.type != "midi" || b.direction != "input")
|
||||||
|
continue;
|
||||||
|
var m = {};
|
||||||
|
for (p in msg)
|
||||||
|
m[p] = msg[p];
|
||||||
|
m.index = i;
|
||||||
|
node.port.postMessage(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMIDIMessage(e) {
|
||||||
|
switch (e.data[0] & 0xf0) {
|
||||||
|
case 0x90:
|
||||||
|
if (e.data[2] != 0)
|
||||||
|
forwardMIDIMessage({ type: "noteOn", note: e.data[1], velocity: e.data[2] / 127 });
|
||||||
|
else
|
||||||
|
forwardMIDIMessage({ type: "noteOff", note: e.data[1], velocity: 64 / 127 });
|
||||||
|
break;
|
||||||
|
case 0x80:
|
||||||
|
forwardMIDIMessage({ type: "noteOff", note: e.data[1], velocity: e.data[2] / 127 });
|
||||||
|
break;
|
||||||
|
case 0xb0:
|
||||||
|
switch(e.data[1]) {
|
||||||
|
case 120:
|
||||||
|
forwardMIDIMessage({ type: "allSoundsOff" });
|
||||||
|
break;
|
||||||
|
case 123:
|
||||||
|
forwardMIDIMessage({ type: "allNotesOff" });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0xd0:
|
||||||
|
forwardMIDIMessage({ type: "channelPressure", value: e.data[1] / 127 });
|
||||||
|
break;
|
||||||
|
case 0xe0:
|
||||||
|
forwardMIDIMessage({ type: "pitchBendChange", value: (e.data[2] << 7 | e.data[1]) / 8192 - 1 });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
midi.inputs.forEach(x => { x.onmidimessage = onMIDIMessage; });
|
||||||
|
}
|
||||||
|
|
||||||
initState = 2;
|
initState = 2;
|
||||||
start.hidden = true;
|
start.hidden = true;
|
||||||
starting.hidden = true;
|
starting.hidden = true;
|
||||||
@ -187,12 +245,13 @@ window.addEventListener("load", function (e) {
|
|||||||
<input id="start" type="button" value="Start">
|
<input id="start" type="button" value="Start">
|
||||||
<p id="starting" hidden>Starting...</p>
|
<p id="starting" hidden>Starting...</p>
|
||||||
<div id="main" hidden>
|
<div id="main" hidden>
|
||||||
|
<div id="player" hidden>
|
||||||
<h2>Player</h2>
|
<h2>Player</h2>
|
||||||
<label for="file">Choose a file:</label>
|
<label for="file">Choose a file:</label>
|
||||||
<input type="file" id="file" name="file" accept="audio/*">
|
<input type="file" id="file" name="file" accept="audio/*">
|
||||||
<br>
|
</div>
|
||||||
<button id="playPause" disabled>Play</button>
|
<button id="playPause" disabled>Play</button>
|
||||||
<h2>Effect</h2>
|
<h2>Controls</h2>
|
||||||
<div id="controls">
|
<div id="controls">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -18,6 +18,16 @@ LDFLAGS = \
|
|||||||
-Wl,-z,stack-size=$$((8*1024*1024)) \
|
-Wl,-z,stack-size=$$((8*1024*1024)) \
|
||||||
-nostdlib
|
-nostdlib
|
||||||
|
|
||||||
|
ifeq (${HAS_MIDI_IN}, yes)
|
||||||
|
LDFLAGS += \
|
||||||
|
-Wl,--export=processor_note_on \
|
||||||
|
-Wl,--export=processor_note_off \
|
||||||
|
-Wl,--export=processor_all_sounds_off \
|
||||||
|
-Wl,--export=processor_all_notes_off \
|
||||||
|
-Wl,--export=processor_channel_pressure \
|
||||||
|
-Wl,--export=processor_pitch_bend_change
|
||||||
|
endif
|
||||||
|
|
||||||
ALL = build/${BUNDLE_NAME}.wasm build/${BUNDLE_NAME}_processor.js build/${BUNDLE_NAME}.js
|
ALL = build/${BUNDLE_NAME}.wasm build/${BUNDLE_NAME}_processor.js build/${BUNDLE_NAME}.js
|
||||||
|
|
||||||
default: all
|
default: all
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
BUNDLE_NAME := {{=it.product.bundleName}}
|
BUNDLE_NAME := {{=it.product.bundleName}}
|
||||||
CFLAGS_EXTRA := {{=it.make && it.make.cflags ? it.make.cflags : ""}} {{=it.web_make && it.web_make.cflags ? it.web_make.cflags : ""}}
|
CFLAGS_EXTRA := {{=it.make && it.make.cflags ? it.make.cflags : ""}} {{=it.web_make && it.web_make.cflags ? it.web_make.cflags : ""}}
|
||||||
LIBS_EXTRA := {{=it.make && it.make.libs ? it.make.libs : ""}} {{=it.web_make && it.web_make.libs ? it.web_make.libs : ""}}
|
LIBS_EXTRA := {{=it.make && it.make.libs ? it.make.libs : ""}} {{=it.web_make && it.web_make.libs ? it.web_make.libs : ""}}
|
||||||
|
HAS_MIDI_IN := {{=it.product.buses.filter(x => x.type == "midi" && x.direction == "input").length > 0 ? "yes" : "no"}}
|
||||||
|
@ -123,3 +123,29 @@ void processor_process(instance *i, int32_t n_samples) {
|
|||||||
i->out_params[j] = plugin_get_parameter(&i->p, param_out_index[j]);
|
i->out_params[j] = plugin_get_parameter(&i->p, param_out_index[j]);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DATA_PRODUCT_MIDI_INPUTS_N > 0
|
||||||
|
void processor_note_on(instance *i, int32_t index, int32_t note, float velocity) {
|
||||||
|
plugin_note_on(&i->p, index, note, velocity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void processor_note_off(instance *i, int32_t index, int32_t note, float velocity) {
|
||||||
|
plugin_note_off(&i->p, index, note, velocity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void processor_all_sounds_off(instance *i, int32_t index) {
|
||||||
|
plugin_all_sounds_off(&i->p, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void processor_all_notes_off(instance *i, int32_t index) {
|
||||||
|
plugin_all_notes_off(&i->p, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void processor_channel_pressure(instance *i, int32_t index, float value) {
|
||||||
|
plugin_channel_pressure(&i->p, index, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void processor_pitch_bend_change(instance *i, int32_t index, float value) {
|
||||||
|
plugin_pitch_bend_change(&i->p, index, value);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
@ -52,6 +52,30 @@ class Processor extends AudioWorkletProcessor {
|
|||||||
this.parametersOutValues = new Float32Array(this.module.memory.buffer, this.module.processor_get_out_params(this.instance), this.parametersOut.length);
|
this.parametersOutValues = new Float32Array(this.module.memory.buffer, this.module.processor_get_out_params(this.instance), this.parametersOut.length);
|
||||||
|
|
||||||
this.paramOutChangeMsg = { type: "paramOutChange", index: NaN, value: NaN };
|
this.paramOutChangeMsg = { type: "paramOutChange", index: NaN, value: NaN };
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
this.port.onmessage = function (e) {
|
||||||
|
switch (e.data.type) {
|
||||||
|
case "noteOn":
|
||||||
|
self.module.processor_note_on(self.instance, e.data.index, e.data.note, e.data.velocity);
|
||||||
|
break;
|
||||||
|
case "noteOff":
|
||||||
|
self.module.processor_note_off(self.instance, e.data.index, e.data.note, e.data.velocity);
|
||||||
|
break;
|
||||||
|
case "allSoundsOff":
|
||||||
|
self.module.processor_all_sounds_off(self.instance, e.data.index);
|
||||||
|
break;
|
||||||
|
case "allNotesOff":
|
||||||
|
self.module.processor_all_notes_off(self.instance, e.data.index);
|
||||||
|
break;
|
||||||
|
case "channelPressure":
|
||||||
|
self.module.processor_channel_pressure(self.instance, e.data.index, e.data.value);
|
||||||
|
break;
|
||||||
|
case "pitchBendChange":
|
||||||
|
self.module.processor_pitch_bend_change(self.instance, e.data.index, e.data.value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
process(inputs, outputs, params) {
|
process(inputs, outputs, params) {
|
||||||
|
@ -15,9 +15,11 @@ typedef struct plugin {
|
|||||||
} plugin;
|
} plugin;
|
||||||
|
|
||||||
static void plugin_init(plugin *instance) {
|
static void plugin_init(plugin *instance) {
|
||||||
|
(void)instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void plugin_fini(plugin *instance) {
|
static void plugin_fini(plugin *instance) {
|
||||||
|
(void)instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void plugin_set_sample_rate(plugin *instance, float sample_rate) {
|
static void plugin_set_sample_rate(plugin *instance, float sample_rate) {
|
||||||
@ -62,6 +64,7 @@ static void plugin_set_parameter(plugin *instance, size_t index, float value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static float plugin_get_parameter(plugin *instance, size_t index) {
|
static float plugin_get_parameter(plugin *instance, size_t index) {
|
||||||
|
(void)index;
|
||||||
return instance->yz1;
|
return instance->yz1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,21 +90,37 @@ static void plugin_process(plugin *instance, const float **inputs, float **outpu
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void plugin_note_on(plugin *instance, size_t index, uint8_t note, float velocity) {
|
static void plugin_note_on(plugin *instance, size_t index, uint8_t note, float velocity) {
|
||||||
|
(void)index;
|
||||||
|
(void)velocity;
|
||||||
//approx instance->cutoff_k = powf(2.f, (1.f / 12.f) * (note - 60));
|
//approx instance->cutoff_k = powf(2.f, (1.f / 12.f) * (note - 60));
|
||||||
instance->cutoff_k = note < 64 ? (-0.19558034980097166f * note - 2.361735109225749f) / (note - 75.57552349522389f) : (393.95397927344214f - 7.660826245588588f * note) / (note - 139.0755234952239f);
|
instance->cutoff_k = note < 64 ? (-0.19558034980097166f * note - 2.361735109225749f) / (note - 75.57552349522389f) : (393.95397927344214f - 7.660826245588588f * note) / (note - 139.0755234952239f);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void plugin_note_off(plugin *instance, size_t index, uint8_t note, float velocity) {
|
static void plugin_note_off(plugin *instance, size_t index, uint8_t note, float velocity) {
|
||||||
|
(void)instance;
|
||||||
|
(void)index;
|
||||||
|
(void)note;
|
||||||
|
(void)velocity;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void plugin_all_sounds_off(plugin *instance, size_t index) {
|
static void plugin_all_sounds_off(plugin *instance, size_t index) {
|
||||||
|
(void)instance;
|
||||||
|
(void)index;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void plugin_all_notes_off(plugin *instance, size_t index) {
|
static void plugin_all_notes_off(plugin *instance, size_t index) {
|
||||||
|
(void)instance;
|
||||||
|
(void)index;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void plugin_channel_pressure(plugin *instance, size_t index, float value) {
|
static void plugin_channel_pressure(plugin *instance, size_t index, float value) {
|
||||||
|
(void)instance;
|
||||||
|
(void)index;
|
||||||
|
(void)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void plugin_pitch_bend_change(plugin *instance, size_t index, float value) {
|
static void plugin_pitch_bend_change(plugin *instance, size_t index, float value) {
|
||||||
|
(void)instance;
|
||||||
|
(void)index;
|
||||||
|
(void)value;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user