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: Voice functions
9  */
10 
11 #include <stdio.h>
12 
13 #include "allegro5/allegro_audio.h"
14 #include "allegro5/internal/aintern_audio.h"
15 #include "allegro5/internal/aintern_audio_cfg.h"
16 
17 ALLEGRO_DEBUG_CHANNEL("audio")
18 
19 
20 /* forward declarations */
21 static void stream_read(void *source, void **vbuf, unsigned int *samples,
22    ALLEGRO_AUDIO_DEPTH buffer_depth, size_t dest_maxc);
23 
24 
25 
26 /* _al_voice_update:
27  *  Reads the attached stream and provides a buffer for the sound card. It is
28  *  the driver's responsiblity to call this and to make sure any
29  *  driver-specific resources associated with the voice are locked. This should
30  *  only be called for streaming sources.
31  *
32  *  The return value is a pointer to the next chunk of audio data in the format
33  *  the voice was allocated with. It may return NULL, in which case it is the
34  *  driver's responsilibty to play silence for the voice. The returned buffer
35  *  must *not* be modified. The 'samples' argument is set to the samples count
36  *  in the returned audio data and it may be less or equal to the requested
37  *  samples count.
38  */
_al_voice_update(ALLEGRO_VOICE * voice,ALLEGRO_MUTEX * mutex,unsigned int * samples)39 const void *_al_voice_update(ALLEGRO_VOICE *voice, ALLEGRO_MUTEX *mutex,
40    unsigned int *samples)
41 {
42    void *buf = NULL;
43 
44    /* The mutex parameter is intended to make it obvious at the call site
45     * that the voice mutex will be acquired here.
46     */
47    ASSERT(voice);
48    ASSERT(voice->mutex == mutex);
49    (void)mutex;
50 
51    al_lock_mutex(voice->mutex);
52    if (voice->attached_stream) {
53       ASSERT(voice->attached_stream->spl_read);
54       voice->attached_stream->spl_read(voice->attached_stream, &buf, samples,
55          voice->depth, 0);
56    }
57    al_unlock_mutex(voice->mutex);
58 
59    return buf;
60 }
61 
62 
63 /* Function: al_create_voice
64  */
al_create_voice(unsigned int freq,ALLEGRO_AUDIO_DEPTH depth,ALLEGRO_CHANNEL_CONF chan_conf)65 ALLEGRO_VOICE *al_create_voice(unsigned int freq,
66    ALLEGRO_AUDIO_DEPTH depth, ALLEGRO_CHANNEL_CONF chan_conf)
67 {
68    ALLEGRO_VOICE *voice = NULL;
69 
70    if (!freq) {
71       _al_set_error(ALLEGRO_INVALID_PARAM, "Invalid Voice Frequency");
72       return NULL;
73    }
74 
75    voice = al_calloc(1, sizeof(*voice));
76    if (!voice) {
77       return NULL;
78    }
79 
80    voice->depth     = depth;
81    voice->chan_conf = chan_conf;
82    voice->frequency = freq;
83 
84    voice->mutex = al_create_mutex();
85    voice->cond = al_create_cond();
86    /* XXX why is this needed? there should only be one active driver */
87    voice->driver = _al_kcm_driver;
88 
89    ASSERT(_al_kcm_driver);
90    if (_al_kcm_driver->allocate_voice(voice) != 0) {
91       al_destroy_mutex(voice->mutex);
92       al_destroy_cond(voice->cond);
93       al_free(voice);
94       return NULL;
95    }
96 
97    voice->dtor_item = _al_kcm_register_destructor("voice", voice,
98       (void (*)(void *)) al_destroy_voice);
99 
100    return voice;
101 }
102 
103 
104 /* Function: al_destroy_voice
105  */
al_destroy_voice(ALLEGRO_VOICE * voice)106 void al_destroy_voice(ALLEGRO_VOICE *voice)
107 {
108    if (voice) {
109       _al_kcm_unregister_destructor(voice->dtor_item);
110 
111       al_detach_voice(voice);
112       ASSERT(al_get_voice_playing(voice) == false);
113 
114       /* We do NOT lock the voice mutex when calling this method. */
115       voice->driver->deallocate_voice(voice);
116       al_destroy_mutex(voice->mutex);
117       al_destroy_cond(voice->cond);
118 
119       al_free(voice);
120    }
121 }
122 
123 
124 /* Function: al_attach_sample_instance_to_voice
125  */
al_attach_sample_instance_to_voice(ALLEGRO_SAMPLE_INSTANCE * spl,ALLEGRO_VOICE * voice)126 bool al_attach_sample_instance_to_voice(ALLEGRO_SAMPLE_INSTANCE *spl,
127    ALLEGRO_VOICE *voice)
128 {
129    bool ret;
130 
131    ASSERT(voice);
132    ASSERT(spl);
133 
134    if (voice->attached_stream) {
135       ALLEGRO_WARN(
136          "Attempted to attach to a voice that already has an attachment\n");
137       _al_set_error(ALLEGRO_INVALID_OBJECT,
138          "Attempted to attach to a voice that already has an attachment");
139       return false;
140    }
141 
142    if (spl->parent.u.ptr) {
143       ALLEGRO_WARN("Attempted to attach a sample that is already attached\n");
144       _al_set_error(ALLEGRO_INVALID_OBJECT,
145          "Attempted to attach a sample that is already attached");
146       return false;
147    }
148 
149    if (voice->chan_conf != spl->spl_data.chan_conf ||
150       voice->frequency != spl->spl_data.frequency ||
151       voice->depth != spl->spl_data.depth)
152    {
153       ALLEGRO_WARN("Sample settings do not match voice settings\n");
154       _al_set_error(ALLEGRO_INVALID_OBJECT,
155          "Sample settings do not match voice settings");
156       return false;
157    }
158 
159    al_lock_mutex(voice->mutex);
160 
161    voice->attached_stream = spl;
162 
163    voice->is_streaming = false;
164    voice->num_buffers = 1;
165    voice->buffer_size = (spl->spl_data.len) *
166                         al_get_channel_count(voice->chan_conf) *
167                         al_get_audio_depth_size(voice->depth);
168 
169    spl->spl_read = NULL;
170    _al_kcm_stream_set_mutex(spl, voice->mutex);
171 
172    spl->parent.u.voice = voice;
173    spl->parent.is_voice = true;
174 
175    if (voice->driver->load_voice(voice, spl->spl_data.buffer.ptr) != 0 ||
176       (spl->is_playing && voice->driver->start_voice(voice) != 0))
177    {
178       voice->attached_stream = NULL;
179       spl->spl_read = NULL;
180       _al_kcm_stream_set_mutex(spl, NULL);
181       spl->parent.u.voice = NULL;
182 
183       ALLEGRO_ERROR("Unable to load sample into voice\n");
184       ret = false;
185    }
186    else {
187       ret = true;
188    }
189 
190    al_unlock_mutex(voice->mutex);
191 
192    return ret;
193 }
194 
195 
196 /* stream_read:
197  *  This passes the next waiting stream buffer to the voice via vbuf.
198  */
stream_read(void * source,void ** vbuf,unsigned int * samples,ALLEGRO_AUDIO_DEPTH buffer_depth,size_t dest_maxc)199 static void stream_read(void *source, void **vbuf, unsigned int *samples,
200    ALLEGRO_AUDIO_DEPTH buffer_depth, size_t dest_maxc)
201 {
202    ALLEGRO_AUDIO_STREAM *stream = (ALLEGRO_AUDIO_STREAM*)source;
203    unsigned int len = stream->spl.spl_data.len;
204    unsigned int pos = stream->spl.pos;
205 
206    if (!stream->spl.is_playing) {
207       *vbuf = NULL;
208       *samples = 0;
209       return;
210    }
211 
212    if (*samples > len)
213       *samples = len;
214 
215    if (pos >= len) {
216       /* XXX: Handle the case where we need to call _al_kcm_refill_stream
217        * multiple times due to ludicrous playback speed. */
218       _al_kcm_refill_stream(stream);
219       if (!stream->pending_bufs[0]) {
220          if (stream->is_draining) {
221             stream->spl.is_playing = false;
222          }
223          *vbuf = NULL;
224          *samples = 0;
225          return;
226       }
227       *vbuf = stream->pending_bufs[0];
228       pos = *samples;
229 
230       _al_kcm_emit_stream_events(stream);
231    }
232    else {
233       int bytes = pos * al_get_channel_count(stream->spl.spl_data.chan_conf)
234                       * al_get_audio_depth_size(stream->spl.spl_data.depth);
235       *vbuf = ((char *)stream->pending_bufs[0]) + bytes;
236 
237       if (pos + *samples > len)
238          *samples = len - pos;
239       pos += *samples;
240    }
241 
242    stream->spl.pos = pos;
243 
244    (void)dest_maxc;
245    (void)buffer_depth;
246 }
247 
248 
249 /* Function: al_attach_audio_stream_to_voice
250  */
al_attach_audio_stream_to_voice(ALLEGRO_AUDIO_STREAM * stream,ALLEGRO_VOICE * voice)251 bool al_attach_audio_stream_to_voice(ALLEGRO_AUDIO_STREAM *stream,
252    ALLEGRO_VOICE *voice)
253 {
254    bool ret;
255 
256    ASSERT(voice);
257    ASSERT(stream);
258 
259    if (voice->attached_stream) {
260       _al_set_error(ALLEGRO_INVALID_OBJECT,
261          "Attempted to attach to a voice that already has an attachment");
262       return false;
263    }
264 
265    if (stream->spl.parent.u.ptr) {
266       _al_set_error(ALLEGRO_INVALID_OBJECT,
267          "Attempted to attach a stream that is already attached");
268       return false;
269    }
270 
271    if (voice->chan_conf != stream->spl.spl_data.chan_conf ||
272       voice->frequency != stream->spl.spl_data.frequency ||
273       voice->depth != stream->spl.spl_data.depth)
274    {
275       _al_set_error(ALLEGRO_INVALID_OBJECT,
276          "Stream settings do not match voice settings");
277       return false;
278    }
279 
280    al_lock_mutex(voice->mutex);
281 
282    voice->attached_stream = &stream->spl;
283 
284    _al_kcm_stream_set_mutex(&stream->spl, voice->mutex);
285 
286    stream->spl.parent.u.voice = voice;
287    stream->spl.parent.is_voice = true;
288 
289    voice->is_streaming = true;
290    voice->num_buffers = stream->buf_count;
291    voice->buffer_size = (stream->spl.spl_data.len) *
292                         al_get_channel_count(stream->spl.spl_data.chan_conf) *
293                         al_get_audio_depth_size(stream->spl.spl_data.depth);
294 
295    ASSERT(stream->spl.spl_read == NULL);
296    stream->spl.spl_read = stream_read;
297 
298    if (voice->driver->start_voice(voice) != 0) {
299       voice->attached_stream = NULL;
300       _al_kcm_stream_set_mutex(&stream->spl, NULL);
301       stream->spl.parent.u.voice = NULL;
302       stream->spl.spl_read = NULL;
303 
304       _al_set_error(ALLEGRO_GENERIC_ERROR, "Unable to start stream");
305       ret = false;
306    }
307    else {
308       ret = true;
309    }
310 
311    al_unlock_mutex(voice->mutex);
312 
313    return ret;
314 }
315 
316 
317 /* Function: al_attach_mixer_to_voice
318  */
al_attach_mixer_to_voice(ALLEGRO_MIXER * mixer,ALLEGRO_VOICE * voice)319 bool al_attach_mixer_to_voice(ALLEGRO_MIXER *mixer, ALLEGRO_VOICE *voice)
320 {
321    bool ret;
322 
323    ASSERT(voice);
324    ASSERT(mixer);
325    ASSERT(mixer->ss.is_mixer);
326 
327    if (voice->attached_stream)
328       return false;
329    if (mixer->ss.parent.u.ptr)
330       return false;
331 
332    if (voice->chan_conf != mixer->ss.spl_data.chan_conf ||
333          voice->frequency != mixer->ss.spl_data.frequency) {
334       return false;
335    }
336 
337    al_lock_mutex(voice->mutex);
338 
339    voice->attached_stream = &mixer->ss;
340    ASSERT(mixer->ss.spl_read == NULL);
341    mixer->ss.spl_read = _al_kcm_mixer_read;
342 
343    _al_kcm_stream_set_mutex(&mixer->ss, voice->mutex);
344 
345    mixer->ss.parent.u.voice = voice;
346    mixer->ss.parent.is_voice = true;
347 
348    voice->is_streaming = true;
349    voice->num_buffers = 0;
350    voice->buffer_size = 0;
351 
352    if (voice->driver->start_voice(voice) != 0) {
353       voice->attached_stream = NULL;
354       _al_kcm_stream_set_mutex(&mixer->ss, NULL);
355       mixer->ss.parent.u.voice = NULL;
356       ret = false;
357    }
358    else {
359       ret = true;
360    }
361 
362    al_unlock_mutex(voice->mutex);
363 
364    return ret;
365 }
366 
367 
368 /* Function: al_detach_voice
369  */
al_detach_voice(ALLEGRO_VOICE * voice)370 void al_detach_voice(ALLEGRO_VOICE *voice)
371 {
372    ASSERT(voice);
373 
374    if (!voice->attached_stream) {
375       return;
376    }
377 
378    al_lock_mutex(voice->mutex);
379 
380    if (!voice->is_streaming) {
381       ALLEGRO_SAMPLE_INSTANCE *spl = voice->attached_stream;
382 
383       spl->pos = voice->driver->get_voice_position(voice);
384       spl->is_playing = voice->driver->voice_is_playing(voice);
385 
386       voice->driver->stop_voice(voice);
387       voice->driver->unload_voice(voice);
388    }
389    else {
390       voice->driver->stop_voice(voice);
391    }
392 
393    _al_kcm_stream_set_mutex(voice->attached_stream, NULL);
394    voice->attached_stream->parent.u.voice = NULL;
395    voice->attached_stream->spl_read = NULL;
396    voice->attached_stream = NULL;
397 
398    al_unlock_mutex(voice->mutex);
399 }
400 
401 
402 /* Function: al_get_voice_frequency
403  */
al_get_voice_frequency(const ALLEGRO_VOICE * voice)404 unsigned int al_get_voice_frequency(const ALLEGRO_VOICE *voice)
405 {
406    ASSERT(voice);
407 
408    return voice->frequency;
409 }
410 
411 
412 /* Function: al_get_voice_position
413  */
al_get_voice_position(const ALLEGRO_VOICE * voice)414 unsigned int al_get_voice_position(const ALLEGRO_VOICE *voice)
415 {
416    ASSERT(voice);
417 
418    if (voice->attached_stream && !voice->is_streaming) {
419       unsigned int ret;
420       al_lock_mutex(voice->mutex);
421       ret = voice->driver->get_voice_position(voice);
422       al_unlock_mutex(voice->mutex);
423       return ret;
424    }
425    else
426       return 0;
427 }
428 
429 
430 /* Function: al_get_voice_channels
431  */
al_get_voice_channels(const ALLEGRO_VOICE * voice)432 ALLEGRO_CHANNEL_CONF al_get_voice_channels(const ALLEGRO_VOICE *voice)
433 {
434    ASSERT(voice);
435 
436    return voice->chan_conf;
437 }
438 
439 
440 /* Function: al_get_voice_depth
441  */
al_get_voice_depth(const ALLEGRO_VOICE * voice)442 ALLEGRO_AUDIO_DEPTH al_get_voice_depth(const ALLEGRO_VOICE *voice)
443 {
444    ASSERT(voice);
445 
446    return voice->depth;
447 }
448 
449 
450 /* Function: al_get_voice_playing
451  */
al_get_voice_playing(const ALLEGRO_VOICE * voice)452 bool al_get_voice_playing(const ALLEGRO_VOICE *voice)
453 {
454    ASSERT(voice);
455 
456    if (voice->attached_stream && !voice->is_streaming) {
457       bool ret;
458       al_lock_mutex(voice->mutex);
459       ret = voice->driver->voice_is_playing(voice);
460       al_unlock_mutex(voice->mutex);
461       return ret;
462    }
463 
464    return voice->attached_stream ? true : false;
465 }
466 
467 
468 /* Function: al_set_voice_position
469  */
al_set_voice_position(ALLEGRO_VOICE * voice,unsigned int val)470 bool al_set_voice_position(ALLEGRO_VOICE *voice, unsigned int val)
471 {
472    ASSERT(voice);
473 
474    if (voice->attached_stream && !voice->is_streaming) {
475       bool ret;
476       al_lock_mutex(voice->mutex);
477       // XXX change method
478       ret = voice->driver->set_voice_position(voice, val) == 0;
479       al_unlock_mutex(voice->mutex);
480       return ret;
481    }
482 
483    return false;
484 }
485 
486 
487 /* Function: al_set_voice_playing
488  */
al_set_voice_playing(ALLEGRO_VOICE * voice,bool val)489 bool al_set_voice_playing(ALLEGRO_VOICE *voice, bool val)
490 {
491    ASSERT(voice);
492 
493    if (!voice->attached_stream) {
494       ALLEGRO_DEBUG("Voice has no attachment\n");
495       return false;
496    }
497 
498    if (voice->is_streaming) {
499       ALLEGRO_WARN("Attempted to change the playing state of a voice "
500          "with a streaming attachment (mixer or audiostreams)\n");
501       return false;
502    }
503    else {
504       bool playing = al_get_voice_playing(voice);
505       if (playing == val) {
506          if (playing) {
507             ALLEGRO_DEBUG("Voice is already playing\n");
508          }
509          else {
510             ALLEGRO_DEBUG("Voice is already stopped\n");
511          }
512          return true;
513       }
514 
515       return _al_kcm_set_voice_playing(voice, voice->mutex, val);
516    }
517 }
518 
519 
_al_kcm_set_voice_playing(ALLEGRO_VOICE * voice,ALLEGRO_MUTEX * mutex,bool val)520 bool _al_kcm_set_voice_playing(ALLEGRO_VOICE *voice, ALLEGRO_MUTEX *mutex,
521    bool val)
522 {
523    bool ret;
524 
525    /* The mutex parameter is intended to make it obvious at the call site
526     * that the voice mutex will be acquired here.
527     */
528    ASSERT(voice);
529    ASSERT(voice->mutex == mutex);
530    (void)mutex;
531 
532    al_lock_mutex(voice->mutex);
533    // XXX change methods
534    if (val)
535       ret = voice->driver->start_voice(voice) == 0;
536    else
537       ret = voice->driver->stop_voice(voice) == 0;
538    al_unlock_mutex(voice->mutex);
539 
540    return ret;
541 }
542 
543 
544 /* vim: set sts=3 sw=3 et: */
545