rationalize web make
This commit is contained in:
parent
d72f1beca7
commit
483bad7196
1
TODO
1
TODO
@ -31,6 +31,5 @@ build system:
|
||||
* make autodependencies (.d?)
|
||||
* make from... directories
|
||||
* order-only prerequisites to avoid multiple make updates? (https://interrupt.memfault.com/blog/gnu-make-guidelines#order-only-prerequisites)
|
||||
* clang + Ofast & bulk memory (maybe using old binaryen) - or implement memset etc.
|
||||
* put common parts of Makefiles together somewhere/somehow (DRY)
|
||||
* recursive make?
|
||||
|
28
examples/common/web/memset.c
Normal file
28
examples/common/web/memset.c
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Brickworks
|
||||
*
|
||||
* Copyright (C) 2021, 2022 Orastron Srl unipersonale
|
||||
*
|
||||
* Brickworks is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3 of the License.
|
||||
*
|
||||
* Brickworks is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Brickworks. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* File author: Stefano D'Angelo
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
void *memset(void *ptr, int value, size_t num) {
|
||||
unsigned char *p = (unsigned char *)ptr;
|
||||
for (size_t i = 0; i < num; i++)
|
||||
p[i] = (unsigned char)value;
|
||||
return ptr;
|
||||
}
|
71
examples/common/web/web.mk
Normal file
71
examples/common/web/web.mk
Normal file
@ -0,0 +1,71 @@
|
||||
CC := clang
|
||||
CFLAGS := \
|
||||
-D__WASM__ \
|
||||
-I${ROOT_DIR}/../src \
|
||||
-I${ROOT_DIR}/../../common/web \
|
||||
-I${ROOT_DIR}/../../../include \
|
||||
--target=wasm32 \
|
||||
-flto \
|
||||
-fvisibility=hidden \
|
||||
-Ofast
|
||||
LDFLAGS := \
|
||||
-Wl,--allow-undefined \
|
||||
-Wl,--no-entry \
|
||||
-Wl,--lto-O3 \
|
||||
-Wl,-strip-all \
|
||||
-Wl,--export-table \
|
||||
-Wl,--export=wrapper_new \
|
||||
-Wl,--export=wrapper_free \
|
||||
-Wl,--export=wrapper_get_ins \
|
||||
-Wl,--export=wrapper_get_outs \
|
||||
-Wl,--export=wrapper_get_param_values \
|
||||
-Wl,--export=wrapper_process \
|
||||
-Wl,--export=wrapper_set_parameter \
|
||||
-Wl,-z,stack-size=$$((8*1024*1024)) \
|
||||
-nostdlib
|
||||
ifdef SYNTH
|
||||
LDFLAGS += \
|
||||
-Wl,--export=wrapper_note_on \
|
||||
-Wl,--export=wrapper_note_off
|
||||
|
||||
INDEX := ${ROOT_DIR}/../../common/web/index-synth.html
|
||||
else
|
||||
INDEX := ${ROOT_DIR}/../../common/web/index-fx.html
|
||||
endif
|
||||
|
||||
SOURCES_COMMON := \
|
||||
${ROOT_DIR}/../../common/web/walloc.c \
|
||||
${ROOT_DIR}/../../common/web/wrapper.c
|
||||
|
||||
ifdef NEEDS_MEMSET
|
||||
SOURCES_COMMON += \
|
||||
${ROOT_DIR}/../../common/web/memset.c
|
||||
|
||||
LDFLAGS += \
|
||||
-Wl,--export=memset
|
||||
endif
|
||||
|
||||
all: build/web/module.wasm build/web/index.html build/web/config.js build/web/processor.js build/web/cert.pem build/web/key.pem
|
||||
|
||||
build/web/module.wasm: build/web ${SOURCES}
|
||||
${CC} ${SOURCES} ${CFLAGS} ${LDFLAGS} -o $@
|
||||
|
||||
build/web/index.html: build/web ${INDEX}
|
||||
cp ${INDEX} $@
|
||||
|
||||
build/web/processor.js: build/web ${ROOT_DIR}/config.js ${ROOT_DIR}/../../common/web/processor.js
|
||||
cat ${ROOT_DIR}/config.js ${ROOT_DIR}/../../common/web//processor.js > $@
|
||||
|
||||
build/web/config.js: build/web ${ROOT_DIR}/config.js
|
||||
cp ${ROOT_DIR}/config.js $@
|
||||
|
||||
build/web/key.pem: build/web/cert.pem
|
||||
|
||||
build/web/cert.pem: build/web
|
||||
yes "" | openssl req -x509 -newkey rsa:2048 -keyout build/web/key.pem -out build/web/cert.pem -days 365 -nodes 2>/dev/null
|
||||
|
||||
build/web:
|
||||
mkdir -p build/web
|
||||
|
||||
clean:
|
||||
rm -fr build/web
|
@ -1,56 +1,4 @@
|
||||
CC=clang
|
||||
CFLAGS= \
|
||||
-D__WASM__ \
|
||||
-I${ROOT_DIR}/../src \
|
||||
-I${ROOT_DIR}/../../common/web \
|
||||
-I${ROOT_DIR}/../../../include \
|
||||
--target=wasm32 \
|
||||
-flto \
|
||||
-fvisibility=hidden \
|
||||
-Ofast
|
||||
LDFLAGS= \
|
||||
-Wl,--allow-undefined \
|
||||
-Wl,--no-entry \
|
||||
-Wl,--lto-O3 \
|
||||
-Wl,-strip-all \
|
||||
-Wl,--export-table \
|
||||
-Wl,--export=wrapper_new \
|
||||
-Wl,--export=wrapper_free \
|
||||
-Wl,--export=wrapper_get_ins \
|
||||
-Wl,--export=wrapper_get_outs \
|
||||
-Wl,--export=wrapper_get_param_values \
|
||||
-Wl,--export=wrapper_process \
|
||||
-Wl,--export=wrapper_set_parameter \
|
||||
-Wl,-z,stack-size=$$((8*1024*1024)) \
|
||||
-nostdlib
|
||||
ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_fx_bitcrush.c
|
||||
|
||||
ROOT_DIR=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
SOURCES= \
|
||||
${ROOT_DIR}/../src/bw_example_fx_bitcrush.c \
|
||||
${ROOT_DIR}/../../common/web/walloc.c \
|
||||
${ROOT_DIR}/../../common/web/wrapper.c
|
||||
|
||||
all: build/web/module.wasm build/web/index.html build/web/config.js build/web/processor.js build/web/cert.pem build/web/key.pem
|
||||
|
||||
build/web/module.wasm: build/web ${SOURCES}
|
||||
${CC} ${SOURCES} ${CFLAGS} ${LDFLAGS} -o $@
|
||||
|
||||
build/web/index.html: build/web ${ROOT_DIR}/index.html
|
||||
cp ${ROOT_DIR}/index.html $@
|
||||
|
||||
build/web/processor.js: build/web ${ROOT_DIR}/config.js ${ROOT_DIR}/../../common/web/processor.js
|
||||
cat ${ROOT_DIR}/config.js ${ROOT_DIR}/../../common/web//processor.js > $@
|
||||
|
||||
build/web/config.js: build/web ${ROOT_DIR}/config.js
|
||||
cp ${ROOT_DIR}/config.js $@
|
||||
|
||||
build/web/key.pem: build/web/cert.pem
|
||||
|
||||
build/web/cert.pem: build/web
|
||||
yes "" | openssl req -x509 -newkey rsa:2048 -keyout build/web/key.pem -out build/web/cert.pem -days 365 -nodes 2>/dev/null
|
||||
|
||||
build/web:
|
||||
mkdir -p build/web
|
||||
|
||||
clean:
|
||||
rm -fr build/web
|
||||
include ${ROOT_DIR}/../../common/web/web.mk
|
||||
|
@ -1,56 +1,4 @@
|
||||
CC=clang
|
||||
CFLAGS= \
|
||||
-D__WASM__ \
|
||||
-I${ROOT_DIR}/../src \
|
||||
-I${ROOT_DIR}/../../common/web \
|
||||
-I${ROOT_DIR}/../../../include \
|
||||
--target=wasm32 \
|
||||
-flto \
|
||||
-fvisibility=hidden \
|
||||
-Ofast
|
||||
LDFLAGS= \
|
||||
-Wl,--allow-undefined \
|
||||
-Wl,--no-entry \
|
||||
-Wl,--lto-O3 \
|
||||
-Wl,-strip-all \
|
||||
-Wl,--export-table \
|
||||
-Wl,--export=wrapper_new \
|
||||
-Wl,--export=wrapper_free \
|
||||
-Wl,--export=wrapper_get_ins \
|
||||
-Wl,--export=wrapper_get_outs \
|
||||
-Wl,--export=wrapper_get_param_values \
|
||||
-Wl,--export=wrapper_process \
|
||||
-Wl,--export=wrapper_set_parameter \
|
||||
-Wl,-z,stack-size=$$((8*1024*1024)) \
|
||||
-nostdlib
|
||||
ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_fx_comp.c
|
||||
|
||||
ROOT_DIR=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
SOURCES= \
|
||||
${ROOT_DIR}/../src/bw_example_fx_comp.c \
|
||||
${ROOT_DIR}/../../common/web/walloc.c \
|
||||
${ROOT_DIR}/../../common/web/wrapper.c
|
||||
|
||||
all: build/web/module.wasm build/web/index.html build/web/config.js build/web/processor.js build/web/cert.pem build/web/key.pem
|
||||
|
||||
build/web/module.wasm: build/web ${SOURCES}
|
||||
${CC} ${SOURCES} ${CFLAGS} ${LDFLAGS} -o $@
|
||||
|
||||
build/web/index.html: build/web ${ROOT_DIR}/index.html
|
||||
cp ${ROOT_DIR}/index.html $@
|
||||
|
||||
build/web/processor.js: build/web ${ROOT_DIR}/config.js ${ROOT_DIR}/../../common/web/processor.js
|
||||
cat ${ROOT_DIR}/config.js ${ROOT_DIR}/../../common/web//processor.js > $@
|
||||
|
||||
build/web/config.js: build/web ${ROOT_DIR}/config.js
|
||||
cp ${ROOT_DIR}/config.js $@
|
||||
|
||||
build/web/key.pem: build/web/cert.pem
|
||||
|
||||
build/web/cert.pem: build/web
|
||||
yes "" | openssl req -x509 -newkey rsa:2048 -keyout build/web/key.pem -out build/web/cert.pem -days 365 -nodes 2>/dev/null
|
||||
|
||||
build/web:
|
||||
mkdir -p build/web
|
||||
|
||||
clean:
|
||||
rm -fr build/web
|
||||
include ${ROOT_DIR}/../../common/web/web.mk
|
||||
|
@ -1,210 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
|
||||
Brickworks
|
||||
|
||||
Copyright (C) 2022 Orastron Srl unipersonale
|
||||
|
||||
Brickworks is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, version 3 of the License.
|
||||
|
||||
Brickworks is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Brickworks. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
File author: Stefano D'Angelo
|
||||
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Brickworks web effect example</title>
|
||||
<script src="config.js"></script>
|
||||
<script>
|
||||
var audioCtx;
|
||||
|
||||
var Effect = {
|
||||
node: null,
|
||||
moduleAdded: false,
|
||||
wasmBytes: null,
|
||||
|
||||
init: async function () {
|
||||
if (!this.moduleAdded) {
|
||||
await audioCtx.audioWorklet.addModule("processor.js");
|
||||
this.moduleAdded = true;
|
||||
}
|
||||
|
||||
if (!this.wasmBytes)
|
||||
this.wasmBytes = await fetch("module.wasm")
|
||||
.then((response) => response.arrayBuffer())
|
||||
.then((bytes) => bytes);
|
||||
if (!this.wasmBytes.byteLength) {
|
||||
this.wasmBytes = null;
|
||||
throw "could not fetch WebAssembly module";
|
||||
}
|
||||
|
||||
if (!this.node)
|
||||
this.node = new AudioWorkletNode(audioCtx, "BWExample",
|
||||
{
|
||||
numberOfInputs: 1,
|
||||
numberOfOutputs: 1,
|
||||
outputChannelCount: [ 1 ],
|
||||
processorOptions: {
|
||||
wasmBytes: this.wasmBytes
|
||||
}
|
||||
});
|
||||
this.node.connect(audioCtx.destination);
|
||||
}
|
||||
};
|
||||
|
||||
var Player = {
|
||||
sourceBuffer: null,
|
||||
playing: false,
|
||||
started: false,
|
||||
|
||||
load: function (buffer, successCb, errorCb) {
|
||||
let t = this;
|
||||
audioCtx.decodeAudioData(buffer,
|
||||
function (data) {
|
||||
if (t.started)
|
||||
t.sourceBuffer.stop();
|
||||
if (t.playing)
|
||||
t.sourceBuffer.disconnect();
|
||||
t.sourceBuffer = audioCtx.createBufferSource();
|
||||
t.sourceBuffer.buffer = data;
|
||||
t.sourceBuffer.loop = true;
|
||||
if (t.started)
|
||||
t.sourceBuffer.start();
|
||||
if (t.playing) {
|
||||
t.started = true;
|
||||
t.sourceBuffer.connect(Effect.node);
|
||||
}
|
||||
successCb();
|
||||
},
|
||||
function () { errorCb(); });
|
||||
},
|
||||
|
||||
togglePlayPause: function () {
|
||||
if (this.playing) {
|
||||
this.sourceBuffer.disconnect();
|
||||
this.playing = false;
|
||||
} else {
|
||||
if (!this.started) {
|
||||
this.sourceBuffer.start();
|
||||
this.started = true;
|
||||
}
|
||||
this.sourceBuffer.connect(Effect.node);
|
||||
this.playing = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var initState = 0; // 0 = not inited, 1 = in progress, 2 = inited
|
||||
|
||||
window.addEventListener("load", function (e) {
|
||||
var start = document.getElementById("start");
|
||||
var starting = document.getElementById("starting");
|
||||
var main = document.getElementById("main");
|
||||
var file = document.getElementById("file");
|
||||
var playPause = document.getElementById("playPause");
|
||||
var controls = document.getElementById("controls");
|
||||
|
||||
// reset on refresh
|
||||
file.value = "";
|
||||
playPause.disabled = true;
|
||||
|
||||
for (var i = 0; i < parameters.length; i++) {
|
||||
var div = document.createElement("div");
|
||||
controls.appendChild(div);
|
||||
|
||||
var label = document.createElement("label");
|
||||
label.setAttribute("for", "p" + i);
|
||||
label.innerText = parameters[i].name;
|
||||
div.appendChild(label);
|
||||
|
||||
var range = document.createElement("input");
|
||||
range.setAttribute("type", "range");
|
||||
range.setAttribute("id", "p" + i);
|
||||
range.setAttribute("name", "p" + i);
|
||||
range.setAttribute("min", "0");
|
||||
range.setAttribute("max", "1");
|
||||
if (!parameters[i].step)
|
||||
range.setAttribute("step", "any");
|
||||
else
|
||||
range.setAttribute("step", 1 / parameters[i].step);
|
||||
range.value = parameters[i].defaultValue;
|
||||
if (parameters[i].output)
|
||||
range.setAttribute("readonly", "true");
|
||||
else {
|
||||
let index = i;
|
||||
range.addEventListener("input", function (e) {
|
||||
var p = Effect.node.parameters.get(parameters[index].name);
|
||||
p.setValueAtTime(e.target.value, 0);
|
||||
});
|
||||
}
|
||||
div.appendChild(range);
|
||||
}
|
||||
|
||||
start.addEventListener("click", async function () {
|
||||
initState = 1;
|
||||
start.disabled = true;
|
||||
starting.hidden = false;
|
||||
|
||||
try {
|
||||
if (!audioCtx)
|
||||
audioCtx = new AudioContext();
|
||||
await Effect.init();
|
||||
|
||||
Effect.node.port.onmessage = function (e) { document.getElementById("p" + e.data.index).value = e.data.value; };
|
||||
|
||||
initState = 2;
|
||||
start.hidden = true;
|
||||
starting.hidden = true;
|
||||
main.hidden = false;
|
||||
} catch (err) {
|
||||
alert("Colud not initialize: " + err);
|
||||
|
||||
initState = 0;
|
||||
start.disabled = false;
|
||||
starting.hidden = true;
|
||||
}
|
||||
});
|
||||
|
||||
file.addEventListener("change", function () {
|
||||
var fileReader = new FileReader();
|
||||
fileReader.readAsArrayBuffer(this.files[0]);
|
||||
fileReader.onload = function (e) {
|
||||
Player.load(e.target.result,
|
||||
function () { playPause.disabled = false; },
|
||||
function () { alert("Could not decode the chosen file"); });
|
||||
};
|
||||
fileReader.onerror = function (e) { alert("Could not read file"); };
|
||||
});
|
||||
|
||||
playPause.addEventListener("click", function () {
|
||||
Player.togglePlayPause();
|
||||
playPause.innerText = Player.playing ? "Pause" : "Play";
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Brickworks web effect example</h1>
|
||||
<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>
|
||||
<button id="playPause" disabled>Play</button>
|
||||
<h2>Effect</h2>
|
||||
<div id="controls">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -1,56 +1,4 @@
|
||||
CC=clang
|
||||
CFLAGS= \
|
||||
-D__WASM__ \
|
||||
-I${ROOT_DIR}/../src \
|
||||
-I${ROOT_DIR}/../../common/web \
|
||||
-I${ROOT_DIR}/../../../include \
|
||||
--target=wasm32 \
|
||||
-flto \
|
||||
-fvisibility=hidden \
|
||||
-Ofast
|
||||
LDFLAGS= \
|
||||
-Wl,--allow-undefined \
|
||||
-Wl,--no-entry \
|
||||
-Wl,--lto-O3 \
|
||||
-Wl,-strip-all \
|
||||
-Wl,--export-table \
|
||||
-Wl,--export=wrapper_new \
|
||||
-Wl,--export=wrapper_free \
|
||||
-Wl,--export=wrapper_get_ins \
|
||||
-Wl,--export=wrapper_get_outs \
|
||||
-Wl,--export=wrapper_get_param_values \
|
||||
-Wl,--export=wrapper_process \
|
||||
-Wl,--export=wrapper_set_parameter \
|
||||
-Wl,-z,stack-size=$$((8*1024*1024)) \
|
||||
-nostdlib
|
||||
ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_fx_mm1.c
|
||||
|
||||
ROOT_DIR=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
SOURCES= \
|
||||
${ROOT_DIR}/../src/bw_example_fx_mm1.c \
|
||||
${ROOT_DIR}/../../common/web/walloc.c \
|
||||
${ROOT_DIR}/../../common/web/wrapper.c
|
||||
|
||||
all: build/web/module.wasm build/web/index.html build/web/config.js build/web/processor.js build/web/cert.pem build/web/key.pem
|
||||
|
||||
build/web/module.wasm: build/web ${SOURCES}
|
||||
${CC} ${SOURCES} ${CFLAGS} ${LDFLAGS} -o $@
|
||||
|
||||
build/web/index.html: build/web ${ROOT_DIR}/index.html
|
||||
cp ${ROOT_DIR}/index.html $@
|
||||
|
||||
build/web/processor.js: build/web ${ROOT_DIR}/config.js ${ROOT_DIR}/../../common/web/processor.js
|
||||
cat ${ROOT_DIR}/config.js ${ROOT_DIR}/../../common/web//processor.js > $@
|
||||
|
||||
build/web/config.js: build/web ${ROOT_DIR}/config.js
|
||||
cp ${ROOT_DIR}/config.js $@
|
||||
|
||||
build/web/key.pem: build/web/cert.pem
|
||||
|
||||
build/web/cert.pem: build/web
|
||||
yes "" | openssl req -x509 -newkey rsa:2048 -keyout build/web/key.pem -out build/web/cert.pem -days 365 -nodes 2>/dev/null
|
||||
|
||||
build/web:
|
||||
mkdir -p build/web
|
||||
|
||||
clean:
|
||||
rm -fr build/web
|
||||
include ${ROOT_DIR}/../../common/web/web.mk
|
||||
|
@ -1,210 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
|
||||
Brickworks
|
||||
|
||||
Copyright (C) 2022 Orastron Srl unipersonale
|
||||
|
||||
Brickworks is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, version 3 of the License.
|
||||
|
||||
Brickworks is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Brickworks. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
File author: Stefano D'Angelo
|
||||
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Brickworks web effect example</title>
|
||||
<script src="config.js"></script>
|
||||
<script>
|
||||
var audioCtx;
|
||||
|
||||
var Effect = {
|
||||
node: null,
|
||||
moduleAdded: false,
|
||||
wasmBytes: null,
|
||||
|
||||
init: async function () {
|
||||
if (!this.moduleAdded) {
|
||||
await audioCtx.audioWorklet.addModule("processor.js");
|
||||
this.moduleAdded = true;
|
||||
}
|
||||
|
||||
if (!this.wasmBytes)
|
||||
this.wasmBytes = await fetch("module.wasm")
|
||||
.then((response) => response.arrayBuffer())
|
||||
.then((bytes) => bytes);
|
||||
if (!this.wasmBytes.byteLength) {
|
||||
this.wasmBytes = null;
|
||||
throw "could not fetch WebAssembly module";
|
||||
}
|
||||
|
||||
if (!this.node)
|
||||
this.node = new AudioWorkletNode(audioCtx, "BWExample",
|
||||
{
|
||||
numberOfInputs: 1,
|
||||
numberOfOutputs: 1,
|
||||
outputChannelCount: [ 1 ],
|
||||
processorOptions: {
|
||||
wasmBytes: this.wasmBytes
|
||||
}
|
||||
});
|
||||
this.node.connect(audioCtx.destination);
|
||||
}
|
||||
};
|
||||
|
||||
var Player = {
|
||||
sourceBuffer: null,
|
||||
playing: false,
|
||||
started: false,
|
||||
|
||||
load: function (buffer, successCb, errorCb) {
|
||||
let t = this;
|
||||
audioCtx.decodeAudioData(buffer,
|
||||
function (data) {
|
||||
if (t.started)
|
||||
t.sourceBuffer.stop();
|
||||
if (t.playing)
|
||||
t.sourceBuffer.disconnect();
|
||||
t.sourceBuffer = audioCtx.createBufferSource();
|
||||
t.sourceBuffer.buffer = data;
|
||||
t.sourceBuffer.loop = true;
|
||||
if (t.started)
|
||||
t.sourceBuffer.start();
|
||||
if (t.playing) {
|
||||
t.started = true;
|
||||
t.sourceBuffer.connect(Effect.node);
|
||||
}
|
||||
successCb();
|
||||
},
|
||||
function () { errorCb(); });
|
||||
},
|
||||
|
||||
togglePlayPause: function () {
|
||||
if (this.playing) {
|
||||
this.sourceBuffer.disconnect();
|
||||
this.playing = false;
|
||||
} else {
|
||||
if (!this.started) {
|
||||
this.sourceBuffer.start();
|
||||
this.started = true;
|
||||
}
|
||||
this.sourceBuffer.connect(Effect.node);
|
||||
this.playing = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var initState = 0; // 0 = not inited, 1 = in progress, 2 = inited
|
||||
|
||||
window.addEventListener("load", function (e) {
|
||||
var start = document.getElementById("start");
|
||||
var starting = document.getElementById("starting");
|
||||
var main = document.getElementById("main");
|
||||
var file = document.getElementById("file");
|
||||
var playPause = document.getElementById("playPause");
|
||||
var controls = document.getElementById("controls");
|
||||
|
||||
// reset on refresh
|
||||
file.value = "";
|
||||
playPause.disabled = true;
|
||||
|
||||
for (var i = 0; i < parameters.length; i++) {
|
||||
var div = document.createElement("div");
|
||||
controls.appendChild(div);
|
||||
|
||||
var label = document.createElement("label");
|
||||
label.setAttribute("for", "p" + i);
|
||||
label.innerText = parameters[i].name;
|
||||
div.appendChild(label);
|
||||
|
||||
var range = document.createElement("input");
|
||||
range.setAttribute("type", "range");
|
||||
range.setAttribute("id", "p" + i);
|
||||
range.setAttribute("name", "p" + i);
|
||||
range.setAttribute("min", "0");
|
||||
range.setAttribute("max", "1");
|
||||
if (!parameters[i].step)
|
||||
range.setAttribute("step", "any");
|
||||
else
|
||||
range.setAttribute("step", 1 / parameters[i].step);
|
||||
range.value = parameters[i].defaultValue;
|
||||
if (parameters[i].output)
|
||||
range.setAttribute("readonly", "true");
|
||||
else {
|
||||
let index = i;
|
||||
range.addEventListener("input", function (e) {
|
||||
var p = Effect.node.parameters.get(parameters[index].name);
|
||||
p.setValueAtTime(e.target.value, 0);
|
||||
});
|
||||
}
|
||||
div.appendChild(range);
|
||||
}
|
||||
|
||||
start.addEventListener("click", async function () {
|
||||
initState = 1;
|
||||
start.disabled = true;
|
||||
starting.hidden = false;
|
||||
|
||||
try {
|
||||
if (!audioCtx)
|
||||
audioCtx = new AudioContext();
|
||||
await Effect.init();
|
||||
|
||||
Effect.node.port.onmessage = function (e) { document.getElementById("p" + e.data.index).value = e.data.value; };
|
||||
|
||||
initState = 2;
|
||||
start.hidden = true;
|
||||
starting.hidden = true;
|
||||
main.hidden = false;
|
||||
} catch (err) {
|
||||
alert("Colud not initialize: " + err);
|
||||
|
||||
initState = 0;
|
||||
start.disabled = false;
|
||||
starting.hidden = true;
|
||||
}
|
||||
});
|
||||
|
||||
file.addEventListener("change", function () {
|
||||
var fileReader = new FileReader();
|
||||
fileReader.readAsArrayBuffer(this.files[0]);
|
||||
fileReader.onload = function (e) {
|
||||
Player.load(e.target.result,
|
||||
function () { playPause.disabled = false; },
|
||||
function () { alert("Could not decode the chosen file"); });
|
||||
};
|
||||
fileReader.onerror = function (e) { alert("Could not read file"); };
|
||||
});
|
||||
|
||||
playPause.addEventListener("click", function () {
|
||||
Player.togglePlayPause();
|
||||
playPause.innerText = Player.playing ? "Pause" : "Play";
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Brickworks web effect example</h1>
|
||||
<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>
|
||||
<button id="playPause" disabled>Play</button>
|
||||
<h2>Effect</h2>
|
||||
<div id="controls">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -1,56 +1,4 @@
|
||||
CC=clang
|
||||
CFLAGS= \
|
||||
-D__WASM__ \
|
||||
-I${ROOT_DIR}/../src \
|
||||
-I${ROOT_DIR}/../../common/web \
|
||||
-I${ROOT_DIR}/../../../include \
|
||||
--target=wasm32 \
|
||||
-flto \
|
||||
-fvisibility=hidden \
|
||||
-Ofast
|
||||
LDFLAGS= \
|
||||
-Wl,--allow-undefined \
|
||||
-Wl,--no-entry \
|
||||
-Wl,--lto-O3 \
|
||||
-Wl,-strip-all \
|
||||
-Wl,--export-table \
|
||||
-Wl,--export=wrapper_new \
|
||||
-Wl,--export=wrapper_free \
|
||||
-Wl,--export=wrapper_get_ins \
|
||||
-Wl,--export=wrapper_get_outs \
|
||||
-Wl,--export=wrapper_get_param_values \
|
||||
-Wl,--export=wrapper_process \
|
||||
-Wl,--export=wrapper_set_parameter \
|
||||
-Wl,-z,stack-size=$$((8*1024*1024)) \
|
||||
-nostdlib
|
||||
ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_fx_mm2.c
|
||||
|
||||
ROOT_DIR=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
SOURCES= \
|
||||
${ROOT_DIR}/../src/bw_example_fx_mm2.c \
|
||||
${ROOT_DIR}/../../common/web/walloc.c \
|
||||
${ROOT_DIR}/../../common/web/wrapper.c
|
||||
|
||||
all: build/web/module.wasm build/web/index.html build/web/config.js build/web/processor.js build/web/cert.pem build/web/key.pem
|
||||
|
||||
build/web/module.wasm: build/web ${SOURCES}
|
||||
${CC} ${SOURCES} ${CFLAGS} ${LDFLAGS} -o $@
|
||||
|
||||
build/web/index.html: build/web ${ROOT_DIR}/index.html
|
||||
cp ${ROOT_DIR}/index.html $@
|
||||
|
||||
build/web/processor.js: build/web ${ROOT_DIR}/config.js ${ROOT_DIR}/../../common/web/processor.js
|
||||
cat ${ROOT_DIR}/config.js ${ROOT_DIR}/../../common/web//processor.js > $@
|
||||
|
||||
build/web/config.js: build/web ${ROOT_DIR}/config.js
|
||||
cp ${ROOT_DIR}/config.js $@
|
||||
|
||||
build/web/key.pem: build/web/cert.pem
|
||||
|
||||
build/web/cert.pem: build/web
|
||||
yes "" | openssl req -x509 -newkey rsa:2048 -keyout build/web/key.pem -out build/web/cert.pem -days 365 -nodes 2>/dev/null
|
||||
|
||||
build/web:
|
||||
mkdir -p build/web
|
||||
|
||||
clean:
|
||||
rm -fr build/web
|
||||
include ${ROOT_DIR}/../../common/web/web.mk
|
||||
|
@ -1,210 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
|
||||
Brickworks
|
||||
|
||||
Copyright (C) 2022 Orastron Srl unipersonale
|
||||
|
||||
Brickworks is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, version 3 of the License.
|
||||
|
||||
Brickworks is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Brickworks. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
File author: Stefano D'Angelo
|
||||
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Brickworks web effect example</title>
|
||||
<script src="config.js"></script>
|
||||
<script>
|
||||
var audioCtx;
|
||||
|
||||
var Effect = {
|
||||
node: null,
|
||||
moduleAdded: false,
|
||||
wasmBytes: null,
|
||||
|
||||
init: async function () {
|
||||
if (!this.moduleAdded) {
|
||||
await audioCtx.audioWorklet.addModule("processor.js");
|
||||
this.moduleAdded = true;
|
||||
}
|
||||
|
||||
if (!this.wasmBytes)
|
||||
this.wasmBytes = await fetch("module.wasm")
|
||||
.then((response) => response.arrayBuffer())
|
||||
.then((bytes) => bytes);
|
||||
if (!this.wasmBytes.byteLength) {
|
||||
this.wasmBytes = null;
|
||||
throw "could not fetch WebAssembly module";
|
||||
}
|
||||
|
||||
if (!this.node)
|
||||
this.node = new AudioWorkletNode(audioCtx, "BWExample",
|
||||
{
|
||||
numberOfInputs: 1,
|
||||
numberOfOutputs: 1,
|
||||
outputChannelCount: [ 1 ],
|
||||
processorOptions: {
|
||||
wasmBytes: this.wasmBytes
|
||||
}
|
||||
});
|
||||
this.node.connect(audioCtx.destination);
|
||||
}
|
||||
};
|
||||
|
||||
var Player = {
|
||||
sourceBuffer: null,
|
||||
playing: false,
|
||||
started: false,
|
||||
|
||||
load: function (buffer, successCb, errorCb) {
|
||||
let t = this;
|
||||
audioCtx.decodeAudioData(buffer,
|
||||
function (data) {
|
||||
if (t.started)
|
||||
t.sourceBuffer.stop();
|
||||
if (t.playing)
|
||||
t.sourceBuffer.disconnect();
|
||||
t.sourceBuffer = audioCtx.createBufferSource();
|
||||
t.sourceBuffer.buffer = data;
|
||||
t.sourceBuffer.loop = true;
|
||||
if (t.started)
|
||||
t.sourceBuffer.start();
|
||||
if (t.playing) {
|
||||
t.started = true;
|
||||
t.sourceBuffer.connect(Effect.node);
|
||||
}
|
||||
successCb();
|
||||
},
|
||||
function () { errorCb(); });
|
||||
},
|
||||
|
||||
togglePlayPause: function () {
|
||||
if (this.playing) {
|
||||
this.sourceBuffer.disconnect();
|
||||
this.playing = false;
|
||||
} else {
|
||||
if (!this.started) {
|
||||
this.sourceBuffer.start();
|
||||
this.started = true;
|
||||
}
|
||||
this.sourceBuffer.connect(Effect.node);
|
||||
this.playing = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var initState = 0; // 0 = not inited, 1 = in progress, 2 = inited
|
||||
|
||||
window.addEventListener("load", function (e) {
|
||||
var start = document.getElementById("start");
|
||||
var starting = document.getElementById("starting");
|
||||
var main = document.getElementById("main");
|
||||
var file = document.getElementById("file");
|
||||
var playPause = document.getElementById("playPause");
|
||||
var controls = document.getElementById("controls");
|
||||
|
||||
// reset on refresh
|
||||
file.value = "";
|
||||
playPause.disabled = true;
|
||||
|
||||
for (var i = 0; i < parameters.length; i++) {
|
||||
var div = document.createElement("div");
|
||||
controls.appendChild(div);
|
||||
|
||||
var label = document.createElement("label");
|
||||
label.setAttribute("for", "p" + i);
|
||||
label.innerText = parameters[i].name;
|
||||
div.appendChild(label);
|
||||
|
||||
var range = document.createElement("input");
|
||||
range.setAttribute("type", "range");
|
||||
range.setAttribute("id", "p" + i);
|
||||
range.setAttribute("name", "p" + i);
|
||||
range.setAttribute("min", "0");
|
||||
range.setAttribute("max", "1");
|
||||
if (!parameters[i].step)
|
||||
range.setAttribute("step", "any");
|
||||
else
|
||||
range.setAttribute("step", 1 / parameters[i].step);
|
||||
range.value = parameters[i].defaultValue;
|
||||
if (parameters[i].output)
|
||||
range.setAttribute("readonly", "true");
|
||||
else {
|
||||
let index = i;
|
||||
range.addEventListener("input", function (e) {
|
||||
var p = Effect.node.parameters.get(parameters[index].name);
|
||||
p.setValueAtTime(e.target.value, 0);
|
||||
});
|
||||
}
|
||||
div.appendChild(range);
|
||||
}
|
||||
|
||||
start.addEventListener("click", async function () {
|
||||
initState = 1;
|
||||
start.disabled = true;
|
||||
starting.hidden = false;
|
||||
|
||||
try {
|
||||
if (!audioCtx)
|
||||
audioCtx = new AudioContext();
|
||||
await Effect.init();
|
||||
|
||||
Effect.node.port.onmessage = function (e) { document.getElementById("p" + e.data.index).value = e.data.value; };
|
||||
|
||||
initState = 2;
|
||||
start.hidden = true;
|
||||
starting.hidden = true;
|
||||
main.hidden = false;
|
||||
} catch (err) {
|
||||
alert("Colud not initialize: " + err);
|
||||
|
||||
initState = 0;
|
||||
start.disabled = false;
|
||||
starting.hidden = true;
|
||||
}
|
||||
});
|
||||
|
||||
file.addEventListener("change", function () {
|
||||
var fileReader = new FileReader();
|
||||
fileReader.readAsArrayBuffer(this.files[0]);
|
||||
fileReader.onload = function (e) {
|
||||
Player.load(e.target.result,
|
||||
function () { playPause.disabled = false; },
|
||||
function () { alert("Could not decode the chosen file"); });
|
||||
};
|
||||
fileReader.onerror = function (e) { alert("Could not read file"); };
|
||||
});
|
||||
|
||||
playPause.addEventListener("click", function () {
|
||||
Player.togglePlayPause();
|
||||
playPause.innerText = Player.playing ? "Pause" : "Play";
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Brickworks web effect example</h1>
|
||||
<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>
|
||||
<button id="playPause" disabled>Play</button>
|
||||
<h2>Effect</h2>
|
||||
<div id="controls">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -1,56 +1,4 @@
|
||||
CC=clang
|
||||
CFLAGS= \
|
||||
-D__WASM__ \
|
||||
-I${ROOT_DIR}/../src \
|
||||
-I${ROOT_DIR}/../../common/web \
|
||||
-I${ROOT_DIR}/../../../include \
|
||||
--target=wasm32 \
|
||||
-flto \
|
||||
-fvisibility=hidden \
|
||||
-Ofast
|
||||
LDFLAGS= \
|
||||
-Wl,--allow-undefined \
|
||||
-Wl,--no-entry \
|
||||
-Wl,--lto-O3 \
|
||||
-Wl,-strip-all \
|
||||
-Wl,--export-table \
|
||||
-Wl,--export=wrapper_new \
|
||||
-Wl,--export=wrapper_free \
|
||||
-Wl,--export=wrapper_get_ins \
|
||||
-Wl,--export=wrapper_get_outs \
|
||||
-Wl,--export=wrapper_get_param_values \
|
||||
-Wl,--export=wrapper_process \
|
||||
-Wl,--export=wrapper_set_parameter \
|
||||
-Wl,-z,stack-size=$$((8*1024*1024)) \
|
||||
-nostdlib
|
||||
ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_fx_noise_gate.c
|
||||
|
||||
ROOT_DIR=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
SOURCES= \
|
||||
${ROOT_DIR}/../src/bw_example_fx_noise_gate.c \
|
||||
${ROOT_DIR}/../../common/web/walloc.c \
|
||||
${ROOT_DIR}/../../common/web/wrapper.c
|
||||
|
||||
all: build/web/module.wasm build/web/index.html build/web/config.js build/web/processor.js build/web/cert.pem build/web/key.pem
|
||||
|
||||
build/web/module.wasm: build/web ${SOURCES}
|
||||
${CC} ${SOURCES} ${CFLAGS} ${LDFLAGS} -o $@
|
||||
|
||||
build/web/index.html: build/web ${ROOT_DIR}/index.html
|
||||
cp ${ROOT_DIR}/index.html $@
|
||||
|
||||
build/web/processor.js: build/web ${ROOT_DIR}/config.js ${ROOT_DIR}/../../common/web/processor.js
|
||||
cat ${ROOT_DIR}/config.js ${ROOT_DIR}/../../common/web//processor.js > $@
|
||||
|
||||
build/web/config.js: build/web ${ROOT_DIR}/config.js
|
||||
cp ${ROOT_DIR}/config.js $@
|
||||
|
||||
build/web/key.pem: build/web/cert.pem
|
||||
|
||||
build/web/cert.pem: build/web
|
||||
yes "" | openssl req -x509 -newkey rsa:2048 -keyout build/web/key.pem -out build/web/cert.pem -days 365 -nodes 2>/dev/null
|
||||
|
||||
build/web:
|
||||
mkdir -p build/web
|
||||
|
||||
clean:
|
||||
rm -fr build/web
|
||||
include ${ROOT_DIR}/../../common/web/web.mk
|
||||
|
@ -1,210 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
|
||||
Brickworks
|
||||
|
||||
Copyright (C) 2022 Orastron Srl unipersonale
|
||||
|
||||
Brickworks is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, version 3 of the License.
|
||||
|
||||
Brickworks is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Brickworks. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
File author: Stefano D'Angelo
|
||||
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Brickworks web effect example</title>
|
||||
<script src="config.js"></script>
|
||||
<script>
|
||||
var audioCtx;
|
||||
|
||||
var Effect = {
|
||||
node: null,
|
||||
moduleAdded: false,
|
||||
wasmBytes: null,
|
||||
|
||||
init: async function () {
|
||||
if (!this.moduleAdded) {
|
||||
await audioCtx.audioWorklet.addModule("processor.js");
|
||||
this.moduleAdded = true;
|
||||
}
|
||||
|
||||
if (!this.wasmBytes)
|
||||
this.wasmBytes = await fetch("module.wasm")
|
||||
.then((response) => response.arrayBuffer())
|
||||
.then((bytes) => bytes);
|
||||
if (!this.wasmBytes.byteLength) {
|
||||
this.wasmBytes = null;
|
||||
throw "could not fetch WebAssembly module";
|
||||
}
|
||||
|
||||
if (!this.node)
|
||||
this.node = new AudioWorkletNode(audioCtx, "BWExample",
|
||||
{
|
||||
numberOfInputs: 1,
|
||||
numberOfOutputs: 1,
|
||||
outputChannelCount: [ 1 ],
|
||||
processorOptions: {
|
||||
wasmBytes: this.wasmBytes
|
||||
}
|
||||
});
|
||||
this.node.connect(audioCtx.destination);
|
||||
}
|
||||
};
|
||||
|
||||
var Player = {
|
||||
sourceBuffer: null,
|
||||
playing: false,
|
||||
started: false,
|
||||
|
||||
load: function (buffer, successCb, errorCb) {
|
||||
let t = this;
|
||||
audioCtx.decodeAudioData(buffer,
|
||||
function (data) {
|
||||
if (t.started)
|
||||
t.sourceBuffer.stop();
|
||||
if (t.playing)
|
||||
t.sourceBuffer.disconnect();
|
||||
t.sourceBuffer = audioCtx.createBufferSource();
|
||||
t.sourceBuffer.buffer = data;
|
||||
t.sourceBuffer.loop = true;
|
||||
if (t.started)
|
||||
t.sourceBuffer.start();
|
||||
if (t.playing) {
|
||||
t.started = true;
|
||||
t.sourceBuffer.connect(Effect.node);
|
||||
}
|
||||
successCb();
|
||||
},
|
||||
function () { errorCb(); });
|
||||
},
|
||||
|
||||
togglePlayPause: function () {
|
||||
if (this.playing) {
|
||||
this.sourceBuffer.disconnect();
|
||||
this.playing = false;
|
||||
} else {
|
||||
if (!this.started) {
|
||||
this.sourceBuffer.start();
|
||||
this.started = true;
|
||||
}
|
||||
this.sourceBuffer.connect(Effect.node);
|
||||
this.playing = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var initState = 0; // 0 = not inited, 1 = in progress, 2 = inited
|
||||
|
||||
window.addEventListener("load", function (e) {
|
||||
var start = document.getElementById("start");
|
||||
var starting = document.getElementById("starting");
|
||||
var main = document.getElementById("main");
|
||||
var file = document.getElementById("file");
|
||||
var playPause = document.getElementById("playPause");
|
||||
var controls = document.getElementById("controls");
|
||||
|
||||
// reset on refresh
|
||||
file.value = "";
|
||||
playPause.disabled = true;
|
||||
|
||||
for (var i = 0; i < parameters.length; i++) {
|
||||
var div = document.createElement("div");
|
||||
controls.appendChild(div);
|
||||
|
||||
var label = document.createElement("label");
|
||||
label.setAttribute("for", "p" + i);
|
||||
label.innerText = parameters[i].name;
|
||||
div.appendChild(label);
|
||||
|
||||
var range = document.createElement("input");
|
||||
range.setAttribute("type", "range");
|
||||
range.setAttribute("id", "p" + i);
|
||||
range.setAttribute("name", "p" + i);
|
||||
range.setAttribute("min", "0");
|
||||
range.setAttribute("max", "1");
|
||||
if (!parameters[i].step)
|
||||
range.setAttribute("step", "any");
|
||||
else
|
||||
range.setAttribute("step", 1 / parameters[i].step);
|
||||
range.value = parameters[i].defaultValue;
|
||||
if (parameters[i].output)
|
||||
range.setAttribute("readonly", "true");
|
||||
else {
|
||||
let index = i;
|
||||
range.addEventListener("input", function (e) {
|
||||
var p = Effect.node.parameters.get(parameters[index].name);
|
||||
p.setValueAtTime(e.target.value, 0);
|
||||
});
|
||||
}
|
||||
div.appendChild(range);
|
||||
}
|
||||
|
||||
start.addEventListener("click", async function () {
|
||||
initState = 1;
|
||||
start.disabled = true;
|
||||
starting.hidden = false;
|
||||
|
||||
try {
|
||||
if (!audioCtx)
|
||||
audioCtx = new AudioContext();
|
||||
await Effect.init();
|
||||
|
||||
Effect.node.port.onmessage = function (e) { document.getElementById("p" + e.data.index).value = e.data.value; };
|
||||
|
||||
initState = 2;
|
||||
start.hidden = true;
|
||||
starting.hidden = true;
|
||||
main.hidden = false;
|
||||
} catch (err) {
|
||||
alert("Colud not initialize: " + err);
|
||||
|
||||
initState = 0;
|
||||
start.disabled = false;
|
||||
starting.hidden = true;
|
||||
}
|
||||
});
|
||||
|
||||
file.addEventListener("change", function () {
|
||||
var fileReader = new FileReader();
|
||||
fileReader.readAsArrayBuffer(this.files[0]);
|
||||
fileReader.onload = function (e) {
|
||||
Player.load(e.target.result,
|
||||
function () { playPause.disabled = false; },
|
||||
function () { alert("Could not decode the chosen file"); });
|
||||
};
|
||||
fileReader.onerror = function (e) { alert("Could not read file"); };
|
||||
});
|
||||
|
||||
playPause.addEventListener("click", function () {
|
||||
Player.togglePlayPause();
|
||||
playPause.innerText = Player.playing ? "Pause" : "Play";
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Brickworks web effect example</h1>
|
||||
<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>
|
||||
<button id="playPause" disabled>Play</button>
|
||||
<h2>Effect</h2>
|
||||
<div id="controls">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -1,56 +1,4 @@
|
||||
CC=clang
|
||||
CFLAGS= \
|
||||
-D__WASM__ \
|
||||
-I${ROOT_DIR}/../src \
|
||||
-I${ROOT_DIR}/../../common/web \
|
||||
-I${ROOT_DIR}/../../../include \
|
||||
--target=wasm32 \
|
||||
-flto \
|
||||
-fvisibility=hidden \
|
||||
-Ofast
|
||||
LDFLAGS= \
|
||||
-Wl,--allow-undefined \
|
||||
-Wl,--no-entry \
|
||||
-Wl,--lto-O3 \
|
||||
-Wl,-strip-all \
|
||||
-Wl,--export-table \
|
||||
-Wl,--export=wrapper_new \
|
||||
-Wl,--export=wrapper_free \
|
||||
-Wl,--export=wrapper_get_ins \
|
||||
-Wl,--export=wrapper_get_outs \
|
||||
-Wl,--export=wrapper_get_param_values \
|
||||
-Wl,--export=wrapper_process \
|
||||
-Wl,--export=wrapper_set_parameter \
|
||||
-Wl,-z,stack-size=$$((8*1024*1024)) \
|
||||
-nostdlib
|
||||
ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_fx_satur.c
|
||||
|
||||
ROOT_DIR=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
SOURCES= \
|
||||
${ROOT_DIR}/../src/bw_example_fx_satur.c \
|
||||
${ROOT_DIR}/../../common/web/walloc.c \
|
||||
${ROOT_DIR}/../../common/web/wrapper.c
|
||||
|
||||
all: build/web/module.wasm build/web/index.html build/web/config.js build/web/processor.js build/web/cert.pem build/web/key.pem
|
||||
|
||||
build/web/module.wasm: build/web ${SOURCES}
|
||||
${CC} ${SOURCES} ${CFLAGS} ${LDFLAGS} -o $@
|
||||
|
||||
build/web/index.html: build/web ${ROOT_DIR}/index.html
|
||||
cp ${ROOT_DIR}/index.html $@
|
||||
|
||||
build/web/processor.js: build/web ${ROOT_DIR}/config.js ${ROOT_DIR}/../../common/web/processor.js
|
||||
cat ${ROOT_DIR}/config.js ${ROOT_DIR}/../../common/web//processor.js > $@
|
||||
|
||||
build/web/config.js: build/web ${ROOT_DIR}/config.js
|
||||
cp ${ROOT_DIR}/config.js $@
|
||||
|
||||
build/web/key.pem: build/web/cert.pem
|
||||
|
||||
build/web/cert.pem: build/web
|
||||
yes "" | openssl req -x509 -newkey rsa:2048 -keyout build/web/key.pem -out build/web/cert.pem -days 365 -nodes 2>/dev/null
|
||||
|
||||
build/web:
|
||||
mkdir -p build/web
|
||||
|
||||
clean:
|
||||
rm -fr build/web
|
||||
include ${ROOT_DIR}/../../common/web/web.mk
|
||||
|
@ -1,210 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
|
||||
Brickworks
|
||||
|
||||
Copyright (C) 2022 Orastron Srl unipersonale
|
||||
|
||||
Brickworks is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, version 3 of the License.
|
||||
|
||||
Brickworks is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Brickworks. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
File author: Stefano D'Angelo
|
||||
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Brickworks web effect example</title>
|
||||
<script src="config.js"></script>
|
||||
<script>
|
||||
var audioCtx;
|
||||
|
||||
var Effect = {
|
||||
node: null,
|
||||
moduleAdded: false,
|
||||
wasmBytes: null,
|
||||
|
||||
init: async function () {
|
||||
if (!this.moduleAdded) {
|
||||
await audioCtx.audioWorklet.addModule("processor.js");
|
||||
this.moduleAdded = true;
|
||||
}
|
||||
|
||||
if (!this.wasmBytes)
|
||||
this.wasmBytes = await fetch("module.wasm")
|
||||
.then((response) => response.arrayBuffer())
|
||||
.then((bytes) => bytes);
|
||||
if (!this.wasmBytes.byteLength) {
|
||||
this.wasmBytes = null;
|
||||
throw "could not fetch WebAssembly module";
|
||||
}
|
||||
|
||||
if (!this.node)
|
||||
this.node = new AudioWorkletNode(audioCtx, "BWExample",
|
||||
{
|
||||
numberOfInputs: 1,
|
||||
numberOfOutputs: 1,
|
||||
outputChannelCount: [ 1 ],
|
||||
processorOptions: {
|
||||
wasmBytes: this.wasmBytes
|
||||
}
|
||||
});
|
||||
this.node.connect(audioCtx.destination);
|
||||
}
|
||||
};
|
||||
|
||||
var Player = {
|
||||
sourceBuffer: null,
|
||||
playing: false,
|
||||
started: false,
|
||||
|
||||
load: function (buffer, successCb, errorCb) {
|
||||
let t = this;
|
||||
audioCtx.decodeAudioData(buffer,
|
||||
function (data) {
|
||||
if (t.started)
|
||||
t.sourceBuffer.stop();
|
||||
if (t.playing)
|
||||
t.sourceBuffer.disconnect();
|
||||
t.sourceBuffer = audioCtx.createBufferSource();
|
||||
t.sourceBuffer.buffer = data;
|
||||
t.sourceBuffer.loop = true;
|
||||
if (t.started)
|
||||
t.sourceBuffer.start();
|
||||
if (t.playing) {
|
||||
t.started = true;
|
||||
t.sourceBuffer.connect(Effect.node);
|
||||
}
|
||||
successCb();
|
||||
},
|
||||
function () { errorCb(); });
|
||||
},
|
||||
|
||||
togglePlayPause: function () {
|
||||
if (this.playing) {
|
||||
this.sourceBuffer.disconnect();
|
||||
this.playing = false;
|
||||
} else {
|
||||
if (!this.started) {
|
||||
this.sourceBuffer.start();
|
||||
this.started = true;
|
||||
}
|
||||
this.sourceBuffer.connect(Effect.node);
|
||||
this.playing = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var initState = 0; // 0 = not inited, 1 = in progress, 2 = inited
|
||||
|
||||
window.addEventListener("load", function (e) {
|
||||
var start = document.getElementById("start");
|
||||
var starting = document.getElementById("starting");
|
||||
var main = document.getElementById("main");
|
||||
var file = document.getElementById("file");
|
||||
var playPause = document.getElementById("playPause");
|
||||
var controls = document.getElementById("controls");
|
||||
|
||||
// reset on refresh
|
||||
file.value = "";
|
||||
playPause.disabled = true;
|
||||
|
||||
for (var i = 0; i < parameters.length; i++) {
|
||||
var div = document.createElement("div");
|
||||
controls.appendChild(div);
|
||||
|
||||
var label = document.createElement("label");
|
||||
label.setAttribute("for", "p" + i);
|
||||
label.innerText = parameters[i].name;
|
||||
div.appendChild(label);
|
||||
|
||||
var range = document.createElement("input");
|
||||
range.setAttribute("type", "range");
|
||||
range.setAttribute("id", "p" + i);
|
||||
range.setAttribute("name", "p" + i);
|
||||
range.setAttribute("min", "0");
|
||||
range.setAttribute("max", "1");
|
||||
if (!parameters[i].step)
|
||||
range.setAttribute("step", "any");
|
||||
else
|
||||
range.setAttribute("step", 1 / parameters[i].step);
|
||||
range.value = parameters[i].defaultValue;
|
||||
if (parameters[i].output)
|
||||
range.setAttribute("readonly", "true");
|
||||
else {
|
||||
let index = i;
|
||||
range.addEventListener("input", function (e) {
|
||||
var p = Effect.node.parameters.get(parameters[index].name);
|
||||
p.setValueAtTime(e.target.value, 0);
|
||||
});
|
||||
}
|
||||
div.appendChild(range);
|
||||
}
|
||||
|
||||
start.addEventListener("click", async function () {
|
||||
initState = 1;
|
||||
start.disabled = true;
|
||||
starting.hidden = false;
|
||||
|
||||
try {
|
||||
if (!audioCtx)
|
||||
audioCtx = new AudioContext();
|
||||
await Effect.init();
|
||||
|
||||
Effect.node.port.onmessage = function (e) { document.getElementById("p" + e.data.index).value = e.data.value; };
|
||||
|
||||
initState = 2;
|
||||
start.hidden = true;
|
||||
starting.hidden = true;
|
||||
main.hidden = false;
|
||||
} catch (err) {
|
||||
alert("Colud not initialize: " + err);
|
||||
|
||||
initState = 0;
|
||||
start.disabled = false;
|
||||
starting.hidden = true;
|
||||
}
|
||||
});
|
||||
|
||||
file.addEventListener("change", function () {
|
||||
var fileReader = new FileReader();
|
||||
fileReader.readAsArrayBuffer(this.files[0]);
|
||||
fileReader.onload = function (e) {
|
||||
Player.load(e.target.result,
|
||||
function () { playPause.disabled = false; },
|
||||
function () { alert("Could not decode the chosen file"); });
|
||||
};
|
||||
fileReader.onerror = function (e) { alert("Could not read file"); };
|
||||
});
|
||||
|
||||
playPause.addEventListener("click", function () {
|
||||
Player.togglePlayPause();
|
||||
playPause.innerText = Player.playing ? "Pause" : "Play";
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Brickworks web effect example</h1>
|
||||
<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>
|
||||
<button id="playPause" disabled>Play</button>
|
||||
<h2>Effect</h2>
|
||||
<div id="controls">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -1,210 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
|
||||
Brickworks
|
||||
|
||||
Copyright (C) 2022 Orastron Srl unipersonale
|
||||
|
||||
Brickworks is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, version 3 of the License.
|
||||
|
||||
Brickworks is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Brickworks. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
File author: Stefano D'Angelo
|
||||
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Brickworks web effect example</title>
|
||||
<script src="config.js"></script>
|
||||
<script>
|
||||
var audioCtx;
|
||||
|
||||
var Effect = {
|
||||
node: null,
|
||||
moduleAdded: false,
|
||||
wasmBytes: null,
|
||||
|
||||
init: async function () {
|
||||
if (!this.moduleAdded) {
|
||||
await audioCtx.audioWorklet.addModule("processor.js");
|
||||
this.moduleAdded = true;
|
||||
}
|
||||
|
||||
if (!this.wasmBytes)
|
||||
this.wasmBytes = await fetch("module.wasm")
|
||||
.then((response) => response.arrayBuffer())
|
||||
.then((bytes) => bytes);
|
||||
if (!this.wasmBytes.byteLength) {
|
||||
this.wasmBytes = null;
|
||||
throw "could not fetch WebAssembly module";
|
||||
}
|
||||
|
||||
if (!this.node)
|
||||
this.node = new AudioWorkletNode(audioCtx, "BWExample",
|
||||
{
|
||||
numberOfInputs: 1,
|
||||
numberOfOutputs: 1,
|
||||
outputChannelCount: [ 1 ],
|
||||
processorOptions: {
|
||||
wasmBytes: this.wasmBytes
|
||||
}
|
||||
});
|
||||
this.node.connect(audioCtx.destination);
|
||||
}
|
||||
};
|
||||
|
||||
var Player = {
|
||||
sourceBuffer: null,
|
||||
playing: false,
|
||||
started: false,
|
||||
|
||||
load: function (buffer, successCb, errorCb) {
|
||||
let t = this;
|
||||
audioCtx.decodeAudioData(buffer,
|
||||
function (data) {
|
||||
if (t.started)
|
||||
t.sourceBuffer.stop();
|
||||
if (t.playing)
|
||||
t.sourceBuffer.disconnect();
|
||||
t.sourceBuffer = audioCtx.createBufferSource();
|
||||
t.sourceBuffer.buffer = data;
|
||||
t.sourceBuffer.loop = true;
|
||||
if (t.started)
|
||||
t.sourceBuffer.start();
|
||||
if (t.playing) {
|
||||
t.started = true;
|
||||
t.sourceBuffer.connect(Effect.node);
|
||||
}
|
||||
successCb();
|
||||
},
|
||||
function () { errorCb(); });
|
||||
},
|
||||
|
||||
togglePlayPause: function () {
|
||||
if (this.playing) {
|
||||
this.sourceBuffer.disconnect();
|
||||
this.playing = false;
|
||||
} else {
|
||||
if (!this.started) {
|
||||
this.sourceBuffer.start();
|
||||
this.started = true;
|
||||
}
|
||||
this.sourceBuffer.connect(Effect.node);
|
||||
this.playing = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var initState = 0; // 0 = not inited, 1 = in progress, 2 = inited
|
||||
|
||||
window.addEventListener("load", function (e) {
|
||||
var start = document.getElementById("start");
|
||||
var starting = document.getElementById("starting");
|
||||
var main = document.getElementById("main");
|
||||
var file = document.getElementById("file");
|
||||
var playPause = document.getElementById("playPause");
|
||||
var controls = document.getElementById("controls");
|
||||
|
||||
// reset on refresh
|
||||
file.value = "";
|
||||
playPause.disabled = true;
|
||||
|
||||
for (var i = 0; i < parameters.length; i++) {
|
||||
var div = document.createElement("div");
|
||||
controls.appendChild(div);
|
||||
|
||||
var label = document.createElement("label");
|
||||
label.setAttribute("for", "p" + i);
|
||||
label.innerText = parameters[i].name;
|
||||
div.appendChild(label);
|
||||
|
||||
var range = document.createElement("input");
|
||||
range.setAttribute("type", "range");
|
||||
range.setAttribute("id", "p" + i);
|
||||
range.setAttribute("name", "p" + i);
|
||||
range.setAttribute("min", "0");
|
||||
range.setAttribute("max", "1");
|
||||
if (!parameters[i].step)
|
||||
range.setAttribute("step", "any");
|
||||
else
|
||||
range.setAttribute("step", 1 / parameters[i].step);
|
||||
range.value = parameters[i].defaultValue;
|
||||
if (parameters[i].output)
|
||||
range.setAttribute("readonly", "true");
|
||||
else {
|
||||
let index = i;
|
||||
range.addEventListener("input", function (e) {
|
||||
var p = Effect.node.parameters.get(parameters[index].name);
|
||||
p.setValueAtTime(e.target.value, 0);
|
||||
});
|
||||
}
|
||||
div.appendChild(range);
|
||||
}
|
||||
|
||||
start.addEventListener("click", async function () {
|
||||
initState = 1;
|
||||
start.disabled = true;
|
||||
starting.hidden = false;
|
||||
|
||||
try {
|
||||
if (!audioCtx)
|
||||
audioCtx = new AudioContext();
|
||||
await Effect.init();
|
||||
|
||||
Effect.node.port.onmessage = function (e) { document.getElementById("p" + e.data.index).value = e.data.value; };
|
||||
|
||||
initState = 2;
|
||||
start.hidden = true;
|
||||
starting.hidden = true;
|
||||
main.hidden = false;
|
||||
} catch (err) {
|
||||
alert("Colud not initialize: " + err);
|
||||
|
||||
initState = 0;
|
||||
start.disabled = false;
|
||||
starting.hidden = true;
|
||||
}
|
||||
});
|
||||
|
||||
file.addEventListener("change", function () {
|
||||
var fileReader = new FileReader();
|
||||
fileReader.readAsArrayBuffer(this.files[0]);
|
||||
fileReader.onload = function (e) {
|
||||
Player.load(e.target.result,
|
||||
function () { playPause.disabled = false; },
|
||||
function () { alert("Could not decode the chosen file"); });
|
||||
};
|
||||
fileReader.onerror = function (e) { alert("Could not read file"); };
|
||||
});
|
||||
|
||||
playPause.addEventListener("click", function () {
|
||||
Player.togglePlayPause();
|
||||
playPause.innerText = Player.playing ? "Pause" : "Play";
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Brickworks web effect example</h1>
|
||||
<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>
|
||||
<button id="playPause" disabled>Play</button>
|
||||
<h2>Effect</h2>
|
||||
<div id="controls">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -1,56 +1,4 @@
|
||||
CC=clang
|
||||
CFLAGS= \
|
||||
-D__WASM__ \
|
||||
-I${ROOT_DIR}/../src \
|
||||
-I${ROOT_DIR}/../../common/web \
|
||||
-I${ROOT_DIR}/../../../include \
|
||||
--target=wasm32 \
|
||||
-flto \
|
||||
-fvisibility=hidden \
|
||||
-Ofast
|
||||
LDFLAGS= \
|
||||
-Wl,--allow-undefined \
|
||||
-Wl,--no-entry \
|
||||
-Wl,--lto-O3 \
|
||||
-Wl,-strip-all \
|
||||
-Wl,--export-table \
|
||||
-Wl,--export=wrapper_new \
|
||||
-Wl,--export=wrapper_free \
|
||||
-Wl,--export=wrapper_get_ins \
|
||||
-Wl,--export=wrapper_get_outs \
|
||||
-Wl,--export=wrapper_get_param_values \
|
||||
-Wl,--export=wrapper_process \
|
||||
-Wl,--export=wrapper_set_parameter \
|
||||
-Wl,-z,stack-size=$$((8*1024*1024)) \
|
||||
-nostdlib
|
||||
ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_fx_wah.c
|
||||
|
||||
ROOT_DIR=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
SOURCES= \
|
||||
${ROOT_DIR}/../src/bw_example_fx_wah.c \
|
||||
${ROOT_DIR}/../../common/web/walloc.c \
|
||||
${ROOT_DIR}/../../common/web/wrapper.c
|
||||
|
||||
all: build/web/module.wasm build/web/index.html build/web/config.js build/web/processor.js build/web/cert.pem build/web/key.pem
|
||||
|
||||
build/web/module.wasm: build/web ${SOURCES}
|
||||
${CC} ${SOURCES} ${CFLAGS} ${LDFLAGS} -o $@
|
||||
|
||||
build/web/index.html: build/web ${ROOT_DIR}/index.html
|
||||
cp ${ROOT_DIR}/index.html $@
|
||||
|
||||
build/web/processor.js: build/web ${ROOT_DIR}/config.js ${ROOT_DIR}/../../common/web/processor.js
|
||||
cat ${ROOT_DIR}/config.js ${ROOT_DIR}/../../common/web//processor.js > $@
|
||||
|
||||
build/web/config.js: build/web ${ROOT_DIR}/config.js
|
||||
cp ${ROOT_DIR}/config.js $@
|
||||
|
||||
build/web/key.pem: build/web/cert.pem
|
||||
|
||||
build/web/cert.pem: build/web
|
||||
yes "" | openssl req -x509 -newkey rsa:2048 -keyout build/web/key.pem -out build/web/cert.pem -days 365 -nodes 2>/dev/null
|
||||
|
||||
build/web:
|
||||
mkdir -p build/web
|
||||
|
||||
clean:
|
||||
rm -fr build/web
|
||||
include ${ROOT_DIR}/../../common/web/web.mk
|
||||
|
@ -1,210 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
|
||||
Brickworks
|
||||
|
||||
Copyright (C) 2022 Orastron Srl unipersonale
|
||||
|
||||
Brickworks is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, version 3 of the License.
|
||||
|
||||
Brickworks is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Brickworks. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
File author: Stefano D'Angelo
|
||||
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Brickworks web effect example</title>
|
||||
<script src="config.js"></script>
|
||||
<script>
|
||||
var audioCtx;
|
||||
|
||||
var Effect = {
|
||||
node: null,
|
||||
moduleAdded: false,
|
||||
wasmBytes: null,
|
||||
|
||||
init: async function () {
|
||||
if (!this.moduleAdded) {
|
||||
await audioCtx.audioWorklet.addModule("processor.js");
|
||||
this.moduleAdded = true;
|
||||
}
|
||||
|
||||
if (!this.wasmBytes)
|
||||
this.wasmBytes = await fetch("module.wasm")
|
||||
.then((response) => response.arrayBuffer())
|
||||
.then((bytes) => bytes);
|
||||
if (!this.wasmBytes.byteLength) {
|
||||
this.wasmBytes = null;
|
||||
throw "could not fetch WebAssembly module";
|
||||
}
|
||||
|
||||
if (!this.node)
|
||||
this.node = new AudioWorkletNode(audioCtx, "BWExample",
|
||||
{
|
||||
numberOfInputs: 1,
|
||||
numberOfOutputs: 1,
|
||||
outputChannelCount: [ 1 ],
|
||||
processorOptions: {
|
||||
wasmBytes: this.wasmBytes
|
||||
}
|
||||
});
|
||||
this.node.connect(audioCtx.destination);
|
||||
}
|
||||
};
|
||||
|
||||
var Player = {
|
||||
sourceBuffer: null,
|
||||
playing: false,
|
||||
started: false,
|
||||
|
||||
load: function (buffer, successCb, errorCb) {
|
||||
let t = this;
|
||||
audioCtx.decodeAudioData(buffer,
|
||||
function (data) {
|
||||
if (t.started)
|
||||
t.sourceBuffer.stop();
|
||||
if (t.playing)
|
||||
t.sourceBuffer.disconnect();
|
||||
t.sourceBuffer = audioCtx.createBufferSource();
|
||||
t.sourceBuffer.buffer = data;
|
||||
t.sourceBuffer.loop = true;
|
||||
if (t.started)
|
||||
t.sourceBuffer.start();
|
||||
if (t.playing) {
|
||||
t.started = true;
|
||||
t.sourceBuffer.connect(Effect.node);
|
||||
}
|
||||
successCb();
|
||||
},
|
||||
function () { errorCb(); });
|
||||
},
|
||||
|
||||
togglePlayPause: function () {
|
||||
if (this.playing) {
|
||||
this.sourceBuffer.disconnect();
|
||||
this.playing = false;
|
||||
} else {
|
||||
if (!this.started) {
|
||||
this.sourceBuffer.start();
|
||||
this.started = true;
|
||||
}
|
||||
this.sourceBuffer.connect(Effect.node);
|
||||
this.playing = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var initState = 0; // 0 = not inited, 1 = in progress, 2 = inited
|
||||
|
||||
window.addEventListener("load", function (e) {
|
||||
var start = document.getElementById("start");
|
||||
var starting = document.getElementById("starting");
|
||||
var main = document.getElementById("main");
|
||||
var file = document.getElementById("file");
|
||||
var playPause = document.getElementById("playPause");
|
||||
var controls = document.getElementById("controls");
|
||||
|
||||
// reset on refresh
|
||||
file.value = "";
|
||||
playPause.disabled = true;
|
||||
|
||||
for (var i = 0; i < parameters.length; i++) {
|
||||
var div = document.createElement("div");
|
||||
controls.appendChild(div);
|
||||
|
||||
var label = document.createElement("label");
|
||||
label.setAttribute("for", "p" + i);
|
||||
label.innerText = parameters[i].name;
|
||||
div.appendChild(label);
|
||||
|
||||
var range = document.createElement("input");
|
||||
range.setAttribute("type", "range");
|
||||
range.setAttribute("id", "p" + i);
|
||||
range.setAttribute("name", "p" + i);
|
||||
range.setAttribute("min", "0");
|
||||
range.setAttribute("max", "1");
|
||||
if (!parameters[i].step)
|
||||
range.setAttribute("step", "any");
|
||||
else
|
||||
range.setAttribute("step", 1 / parameters[i].step);
|
||||
range.value = parameters[i].defaultValue;
|
||||
if (parameters[i].output)
|
||||
range.setAttribute("readonly", "true");
|
||||
else {
|
||||
let index = i;
|
||||
range.addEventListener("input", function (e) {
|
||||
var p = Effect.node.parameters.get(parameters[index].name);
|
||||
p.setValueAtTime(e.target.value, 0);
|
||||
});
|
||||
}
|
||||
div.appendChild(range);
|
||||
}
|
||||
|
||||
start.addEventListener("click", async function () {
|
||||
initState = 1;
|
||||
start.disabled = true;
|
||||
starting.hidden = false;
|
||||
|
||||
try {
|
||||
if (!audioCtx)
|
||||
audioCtx = new AudioContext();
|
||||
await Effect.init();
|
||||
|
||||
Effect.node.port.onmessage = function (e) { document.getElementById("p" + e.data.index).value = e.data.value; };
|
||||
|
||||
initState = 2;
|
||||
start.hidden = true;
|
||||
starting.hidden = true;
|
||||
main.hidden = false;
|
||||
} catch (err) {
|
||||
alert("Colud not initialize: " + err);
|
||||
|
||||
initState = 0;
|
||||
start.disabled = false;
|
||||
starting.hidden = true;
|
||||
}
|
||||
});
|
||||
|
||||
file.addEventListener("change", function () {
|
||||
var fileReader = new FileReader();
|
||||
fileReader.readAsArrayBuffer(this.files[0]);
|
||||
fileReader.onload = function (e) {
|
||||
Player.load(e.target.result,
|
||||
function () { playPause.disabled = false; },
|
||||
function () { alert("Could not decode the chosen file"); });
|
||||
};
|
||||
fileReader.onerror = function (e) { alert("Could not read file"); };
|
||||
});
|
||||
|
||||
playPause.addEventListener("click", function () {
|
||||
Player.togglePlayPause();
|
||||
playPause.innerText = Player.playing ? "Pause" : "Play";
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Brickworks web effect example</h1>
|
||||
<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>
|
||||
<button id="playPause" disabled>Play</button>
|
||||
<h2>Effect</h2>
|
||||
<div id="controls">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -1,60 +1,6 @@
|
||||
CC=clang
|
||||
CFLAGS= \
|
||||
-D__WASM__ \
|
||||
-I${ROOT_DIR}/../src \
|
||||
-I${ROOT_DIR}/../../common/web \
|
||||
-I${ROOT_DIR}/../../../include \
|
||||
--target=wasm32 \
|
||||
-flto \
|
||||
-fvisibility=hidden \
|
||||
#-Ofast - had to turn this off as there are issues with wasm bulk memory
|
||||
LDFLAGS= \
|
||||
-Wl,--allow-undefined \
|
||||
-Wl,--no-entry \
|
||||
-Wl,--lto-O3 \
|
||||
-Wl,-strip-all \
|
||||
-Wl,--export-table \
|
||||
-Wl,--export=wrapper_new \
|
||||
-Wl,--export=wrapper_free \
|
||||
-Wl,--export=wrapper_get_ins \
|
||||
-Wl,--export=wrapper_get_outs \
|
||||
-Wl,--export=wrapper_get_param_values \
|
||||
-Wl,--export=wrapper_process \
|
||||
-Wl,--export=wrapper_set_parameter \
|
||||
-Wl,--export=wrapper_note_on \
|
||||
-Wl,--export=wrapper_note_off \
|
||||
-Wl,--export=wrapper_pitch_bend \
|
||||
-Wl,--export=wrapper_mod_wheel \
|
||||
-Wl,-z,stack-size=$$((8*1024*1024)) \
|
||||
-nostdlib
|
||||
ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_synth_mono.c
|
||||
SYNTH := yes
|
||||
NEEDS_MEMSET := yes
|
||||
|
||||
ROOT_DIR=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
SOURCES= \
|
||||
${ROOT_DIR}/../src/bw_example_synth_mono.c \
|
||||
${ROOT_DIR}/../../common/web/walloc.c \
|
||||
${ROOT_DIR}/../../common/web/wrapper.c
|
||||
|
||||
all: build/web/module.wasm build/web/index.html build/web/config.js build/web/processor.js build/web/cert.pem build/web/key.pem
|
||||
|
||||
build/web/module.wasm: build/web ${SOURCES}
|
||||
${CC} ${SOURCES} ${CFLAGS} ${LDFLAGS} -o $@
|
||||
|
||||
build/web/index.html: build/web ${ROOT_DIR}/index.html
|
||||
cp ${ROOT_DIR}/index.html $@
|
||||
|
||||
build/web/processor.js: build/web ${ROOT_DIR}/config.js ${ROOT_DIR}/../../common/web/processor.js
|
||||
cat ${ROOT_DIR}/config.js ${ROOT_DIR}/../../common/web//processor.js > $@
|
||||
|
||||
build/web/config.js: build/web ${ROOT_DIR}/config.js
|
||||
cp ${ROOT_DIR}/config.js $@
|
||||
|
||||
build/web/key.pem: build/web/cert.pem
|
||||
|
||||
build/web/cert.pem: build/web
|
||||
yes "" | openssl req -x509 -newkey rsa:2048 -keyout build/web/key.pem -out build/web/cert.pem -days 365 -nodes 2>/dev/null
|
||||
|
||||
build/web:
|
||||
mkdir -p build/web
|
||||
|
||||
clean:
|
||||
rm -fr build/web
|
||||
include ${ROOT_DIR}/../../common/web/web.mk
|
||||
|
@ -1,58 +1,5 @@
|
||||
CC=clang
|
||||
CFLAGS= \
|
||||
-D__WASM__ \
|
||||
-I${ROOT_DIR}/../src \
|
||||
-I${ROOT_DIR}/../../common/web \
|
||||
-I${ROOT_DIR}/../../../include \
|
||||
--target=wasm32 \
|
||||
-flto \
|
||||
-fvisibility=hidden \
|
||||
#-Ofast - had to turn this off as there are issues with wasm bulk memory
|
||||
LDFLAGS= \
|
||||
-Wl,--allow-undefined \
|
||||
-Wl,--no-entry \
|
||||
-Wl,--lto-O3 \
|
||||
-Wl,-strip-all \
|
||||
-Wl,--export-table \
|
||||
-Wl,--export=wrapper_new \
|
||||
-Wl,--export=wrapper_free \
|
||||
-Wl,--export=wrapper_get_ins \
|
||||
-Wl,--export=wrapper_get_outs \
|
||||
-Wl,--export=wrapper_get_param_values \
|
||||
-Wl,--export=wrapper_process \
|
||||
-Wl,--export=wrapper_set_parameter \
|
||||
-Wl,--export=wrapper_note_on \
|
||||
-Wl,--export=wrapper_note_off \
|
||||
-Wl,-z,stack-size=$$((8*1024*1024)) \
|
||||
-nostdlib
|
||||
ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_synth_simple.c
|
||||
SYNTH := yes
|
||||
|
||||
ROOT_DIR=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
SOURCES= \
|
||||
${ROOT_DIR}/../src/bw_example_synth_simple.c \
|
||||
${ROOT_DIR}/../../common/web/walloc.c \
|
||||
${ROOT_DIR}/../../common/web/wrapper.c
|
||||
|
||||
all: build/web/module.wasm build/web/index.html build/web/config.js build/web/processor.js build/web/cert.pem build/web/key.pem
|
||||
|
||||
build/web/module.wasm: build/web ${SOURCES}
|
||||
${CC} ${SOURCES} ${CFLAGS} ${LDFLAGS} -o $@
|
||||
|
||||
build/web/index.html: build/web ${ROOT_DIR}/index.html
|
||||
cp ${ROOT_DIR}/index.html $@
|
||||
|
||||
build/web/processor.js: build/web ${ROOT_DIR}/config.js ${ROOT_DIR}/../../common/web/processor.js
|
||||
cat ${ROOT_DIR}/config.js ${ROOT_DIR}/../../common/web//processor.js > $@
|
||||
|
||||
build/web/config.js: build/web ${ROOT_DIR}/config.js
|
||||
cp ${ROOT_DIR}/config.js $@
|
||||
|
||||
build/web/key.pem: build/web/cert.pem
|
||||
|
||||
build/web/cert.pem: build/web
|
||||
yes "" | openssl req -x509 -newkey rsa:2048 -keyout build/web/key.pem -out build/web/cert.pem -days 365 -nodes 2>/dev/null
|
||||
|
||||
build/web:
|
||||
mkdir -p build/web
|
||||
|
||||
clean:
|
||||
rm -fr build/web
|
||||
include ${ROOT_DIR}/../../common/web/web.mk
|
||||
|
@ -1,167 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
|
||||
Brickworks
|
||||
|
||||
Copyright (C) 2022 Orastron Srl unipersonale
|
||||
|
||||
Brickworks is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, version 3 of the License.
|
||||
|
||||
Brickworks is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Brickworks. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
File author: Stefano D'Angelo
|
||||
|
||||
-->
|
||||
<html
|
||||
<html>
|
||||
<head>
|
||||
<title>Brickworks web synth example</title>
|
||||
<script src="config.js"></script>
|
||||
<script>
|
||||
var audioCtx;
|
||||
var midi;
|
||||
|
||||
var Synth = {
|
||||
node: null,
|
||||
moduleAdded: false,
|
||||
wasmBytes: null,
|
||||
|
||||
init: async function () {
|
||||
if (!this.moduleAdded) {
|
||||
await audioCtx.audioWorklet.addModule("processor.js");
|
||||
this.moduleAdded = true;
|
||||
}
|
||||
|
||||
if (!this.wasmBytes)
|
||||
this.wasmBytes = await fetch("module.wasm")
|
||||
.then((response) => response.arrayBuffer())
|
||||
.then((bytes) => bytes);
|
||||
if (!this.wasmBytes.byteLength) {
|
||||
this.wasmBytes = null;
|
||||
throw "could not fetch WebAssembly module";
|
||||
}
|
||||
|
||||
if (!this.node)
|
||||
this.node = new AudioWorkletNode(audioCtx, "BWExample",
|
||||
{
|
||||
numberOfInputs: 0,
|
||||
numberOfOutputs: 1,
|
||||
outputChannelCount: [ 1 ],
|
||||
processorOptions: { wasmBytes: this.wasmBytes }
|
||||
});
|
||||
this.node.connect(audioCtx.destination);
|
||||
},
|
||||
|
||||
noteOn: function (note, velocity) {
|
||||
this.node.port.postMessage({ type: "noteOn", note: note, velocity: velocity });
|
||||
},
|
||||
|
||||
noteOff: function (note) {
|
||||
this.node.port.postMessage({ type: "noteOff", note: note });
|
||||
}
|
||||
};
|
||||
|
||||
var initState = 0; // 0 = not inited, 1 = in progress, 2 = inited
|
||||
|
||||
window.addEventListener("load", async function (e) {
|
||||
var start = document.getElementById("start");
|
||||
var starting = document.getElementById("starting");
|
||||
var main = document.getElementById("main");
|
||||
var controls = document.getElementById("controls");
|
||||
|
||||
if (!navigator.requestMIDIAccess) {
|
||||
alert("This won't work. Your browser doesn't support the Web MIDI API.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < parameters.length; i++) {
|
||||
var div = document.createElement("div");
|
||||
controls.appendChild(div);
|
||||
|
||||
var label = document.createElement("label");
|
||||
label.setAttribute("for", "p" + i);
|
||||
label.innerText = parameters[i].name;
|
||||
div.appendChild(label);
|
||||
|
||||
var range = document.createElement("input");
|
||||
range.setAttribute("type", "range");
|
||||
range.setAttribute("id", "p" + i);
|
||||
range.setAttribute("name", "p" + i);
|
||||
range.setAttribute("min", "0");
|
||||
range.setAttribute("max", "1");
|
||||
if (!parameters[i].step)
|
||||
range.setAttribute("step", "any");
|
||||
else
|
||||
range.setAttribute("step", 1 / parameters[i].step);
|
||||
range.value = parameters[i].defaultValue;
|
||||
if (parameters[i].output)
|
||||
range.setAttribute("readonly", "true");
|
||||
else {
|
||||
let index = i;
|
||||
range.addEventListener("input", function (e) {
|
||||
var p = Synth.node.parameters.get(parameters[index].name);
|
||||
p.setValueAtTime(e.target.value, 0);
|
||||
});
|
||||
}
|
||||
div.appendChild(range);
|
||||
}
|
||||
|
||||
start.addEventListener("click", async function () {
|
||||
initState = 1;
|
||||
start.disabled = true;
|
||||
starting.hidden = false;
|
||||
|
||||
try {
|
||||
if (!audioCtx)
|
||||
audioCtx = new AudioContext();
|
||||
midi = await navigator.requestMIDIAccess();
|
||||
await Synth.init();
|
||||
|
||||
Synth.node.port.onmessage = function (e) { document.getElementById("p" + e.data.index).value = e.data.value; };
|
||||
|
||||
function onMIDIMessage(e) {
|
||||
switch (e.data[0] & 0xf0) {
|
||||
case 0x90:
|
||||
Synth.noteOn(e.data[1], e.data[2]);
|
||||
break;
|
||||
case 0x80:
|
||||
Synth.noteOff(e.data[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
midi.inputs.forEach((entry) => { entry.onmidimessage = onMIDIMessage; });
|
||||
|
||||
initState = 2;
|
||||
start.hidden = true;
|
||||
starting.hidden = true;
|
||||
main.hidden = false;
|
||||
} catch (err) {
|
||||
alert("Colud not initialize: " + err);
|
||||
|
||||
initState = 0;
|
||||
start.disabled = false;
|
||||
starting.hidden = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Brickworks web synth example</h1>
|
||||
<input id="start" type="button" value="Start">
|
||||
<p id="starting" hidden>Starting...</p>
|
||||
<div id="main" hidden>
|
||||
<p>Just play notes on your MIDI device.</p>
|
||||
<div id="controls">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user