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