Skip to content

Commit

Permalink
Added AudioWorklet with WASM support
Browse files Browse the repository at this point in the history
  • Loading branch information
ISNIT0 committed Aug 21, 2018
1 parent 1b530e3 commit 1655635
Show file tree
Hide file tree
Showing 10 changed files with 957 additions and 10 deletions.
179 changes: 173 additions & 6 deletions dist/app.js
Expand Up @@ -246,7 +246,7 @@ module.exports = {
output:require('./outputs/index'),
modifier:require('./modifiers/index')
}
},{"./inputs/index":5,"./modifiers/index":12,"./outputs/index":13}],4:[function(require,module,exports){
},{"./inputs/index":5,"./modifiers/index":13,"./outputs/index":14}],4:[function(require,module,exports){
module.exports = {
default () {
return {
Expand Down Expand Up @@ -687,6 +687,172 @@ ${nodeName}.type.value = "${node.options.type}";
}
}
},{}],10:[function(require,module,exports){
function intArrayFromBase64(s) {
var decoded = atob(s);
var bytes = new Uint8Array(decoded.length);
for (var i = 0; i < decoded.length; ++i) {
bytes[i] = decoded.charCodeAt(i);
}
return bytes;
}

//export function processor(input_ptr: i32, output_ptr: i32, length: i32): void {
const gainTsSource = `
let gain = <f32>0.5;
for (let byte = <i32>0; byte < length; ++byte) {
let inp = load<f32>(input_ptr + 4 * byte);
store<f32>(output_ptr + 4 * byte, inp * gain);
}
`;
//}



//Yuk Yuk Yuk
function makeProcessorsFile(id, parsedBinary) {
return `
const wasmBinary = new Uint8Array([${parsedBinary}]);
const memory = new WebAssembly.Memory({
initial: 10
});
const module = new WebAssembly.Module(wasmBinary);
const instance = new WebAssembly.Instance(module, {
env: {
abort(msg, file, line, column) {
console.error("abort called at main.ts:" + line + ":" + column);
},
memory
}
});
const result = {
instance
};
const exp = result.instance.exports;
class MyWorkletProcessor extends AudioWorkletProcessor {
constructor() {
super();
}
process(inputs, outputs, parameters) {
let input = inputs[0];
let output = outputs[0];
let channelCount = input.length;
for (let channel = 0; channel < channelCount; ++channel) {
let arr = new Float32Array(memory.buffer);
for (let i = 0; i < input[channel].length; i++) {
arr[i] = input[channel][i];
}
// console.log(arr.slice(0,3));
exp.processor(0, input[channel].length * Float32Array.BYTES_PER_ELEMENT, input[channel].length);
for (let i = 0; i < input[channel].length; i++) {
output[channel][i] = arr[128 + i];
}
}
return true;
}
}
registerProcessor('${id}', MyWorkletProcessor);
`;
}


let latestParsedBin = null;


function makeWorkletPromise(audioCtx, ts) {
const id = '' + Date.now(); //TODO: better ID
return $.ajax(`/makeWorkletBinary/${id}`, {
data: JSON.stringify({
ts
}),
contentType: 'application/JSON',
type: 'POST'
}).then(binary => {
const parsedBin = intArrayFromBase64(binary);
const fileContent = makeProcessorsFile(id, parsedBin);
latestParsedBin = parsedBin; //More bad things
return audioCtx.audioWorklet.addModule(`data:application/javascript;base64,${btoa(fileContent)}`).then(() => {
return new AudioWorkletNode(audioCtx, id);
});
});
}

let prevTsSrc = null;

module.exports = {
default () {
return {
kind: 'modifier',
type: 'customWorklet',
options: {
tsSource: gainTsSource
}
}
},
initWANode(audioCtx, node) {
prevTsSrc = node.options.tsSource;
return makeWorkletPromise(audioCtx, node.options.tsSource);
},
updateWANode(customNode, node, nodeIndex, graph) {
if (prevTsSrc !== node.options.tsSource) {
return makeWorkletPromise(customNode.context, node.options.tsSource)
.then(workletNode => {
const prevNode = graph.nodes[nodeIndex - 1];
const thisNode = graph.nodes[nodeIndex];
const nextNode = graph.nodes[nodeIndex + 1];

prevNode.waNode.connect(workletNode);
workletNode.connect(nextNode.waNode);

try {
prevNode.waNode.disconnect(thisNode.waNode);
thisNode.waNode.disconnect(nexNode.waNode);
} catch (err) {}

node.waNode = workletNode;
});
} else {
return Promise.resolve();
}
},
renderView(state, affect, node, nodeIndex) {
return [
h('h3', `Custom`),
]
},
renderDetail(state, affect, node, nodeIndex) {
return [
h('textarea.ts-editor', {
value: node.options.tsSource,
onblur(ev) {
affect.set(`graph.nodes.${nodeIndex}.options.tsSource`, ev.target.value);
}
}),
h('button', 'Save')
];
},
generateCode(nodeName, node) {
if (latestParsedBin) {
const fileContent = makeProcessorsFile(nodeName, latestParsedBin);
return `
const ${nodeName} = await audioCtx.audioWorklet.addModule(\`data:application/javascript;base64,${btoa(fileContent)}\`).then(() => {
return new AudioWorkletNode(audioCtx, '${nodeName}');
});
`;
} else {
return '';
}
}
}
},{}],11:[function(require,module,exports){
module.exports = {
default () {
return {
Expand Down Expand Up @@ -738,7 +904,7 @@ ${nodeName}.delayTime.value = ${node.options.value};
`;
}
}
},{}],11:[function(require,module,exports){
},{}],12:[function(require,module,exports){
module.exports = {
default () {
return {
Expand Down Expand Up @@ -790,18 +956,19 @@ ${nodeName}.gain.value = ${node.options.value};
`;
}
}
},{}],12:[function(require,module,exports){
},{}],13:[function(require,module,exports){
module.exports = {
delay: require('./delay'),
gain: require('./gain'),
biquadFilter: require('./biquadFilter'),
analyser: require('./analyser')
analyser: require('./analyser'),
customWorklet: require('./customWorklet'),
};
},{"./analyser":8,"./biquadFilter":9,"./delay":10,"./gain":11}],13:[function(require,module,exports){
},{"./analyser":8,"./biquadFilter":9,"./customWorklet":10,"./delay":11,"./gain":12}],14:[function(require,module,exports){
module.exports = {
speaker: require('./speaker')
};
},{"./speaker":14}],14:[function(require,module,exports){
},{"./speaker":15}],15:[function(require,module,exports){
module.exports = {
initWANode(audioCtx, node) {
return Promise.resolve(audioCtx.destination);
Expand Down
9 changes: 9 additions & 0 deletions dist/style.css
Expand Up @@ -99,3 +99,12 @@ input {
background: #EB5757;
font-size: 13;
}
.ts-editor {
padding: 10px;
color: white;
background: #333;
width: 100%;
min-height: 200px;
font-family: monospace;
border: none;
}
2 changes: 2 additions & 0 deletions files/.gitignore
@@ -0,0 +1,2 @@
*
!.gitignore
1 change: 1 addition & 0 deletions index.html
Expand Up @@ -4,6 +4,7 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/peerjs/0.3.9/peer.min.js"></script>
<script src='https://rawgit.com/ISNIT0/nimble-js/master/dist.js'></script>
<script src="https://www.html5rocks.com/en/tutorials/webaudio/intro/js/buffer-loader.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<link rel="stylesheet" href="dist/style.css">
<link href="https://fonts.googleapis.com/css?family=Roboto|Roboto+Mono" rel="stylesheet">
</head>
Expand Down

0 comments on commit 1655635

Please sign in to comment.