1 /* ______ ___ ___
2 * /\ _ \ /\_ \ /\_ \
3 * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___
4 * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\
5 * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \
6 * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7 * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8 * /\____/
9 * \_/__/
10 *
11 * Sample audio interface.
12 *
13 * By Peter Wang.
14 *
15 * See LICENSE.txt for copyright information.
16 */
17
18 /* Title: Sample audio interface
19 */
20
21 #include "allegro5/allegro.h"
22 #include "allegro5/allegro_audio.h"
23 #include "allegro5/internal/aintern.h"
24 #include "allegro5/internal/aintern_audio.h"
25 #include "allegro5/internal/aintern_vector.h"
26
27 ALLEGRO_DEBUG_CHANNEL("audio")
28
29
30 static ALLEGRO_VOICE *allegro_voice = NULL;
31 static ALLEGRO_MIXER *allegro_mixer = NULL;
32 static ALLEGRO_MIXER *default_mixer = NULL;
33
34
35 typedef struct AUTO_SAMPLE {
36 ALLEGRO_SAMPLE_INSTANCE *instance;
37 int id;
38 bool locked;
39 } AUTO_SAMPLE;
40
41 static _AL_VECTOR auto_samples = _AL_VECTOR_INITIALIZER(AUTO_SAMPLE);
42
43
44 static bool create_default_mixer(void);
45 static bool do_play_sample(ALLEGRO_SAMPLE_INSTANCE *spl, ALLEGRO_SAMPLE *data,
46 float gain, float pan, float speed, ALLEGRO_PLAYMODE loop);
47 static void free_sample_vector(void);
48
49
string_to_depth(const char * s)50 static int string_to_depth(const char *s)
51 {
52 // FIXME: fill in the rest
53 if (!_al_stricmp(s, "int16")) {
54 return ALLEGRO_AUDIO_DEPTH_INT16;
55 }
56 else {
57 return ALLEGRO_AUDIO_DEPTH_FLOAT32;
58 }
59 }
60
61
62 /* Creates the default voice and mixer if they haven't been created yet. */
create_default_mixer(void)63 static bool create_default_mixer(void)
64 {
65 int voice_frequency = 44100;
66 int voice_depth = ALLEGRO_AUDIO_DEPTH_INT16;
67 int mixer_frequency = 44100;
68 int mixer_depth = ALLEGRO_AUDIO_DEPTH_FLOAT32;
69
70 ALLEGRO_CONFIG *config = al_get_system_config();
71 const char *p;
72 p = al_get_config_value(config, "audio", "primary_voice_frequency");
73 if (p && p[0] != '\0') {
74 voice_frequency = atoi(p);
75 }
76 p = al_get_config_value(config, "audio", "primary_mixer_frequency");
77 if (p && p[0] != '\0') {
78 mixer_frequency = atoi(p);
79 }
80 p = al_get_config_value(config, "audio", "primary_voice_depth");
81 if (p && p[0] != '\0') {
82 voice_depth = string_to_depth(p);
83 }
84 p = al_get_config_value(config, "audio", "primary_mixer_depth");
85 if (p && p[0] != '\0') {
86 mixer_depth = string_to_depth(p);
87 }
88
89 if (!allegro_voice) {
90 allegro_voice = al_create_voice(voice_frequency, voice_depth,
91 ALLEGRO_CHANNEL_CONF_2);
92 if (!allegro_voice) {
93 ALLEGRO_ERROR("al_create_voice failed\n");
94 goto Error;
95 }
96 }
97
98 if (!allegro_mixer) {
99 allegro_mixer = al_create_mixer(mixer_frequency, mixer_depth,
100 ALLEGRO_CHANNEL_CONF_2);
101 if (!allegro_mixer) {
102 ALLEGRO_ERROR("al_create_voice failed\n");
103 goto Error;
104 }
105 }
106
107 /* In case this function is called multiple times. */
108 al_detach_mixer(allegro_mixer);
109
110 if (!al_attach_mixer_to_voice(allegro_mixer, allegro_voice)) {
111 ALLEGRO_ERROR("al_attach_mixer_to_voice failed\n");
112 goto Error;
113 }
114
115 return true;
116
117 Error:
118
119 if (allegro_mixer) {
120 al_destroy_mixer(allegro_mixer);
121 allegro_mixer = NULL;
122 }
123
124 if (allegro_voice) {
125 al_destroy_voice(allegro_voice);
126 allegro_voice = NULL;
127 }
128
129 return false;
130 }
131
132
133 /* Function: al_create_sample
134 */
al_create_sample(void * buf,unsigned int samples,unsigned int freq,ALLEGRO_AUDIO_DEPTH depth,ALLEGRO_CHANNEL_CONF chan_conf,bool free_buf)135 ALLEGRO_SAMPLE *al_create_sample(void *buf, unsigned int samples,
136 unsigned int freq, ALLEGRO_AUDIO_DEPTH depth,
137 ALLEGRO_CHANNEL_CONF chan_conf, bool free_buf)
138 {
139 ALLEGRO_SAMPLE *spl;
140
141 ASSERT(buf);
142
143 if (!freq) {
144 _al_set_error(ALLEGRO_INVALID_PARAM, "Invalid sample frequency");
145 return NULL;
146 }
147
148 spl = al_calloc(1, sizeof(*spl));
149 if (!spl) {
150 _al_set_error(ALLEGRO_GENERIC_ERROR,
151 "Out of memory allocating sample data object");
152 return NULL;
153 }
154
155 spl->depth = depth;
156 spl->chan_conf = chan_conf;
157 spl->frequency = freq;
158 spl->len = samples;
159 spl->buffer.ptr = buf;
160 spl->free_buf = free_buf;
161
162 spl->dtor_item = _al_kcm_register_destructor("sample", spl, (void (*)(void *)) al_destroy_sample);
163
164 return spl;
165 }
166
167
168 /* Stop any sample instances which are still playing a sample buffer which
169 * is about to be destroyed.
170 */
stop_sample_instances_helper(void * object,void (* func)(void *),void * userdata)171 static void stop_sample_instances_helper(void *object, void (*func)(void *),
172 void *userdata)
173 {
174 ALLEGRO_SAMPLE_INSTANCE *splinst = object;
175
176 /* This is ugly. */
177 if (func == (void (*)(void *)) al_destroy_sample_instance
178 && al_get_sample_data(al_get_sample(splinst)) == userdata
179 && al_get_sample_instance_playing(splinst))
180 {
181 al_stop_sample_instance(splinst);
182 }
183 }
184
185
186 /* Function: al_destroy_sample
187 */
al_destroy_sample(ALLEGRO_SAMPLE * spl)188 void al_destroy_sample(ALLEGRO_SAMPLE *spl)
189 {
190 if (spl) {
191 _al_kcm_foreach_destructor(stop_sample_instances_helper,
192 al_get_sample_data(spl));
193 _al_kcm_unregister_destructor(spl->dtor_item);
194
195 if (spl->free_buf && spl->buffer.ptr) {
196 al_free(spl->buffer.ptr);
197 }
198 spl->buffer.ptr = NULL;
199 spl->free_buf = false;
200 al_free(spl);
201 }
202 }
203
204
205 /* Function: al_reserve_samples
206 */
al_reserve_samples(int reserve_samples)207 bool al_reserve_samples(int reserve_samples)
208 {
209 int i;
210 int current_samples_count = (int) _al_vector_size(&auto_samples);
211
212 ASSERT(reserve_samples >= 0);
213
214 /* If no default mixer has been set by the user, then create a voice
215 * and a mixer, and set them to be the default one for use with
216 * al_play_sample().
217 */
218 if (default_mixer == NULL) {
219 if (!al_restore_default_mixer())
220 goto Error;
221 }
222
223 if (current_samples_count < reserve_samples) {
224 /* We need to reserve more samples than currently are reserved. */
225 for (i = 0; i < reserve_samples - current_samples_count; i++) {
226 AUTO_SAMPLE *slot = _al_vector_alloc_back(&auto_samples);
227 slot->id = 0;
228 slot->instance = al_create_sample_instance(NULL);
229 slot->locked = false;
230 if (!slot->instance) {
231 ALLEGRO_ERROR("al_create_sample failed\n");
232 goto Error;
233 }
234 if (!al_attach_sample_instance_to_mixer(slot->instance, default_mixer)) {
235 ALLEGRO_ERROR("al_attach_mixer_to_sample failed\n");
236 goto Error;
237 }
238 }
239 }
240 else if (current_samples_count > reserve_samples) {
241 /* We need to reserve fewer samples than currently are reserved. */
242 while (current_samples_count-- > reserve_samples) {
243 AUTO_SAMPLE *slot = _al_vector_ref(&auto_samples, current_samples_count);
244 al_destroy_sample_instance(slot->instance);
245 _al_vector_delete_at(&auto_samples, current_samples_count);
246 }
247 }
248
249 return true;
250
251 Error:
252 free_sample_vector();
253
254 return false;
255 }
256
257
258 /* Function: al_get_default_mixer
259 */
al_get_default_mixer(void)260 ALLEGRO_MIXER *al_get_default_mixer(void)
261 {
262 return default_mixer;
263 }
264
265
266 /* Function: al_set_default_mixer
267 */
al_set_default_mixer(ALLEGRO_MIXER * mixer)268 bool al_set_default_mixer(ALLEGRO_MIXER *mixer)
269 {
270 ASSERT(mixer != NULL);
271
272 if (mixer != default_mixer) {
273 int i;
274
275 default_mixer = mixer;
276
277 /* Destroy all current sample instances, recreate them, and
278 * attach them to the new mixer */
279 for (i = 0; i < (int) _al_vector_size(&auto_samples); i++) {
280 AUTO_SAMPLE *slot = _al_vector_ref(&auto_samples, i);
281
282 slot->id = 0;
283 al_destroy_sample_instance(slot->instance);
284 slot->locked = false;
285
286 slot->instance = al_create_sample_instance(NULL);
287 if (!slot->instance) {
288 ALLEGRO_ERROR("al_create_sample failed\n");
289 goto Error;
290 }
291 if (!al_attach_sample_instance_to_mixer(slot->instance, default_mixer)) {
292 ALLEGRO_ERROR("al_attach_mixer_to_sample failed\n");
293 goto Error;
294 }
295 }
296 }
297
298 return true;
299
300 Error:
301 free_sample_vector();
302 default_mixer = NULL;
303 return false;
304 }
305
306
307 /* Function: al_restore_default_mixer
308 */
al_restore_default_mixer(void)309 bool al_restore_default_mixer(void)
310 {
311 if (!create_default_mixer())
312 return false;
313
314 if (!al_set_default_mixer(allegro_mixer))
315 return false;
316
317 return true;
318 }
319
320
321 /* Function: al_get_default_voice
322 */
al_get_default_voice(void)323 ALLEGRO_VOICE *al_get_default_voice(void)
324 {
325 return allegro_voice;
326 }
327
328
329 /* Function: al_set_default_voice
330 */
al_set_default_voice(ALLEGRO_VOICE * voice)331 void al_set_default_voice(ALLEGRO_VOICE *voice)
332 {
333 if (allegro_voice) {
334 al_destroy_voice(allegro_voice);
335 }
336 allegro_voice = voice;
337 }
338
339
340 /* Function: al_play_sample
341 */
al_play_sample(ALLEGRO_SAMPLE * spl,float gain,float pan,float speed,ALLEGRO_PLAYMODE loop,ALLEGRO_SAMPLE_ID * ret_id)342 bool al_play_sample(ALLEGRO_SAMPLE *spl, float gain, float pan, float speed,
343 ALLEGRO_PLAYMODE loop, ALLEGRO_SAMPLE_ID *ret_id)
344 {
345 static int next_id = 0;
346 unsigned int i;
347
348 ASSERT(spl);
349
350 if (ret_id != NULL) {
351 ret_id->_id = -1;
352 ret_id->_index = 0;
353 }
354
355 for (i = 0; i < _al_vector_size(&auto_samples); i++) {
356 AUTO_SAMPLE *slot = _al_vector_ref(&auto_samples, i);
357
358 if (!al_get_sample_instance_playing(slot->instance) && !slot->locked) {
359 if (!do_play_sample(slot->instance, spl, gain, pan, speed, loop))
360 break;
361
362 if (ret_id != NULL) {
363 ret_id->_index = (int) i;
364 ret_id->_id = slot->id = ++next_id;
365 }
366
367 return true;
368 }
369 }
370
371 return false;
372 }
373
374
do_play_sample(ALLEGRO_SAMPLE_INSTANCE * splinst,ALLEGRO_SAMPLE * spl,float gain,float pan,float speed,ALLEGRO_PLAYMODE loop)375 static bool do_play_sample(ALLEGRO_SAMPLE_INSTANCE *splinst,
376 ALLEGRO_SAMPLE *spl, float gain, float pan, float speed, ALLEGRO_PLAYMODE loop)
377 {
378 if (!al_set_sample(splinst, spl)) {
379 ALLEGRO_ERROR("al_set_sample failed\n");
380 return false;
381 }
382
383 if (!al_set_sample_instance_gain(splinst, gain) ||
384 !al_set_sample_instance_pan(splinst, pan) ||
385 !al_set_sample_instance_speed(splinst, speed) ||
386 !al_set_sample_instance_playmode(splinst, loop)) {
387 return false;
388 }
389
390 if (!al_play_sample_instance(splinst)) {
391 ALLEGRO_ERROR("al_play_sample_instance failed\n");
392 return false;
393 }
394
395 return true;
396 }
397
398
399 /* Function: al_stop_sample
400 */
al_stop_sample(ALLEGRO_SAMPLE_ID * spl_id)401 void al_stop_sample(ALLEGRO_SAMPLE_ID *spl_id)
402 {
403 AUTO_SAMPLE *slot;
404
405 ASSERT(spl_id->_id != -1);
406 ASSERT(spl_id->_index < (int) _al_vector_size(&auto_samples));
407
408 slot = _al_vector_ref(&auto_samples, spl_id->_index);
409 if (slot->id == spl_id->_id) {
410 al_stop_sample_instance(slot->instance);
411 }
412 }
413
414
415 /* Function: al_lock_sample_id
416 */
al_lock_sample_id(ALLEGRO_SAMPLE_ID * spl_id)417 ALLEGRO_SAMPLE_INSTANCE* al_lock_sample_id(ALLEGRO_SAMPLE_ID *spl_id)
418 {
419 AUTO_SAMPLE *slot;
420
421 ASSERT(spl_id->_id != -1);
422 ASSERT(spl_id->_index < (int) _al_vector_size(&auto_samples));
423
424 slot = _al_vector_ref(&auto_samples, spl_id->_index);
425 if (slot->id == spl_id->_id) {
426 slot->locked = true;
427 return slot->instance;
428 }
429 return NULL;
430 }
431
432
433 /* Function: al_unlock_sample_id
434 */
al_unlock_sample_id(ALLEGRO_SAMPLE_ID * spl_id)435 void al_unlock_sample_id(ALLEGRO_SAMPLE_ID *spl_id)
436 {
437 AUTO_SAMPLE *slot;
438
439 ASSERT(spl_id->_id != -1);
440 ASSERT(spl_id->_index < (int) _al_vector_size(&auto_samples));
441
442 slot = _al_vector_ref(&auto_samples, spl_id->_index);
443 if (slot->id == spl_id->_id) {
444 slot->locked = false;
445 }
446 }
447
448
449 /* Function: al_stop_samples
450 */
al_stop_samples(void)451 void al_stop_samples(void)
452 {
453 unsigned int i;
454
455 for (i = 0; i < _al_vector_size(&auto_samples); i++) {
456 AUTO_SAMPLE *slot = _al_vector_ref(&auto_samples, i);
457 al_stop_sample_instance(slot->instance);
458 }
459 }
460
461
462 /* Function: al_get_sample_frequency
463 */
al_get_sample_frequency(const ALLEGRO_SAMPLE * spl)464 unsigned int al_get_sample_frequency(const ALLEGRO_SAMPLE *spl)
465 {
466 ASSERT(spl);
467
468 return spl->frequency;
469 }
470
471
472 /* Function: al_get_sample_length
473 */
al_get_sample_length(const ALLEGRO_SAMPLE * spl)474 unsigned int al_get_sample_length(const ALLEGRO_SAMPLE *spl)
475 {
476 ASSERT(spl);
477
478 return spl->len;
479 }
480
481
482 /* Function: al_get_sample_depth
483 */
al_get_sample_depth(const ALLEGRO_SAMPLE * spl)484 ALLEGRO_AUDIO_DEPTH al_get_sample_depth(const ALLEGRO_SAMPLE *spl)
485 {
486 ASSERT(spl);
487
488 return spl->depth;
489 }
490
491
492 /* Function: al_get_sample_channels
493 */
al_get_sample_channels(const ALLEGRO_SAMPLE * spl)494 ALLEGRO_CHANNEL_CONF al_get_sample_channels(const ALLEGRO_SAMPLE *spl)
495 {
496 ASSERT(spl);
497
498 return spl->chan_conf;
499 }
500
501
502 /* Function: al_get_sample_data
503 */
al_get_sample_data(const ALLEGRO_SAMPLE * spl)504 void *al_get_sample_data(const ALLEGRO_SAMPLE *spl)
505 {
506 ASSERT(spl);
507
508 return spl->buffer.ptr;
509 }
510
511
512 /* Destroy all sample instances, and frees the associated vectors. */
free_sample_vector(void)513 static void free_sample_vector(void)
514 {
515 int j;
516
517 for (j = 0; j < (int) _al_vector_size(&auto_samples); j++) {
518 AUTO_SAMPLE *slot = _al_vector_ref(&auto_samples, j);
519 al_destroy_sample_instance(slot->instance);
520 }
521 _al_vector_free(&auto_samples);
522 }
523
524
_al_kcm_shutdown_default_mixer(void)525 void _al_kcm_shutdown_default_mixer(void)
526 {
527 free_sample_vector();
528 al_destroy_mixer(allegro_mixer);
529 al_destroy_voice(allegro_voice);
530
531 allegro_mixer = NULL;
532 allegro_voice = NULL;
533 default_mixer = NULL;
534 }
535
536
537 /* vim: set sts=3 sw=3 et: */
538