diff --git a/ISSUE 35 - Audio API tests/audioWorkletProcessor.js b/ISSUE 35 - Audio API tests/audioWorkletProcessor.js new file mode 100644 index 0000000000000000000000000000000000000000..6d520211d0c2a5fcedb4e2bda3a91389976bfa6b --- /dev/null +++ b/ISSUE 35 - Audio API tests/audioWorkletProcessor.js @@ -0,0 +1,112 @@ +class Processor extends AudioWorkletProcessor { + constructor() { + super(); + this.port.onmessage = e => { + console.log("e data ", e.data) + if (e.data.type === 'recv-audio-queue') { + this.is_paused = e.data.is_paused; + this.head = e.data.head; + this.tail = e.data.tail; + this.can_write = e.data.can_write; + this.volume = e.data.volume; + this.is_muted = e.data.is_muted; + this.storage = e.data.storage; + } else { + throw 'unexpected.'; + } + }; + } + + process(inputs, outputs, parameters) { + const output = outputs[0]; + const nbChannels = output.length; + const nbSamples = output[0].length; + if (this.head.buffer.byteLength == 0) { + throw new Error('wasmMemory grew'); + } + var head = Atomics.load(this.head, 0) / 4; + var tail = Atomics.load(this.tail, 0) / 4; + var i = 0; + //var volume = Atomics.load(this.volume, 0) / 100; + if (Atomics.load(this.is_paused, 0) != 0 || Atomics.load(this.is_muted, 0) != 0) { + volume = 0; + } + while (tail != head && i < nbSamples) + { + for (let c = 0; c < nbChannels; c++) { + output[c][i] = this.storage[tail]; + // output[c][i] = Math.random() * 2 - 1; + //output[c][i] = this.storage[tail] * volume; + tail++; + if (tail == this.storage.length) { + tail = 0; + } + } + i++; + } + Atomics.store(this.tail, 0, tail * 4); + //Atomics.store(this.can_write, 0, 1); + //Atomics.notify(this.can_write, 0); + return true; + } + + + + // process (inputs, outputs, parameters) { + // const output = outputs[0] + + // for (let i = 0; i < output[0].length; i++) { + // output[0][i] = Math.random() * 2 - 1; + + // this.step++; + // } + // return true + // } + } + +// class SineProcessor extends AudioWorkletProcessor { + +// constructor() +// { +// super(); +// this.step = 7500; +// console.log(Math.sin(2 * Math.PI * this.step)); +// this.port.onmessage = e => { +// if (e.data.type === 'recv-audio-queue') { +// this.is_paused = e.data.is_paused; +// this.head = e.data.head; +// this.tail = e.data.tail; +// this.can_write = e.data.can_write; +// this.volume = e.data.volume; +// this.is_muted = e.data.is_muted; +// this.storage = e.data.storage; +// } else { +// throw 'unexpected.'; +// } +// }; +// } + +// process (inputs, outputs, parameters) { +// const output = outputs[0] +// console.log("nbChannels:", output.length) + +// console.log('NbSamble: ', output[0].length) + +// for (let i = 0; i < output[0].length; i++) { +// output[0][i] = Math.sin((2 * Math.PI * this.step * 440) / 44100); + +// this.step++; +// } + +// output.forEach(channel => { +// for (let i = 0; i < channel.length; i++) { +// channel[i] = Math.sin((2 * Math.PI * this.step * 440) / 44100); + +// this.step++; +// } +// }) +// return true +// } +// } + + registerProcessor('worklet-processor', Processor); \ No newline at end of file diff --git a/ISSUE 35 - Audio API tests/index.html b/ISSUE 35 - Audio API tests/index.html new file mode 100644 index 0000000000000000000000000000000000000000..9e52c6d489799013517b630366589e8c2768df8b --- /dev/null +++ b/ISSUE 35 - Audio API tests/index.html @@ -0,0 +1,3 @@ +<button id="button">PLAY</button> + +<script src="audio.js"></script> \ No newline at end of file diff --git a/ISSUE 35 - Audio API tests/main.c b/ISSUE 35 - Audio API tests/main.c new file mode 100644 index 0000000000000000000000000000000000000000..eb2c400f74ec21794fcddb6458cfbbab27d20940 --- /dev/null +++ b/ISSUE 35 - Audio API tests/main.c @@ -0,0 +1,132 @@ +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> +#include <emscripten.h> + +#include "main.h" +#include <assert.h> + +#define STORAGE_SIZE 1024 * 1024 + +extern sound_buffer_t *init(int sampleRate, int nbChannels, bool aloneMemory); + +int push(float *buffer, void *user_data, size_t data_size) +{ + printf("PUSH\n"); + sound_buffer_t *sab = (sound_buffer_t*)user_data; + + float *sab_view = sab->storage; + int32_t head = sab->head; + int32_t tail = sab->tail; + int32_t volume = sab->volume; + if (!sab->can_write) + { + return -1; + } + printf("%d\n", sab->head); + // TODO: check that we do not write on unconsumed data. + if (head + data_size > STORAGE_SIZE) { + // Copy the part of the data at the buffer end + int32_t data_size_copy_end = STORAGE_SIZE - head; + memcpy(sab_view + head, buffer, data_size_copy_end); + head = 0; + + // Copy the part of the data at the buffer start + int32_t data_size_copy_start = data_size - data_size_copy_end; + memcpy(sab_view + head, buffer, data_size_copy_start); + head = data_size_copy_start; + } + else { + memcpy(sab_view + head, buffer, data_size); + head += data_size; + } + sab->head = head; + printf("SAB HEAD: %d\n", sab->head); + return 0; // return success to indicate successful push. +} + +int is_pushed(void *result) +{ + if (result == NULL) + return 1; + return 0; +} + +int main() +{ + FILE *fd = fopen( "daft.raw", "r" ); + if (fd == NULL) + { + return -1; + } + + sound_buffer_t *sound_buffer = init(44100, 2, true); + + printf("coucou\n"); + + /* Go to the end of the file. */ + if (fseek(fd, 0L, SEEK_END) == 0) { + /* Get the size of the file. */ + long bufsize = ftell(fd); + if (bufsize == -1) { /* Error */ } + + // /* Allocate our buffer to that size. */ + // uint16_t *source = malloc(sizeof(uint16_t) * (bufsize + 1)); + + /* Go back to the start of the file. */ + if (fseek(fd, 0L, SEEK_SET) != 0) { /* Error */ } + + // /* Read the entire file into memory. */ + // size_t newLen = fread(source, sizeof(uint16_t), bufsize, fd); + // if ( ferror( fd ) != 0 ) { + // fputs("Error reading file", stderr); + // } + // else { + // source[newLen++] = '\0'; /* Just to be safe. */ + // } + + /* Loop to push buffers from source */ + float *buffer = malloc(sizeof(float) * STORAGE_SIZE); + + long curPos = 0; + + while(curPos < bufsize) { + /* Fill the buffer */ + printf("BUFFER SIZE: %lu\n", bufsize); + size_t read_bytes = fread(buffer, STORAGE_SIZE, 1, fd) * STORAGE_SIZE; + if (read_bytes == 0) + { + printf("Quitting loop\n"); + if (ferror(fd) != 0) + { + printf("ERROR\n"); + } + else + { + printf("Reached EOF\n"); + } + break; + } + + /* Push the buffer in the worklet */ + + if (push(buffer, sound_buffer, read_bytes) != 0) { printf("Error pushing\n"); } + + // /* Offset the file pointer by BUFFER_SIZE */ + // if (fseek(source, STORAGE_SIZE, SEEK_CUR) != 0) { /* Error */ } + + curPos += read_bytes; + } + + free(buffer); + } + fclose(fd); + + // Nous devons passer un pointeur vers soundbuffer poour l'init + // On alloue une partie de la mémoire d'un côté et les metadatas de l'autre mais rine ne se met à jour + // Revoir les mallocs + + return 0; +} \ No newline at end of file diff --git a/ISSUE 35 - Audio API tests/main.h b/ISSUE 35 - Audio API tests/main.h new file mode 100644 index 0000000000000000000000000000000000000000..399d3457c5c0f21b03ece4358dcfb08a2a19d3fa --- /dev/null +++ b/ISSUE 35 - Audio API tests/main.h @@ -0,0 +1,13 @@ +#pragma once + +#define STORAGE_SIZE 1024 * 1024 + +typedef struct sound_buffer_t { + int32_t head; + int32_t tail; + int32_t volume; + float storage[STORAGE_SIZE]; + int32_t is_paused; + int32_t can_write; + int32_t is_muted; +} sound_buffer_t; \ No newline at end of file diff --git a/ISSUE 35 - Audio API tests/sine.js b/ISSUE 35 - Audio API tests/sine.js new file mode 100644 index 0000000000000000000000000000000000000000..00d731928f3b545121a334cfa11de5f5e6544699 --- /dev/null +++ b/ISSUE 35 - Audio API tests/sine.js @@ -0,0 +1,98 @@ +const STORAGE_SIZE = 1024 * 1024; +const STRUCT_SIZE = 28; + +mergeInto(LibraryManager.library, { + init: function (sampleRate, nbChannels, aloneMemory) + { + console.log("INIT"); + var STORAGE_SIZE = 1024 * 1024; + var STRUCT_SIZE = 28; + + var audioCtx = new AudioContext(); + audioCtx.sampleRate = sampleRate; + audioCtx.suspend(); + + if (audioCtx.destination.channelCount < nbChannels) { + console.log("Max number of channels of the browser is ", audioCtx.destination.channelCount) + nbChannels = audioCtx.destination.channelCount; + } + + const playButton = document.getElementById('button') + + playButton.onclick = function() { + if(audioCtx.state === 'running') { + audioCtx.suspend().then(function() { + playButton.textContent = 'Resume context'; + }); + } else if(audioCtx.state === 'suspended') { + audioCtx.resume().then(function() { + playButton.textContent = 'Suspend context'; + }); + } + } + if (aloneMemory) { + var soundBufferPtr = Module._malloc(STORAGE_SIZE + STRUCT_SIZE); + } + + var offset = soundBufferPtr; + // Needs otimization, be careful with the struct and the offset, and the sizes + + // Offset : if Int32 -> 32/8 * nmem + // if Int8 -> 8/8 * nmem + + // Offset here is 0 + var msg = {}; + msg["type"] = "recv-audio-queue"; + msg["head"] = new Int32Array(wasmMemory.buffer, offset, 1); + console.log("Offset @head ", offset); + offset += (32 / 8) * 1 + // here 4 * 1 because 0 + 32/8 * 1 + msg["tail"] = new Int32Array(wasmMemory.buffer, offset, 1); + console.log("Offset @tail ", offset); + offset += (32 / 8) * 1 + // here 4 * 2 because 4 + 32/8 * 1 + msg["volume"] = new Int32Array(wasmMemory.buffer, offset, 1); + console.log("Offset @volume ", offset); + offset += (32 / 8) * 1 + // here 4 * 3 because 8 + 32/8 * 1 + msg["storage"] = new Float32Array(wasmMemory.buffer, offset, STORAGE_SIZE); + console.log("Offset @storage ", offset); + offset += (32 / 8) * STORAGE_SIZE + // here, 12 + (32/8) * (this.STORAGE_SIZE / 4) = 2097164 + msg["is_paused"] = new Int32Array(wasmMemory.buffer, offset, 1); + console.log("Offset @pause ", offset); + offset += (32 / 8) * 1 + msg["can_write"] = new Int32Array(wasmMemory.buffer, offset, 1) + msg["can_write"][0] = 1; + console.log("Offset @can_write", offset); + offset += (32 / 8) * 1 + msg["is_muted"] = new Int32Array(wasmMemory.buffer, offset, 1); + console.log("Offset @is_muted", offset); + + audioCtx.audioWorklet.addModule('audioWorkletProcessor.js') + .then(() => { + const node = new AudioWorkletNode(audioCtx, 'worklet-processor', { + numberOfInputs: 0, + numberOfOutputs: 1, + outputChannelCount: [2] + }); + node.connect(audioCtx.destination) + console.log('Connected') + + node["port"].postMessage(msg); + }) + + + // var blob = this.createAudioWorkletBlob() + // var url = URL.createObjectURL(blob) + + // await context.audioWorklet.addModule(url) + // .then()); + // const sineNode = new AudioWorkletNode(context, 'sine') + // sineNode.connect(context.destination) + // console.log('Connected') + return (soundBufferPtr); + } +}) + +// registerProcessor('sine', SineProcessor) \ No newline at end of file diff --git a/ISSUE 35 - Audio API tests/snare.raw b/ISSUE 35 - Audio API tests/snare.raw new file mode 100644 index 0000000000000000000000000000000000000000..f97647bd3318c2e5b30595c78a7731ccc80b26f1 Binary files /dev/null and b/ISSUE 35 - Audio API tests/snare.raw differ diff --git a/lib/module-loader.js b/lib/module-loader.js index 1e89cc4e066b681e8e7e6354643e95d3ec92c07e..9ac5fa15cab9142fcdc0fede1b8ac27d7200a5e8 100644 --- a/lib/module-loader.js +++ b/lib/module-loader.js @@ -2,9 +2,14 @@ var statusElement = document.getElementById('status'); var progressElement = document.getElementById('progress'); var spinnerElement = document.getElementById('spinner'); +// This should be set to true once the user clicks on the canvas for the first time +var was_clicked = false; + var VlcModuleExt = { - preRun: [], - postRun: [ function() { + preRun: [ function() { + window.display_overlay = true + }], + onRuntimeInitialized: function() { // This should run after the wasm module is instantiated // before, the Pthread object won't be available VlcModuleExt.PThread.receiveObjectTransfer = function (data) { @@ -14,7 +19,7 @@ var VlcModuleExt = { }); window.dispatchEvent(event); }; - }], + }, print: (function() { var element = document.getElementById('output'); @@ -48,16 +53,21 @@ var VlcModuleExt = { overlay.addEventListener('click', (event) => { window.on_overlay_click(overlay, event); - }); - // Create a global window.display_overlay variable to track whether - // the UI should be visible - see update_overlay function - window.display_overlay = true, - overlay.addEventListener('mouseenter', e => { - window.display_overlay = true; - }); - overlay.addEventListener('mouseleave', e => { - window.display_overlay = false; + // Should only add EventListeners once + if (was_clicked === false) { + // Create a global window.display_overlay variable to track whether + // the UI should be visible - see update_overlay function + overlay.addEventListener('mouseenter', e => { + window.display_overlay = true; + }); + overlay.addEventListener('mouseleave', e => { + window.display_overlay = false; + }); + + // Setting to true after first click on the canvas + was_clicked = true; + } }); return canvas;