2020-08-23 14:28:03 +02:00
|
|
|
module audio
|
|
|
|
|
2021-05-08 12:32:29 +02:00
|
|
|
$if linux {
|
|
|
|
// provide a nicer error for the user that does not have ALSA installed
|
|
|
|
#include <alsa/asoundlib.h> # Please install the `libasound2-dev` package
|
|
|
|
}
|
|
|
|
|
2021-04-19 18:01:47 +02:00
|
|
|
#flag -I @VEXEROOT/thirdparty/sokol
|
2020-08-23 14:28:03 +02:00
|
|
|
#define SOKOL_IMPL
|
|
|
|
#include "sokol_audio.h"
|
|
|
|
#flag linux -lasound
|
2020-08-23 19:55:01 +02:00
|
|
|
#flag darwin -framework AudioToolbox
|
2020-08-23 20:15:21 +02:00
|
|
|
#flag windows -lole32
|
2021-05-08 12:32:29 +02:00
|
|
|
|
2022-01-07 12:28:50 +01:00
|
|
|
// callback function for `stream_cb` in [[C.saudio_desc](#C.saudio_desc)] when calling [audio.setup()](#setup)
|
2020-08-23 14:28:03 +02:00
|
|
|
//
|
2022-01-07 12:28:50 +01:00
|
|
|
// sokol callback functions run in a separate thread
|
|
|
|
//
|
|
|
|
// This function will be called with a reference to the C buffer and the maximum number of frames and channels
|
|
|
|
// the audio backend is expecting in its buffer.
|
|
|
|
//
|
|
|
|
// Terms:
|
|
|
|
// - *sample* - a 32-bit floating point number from `-1.0` to `+1.0` representing the waveform amplitude at that instant
|
|
|
|
// - *frame* - one sample for each channel at that instant
|
|
|
|
//
|
|
|
|
// To determine the number of samples expected, do `num_frames * num_channels`.
|
|
|
|
// Then, write up to that many `f32` samples into `buffer` using unsafe operations.
|
|
|
|
//
|
|
|
|
// Do not write more data to the buffer than it is requesting, but you may write less. The buffer is initialized with
|
|
|
|
// zeroes, so unwritten data will result in audio silence.
|
|
|
|
// Example: unsafe { C.memcpy(buffer, &samples, samples.len * int(sizeof(f32))) }
|
|
|
|
// Example: unsafe { mut b := buffer; for i, sample in samples { b[i] = sample } }
|
2020-10-21 11:23:03 +02:00
|
|
|
pub type FNStreamingCB = fn (buffer &f32, num_frames int, num_channels int)
|
2020-08-23 14:28:03 +02:00
|
|
|
|
2022-01-07 12:28:50 +01:00
|
|
|
// callback function for `stream_userdata_cb` to use in `C.saudio_desc` when calling [audio.setup()](#setup)
|
|
|
|
//
|
|
|
|
// sokol callback functions run in a separate thread
|
|
|
|
//
|
|
|
|
// This function operates the same way as [[FNStreamingCB](#FNStreamingCB)] but it passes customizable `user_data` to the
|
|
|
|
// callback. This is the method to use if your audio data is stored in a struct or array. Identify the
|
|
|
|
// `user_data` when you call `audio.setup()` and that object will be passed to the callback as the last arg.
|
|
|
|
// Example: mut soundbuffer := []f32
|
|
|
|
// Example: soundbuffer << previously_parsed_wavfile_bytes
|
|
|
|
// Example: audio.setup(stream_userdata_cb: mycallback, user_data: soundbuffer)
|
|
|
|
// Example: fn mycallback(buffer &f32, num_frames int, num_channels int, mut sb []f32) { ... }
|
2020-10-21 11:23:03 +02:00
|
|
|
pub type FnStreamingCBWithUserData = fn (buffer &f32, num_frames int, num_channels int, user_data voidptr)
|
2020-08-23 14:28:03 +02:00
|
|
|
|
2020-08-23 19:49:16 +02:00
|
|
|
pub fn (x FNStreamingCB) str() string {
|
|
|
|
return '&FNStreamingCB{ ${ptr_str(x)} }'
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn (x FnStreamingCBWithUserData) str() string {
|
|
|
|
return '&FnStreamingCBWithUserData{ ${ptr_str(x)} }'
|
|
|
|
}
|
|
|
|
|
2022-01-07 12:28:50 +01:00
|
|
|
// only one of `stream_cb` or `stream_userdata_cb` should be used
|
|
|
|
//
|
|
|
|
// default values (internal to sokol C library):
|
2020-08-23 14:28:03 +02:00
|
|
|
//
|
2022-01-07 12:28:50 +01:00
|
|
|
// | variable | default | note |
|
|
|
|
// | :----------- | -------: | :--------- |
|
|
|
|
// | sample_rate | 44100 | higher sample rates take more memory but are higher quality |
|
|
|
|
// | num_channels | 1 | for stereo sound, this should be 2 |
|
|
|
|
// | buffer_frames | 2048 | buffer size in frames, larger is more latency, smaller means higher CPU |
|
|
|
|
// | packet_frames | 128 | push model only, number of frames that will be pushed in each packet |
|
|
|
|
// | num_packets | 64 | for push model only, number of packets in the backend ringbuffer |
|
2020-08-23 14:28:03 +02:00
|
|
|
pub struct C.saudio_desc {
|
|
|
|
sample_rate int
|
|
|
|
num_channels int
|
|
|
|
buffer_frames int
|
|
|
|
packet_frames int
|
|
|
|
num_packets int
|
|
|
|
stream_cb FNStreamingCB
|
|
|
|
stream_userdata_cb FnStreamingCBWithUserData
|
|
|
|
user_data voidptr
|
|
|
|
}
|
|
|
|
|
|
|
|
fn C.saudio_setup(desc &C.saudio_desc)
|
|
|
|
|
|
|
|
fn C.saudio_shutdown()
|
|
|
|
|
|
|
|
fn C.saudio_isvalid() bool
|
|
|
|
|
|
|
|
fn C.saudio_userdata() voidptr
|
|
|
|
|
|
|
|
fn C.saudio_query_desc() C.saudio_desc
|
|
|
|
|
|
|
|
fn C.saudio_sample_rate() int
|
|
|
|
|
|
|
|
fn C.saudio_buffer_frames() int
|
|
|
|
|
|
|
|
fn C.saudio_channels() int
|
|
|
|
|
2022-01-05 17:23:14 +01:00
|
|
|
fn C.saudio_suspended() bool
|
|
|
|
|
2020-08-23 14:28:03 +02:00
|
|
|
fn C.saudio_expect() int
|
|
|
|
|
|
|
|
fn C.saudio_push(frames &f32, num_frames int) int
|
|
|
|
|
2022-01-09 15:12:33 +01:00
|
|
|
// setup - setup sokol-audio
|
2020-08-23 14:28:03 +02:00
|
|
|
pub fn setup(desc C.saudio_desc) {
|
|
|
|
C.saudio_setup(&desc)
|
|
|
|
}
|
|
|
|
|
2022-01-09 15:12:33 +01:00
|
|
|
// shutdown - shutdown sokol-audio
|
2020-08-23 14:28:03 +02:00
|
|
|
pub fn shutdown() {
|
|
|
|
C.saudio_shutdown()
|
|
|
|
}
|
|
|
|
|
2022-01-09 15:12:33 +01:00
|
|
|
// is_valid - true after setup if audio backend was successfully initialized
|
2020-08-23 14:28:03 +02:00
|
|
|
pub fn is_valid() bool {
|
|
|
|
return C.saudio_isvalid()
|
|
|
|
}
|
|
|
|
|
2022-01-09 15:12:33 +01:00
|
|
|
// userdata - return the saudio_desc.user_data pointer
|
2020-08-23 14:28:03 +02:00
|
|
|
pub fn user_data() voidptr {
|
|
|
|
return C.saudio_userdata()
|
|
|
|
}
|
|
|
|
|
2022-01-09 15:12:33 +01:00
|
|
|
// query - return a copy of the original saudio_desc struct
|
2020-08-23 14:28:03 +02:00
|
|
|
pub fn query() C.saudio_desc {
|
|
|
|
return C.saudio_query_desc()
|
|
|
|
}
|
|
|
|
|
2022-01-09 15:12:33 +01:00
|
|
|
// sample_rate - return the actual sample rate
|
2020-08-23 14:28:03 +02:00
|
|
|
pub fn sample_rate() int {
|
|
|
|
return C.saudio_sample_rate()
|
|
|
|
}
|
|
|
|
|
2022-01-09 15:12:33 +01:00
|
|
|
// buffer_frames - return the actual backend buffer size in number of frames
|
2020-08-23 14:28:03 +02:00
|
|
|
pub fn buffer_frames() int {
|
|
|
|
return C.saudio_buffer_frames()
|
|
|
|
}
|
|
|
|
|
2022-01-09 15:12:33 +01:00
|
|
|
// channels - return the actual number of channels
|
2020-08-23 14:28:03 +02:00
|
|
|
pub fn channels() int {
|
|
|
|
return C.saudio_channels()
|
|
|
|
}
|
|
|
|
|
2022-01-05 17:23:14 +01:00
|
|
|
// suspended returns true if audio context is currently suspended
|
|
|
|
// (only in WebAudio backend, all other backends return false)
|
|
|
|
pub fn suspended() bool {
|
|
|
|
return C.saudio_suspended()
|
|
|
|
}
|
|
|
|
|
2022-01-09 15:12:33 +01:00
|
|
|
// expect - get current number of frames to fill packet queue; use in combination with audio.push
|
2020-08-23 14:28:03 +02:00
|
|
|
pub fn expect() int {
|
|
|
|
return C.saudio_expect()
|
|
|
|
}
|
|
|
|
|
2022-01-09 15:12:33 +01:00
|
|
|
// push - push sample frames from main thread, returns number of frames actually pushed
|
2020-08-23 14:28:03 +02:00
|
|
|
pub fn push(frames &f32, num_frames int) int {
|
|
|
|
return C.saudio_push(frames, num_frames)
|
|
|
|
}
|
|
|
|
|
2022-01-09 15:12:33 +01:00
|
|
|
// fclamp - helper function to 'clamp' a number to a certain range
|
2022-01-07 12:28:50 +01:00
|
|
|
// Example: realsample := audio.fclamp(sample, -1.0, 1.0)
|
2020-08-23 14:28:03 +02:00
|
|
|
[inline]
|
2020-10-21 11:23:03 +02:00
|
|
|
pub fn fclamp(x f32, flo f32, fhi f32) f32 {
|
2020-08-23 14:28:03 +02:00
|
|
|
if x > fhi {
|
|
|
|
return fhi
|
|
|
|
}
|
|
|
|
if x < flo {
|
|
|
|
return flo
|
|
|
|
}
|
|
|
|
return x
|
|
|
|
}
|
2020-08-23 19:49:16 +02:00
|
|
|
|
2022-01-09 15:12:33 +01:00
|
|
|
// min - helper function to return the smaller of two numbers
|
2022-01-07 12:28:50 +01:00
|
|
|
//
|
2022-01-09 15:12:33 +01:00
|
|
|
// NOTE: math.min returns `f32` values, this returns `int` values
|
2022-01-07 12:28:50 +01:00
|
|
|
// Example: smaller := audio.min(1, 5) // smaller == 1
|
2020-10-21 11:23:03 +02:00
|
|
|
pub fn min(x int, y int) int {
|
2020-08-23 19:49:16 +02:00
|
|
|
if x < y {
|
|
|
|
return x
|
|
|
|
}
|
|
|
|
return y
|
|
|
|
}
|
|
|
|
|
2022-01-09 15:12:33 +01:00
|
|
|
// max - helper function to return the larger of two numbers
|
2022-01-07 12:28:50 +01:00
|
|
|
//
|
2022-01-09 15:12:33 +01:00
|
|
|
// NOTE: math.max returns `f32` values, this returns `int` values
|
2022-01-07 12:28:50 +01:00
|
|
|
// Example: larger := audio.max(1, 5) // larger == 5
|
2020-10-21 11:23:03 +02:00
|
|
|
pub fn max(x int, y int) int {
|
2020-08-23 19:49:16 +02:00
|
|
|
if x < y {
|
|
|
|
return y
|
|
|
|
}
|
|
|
|
return x
|
|
|
|
}
|