1 /**
2 * Originally digi.c from allegro wiki
3 * Original authors: KC/Milan
4 *
5 * Converted to allegro5 by Ryan Dickie
6 */
7
8 /* Title: Sample Instance functions
9 */
10
11 #include <math.h>
12 #include <stdio.h>
13
14 #include "allegro5/allegro_audio.h"
15 #include "allegro5/internal/aintern_audio.h"
16 #include "allegro5/internal/aintern_audio_cfg.h"
17
18
maybe_lock_mutex(ALLEGRO_MUTEX * mutex)19 static void maybe_lock_mutex(ALLEGRO_MUTEX *mutex)
20 {
21 if (mutex) {
22 al_lock_mutex(mutex);
23 }
24 }
25
26
maybe_unlock_mutex(ALLEGRO_MUTEX * mutex)27 static void maybe_unlock_mutex(ALLEGRO_MUTEX *mutex)
28 {
29 if (mutex) {
30 al_unlock_mutex(mutex);
31 }
32 }
33
34
35 /* _al_kcm_stream_set_mutex:
36 * This function sets a sample's mutex pointer to the specified value. It is
37 * ALLEGRO_MIXER aware, and will recursively set any attached streams' mutex
38 * to the same value.
39 */
_al_kcm_stream_set_mutex(ALLEGRO_SAMPLE_INSTANCE * stream,ALLEGRO_MUTEX * mutex)40 void _al_kcm_stream_set_mutex(ALLEGRO_SAMPLE_INSTANCE *stream, ALLEGRO_MUTEX *mutex)
41 {
42 ASSERT(stream);
43
44 if (stream->mutex == mutex)
45 return;
46 stream->mutex = mutex;
47
48 /* If this is a mixer, we need to make sure all the attached streams also
49 * set the same mutex.
50 */
51 if (stream->is_mixer) {
52 ALLEGRO_MIXER *mixer = (ALLEGRO_MIXER *)stream;
53 int i;
54
55 for (i = _al_vector_size(&mixer->streams) - 1; i >= 0; i--) {
56 ALLEGRO_SAMPLE_INSTANCE **slot = _al_vector_ref(&mixer->streams, i);
57 ALLEGRO_SAMPLE_INSTANCE *spl = *slot;
58 _al_kcm_stream_set_mutex(spl, mutex);
59 }
60 }
61 }
62
63
64 /* stream_free:
65 * This function is ALLEGRO_MIXER aware and frees the memory associated with
66 * the sample or mixer, and detaches any attached streams or mixers.
67 */
stream_free(ALLEGRO_SAMPLE_INSTANCE * spl)68 static void stream_free(ALLEGRO_SAMPLE_INSTANCE *spl)
69 {
70 if (spl) {
71 /* Make sure we free the mixer buffer and de-reference the attached
72 * streams if this is a mixer stream.
73 */
74 if (spl->is_mixer) {
75 ALLEGRO_MIXER *mixer = (ALLEGRO_MIXER *)spl;
76 int i;
77
78 _al_kcm_stream_set_mutex(&mixer->ss, NULL);
79
80 for (i = _al_vector_size(&mixer->streams) - 1; i >= 0; i--) {
81 ALLEGRO_SAMPLE_INSTANCE **slot = _al_vector_ref(&mixer->streams, i);
82 ALLEGRO_SAMPLE_INSTANCE *spl = *slot;
83
84 spl->parent.u.ptr = NULL;
85 spl->spl_read = NULL;
86 al_free(spl->matrix);
87 spl->matrix = NULL;
88 }
89
90 _al_vector_free(&mixer->streams);
91
92 if (spl->spl_data.buffer.ptr) {
93 ASSERT(spl->spl_data.free_buf);
94 al_free(spl->spl_data.buffer.ptr);
95 spl->spl_data.buffer.ptr = NULL;
96 }
97 spl->spl_data.free_buf = false;
98 }
99
100 ASSERT(! spl->spl_data.free_buf);
101
102 al_free(spl);
103 }
104 }
105
106
107 /* _al_kcm_detach_from_parent:
108 * This detaches the sample, stream, or mixer from anything it may be attached
109 * to.
110 */
_al_kcm_detach_from_parent(ALLEGRO_SAMPLE_INSTANCE * spl)111 void _al_kcm_detach_from_parent(ALLEGRO_SAMPLE_INSTANCE *spl)
112 {
113 ALLEGRO_MIXER *mixer;
114 int i;
115
116 if (!spl || !spl->parent.u.ptr)
117 return;
118
119 if (spl->parent.is_voice) {
120 al_detach_voice(spl->parent.u.voice);
121 return;
122 }
123
124 mixer = spl->parent.u.mixer;
125
126 /* Search through the streams and check for this one */
127 for (i = _al_vector_size(&mixer->streams) - 1; i >= 0; i--) {
128 ALLEGRO_SAMPLE_INSTANCE **slot = _al_vector_ref(&mixer->streams, i);
129
130 if (*slot == spl) {
131 maybe_lock_mutex(mixer->ss.mutex);
132
133 _al_vector_delete_at(&mixer->streams, i);
134 spl->parent.u.mixer = NULL;
135 _al_kcm_stream_set_mutex(spl, NULL);
136
137 spl->spl_read = NULL;
138
139 maybe_unlock_mutex(mixer->ss.mutex);
140
141 break;
142 }
143 }
144
145 al_free(spl->matrix);
146 spl->matrix = NULL;
147 }
148
149
150 /* Function: al_create_sample_instance
151 */
al_create_sample_instance(ALLEGRO_SAMPLE * sample_data)152 ALLEGRO_SAMPLE_INSTANCE *al_create_sample_instance(ALLEGRO_SAMPLE *sample_data)
153 {
154 ALLEGRO_SAMPLE_INSTANCE *spl;
155
156 spl = al_calloc(1, sizeof(*spl));
157 if (!spl) {
158 _al_set_error(ALLEGRO_GENERIC_ERROR,
159 "Out of memory allocating sample object");
160 return NULL;
161 }
162
163 if (sample_data) {
164 spl->spl_data = *sample_data;
165 }
166 spl->spl_data.free_buf = false;
167
168 spl->loop = ALLEGRO_PLAYMODE_ONCE;
169 spl->speed = 1.0f;
170 spl->gain = 1.0f;
171 spl->pan = 0.0f;
172 spl->pos = 0;
173 spl->loop_start = 0;
174 spl->loop_end = sample_data ? sample_data->len : 0;
175 spl->step = 0;
176
177 spl->matrix = NULL;
178
179 spl->is_mixer = false;
180 spl->spl_read = NULL;
181
182 spl->mutex = NULL;
183 spl->parent.u.ptr = NULL;
184
185 spl->dtor_item = _al_kcm_register_destructor("sample_instance", spl,
186 (void (*)(void *))al_destroy_sample_instance);
187
188 return spl;
189 }
190
191
192 /* This function is ALLEGRO_MIXER aware */
193 /* Function: al_destroy_sample_instance
194 */
al_destroy_sample_instance(ALLEGRO_SAMPLE_INSTANCE * spl)195 void al_destroy_sample_instance(ALLEGRO_SAMPLE_INSTANCE *spl)
196 {
197 _al_kcm_destroy_sample(spl, true);
198 }
199
200
201 /* Internal function: _al_kcm_destroy_sample
202 */
_al_kcm_destroy_sample(ALLEGRO_SAMPLE_INSTANCE * spl,bool unregister)203 void _al_kcm_destroy_sample(ALLEGRO_SAMPLE_INSTANCE *spl, bool unregister)
204 {
205 if (spl) {
206 if (unregister) {
207 _al_kcm_unregister_destructor(spl->dtor_item);
208 }
209
210 _al_kcm_detach_from_parent(spl);
211 stream_free(spl);
212 }
213 }
214
215
216 /* Function: al_play_sample_instance
217 */
al_play_sample_instance(ALLEGRO_SAMPLE_INSTANCE * spl)218 bool al_play_sample_instance(ALLEGRO_SAMPLE_INSTANCE *spl)
219 {
220 ASSERT(spl);
221
222 return al_set_sample_instance_playing(spl, true);
223 }
224
225
226 /* Function: al_stop_sample_instance
227 */
al_stop_sample_instance(ALLEGRO_SAMPLE_INSTANCE * spl)228 bool al_stop_sample_instance(ALLEGRO_SAMPLE_INSTANCE *spl)
229 {
230 ASSERT(spl);
231
232 return al_set_sample_instance_playing(spl, false);
233 }
234
235
236 /* Function: al_get_sample_instance_frequency
237 */
al_get_sample_instance_frequency(const ALLEGRO_SAMPLE_INSTANCE * spl)238 unsigned int al_get_sample_instance_frequency(const ALLEGRO_SAMPLE_INSTANCE *spl)
239 {
240 ASSERT(spl);
241
242 return spl->spl_data.frequency;
243 }
244
245
246 /* Function: al_get_sample_instance_length
247 */
al_get_sample_instance_length(const ALLEGRO_SAMPLE_INSTANCE * spl)248 unsigned int al_get_sample_instance_length(const ALLEGRO_SAMPLE_INSTANCE *spl)
249 {
250 ASSERT(spl);
251
252 return spl->spl_data.len;
253 }
254
255
256 /* Function: al_get_sample_instance_position
257 */
al_get_sample_instance_position(const ALLEGRO_SAMPLE_INSTANCE * spl)258 unsigned int al_get_sample_instance_position(const ALLEGRO_SAMPLE_INSTANCE *spl)
259 {
260 ASSERT(spl);
261
262 if (spl->parent.u.ptr && spl->parent.is_voice) {
263 ALLEGRO_VOICE *voice = spl->parent.u.voice;
264 return al_get_voice_position(voice);
265 }
266
267 return spl->pos;
268 }
269
270
271 /* Function: al_get_sample_instance_speed
272 */
al_get_sample_instance_speed(const ALLEGRO_SAMPLE_INSTANCE * spl)273 float al_get_sample_instance_speed(const ALLEGRO_SAMPLE_INSTANCE *spl)
274 {
275 ASSERT(spl);
276
277 return spl->speed;
278 }
279
280
281 /* Function: al_get_sample_instance_gain
282 */
al_get_sample_instance_gain(const ALLEGRO_SAMPLE_INSTANCE * spl)283 float al_get_sample_instance_gain(const ALLEGRO_SAMPLE_INSTANCE *spl)
284 {
285 ASSERT(spl);
286
287 return spl->gain;
288 }
289
290
291 /* Function: al_get_sample_instance_pan
292 */
al_get_sample_instance_pan(const ALLEGRO_SAMPLE_INSTANCE * spl)293 float al_get_sample_instance_pan(const ALLEGRO_SAMPLE_INSTANCE *spl)
294 {
295 ASSERT(spl);
296
297 return spl->pan;
298 }
299
300
301 /* Function: al_get_sample_instance_time
302 */
al_get_sample_instance_time(const ALLEGRO_SAMPLE_INSTANCE * spl)303 float al_get_sample_instance_time(const ALLEGRO_SAMPLE_INSTANCE *spl)
304 {
305 ASSERT(spl);
306
307 return (float)(spl->spl_data.len)
308 / (float)spl->spl_data.frequency;
309 }
310
311
312 /* Function: al_get_sample_instance_depth
313 */
al_get_sample_instance_depth(const ALLEGRO_SAMPLE_INSTANCE * spl)314 ALLEGRO_AUDIO_DEPTH al_get_sample_instance_depth(const ALLEGRO_SAMPLE_INSTANCE *spl)
315 {
316 ASSERT(spl);
317
318 return spl->spl_data.depth;
319 }
320
321
322 /* Function: al_get_sample_instance_channels
323 */
al_get_sample_instance_channels(const ALLEGRO_SAMPLE_INSTANCE * spl)324 ALLEGRO_CHANNEL_CONF al_get_sample_instance_channels(
325 const ALLEGRO_SAMPLE_INSTANCE *spl)
326 {
327 ASSERT(spl);
328
329 return spl->spl_data.chan_conf;
330 }
331
332
333 /* Function: al_get_sample_instance_playmode
334 */
al_get_sample_instance_playmode(const ALLEGRO_SAMPLE_INSTANCE * spl)335 ALLEGRO_PLAYMODE al_get_sample_instance_playmode(const ALLEGRO_SAMPLE_INSTANCE *spl)
336 {
337 ASSERT(spl);
338
339 return spl->loop;
340 }
341
342
343 /* Function: al_get_sample_instance_playing
344 */
al_get_sample_instance_playing(const ALLEGRO_SAMPLE_INSTANCE * spl)345 bool al_get_sample_instance_playing(const ALLEGRO_SAMPLE_INSTANCE *spl)
346 {
347 ASSERT(spl);
348
349 if (spl->parent.u.ptr && spl->parent.is_voice) {
350 ALLEGRO_VOICE *voice = spl->parent.u.voice;
351 return al_get_voice_playing(voice);
352 }
353
354 return spl->is_playing;
355 }
356
357
358 /* Function: al_get_sample_instance_attached
359 */
al_get_sample_instance_attached(const ALLEGRO_SAMPLE_INSTANCE * spl)360 bool al_get_sample_instance_attached(const ALLEGRO_SAMPLE_INSTANCE *spl)
361 {
362 ASSERT(spl);
363
364 return (spl->parent.u.ptr != NULL);
365 }
366
367
368 /* Function: al_set_sample_instance_position
369 */
al_set_sample_instance_position(ALLEGRO_SAMPLE_INSTANCE * spl,unsigned int val)370 bool al_set_sample_instance_position(ALLEGRO_SAMPLE_INSTANCE *spl,
371 unsigned int val)
372 {
373 ASSERT(spl);
374
375 if (spl->parent.u.ptr && spl->parent.is_voice) {
376 ALLEGRO_VOICE *voice = spl->parent.u.voice;
377 if (!al_set_voice_position(voice, val))
378 return false;
379 }
380 else {
381 maybe_lock_mutex(spl->mutex);
382 spl->pos = val;
383 maybe_unlock_mutex(spl->mutex);
384 }
385
386 return true;
387 }
388
389
390 /* Function: al_set_sample_instance_length
391 */
al_set_sample_instance_length(ALLEGRO_SAMPLE_INSTANCE * spl,unsigned int val)392 bool al_set_sample_instance_length(ALLEGRO_SAMPLE_INSTANCE *spl,
393 unsigned int val)
394 {
395 ASSERT(spl);
396
397 if (spl->is_playing) {
398 _al_set_error(ALLEGRO_INVALID_OBJECT,
399 "Attempted to change the length of a playing sample");
400 return false;
401 }
402
403 spl->spl_data.len = val;
404 spl->loop_end = val;
405 return true;
406 }
407
408
409 /* Function: al_set_sample_instance_speed
410 */
al_set_sample_instance_speed(ALLEGRO_SAMPLE_INSTANCE * spl,float val)411 bool al_set_sample_instance_speed(ALLEGRO_SAMPLE_INSTANCE *spl, float val)
412 {
413 ASSERT(spl);
414
415 if (fabsf(val) < (1.0f/64.0f)) {
416 _al_set_error(ALLEGRO_INVALID_PARAM,
417 "Attempted to set zero speed");
418 return false;
419 }
420
421 if (spl->parent.u.ptr && spl->parent.is_voice) {
422 _al_set_error(ALLEGRO_GENERIC_ERROR,
423 "Could not set voice playback speed");
424 return false;
425 }
426
427 spl->speed = val;
428 if (spl->parent.u.mixer) {
429 ALLEGRO_MIXER *mixer = spl->parent.u.mixer;
430
431 maybe_lock_mutex(spl->mutex);
432
433 spl->step = (spl->spl_data.frequency) * spl->speed;
434 spl->step_denom = mixer->ss.spl_data.frequency;
435 /* Don't wanna be trapped with a step value of 0 */
436 if (spl->step == 0) {
437 if (spl->speed > 0.0f)
438 spl->step = 1;
439 else
440 spl->step = -1;
441 }
442
443 maybe_unlock_mutex(spl->mutex);
444 }
445
446 return true;
447 }
448
449
450 /* Function: al_set_sample_instance_gain
451 */
al_set_sample_instance_gain(ALLEGRO_SAMPLE_INSTANCE * spl,float val)452 bool al_set_sample_instance_gain(ALLEGRO_SAMPLE_INSTANCE *spl, float val)
453 {
454 ASSERT(spl);
455
456 if (spl->parent.u.ptr && spl->parent.is_voice) {
457 _al_set_error(ALLEGRO_GENERIC_ERROR,
458 "Could not set gain of sample attached to voice");
459 return false;
460 }
461
462 if (spl->gain != val) {
463 spl->gain = val;
464
465 /* If attached to a mixer already, need to recompute the sample
466 * matrix to take into account the gain.
467 */
468 if (spl->parent.u.mixer) {
469 ALLEGRO_MIXER *mixer = spl->parent.u.mixer;
470
471 maybe_lock_mutex(spl->mutex);
472 _al_kcm_mixer_rejig_sample_matrix(mixer, spl);
473 maybe_unlock_mutex(spl->mutex);
474 }
475 }
476
477 return true;
478 }
479
480
481 /* Function: al_set_sample_instance_pan
482 */
al_set_sample_instance_pan(ALLEGRO_SAMPLE_INSTANCE * spl,float val)483 bool al_set_sample_instance_pan(ALLEGRO_SAMPLE_INSTANCE *spl, float val)
484 {
485 ASSERT(spl);
486
487 if (spl->parent.u.ptr && spl->parent.is_voice) {
488 _al_set_error(ALLEGRO_GENERIC_ERROR,
489 "Could not set panning of sample attached to voice");
490 return false;
491 }
492 if (val != ALLEGRO_AUDIO_PAN_NONE && (val < -1.0 || val > 1.0)) {
493 _al_set_error(ALLEGRO_GENERIC_ERROR, "Invalid pan value");
494 return false;
495 }
496
497 if (spl->pan != val) {
498 spl->pan = val;
499
500 /* If attached to a mixer already, need to recompute the sample
501 * matrix to take into account the panning.
502 */
503 if (spl->parent.u.mixer) {
504 ALLEGRO_MIXER *mixer = spl->parent.u.mixer;
505
506 maybe_lock_mutex(spl->mutex);
507 _al_kcm_mixer_rejig_sample_matrix(mixer, spl);
508 maybe_unlock_mutex(spl->mutex);
509 }
510 }
511
512 return true;
513 }
514
515
516 /* Function: al_set_sample_instance_playmode
517 */
al_set_sample_instance_playmode(ALLEGRO_SAMPLE_INSTANCE * spl,ALLEGRO_PLAYMODE val)518 bool al_set_sample_instance_playmode(ALLEGRO_SAMPLE_INSTANCE *spl,
519 ALLEGRO_PLAYMODE val)
520 {
521 ASSERT(spl);
522
523 if (val < ALLEGRO_PLAYMODE_ONCE || val > ALLEGRO_PLAYMODE_BIDIR) {
524 _al_set_error(ALLEGRO_INVALID_PARAM,
525 "Invalid loop mode");
526 return false;
527 }
528
529 maybe_lock_mutex(spl->mutex);
530
531 spl->loop = val;
532 if (spl->loop != ALLEGRO_PLAYMODE_ONCE) {
533 if (spl->pos < spl->loop_start)
534 spl->pos = spl->loop_start;
535 else if (spl->pos > spl->loop_end-1)
536 spl->pos = spl->loop_end-1;
537 }
538
539 maybe_unlock_mutex(spl->mutex);
540
541 return true;
542 }
543
544
545 /* Function: al_set_sample_instance_playing
546 */
al_set_sample_instance_playing(ALLEGRO_SAMPLE_INSTANCE * spl,bool val)547 bool al_set_sample_instance_playing(ALLEGRO_SAMPLE_INSTANCE *spl, bool val)
548 {
549 ASSERT(spl);
550
551 if (!spl->parent.u.ptr || !spl->spl_data.buffer.ptr) {
552 spl->is_playing = val;
553 return true;
554 }
555
556 if (spl->parent.is_voice) {
557 /* parent is voice */
558 ALLEGRO_VOICE *voice = spl->parent.u.voice;
559
560 return al_set_voice_playing(voice, val);
561 }
562
563 /* parent is mixer */
564 maybe_lock_mutex(spl->mutex);
565 spl->is_playing = val;
566 if (!val)
567 spl->pos = 0;
568 maybe_unlock_mutex(spl->mutex);
569 return true;
570 }
571
572
573 /* Function: al_detach_sample_instance
574 */
al_detach_sample_instance(ALLEGRO_SAMPLE_INSTANCE * spl)575 bool al_detach_sample_instance(ALLEGRO_SAMPLE_INSTANCE *spl)
576 {
577 ASSERT(spl);
578
579 _al_kcm_detach_from_parent(spl);
580 ASSERT(spl->spl_read == NULL);
581 return true;
582 }
583
584
585 /* Function: al_set_sample
586 */
al_set_sample(ALLEGRO_SAMPLE_INSTANCE * spl,ALLEGRO_SAMPLE * data)587 bool al_set_sample(ALLEGRO_SAMPLE_INSTANCE *spl, ALLEGRO_SAMPLE *data)
588 {
589 sample_parent_t old_parent;
590 bool need_reattach;
591
592 ASSERT(spl);
593
594 /* Stop the sample if it is playing. */
595 if (spl->is_playing) {
596 if (!al_set_sample_instance_playing(spl, false)) {
597 /* Shouldn't happen. */
598 ASSERT(false);
599 return false;
600 }
601 }
602
603 if (!data) {
604 if (spl->parent.u.ptr) {
605 _al_kcm_detach_from_parent(spl);
606 }
607 spl->spl_data.buffer.ptr = NULL;
608 return true;
609 }
610
611 /* Have data. */
612
613 need_reattach = false;
614 if (spl->parent.u.ptr != NULL) {
615 if (spl->spl_data.frequency != data->frequency ||
616 spl->spl_data.depth != data->depth ||
617 spl->spl_data.chan_conf != data->chan_conf) {
618 old_parent = spl->parent;
619 need_reattach = true;
620 _al_kcm_detach_from_parent(spl);
621 }
622 }
623
624 spl->spl_data = *data;
625 spl->spl_data.free_buf = false;
626 spl->pos = 0;
627 spl->loop_start = 0;
628 spl->loop_end = data->len;
629 /* Should we reset the loop mode? */
630
631 if (need_reattach) {
632 if (old_parent.is_voice) {
633 if (!al_attach_sample_instance_to_voice(spl, old_parent.u.voice)) {
634 spl->spl_data.buffer.ptr = NULL;
635 return false;
636 }
637 }
638 else {
639 if (!al_attach_sample_instance_to_mixer(spl, old_parent.u.mixer)) {
640 spl->spl_data.buffer.ptr = NULL;
641 return false;
642 }
643 }
644 }
645
646 return true;
647 }
648
649
650 /* Function: al_get_sample
651 */
al_get_sample(ALLEGRO_SAMPLE_INSTANCE * spl)652 ALLEGRO_SAMPLE *al_get_sample(ALLEGRO_SAMPLE_INSTANCE *spl)
653 {
654 ASSERT(spl);
655
656 return &(spl->spl_data);
657 }
658
659
660 /* Function: al_set_sample_instance_channel_matrix
661 */
al_set_sample_instance_channel_matrix(ALLEGRO_SAMPLE_INSTANCE * spl,const float * matrix)662 bool al_set_sample_instance_channel_matrix(ALLEGRO_SAMPLE_INSTANCE *spl, const float *matrix)
663 {
664 ASSERT(spl);
665 ASSERT(matrix);
666
667 if (spl->parent.u.ptr && spl->parent.is_voice) {
668 _al_set_error(ALLEGRO_GENERIC_ERROR,
669 "Could not set channel matrix of sample attached to voice");
670 return false;
671 }
672
673 if (spl->parent.u.mixer) {
674 ALLEGRO_MIXER *mixer = spl->parent.u.mixer;
675 size_t dst_chans = al_get_channel_count(mixer->ss.spl_data.chan_conf);
676 size_t src_chans = al_get_channel_count(spl->spl_data.chan_conf);
677 ASSERT(spl->matrix);
678
679 maybe_lock_mutex(spl->mutex);
680
681 memcpy(spl->matrix, matrix, dst_chans * src_chans * sizeof(float));
682
683 maybe_unlock_mutex(spl->mutex);
684 }
685
686 return true;
687 }
688
689
690 /* vim: set sts=3 sw=3 et: */
691