diff options
author | Mitya Selivanov <automainint@guattari.tech> | 2024-11-20 06:46:36 +0100 |
---|---|---|
committer | Mitya Selivanov <automainint@guattari.tech> | 2024-11-20 06:46:36 +0100 |
commit | b1b1383304bfa981ce61beb5c6531d6160a75a6b (patch) | |
tree | 3a86ca7f6155bb4f94994a2144c19683d1f7db8b | |
parent | 0a8c2f838fcbb219c54e9a313608f389d403548d (diff) | |
download | reduced_system_layer-b1b1383304bfa981ce61beb5c6531d6160a75a6b.zip |
Web audio ring buffer impl
-rw-r--r-- | Dockerfile | 2 | ||||
-rw-r--r-- | index.htm | 112 | ||||
-rw-r--r-- | nginx.conf | 2 | ||||
-rwxr-xr-x | reduced_system_layer.c | 59 |
4 files changed, 129 insertions, 46 deletions
@@ -1,3 +1,5 @@ +# sudo docker run -p 8080:80 -it $(sudo docker build -q .) + FROM alpine as build RUN apk add clang lld COPY examples /usr/examples @@ -14,18 +14,37 @@ class Sound_Ring extends AudioWorkletProcessor { constructor(options) { super(); - this.state = options.processorOptions.state; - } - process(input, output, parameters) { - // TODO - let sample_rate = sampleRate; - let num_channels = output[0].length; - let num_samples = output[0][0].length; - let max_num_frames = this.state.max_num_frames; + this.ring = options.processorOptions.ring; - let position = this.state.program.instance.exports.js_sound_playback(num_samples); + this.position = 0; + this.max_num_frames = this.ring.byteLength / 4; + } + process(input, output, parameters) { + let num_channels = output[0].length; + let num_samples = output[0][0].length; + let num_frames = num_samples * num_channels; + + let frames = new Float32Array(this.ring); + + if (num_frames <= this.max_num_frames - this.position) + for (let j = 0; j < num_channels; ++j) + for (let i = 0; i < num_samples; ++i) + output[0][j][i] = frames[this.position + i * num_channels + j]; + else { + let part_one = this.max_num_frames - this.position; + let part_two = num_frames - part_one; + + for (let j = 0; j < num_channels; ++j) { + for (let i = 0; i < part_one; ++i) + output[0][j][i] = frames[this.position + i * num_channels + j]; + for (let i = 0; i < part_two; ++i) + output[0][j][part_one + i] = frames[i * num_channels + j]; + } + } + + this.position = (this.position + num_frames) % this.max_num_frames; return true; } } @@ -165,13 +184,12 @@ let sound_context; let sound_init; let sound_node; + let sound_num_channels; + let sound_buffer_address; + let sound_max_num_frames; - let sound_state = { - program : null, - memory : null, - max_num_frames : 0, - buffer_address : 0, - }; + let sound_shared_ring; + let sound_position = 0; canvas = attrs.canvas; context = canvas.getContext("2d"); @@ -191,21 +209,24 @@ await sound_context.audioWorklet.addModule(URL.createObjectURL(blob)); - sound_state.program = program; - sound_state.memory = memory; - sound_state.max_num_frames = program.instance.exports.js_max_num_audio_frames(); - sound_state.buffer_address = program.instance.exports.js_sound_buffer(); + sound_num_channels = program.instance.exports.js_num_channels(); + sound_max_num_frames = program.instance.exports.js_max_num_audio_frames(); + sound_buffer_address = program.instance.exports.js_sound_buffer(); + + sound_shared_ring = new SharedArrayBuffer(sound_max_num_frames * 4); sound_node = new AudioWorkletNode( sound_context, "Sound_Ring", - { numberOfInputs : 0, - outputChannelCount : [ program.instance.exports.js_num_channels() ], - processorOptions : { state : sound_state }, } + { numberOfInputs : 0, + outputChannelCount : [ sound_num_channels ], + processorOptions : { ring : sound_shared_ring, }, } ); sound_node.connect(sound_context.destination); + program.instance.exports.js_sound_sync(); + sound_ready = true; }; @@ -252,6 +273,53 @@ p_sleep_for : (time) => { sleep_duration += time; }, p_time : Date.now, + p_handle_audio_impl : (samples_elapsed) => { + let num_frames = samples_elapsed * sound_num_channels; + + if (num_frames <= sound_max_num_frames - sound_position) + new Uint8Array( + sound_shared_ring.slice( + sound_position * 4, + sound_position * 4 + num_frames * 4 + ) + ).set( + memory.subarray( + sound_buffer_address + sound_position * 4, + sound_buffer_address + sound_position * 4 + num_frames * 4 + ) + ); + else { + let part_one = sound_max_num_frames - sound_position; + let part_two = num_frames - part_one; + + new Uint8Array( + sound_shared_ring.slice( + sound_position * 4, + sound_position * 4 + part_one * 4 + ) + ).set( + memory.subarray( + sound_buffer_address + sound_position * 4, + sound_buffer_address + sound_position * 4 + part_one * 4 + ) + ); + + new Uint8Array( + sound_shared_ring.slice( + 0, + part_two * 4 + ) + ).set( + memory.subarray( + sound_buffer_address, + sound_buffer_address + part_two * 4 + ) + ); + } + + sound_position = (sound_position + samples_elapsed * sound_num_channels) % sound_max_num_frames; + }, + floor : Math.floor, ceil : Math.ceil, sqrt : Math.sqrt, @@ -20,6 +20,8 @@ http { listen 80; listen [::]:80; location / { + add_header Cross-Origin-Opener-Policy same-origin; + add_header Cross-Origin-Embedder-Policy require-corp; root /srv; try_files $uri $uri/index.htm =404; } diff --git a/reduced_system_layer.c b/reduced_system_layer.c index 17956b3..e65653e 100755 --- a/reduced_system_layer.c +++ b/reduced_system_layer.c @@ -1686,19 +1686,50 @@ __attribute__((export_name("js_keyup"))) void js_keyup(u32 key, u32 mod) { #ifdef __wasm__ -i64 _sound_write = 0; i64 _sound_position = 0; f32 _sound_ring[MAX_NUM_AUDIO_FRAMES] = {0}; +void p_handle_audio_impl(i64 samples_elapsed); + void p_handle_audio(i64 samples_elapsed) { if (samples_elapsed <= 0) return; - _sound_write = (_sound_write + samples_elapsed * AUDIO_NUM_CHANNELS) % MAX_NUM_AUDIO_FRAMES; + _sound_position = (_sound_position + samples_elapsed * AUDIO_NUM_CHANNELS) % MAX_NUM_AUDIO_FRAMES; + + p_handle_audio_impl(samples_elapsed); } void p_queue_sound(i64 delay_in_samples, i64 num_samples, f32 *frames) { - // TODO + if (frames == NULL) + return; + + if (delay_in_samples < 0) { + num_samples -= delay_in_samples; + delay_in_samples = 0; + } + + if (num_samples <= 0) + return; + + i64 num_frames = num_samples * AUDIO_NUM_CHANNELS; + if (num_frames > MAX_NUM_AUDIO_FRAMES) + return; + + i64 begin = (_sound_position + delay_in_samples) % MAX_NUM_AUDIO_FRAMES; + + if (num_frames <= MAX_NUM_AUDIO_FRAMES - begin) + for (i64 i = 0; i < num_frames; ++i) + _sound_ring[begin + i] = frames[i]; + else { + i64 part_one = MAX_NUM_AUDIO_FRAMES - begin; + i64 part_two = num_frames - part_one; + + for (i64 i = 0; i < part_one; ++i) + _sound_ring[begin + i] = frames[i]; + for (i64 i = 0; i < part_two; ++i) + _sound_ring[i] = frames[part_one + i]; + } } __attribute__((export_name("js_sample_rate"))) f64 js_sample_rate(void) { @@ -1718,31 +1749,11 @@ __attribute__((export_name("js_sound_buffer"))) void *js_sound_buffer(void) { } __attribute__((export_name("js_sound_sync"))) i64 js_sound_sync(void) { - _sound_write = AUDIO_AVAIL_MIN; - _sound_position = 0; + _sound_position = AUDIO_AVAIL_MIN % MAX_NUM_AUDIO_FRAMES; for (i64 i = 0; i < MAX_NUM_AUDIO_FRAMES; ++i) _sound_ring[i] = 0.f; } -__attribute__((export_name("js_sound_playback"))) i64 js_sound_playback(i64 num_samples) { - i64 position = _sound_position; - i64 num_frames = AUDIO_NUM_CHANNELS * num_samples; - - if (_sound_position + num_frames <= MAX_NUM_AUDIO_FRAMES) { - for (i64 i = _sound_position; i < _sound_position + num_frames; ++i) - _sound_ring[i] = 0.f; - } else { - for (i64 i = _sound_position; i < MAX_NUM_AUDIO_FRAMES; ++i) - _sound_ring[i] = 0.f; - i64 i_end = _sound_position + num_frames - MAX_NUM_AUDIO_FRAMES; - for (i64 i = 0; i < i_end; ++i) - _sound_ring[i] = 0.f; - } - - _sound_position = (_sound_position + num_frames) % MAX_NUM_AUDIO_FRAMES; - return position; -} - #endif // __wasm__ // ================================================================ |