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