/
audio.c.v
223 lines (188 loc) · 6.51 KB
/
audio.c.v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
module audio
import sokol.memory
$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
}
#flag -I @VEXEROOT/thirdparty/sokol
#define SOKOL_IMPL
#include "sokol_audio.h"
#flag linux -lasound
#flag darwin -framework AudioToolbox
#flag windows -lole32
// callback function for `stream_cb` in [[C.saudio_desc](#C.saudio_desc)] when calling [audio.setup()](#setup)
//
// 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 } }
pub type FNStreamingCB = fn (buffer &f32, num_frames int, num_channels int)
// 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) { ... }
pub type FnStreamingCBWithUserData = fn (buffer &f32, num_frames int, num_channels int, user_data voidptr)
pub fn (x FNStreamingCB) str() string {
return '&FNStreamingCB{ ${ptr_str(x)} }'
}
pub fn (x FnStreamingCBWithUserData) str() string {
return '&FnStreamingCBWithUserData{ ${ptr_str(x)} }'
}
@[typedef]
pub struct C.saudio_allocator {
pub mut:
alloc_fn memory.FnAllocatorAlloc
free_fn memory.FnAllocatorFree
user_data voidptr
}
@[typedef]
pub struct C.saudio_logger {
pub mut:
func memory.FnLogCb
user_data voidptr
}
// only one of `stream_cb` or `stream_userdata_cb` should be used
//
// default values (internal to sokol C library):
//
// | 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 |
@[typedef]
pub struct C.saudio_desc {
pub:
sample_rate int
num_channels int
buffer_frames int
packet_frames int
num_packets int
stream_cb FNStreamingCB
stream_userdata_cb FnStreamingCBWithUserData
pub mut:
user_data voidptr
allocator C.saudio_allocator
logger C.saudio_logger
}
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
fn C.saudio_suspended() bool
fn C.saudio_expect() int
fn C.saudio_push(frames &f32, num_frames int) int
// setup - setup sokol-audio
pub fn setup(desc &C.saudio_desc) {
if desc.allocator.alloc_fn == unsafe { nil } && desc.allocator.free_fn == unsafe { nil } {
unsafe {
desc.allocator.alloc_fn = memory.salloc
desc.allocator.free_fn = memory.sfree
desc.allocator.user_data = voidptr(0x100a0d10)
}
}
if desc.logger.func == unsafe { nil } {
unsafe {
desc.logger.func = memory.slog
}
}
C.saudio_setup(desc)
}
// shutdown - shutdown sokol-audio
pub fn shutdown() {
C.saudio_shutdown()
}
// is_valid - true after setup if audio backend was successfully initialized
pub fn is_valid() bool {
return C.saudio_isvalid()
}
// userdata - return the saudio_desc.user_data pointer
pub fn user_data() voidptr {
return C.saudio_userdata()
}
// query - return a copy of the original saudio_desc struct
pub fn query() C.saudio_desc {
return C.saudio_query_desc()
}
// sample_rate - return the actual sample rate
pub fn sample_rate() int {
return C.saudio_sample_rate()
}
// buffer_frames - return the actual backend buffer size in number of frames
pub fn buffer_frames() int {
return C.saudio_buffer_frames()
}
// channels - return the actual number of channels
pub fn channels() int {
return C.saudio_channels()
}
// 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()
}
// expect - get current number of frames to fill packet queue; use in combination with audio.push
pub fn expect() int {
return C.saudio_expect()
}
// push - push sample frames from main thread, returns number of frames actually pushed
pub fn push(frames &f32, num_frames int) int {
return C.saudio_push(frames, num_frames)
}
// fclamp - helper function to 'clamp' a number to a certain range
// Example: realsample := audio.fclamp(sample, -1.0, 1.0)
@[inline]
pub fn fclamp(x f32, flo f32, fhi f32) f32 {
if x > fhi {
return fhi
}
if x < flo {
return flo
}
return x
}
// min - helper function to return the smaller of two numbers
//
// NOTE: math.min returns `f32` values, this returns `int` values
// Example: smaller := audio.min(1, 5) // smaller == 1
pub fn min(x int, y int) int {
if x < y {
return x
}
return y
}
// max - helper function to return the larger of two numbers
//
// NOTE: math.max returns `f32` values, this returns `int` values
// Example: larger := audio.max(1, 5) // larger == 5
pub fn max(x int, y int) int {
if x < y {
return y
}
return x
}