sysaudio: webaudio: Implement audio playback using data_callback
Supports passing user context as well as provides the current device.
This commit is contained in:
parent
c22e54bcd7
commit
d12ba02c38
1 changed files with 37 additions and 3 deletions
|
|
@ -5,6 +5,8 @@ const js = @import("sysjs");
|
|||
|
||||
const Audio = @This();
|
||||
|
||||
pub const DataCallback = fn (device: *Device, user_data: ?*anyopaque, sample: *f32) void;
|
||||
|
||||
pub const Device = struct {
|
||||
context: js.Object,
|
||||
|
||||
|
|
@ -12,6 +14,13 @@ pub const Device = struct {
|
|||
device.context.deinit();
|
||||
}
|
||||
|
||||
pub fn setCallback(device: Device, callback: DataCallback, user_data: ?*anyopaque) void {
|
||||
device.context.set("device", js.createNumber(@intToFloat(f64, @ptrToInt(&device))));
|
||||
device.context.set("callback", js.createNumber(@intToFloat(f64, @ptrToInt(&callback))));
|
||||
if (user_data) |ud|
|
||||
device.context.set("user_data", js.createNumber(@intToFloat(f64, @ptrToInt(ud))));
|
||||
}
|
||||
|
||||
pub fn pause(device: Device) void {
|
||||
device.context.call("suspend", &.{});
|
||||
}
|
||||
|
|
@ -78,7 +87,7 @@ pub fn requestDevice(audio: Audio, config: DeviceDescriptor) Error!Device {
|
|||
context.set("node", node.toValue());
|
||||
|
||||
{
|
||||
const audio_process_event = js.createFunction(audioProcessEvent);
|
||||
const audio_process_event = js.createFunction(audioProcessEvent, &.{context.toValue()});
|
||||
defer audio_process_event.deinit();
|
||||
node.set("onaudioprocess", audio_process_event.toValue());
|
||||
}
|
||||
|
|
@ -92,9 +101,34 @@ pub fn requestDevice(audio: Audio, config: DeviceDescriptor) Error!Device {
|
|||
return Device{ .context = context };
|
||||
}
|
||||
|
||||
fn audioProcessEvent(args: js.Object, _: usize) js.Value {
|
||||
fn audioProcessEvent(args: js.Object, _: usize, captures: []js.Value) js.Value {
|
||||
const device_context = captures[0].view(.object);
|
||||
|
||||
const audio_event = args.getIndex(0).view(.object);
|
||||
_ = audio_event;
|
||||
const output_buffer = audio_event.get("outputBuffer").view(.object);
|
||||
|
||||
const callback = device_context.get("callback");
|
||||
if (!callback.is(.undef)) {
|
||||
// Do not deinit, we are not making a new device, just creating a view to the current one.
|
||||
var dev = Device{ .context = device_context };
|
||||
const cb = @intToPtr(*DataCallback, @floatToInt(usize, callback.view(.num)));
|
||||
const user_data = device_context.get("user_data");
|
||||
const ud = if (user_data.is(.undef)) null else @intToPtr(*anyopaque, @floatToInt(usize, user_data.view(.num)));
|
||||
|
||||
var channel: usize = 0;
|
||||
while (channel < @floatToInt(usize, output_buffer.get("numberOfChannels").view(.num))) : (channel += 1) {
|
||||
const output_data = output_buffer.call("getChannelData", &.{js.createNumber(@intToFloat(f64, channel))}).view(.object);
|
||||
defer output_data.deinit();
|
||||
|
||||
var sample: usize = 0;
|
||||
while (sample < @floatToInt(usize, output_buffer.get("length").view(.num))) : (sample += 1) {
|
||||
var ret_sample: f32 = undefined;
|
||||
cb.*(&dev, ud, &ret_sample);
|
||||
output_data.setIndex(sample, js.createNumber(ret_sample));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return js.createUndefined();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue