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
|
||||
* copyrights
|
||||
* dotjs error on undefined or JSON schema
|
||||
|
@ -7,8 +7,9 @@ import * as demo from "./{{=it.product.bundleName}}.js";
|
||||
window.demo = demo;
|
||||
</script>
|
||||
<script>
|
||||
var audioCtx;
|
||||
var audioCtx, midi;
|
||||
var module, node;
|
||||
var hasAudioInput, hasMidiInput;
|
||||
|
||||
var Player = {
|
||||
sourceBuffer: null,
|
||||
@ -68,10 +69,19 @@ window.addEventListener("load", function (e) {
|
||||
var start = document.getElementById("start");
|
||||
var starting = document.getElementById("starting");
|
||||
var main = document.getElementById("main");
|
||||
var player = document.getElementById("player");
|
||||
var file = document.getElementById("file");
|
||||
var playPause = document.getElementById("playPause");
|
||||
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
|
||||
file.value = "";
|
||||
playPause.disabled = true;
|
||||
@ -131,6 +141,8 @@ window.addEventListener("load", function (e) {
|
||||
audioCtx = new AudioContext();
|
||||
if (!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");
|
||||
node = new demo.Node(module);
|
||||
node.connect(audioCtx.destination);
|
||||
@ -150,6 +162,52 @@ window.addEventListener("load", function (e) {
|
||||
document.getElementById("p" + e.data.index).value = unmap(e.data.index, 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;
|
||||
start.hidden = true;
|
||||
@ -187,12 +245,13 @@ window.addEventListener("load", function (e) {
|
||||
<input id="start" type="button" value="Start">
|
||||
<p id="starting" hidden>Starting...</p>
|
||||
<div id="main" hidden>
|
||||
<h2>Player</h2>
|
||||
<label for="file">Choose a file:</label>
|
||||
<input type="file" id="file" name="file" accept="audio/*">
|
||||
<br>
|
||||
<div id="player" hidden>
|
||||
<h2>Player</h2>
|
||||
<label for="file">Choose a file:</label>
|
||||
<input type="file" id="file" name="file" accept="audio/*">
|
||||
</div>
|
||||
<button id="playPause" disabled>Play</button>
|
||||
<h2>Effect</h2>
|
||||
<h2>Controls</h2>
|
||||
<div id="controls">
|
||||
</div>
|
||||
</div>
|
||||
|
@ -18,6 +18,16 @@ LDFLAGS = \
|
||||
-Wl,-z,stack-size=$$((8*1024*1024)) \
|
||||
-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
|
||||
|
||||
default: all
|
||||
|
@ -1,3 +1,4 @@
|
||||
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 : ""}}
|
||||
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]);
|
||||
#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.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) {
|
||||
|
@ -15,9 +15,11 @@ typedef struct plugin {
|
||||
} plugin;
|
||||
|
||||
static void plugin_init(plugin *instance) {
|
||||
(void)instance;
|
||||
}
|
||||
|
||||
static void plugin_fini(plugin *instance) {
|
||||
(void)instance;
|
||||
}
|
||||
|
||||
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) {
|
||||
(void)index;
|
||||
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) {
|
||||
(void)index;
|
||||
(void)velocity;
|
||||
//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);
|
||||
}
|
||||
|
||||
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) {
|
||||
(void)instance;
|
||||
(void)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) {
|
||||
(void)instance;
|
||||
(void)index;
|
||||
(void)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