v / vlib / sokol / audio / audio.c.v
229 lines · 192 sloc · 6.84 KB · be5f14a17085b766d34320b2a66aac794239f658
Raw
1module audio
2
3import sokol.memory
4
5$if linux {
6 // provide a nicer error for the user that does not have ALSA installed
7 #include <alsa/asoundlib.h> # Please install the `libasound2-dev` package
8}
9
10$if openbsd {
11 $compile_error('sokol/audio not supported on OpenBSD')
12}
13
14#flag -I @VEXEROOT/thirdparty/sokol
15// FreeBSD requires the audio/alsa-lib to be installed
16#flag freebsd -I/usr/local/include
17
18@[use_once]
19#define SOKOL_IMPL
20#include "sokol_audio.h"
21#flag linux -lasound -lpthread
22#flag darwin -framework AudioToolbox
23#flag windows -lole32
24#flag freebsd -L/usr/local/lib
25#flag freebsd -lasound
26#flag android -laaudio
27
28// callback function for `stream_cb` in [[C.saudio_desc](#C.saudio_desc)] when calling [audio.setup()](#setup)
29//
30// sokol callback functions run in a separate thread
31//
32// This function will be called with a reference to the C buffer and the maximum number of frames and channels
33// the audio backend is expecting in its buffer.
34//
35// Terms:
36// - *sample* - a 32-bit floating point number from `-1.0` to `+1.0` representing the waveform amplitude at that instant
37// - *frame* - one sample for each channel at that instant
38//
39// To determine the number of samples expected, do `num_frames * num_channels`.
40// Then, write up to that many `f32` samples into `buffer` using unsafe operations.
41//
42// Do not write more data to the buffer than it is requesting, but you may write less. The buffer is initialized with
43// zeroes, so unwritten data will result in audio silence.
44// Example body: unsafe { C.memcpy(buffer, &samples, samples.len * int(sizeof(f32))) }
45// Example body: unsafe { mut b := buffer; for i, sample in samples { b[i] = sample } }
46pub type FNStreamingCB = fn (buffer &f32, num_frames int, num_channels int)
47
48// callback function for `stream_userdata_cb` to use in `C.saudio_desc` when calling [audio.setup()](#setup)
49// This function operates the same way as [[FNStreamingCB](#FNStreamingCB)] but it passes customizable `user_data` to the
50// callback. This is the method to use if your audio data is stored in a struct or array. Identify the
51// `user_data` when you call `audio.setup()` and that object will be passed to the callback as the last arg.
52// Note: Sokol's callback functions run in a separate thread.
53// Example: previously_parsed_wavfile_bytes := [f32(0),0,0,0]; mycallback := fn (buffer &f32, num_frames int, num_channels int, mut sb []f32) {}; mut soundbuffer := []f32{}; soundbuffer << previously_parsed_wavfile_bytes; audio.setup(stream_userdata_cb: mycallback, user_data: soundbuffer.data);
54pub type FnStreamingCBWithUserData = fn (buffer &f32, num_frames int, num_channels int, user_data voidptr)
55
56pub fn (x FNStreamingCB) str() string {
57 return '&FNStreamingCB{ ${ptr_str(x)} }'
58}
59
60pub fn (x FnStreamingCBWithUserData) str() string {
61 return '&FnStreamingCBWithUserData{ ${ptr_str(x)} }'
62}
63
64@[typedef]
65pub struct C.saudio_allocator {
66pub mut:
67 alloc_fn memory.FnAllocatorAlloc
68 free_fn memory.FnAllocatorFree
69 user_data voidptr
70}
71
72@[typedef]
73pub struct C.saudio_logger {
74pub mut:
75 func memory.FnLogCb
76 user_data voidptr
77}
78
79// only one of `stream_cb` or `stream_userdata_cb` should be used
80//
81// default values (internal to sokol C library):
82//
83// | variable | default | note |
84// | :----------- | -------: | :--------- |
85// | sample_rate | 44100 | higher sample rates take more memory but are higher quality |
86// | num_channels | 1 | for stereo sound, this should be 2 |
87// | buffer_frames | 2048 | buffer size in frames, larger is more latency, smaller means higher CPU |
88// | packet_frames | 128 | push model only, number of frames that will be pushed in each packet |
89// | num_packets | 64 | for push model only, number of packets in the backend ringbuffer |
90@[params; typedef]
91pub struct C.saudio_desc {
92pub:
93 sample_rate int
94 num_channels int
95 buffer_frames int
96 packet_frames int
97 num_packets int
98 stream_cb FNStreamingCB
99 stream_userdata_cb FnStreamingCBWithUserData
100pub mut:
101 user_data voidptr
102 allocator C.saudio_allocator
103 logger C.saudio_logger
104}
105
106fn C.saudio_setup(const_desc &C.saudio_desc)
107
108fn C.saudio_shutdown()
109
110fn C.saudio_isvalid() bool
111
112fn C.saudio_userdata() voidptr
113
114fn C.saudio_query_desc() C.saudio_desc
115
116fn C.saudio_sample_rate() i32
117
118fn C.saudio_buffer_frames() i32
119
120fn C.saudio_channels() i32
121
122fn C.saudio_suspended() bool
123
124fn C.saudio_expect() i32
125
126fn C.saudio_push(const_frames &f32, num_frames i32) i32
127
128// setup - setup sokol-audio
129pub fn setup(desc &C.saudio_desc) {
130 if desc.allocator.alloc_fn == unsafe { nil } && desc.allocator.free_fn == unsafe { nil } {
131 unsafe {
132 desc.allocator.alloc_fn = memory.salloc
133 desc.allocator.free_fn = memory.sfree
134 desc.allocator.user_data = voidptr(0x100a0d10)
135 }
136 }
137 if desc.logger.func == unsafe { nil } {
138 unsafe {
139 desc.logger.func = memory.slog
140 }
141 }
142 C.saudio_setup(desc)
143}
144
145// shutdown - shutdown sokol-audio
146pub fn shutdown() {
147 C.saudio_shutdown()
148}
149
150// is_valid - true after setup if audio backend was successfully initialized
151pub fn is_valid() bool {
152 return C.saudio_isvalid()
153}
154
155// userdata - return the saudio_desc.user_data pointer
156pub fn user_data() voidptr {
157 return C.saudio_userdata()
158}
159
160// query - return a copy of the original saudio_desc struct
161pub fn query() C.saudio_desc {
162 return C.saudio_query_desc()
163}
164
165// sample_rate - return the actual sample rate
166pub fn sample_rate() int {
167 return C.saudio_sample_rate()
168}
169
170// buffer_frames - return the actual backend buffer size in number of frames
171pub fn buffer_frames() int {
172 return C.saudio_buffer_frames()
173}
174
175// channels - return the actual number of channels
176pub fn channels() int {
177 return C.saudio_channels()
178}
179
180// suspended returns true if audio context is currently suspended
181// (only in WebAudio backend, all other backends return false)
182pub fn suspended() bool {
183 return C.saudio_suspended()
184}
185
186// expect - get current number of frames to fill packet queue; use in combination with audio.push
187pub fn expect() int {
188 return C.saudio_expect()
189}
190
191// push - push sample frames from main thread, returns number of frames actually pushed
192pub fn push(frames &f32, num_frames int) int {
193 return C.saudio_push(frames, num_frames)
194}
195
196// fclamp - helper function to 'clamp' a number to a certain range
197// Example: sample := f32(3.14); realsample := audio.fclamp(sample, -1.0, 1.0); assert realsample == 1.0
198@[inline]
199pub fn fclamp(x f32, flo f32, fhi f32) f32 {
200 if x > fhi {
201 return fhi
202 }
203 if x < flo {
204 return flo
205 }
206 return x
207}
208
209// min - helper function to return the smaller of two numbers
210//
211// NOTE: math.min returns `f32` values, this returns `int` values
212// Example: println(audio.min(1, 5))
213pub fn min(x int, y int) int {
214 if x < y {
215 return x
216 }
217 return y
218}
219
220// max - helper function to return the larger of two numbers
221//
222// NOTE: math.max returns `f32` values, this returns `int` values
223// Example: println(audio.max(1, 5))
224pub fn max(x int, y int) int {
225 if x < y {
226 return y
227 }
228 return x
229}
230