summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitya Selivanov <automainint@guattari.tech>2024-11-20 06:46:36 +0100
committerMitya Selivanov <automainint@guattari.tech>2024-11-20 06:46:36 +0100
commitb1b1383304bfa981ce61beb5c6531d6160a75a6b (patch)
tree3a86ca7f6155bb4f94994a2144c19683d1f7db8b
parent0a8c2f838fcbb219c54e9a313608f389d403548d (diff)
downloadreduced_system_layer-b1b1383304bfa981ce61beb5c6531d6160a75a6b.zip
Web audio ring buffer impl
-rw-r--r--Dockerfile2
-rw-r--r--index.htm112
-rw-r--r--nginx.conf2
-rwxr-xr-xreduced_system_layer.c59
4 files changed, 129 insertions, 46 deletions
diff --git a/Dockerfile b/Dockerfile
index a4750ca..050a515 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -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
diff --git a/index.htm b/index.htm
index e2e9d55..6624c78 100644
--- a/index.htm
+++ b/index.htm
@@ -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,
diff --git a/nginx.conf b/nginx.conf
index 1297f03..7d641d8 100644
--- a/nginx.conf
+++ b/nginx.conf
@@ -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__
// ================================================================