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