This commit is contained in:
Stefano D'Angelo 2024-01-19 17:28:00 +01:00
parent b79381e85f
commit a001c8c9a2
7 changed files with 147 additions and 6 deletions

2
TODO
View File

@ -1,3 +1,5 @@
* mod wheel
* recursive object merge in tibia
* copyrights
* dotjs error on undefined or JSON schema

View File

@ -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>

View File

@ -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

View File

@ -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"}}

View File

@ -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

View File

@ -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) {

View File

@ -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;
}