1 #include <rl_sound.h>
2 #include <rl_memory.h>
3 #include <rl_config.h>
4
5 #include <string.h>
6 #include <stdlib.h>
7 #include <stddef.h>
8 #include <math.h>
9
10 #include <rl_endian.c>
11
12 /*---------------------------------------------------------------------------*/
13 #ifdef RL_OGG_VORBIS
14
15 // #define STB_VORBIS_NO_CRT
16 #define STB_VORBIS_NO_STDIO
17 #define STB_VORBIS_NO_PUSHDATA_API
18
19 // #define assert( x )
20 // #define malloc rl_malloc
21 // #define realloc rl_realloc
22 // #define free rl_free
23 // #define pow pow
24 // #define floor floor
25 #define alloca( a ) 0
26
27 #include <stb_vorbis.c>
28
29 #endif
30 /*---------------------------------------------------------------------------*/
31
32 typedef struct
33 {
34 const rl_sound_t* sound;
35 rl_soundstop_t stop_cb;
36
37 int position;
38 int repeat;
39 }
40 voice_t;
41
42 static int16_t audio_buffer[ RL_SAMPLES_PER_FRAME * 2 ];
43 static voice_t voices[ RL_MAX_VOICES ];
44 static int active;
45
46 #ifdef RL_OGG_VORBIS
47 static stb_vorbis* ogg_stream;
48 static stb_vorbis_alloc ogg_alloc;
49 static int16_t ogg_pcm[ 4096 ];
50 static int ogg_position;
51 static int ogg_available;
52 static int ogg_repeat;
53 static rl_soundstop_t ogg_stop_cb;
54 #endif
55
rl_sound_init(void)56 void rl_sound_init( void )
57 {
58 for ( int i = 0; i < RL_MAX_VOICES; i++ )
59 {
60 voices[ i ].sound = NULL;
61 }
62
63 active = 1;
64
65 #ifdef RL_OGG_VORBIS
66 ogg_stream = NULL;
67 #endif
68 }
69
rl_sound_done(void)70 void rl_sound_done( void )
71 {
72 #ifdef RL_OGG_VORBIS
73 if ( ogg_stream )
74 {
75 stb_vorbis_close( ogg_stream );
76 rl_free( ogg_alloc.alloc_buffer );
77 }
78 #endif
79 }
80
rl_sound_pause(void)81 void rl_sound_pause( void )
82 {
83 active -= active != 0;
84 }
85
rl_sound_resume(void)86 void rl_sound_resume( void )
87 {
88 active++;
89 }
90
rl_sound_is_active(void)91 int rl_sound_is_active( void )
92 {
93 return active != 0;
94 }
95
rl_sound_create(const void * data,size_t size,int stereo)96 rl_sound_t* rl_sound_create( const void* data, size_t size, int stereo )
97 {
98 union
99 {
100 const void* restrict v;
101 const uint16_t* restrict u16;
102 }
103 ptr;
104
105 rl_sound_t* sound = (rl_sound_t*)rl_malloc( sizeof( rl_sound_t ) + size );
106
107 if ( sound )
108 {
109 size /= 2;
110
111 sound->samples = size;
112 sound->stereo = stereo;
113
114 uint16_t* restrict pcm = (uint16_t*)( (uint8_t*)sound + sizeof( *sound ) );
115 ptr.v = data;
116
117 const uint16_t* restrict end = pcm + size;
118
119 while ( pcm < end )
120 {
121 *pcm++ = ne16( *ptr.u16++ );
122 }
123
124 return sound;
125 }
126
127 return NULL;
128 }
129
rl_sound_play(const rl_sound_t * sound,int repeat,rl_soundstop_t stop_cb)130 int rl_sound_play( const rl_sound_t* sound, int repeat, rl_soundstop_t stop_cb )
131 {
132 voice_t* restrict voice = voices;
133 const voice_t* restrict end = voices + RL_MAX_VOICES;
134
135 do
136 {
137 if ( !voice->sound )
138 {
139 voice->sound = sound;
140 voice->stop_cb = stop_cb;
141 voice->position = 0;
142 voice->repeat = repeat;
143
144 return voice - voices;
145 }
146
147 voice++;
148 }
149 while ( voice < end );
150
151 return -1;
152 }
153
rl_sound_stop(int index)154 void rl_sound_stop( int index )
155 {
156 voice_t* voice = voices + index;
157
158 if ( voice->stop_cb && voice->sound )
159 {
160 voice->stop_cb( voice->sound );
161 }
162
163 voice->sound = NULL;
164 }
165
rl_sound_stop_all(void)166 void rl_sound_stop_all( void )
167 {
168 voice_t* restrict voice = voices;
169 const voice_t* restrict end = voices + RL_MAX_VOICES;
170
171 while ( voice < end )
172 {
173 if ( voice->stop_cb && voice->sound )
174 {
175 voice->stop_cb( voice->sound );
176 }
177
178 voice->sound = NULL;
179 voice++;
180 }
181 }
182
183 #ifdef RL_OGG_VORBIS
rl_sound_play_ogg(const void * data,size_t size,int repeat,rl_soundstop_t stop_cb)184 int rl_sound_play_ogg( const void* data, size_t size, int repeat, rl_soundstop_t stop_cb )
185 {
186 if ( !ogg_stream )
187 {
188 // This scheme is failing with a SIGSEGV when the buffer size reaches 128Kb
189 // so we just allocate 256Kb for now which seems to work.
190
191 /*
192 ogg_alloc.alloc_buffer = NULL;
193 ogg_alloc.alloc_buffer_length_in_bytes = 0;
194
195 int res;
196
197 do
198 {
199 ogg_alloc.alloc_buffer_length_in_bytes += RL_OGG_INCREMENT;
200
201 void* new_buffer = rl_realloc( ogg_alloc.alloc_buffer, ogg_alloc.alloc_buffer_length_in_bytes );
202
203 if ( !new_buffer )
204 {
205 rl_free( ogg_alloc.alloc_buffer );
206 return -1;
207 }
208
209 ogg_alloc.alloc_buffer = (char*)new_buffer;
210 ogg_stream = stb_vorbis_open_memory( (const unsigned char*)data, size, &res, &ogg_alloc );
211 }
212 while ( !ogg_stream && res == VORBIS_outofmem );
213 */
214
215 ogg_alloc.alloc_buffer = (char*)rl_malloc( 256 * 1024 );
216
217 if ( ogg_alloc.alloc_buffer )
218 {
219 ogg_alloc.alloc_buffer_length_in_bytes = 256 * 1024;
220
221 int res;
222 ogg_stream = stb_vorbis_open_memory( (const unsigned char*)data, size, &res, &ogg_alloc );
223
224 if ( ogg_stream )
225 {
226 ogg_position = ogg_available = 0;
227 ogg_repeat = repeat;
228 ogg_stop_cb = stop_cb;
229 return 0;
230 }
231
232 rl_free( ogg_alloc.alloc_buffer );
233 }
234 }
235
236 ogg_stream = NULL;
237 return -1;
238 }
239
rl_sound_stop_ogg(void)240 void rl_sound_stop_ogg( void )
241 {
242 if ( ogg_stream )
243 {
244 stb_vorbis_close( ogg_stream );
245 rl_free( ogg_alloc.alloc_buffer );
246
247 if ( ogg_stop_cb )
248 {
249 ogg_stop_cb( NULL );
250 }
251
252 ogg_stream = NULL;
253 }
254 }
255
ogg_mix(int32_t * buffer)256 static void ogg_mix( int32_t* buffer )
257 {
258 if ( ogg_stream )
259 {
260 int buf_free = RL_SAMPLES_PER_FRAME * 2;
261
262 if ( ogg_position == ogg_available )
263 {
264 again:
265 ogg_available = stb_vorbis_get_frame_short_interleaved( ogg_stream, 2, ogg_pcm, sizeof( ogg_pcm ) / sizeof( ogg_pcm[ 0 ] ) );
266
267 if ( !ogg_available )
268 {
269 if ( ogg_repeat )
270 {
271 stb_vorbis_seek_start( ogg_stream );
272 goto again;
273 }
274 else
275 {
276 if ( ogg_stop_cb )
277 {
278 ogg_stop_cb( NULL );
279 }
280
281 stb_vorbis_close( ogg_stream );
282 ogg_stream = NULL;
283 return;
284 }
285 }
286
287 ogg_available *= 2;
288 ogg_position = 0;
289 }
290
291 const int16_t* pcm = ogg_pcm + ogg_position;
292
293 if ( ogg_available < buf_free )
294 {
295 for ( int i = ogg_available; i != 0; --i )
296 {
297 *buffer++ += *pcm++;
298 }
299
300 buf_free -= ogg_available;
301 goto again;
302 }
303 else
304 {
305 for ( int i = buf_free; i != 0; --i )
306 {
307 *buffer++ += *pcm++;
308 }
309
310 ogg_position += buf_free;
311 ogg_available -= buf_free;
312 }
313 }
314 }
315 #endif /* RL_OGG_VORBIS */
316
mix(int32_t * buffer,voice_t * voice)317 static void mix( int32_t* buffer, voice_t* voice )
318 {
319 int buf_free = RL_SAMPLES_PER_FRAME * 2;
320 const rl_sound_t* sound = voice->sound;
321
322 again:
323 if ( sound->stereo )
324 {
325 int pcm_available = sound->samples - voice->position;
326 const int16_t* pcm = sound->pcm + voice->position;
327
328 if ( pcm_available < buf_free )
329 {
330 for ( int i = pcm_available; i != 0; --i )
331 {
332 *buffer++ += *pcm++;
333 }
334
335 if ( voice->repeat )
336 {
337 buf_free -= pcm_available;
338 voice->position = 0;
339 goto again;
340 }
341
342 if ( voice->stop_cb )
343 {
344 voice->stop_cb( voice->sound );
345 }
346
347 voice->sound = NULL;
348 }
349 else
350 {
351 for ( int i = buf_free; i != 0; --i )
352 {
353 *buffer++ += *pcm++;
354 }
355
356 voice->position += buf_free;
357 }
358 }
359 else
360 {
361 int pcm_available = sound->samples - voice->position;
362 const int16_t* pcm = sound->pcm + voice->position;
363
364 buf_free /= 2;
365
366 if ( pcm_available < buf_free )
367 {
368 for ( int i = pcm_available; i != 0; --i )
369 {
370 *buffer++ += *pcm;
371 *buffer++ += *pcm++;
372 }
373
374 if ( voice->repeat )
375 {
376 buf_free -= pcm_available;
377 voice->position = 0;
378 goto again;
379 }
380
381 if ( voice->stop_cb )
382 {
383 voice->stop_cb( voice->sound );
384 }
385
386 voice->sound = NULL;
387 }
388 else
389 {
390 for ( int i = buf_free; i != 0; --i )
391 {
392 *buffer++ += *pcm;
393 *buffer++ += *pcm++;
394 }
395
396 voice->position += buf_free;
397 }
398 }
399 }
400
rl_sound_mix(void)401 const int16_t* rl_sound_mix( void )
402 {
403 int32_t buffer[ RL_SAMPLES_PER_FRAME * 2 ];
404
405 if ( !active )
406 {
407 memset( audio_buffer, 0, sizeof( audio_buffer ) );
408 return audio_buffer;
409 }
410
411 memset( buffer, 0, sizeof( buffer ) );
412
413 voice_t* restrict voice = voices;
414 const voice_t* restrict end = voices + RL_MAX_VOICES;
415
416 do
417 {
418 if ( voice->sound )
419 {
420 mix( buffer, voice );
421 }
422
423 voice++;
424 }
425 while ( voice < end );
426
427 #ifdef RL_OGG_VORBIS
428 ogg_mix( buffer );
429 #endif
430
431 for ( int i = 0; i < RL_SAMPLES_PER_FRAME * 2; i++ )
432 {
433 int32_t sample = buffer[ i ];
434 audio_buffer[ i ] = sample < -32768 ? -32768 : sample > 32767 ? 32767 : sample;
435 }
436
437 return audio_buffer;
438 }
439