Mercurial > hg > sv-dependency-builds
comparison src/libsamplerate-0.1.9/examples/audio_out.c @ 41:481f5f8c5634
Current libsamplerate source
author | Chris Cannam |
---|---|
date | Tue, 18 Oct 2016 13:24:45 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
40:1df64224f5ac | 41:481f5f8c5634 |
---|---|
1 /* | |
2 ** Copyright (c) 1999-2016, Erik de Castro Lopo <erikd@mega-nerd.com> | |
3 ** All rights reserved. | |
4 ** | |
5 ** This code is released under 2-clause BSD license. Please see the | |
6 ** file at : https://github.com/erikd/libsamplerate/blob/master/COPYING | |
7 */ | |
8 | |
9 #include <stdio.h> | |
10 #include <stdlib.h> | |
11 #include <string.h> | |
12 #include <unistd.h> | |
13 | |
14 #include <config.h> | |
15 | |
16 #include "audio_out.h" | |
17 | |
18 #if HAVE_ALSA_ASOUNDLIB_H | |
19 #define ALSA_PCM_NEW_HW_PARAMS_API | |
20 #define ALSA_PCM_NEW_SW_PARAMS_API | |
21 #include <alsa/asoundlib.h> | |
22 #include <sys/time.h> | |
23 #endif | |
24 | |
25 #if (HAVE_SNDFILE) | |
26 | |
27 #include <float_cast.h> | |
28 | |
29 #include <sndfile.h> | |
30 | |
31 #define BUFFER_LEN (2048) | |
32 | |
33 #define MAKE_MAGIC(a,b,c,d,e,f,g,h) \ | |
34 ((a) + ((b) << 1) + ((c) << 2) + ((d) << 3) + ((e) << 4) + ((f) << 5) + ((g) << 6) + ((h) << 7)) | |
35 | |
36 typedef struct AUDIO_OUT_s | |
37 { int magic ; | |
38 } AUDIO_OUT ; | |
39 | |
40 | |
41 /*------------------------------------------------------------------------------ | |
42 ** Linux (ALSA and OSS) functions for playing a sound. | |
43 */ | |
44 | |
45 #if defined (__linux__) | |
46 | |
47 #if HAVE_ALSA_ASOUNDLIB_H | |
48 | |
49 #define ALSA_MAGIC MAKE_MAGIC ('L', 'n', 'x', '-', 'A', 'L', 'S', 'A') | |
50 | |
51 typedef struct | |
52 { int magic ; | |
53 snd_pcm_t * dev ; | |
54 int channels ; | |
55 } ALSA_AUDIO_OUT ; | |
56 | |
57 static int alsa_write_float (snd_pcm_t *alsa_dev, float *data, int frames, int channels) ; | |
58 | |
59 static AUDIO_OUT * | |
60 alsa_open (int channels, unsigned samplerate) | |
61 { ALSA_AUDIO_OUT *alsa_out ; | |
62 const char * device = "default" ; | |
63 snd_pcm_hw_params_t *hw_params ; | |
64 snd_pcm_uframes_t buffer_size ; | |
65 snd_pcm_uframes_t alsa_period_size, alsa_buffer_frames ; | |
66 snd_pcm_sw_params_t *sw_params ; | |
67 | |
68 int err ; | |
69 | |
70 alsa_period_size = 1024 ; | |
71 alsa_buffer_frames = 4 * alsa_period_size ; | |
72 | |
73 if ((alsa_out = calloc (1, sizeof (ALSA_AUDIO_OUT))) == NULL) | |
74 { perror ("alsa_open : malloc ") ; | |
75 exit (1) ; | |
76 } ; | |
77 | |
78 alsa_out->magic = ALSA_MAGIC ; | |
79 alsa_out->channels = channels ; | |
80 | |
81 if ((err = snd_pcm_open (&alsa_out->dev, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) | |
82 { fprintf (stderr, "cannot open audio device \"%s\" (%s)\n", device, snd_strerror (err)) ; | |
83 goto catch_error ; | |
84 } ; | |
85 | |
86 snd_pcm_nonblock (alsa_out->dev, 0) ; | |
87 | |
88 if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) | |
89 { fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n", snd_strerror (err)) ; | |
90 goto catch_error ; | |
91 } ; | |
92 | |
93 if ((err = snd_pcm_hw_params_any (alsa_out->dev, hw_params)) < 0) | |
94 { fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n", snd_strerror (err)) ; | |
95 goto catch_error ; | |
96 } ; | |
97 | |
98 if ((err = snd_pcm_hw_params_set_access (alsa_out->dev, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) | |
99 { fprintf (stderr, "cannot set access type (%s)\n", snd_strerror (err)) ; | |
100 goto catch_error ; | |
101 } ; | |
102 | |
103 if ((err = snd_pcm_hw_params_set_format (alsa_out->dev, hw_params, SND_PCM_FORMAT_FLOAT)) < 0) | |
104 { fprintf (stderr, "cannot set sample format (%s)\n", snd_strerror (err)) ; | |
105 goto catch_error ; | |
106 } ; | |
107 | |
108 if ((err = snd_pcm_hw_params_set_rate_near (alsa_out->dev, hw_params, &samplerate, 0)) < 0) | |
109 { fprintf (stderr, "cannot set sample rate (%s)\n", snd_strerror (err)) ; | |
110 goto catch_error ; | |
111 } ; | |
112 | |
113 if ((err = snd_pcm_hw_params_set_channels (alsa_out->dev, hw_params, channels)) < 0) | |
114 { fprintf (stderr, "cannot set channel count (%s)\n", snd_strerror (err)) ; | |
115 goto catch_error ; | |
116 } ; | |
117 | |
118 if ((err = snd_pcm_hw_params_set_buffer_size_near (alsa_out->dev, hw_params, &alsa_buffer_frames)) < 0) | |
119 { fprintf (stderr, "cannot set buffer size (%s)\n", snd_strerror (err)) ; | |
120 goto catch_error ; | |
121 } ; | |
122 | |
123 if ((err = snd_pcm_hw_params_set_period_size_near (alsa_out->dev, hw_params, &alsa_period_size, 0)) < 0) | |
124 { fprintf (stderr, "cannot set period size (%s)\n", snd_strerror (err)) ; | |
125 goto catch_error ; | |
126 } ; | |
127 | |
128 if ((err = snd_pcm_hw_params (alsa_out->dev, hw_params)) < 0) | |
129 { fprintf (stderr, "cannot set parameters (%s)\n", snd_strerror (err)) ; | |
130 goto catch_error ; | |
131 } ; | |
132 | |
133 /* extra check: if we have only one period, this code won't work */ | |
134 snd_pcm_hw_params_get_period_size (hw_params, &alsa_period_size, 0) ; | |
135 snd_pcm_hw_params_get_buffer_size (hw_params, &buffer_size) ; | |
136 if (alsa_period_size == buffer_size) | |
137 { fprintf (stderr, "Can't use period equal to buffer size (%lu == %lu)", alsa_period_size, buffer_size) ; | |
138 goto catch_error ; | |
139 } ; | |
140 | |
141 snd_pcm_hw_params_free (hw_params) ; | |
142 | |
143 if ((err = snd_pcm_sw_params_malloc (&sw_params)) != 0) | |
144 { fprintf (stderr, "%s: snd_pcm_sw_params_malloc: %s", __func__, snd_strerror (err)) ; | |
145 goto catch_error ; | |
146 } ; | |
147 | |
148 if ((err = snd_pcm_sw_params_current (alsa_out->dev, sw_params)) != 0) | |
149 { fprintf (stderr, "%s: snd_pcm_sw_params_current: %s", __func__, snd_strerror (err)) ; | |
150 goto catch_error ; | |
151 } ; | |
152 | |
153 /* note: set start threshold to delay start until the ring buffer is full */ | |
154 snd_pcm_sw_params_current (alsa_out->dev, sw_params) ; | |
155 | |
156 if ((err = snd_pcm_sw_params_set_start_threshold (alsa_out->dev, sw_params, buffer_size)) < 0) | |
157 { fprintf (stderr, "cannot set start threshold (%s)\n", snd_strerror (err)) ; | |
158 goto catch_error ; | |
159 } ; | |
160 | |
161 if ((err = snd_pcm_sw_params (alsa_out->dev, sw_params)) != 0) | |
162 { fprintf (stderr, "%s: snd_pcm_sw_params: %s", __func__, snd_strerror (err)) ; | |
163 goto catch_error ; | |
164 } ; | |
165 | |
166 snd_pcm_sw_params_free (sw_params) ; | |
167 | |
168 snd_pcm_reset (alsa_out->dev) ; | |
169 | |
170 catch_error : | |
171 | |
172 if (err < 0 && alsa_out->dev != NULL) | |
173 { snd_pcm_close (alsa_out->dev) ; | |
174 return NULL ; | |
175 } ; | |
176 | |
177 return (AUDIO_OUT *) alsa_out ; | |
178 } /* alsa_open */ | |
179 | |
180 static void | |
181 alsa_play (get_audio_callback_t callback, AUDIO_OUT *audio_out, void *callback_data) | |
182 { static float buffer [BUFFER_LEN] ; | |
183 ALSA_AUDIO_OUT *alsa_out ; | |
184 int read_frames ; | |
185 | |
186 if ((alsa_out = (ALSA_AUDIO_OUT*) audio_out) == NULL) | |
187 { printf ("alsa_close : AUDIO_OUT is NULL.\n") ; | |
188 return ; | |
189 } ; | |
190 | |
191 if (alsa_out->magic != ALSA_MAGIC) | |
192 { printf ("alsa_close : Bad magic number.\n") ; | |
193 return ; | |
194 } ; | |
195 | |
196 while ((read_frames = callback (callback_data, buffer, BUFFER_LEN / alsa_out->channels))) | |
197 alsa_write_float (alsa_out->dev, buffer, read_frames, alsa_out->channels) ; | |
198 | |
199 return ; | |
200 } /* alsa_play */ | |
201 | |
202 static int | |
203 alsa_write_float (snd_pcm_t *alsa_dev, float *data, int frames, int channels) | |
204 { static int epipe_count = 0 ; | |
205 | |
206 int total = 0 ; | |
207 int retval ; | |
208 | |
209 if (epipe_count > 0) | |
210 epipe_count -- ; | |
211 | |
212 while (total < frames) | |
213 { retval = snd_pcm_writei (alsa_dev, data + total * channels, frames - total) ; | |
214 | |
215 if (retval >= 0) | |
216 { total += retval ; | |
217 if (total == frames) | |
218 return total ; | |
219 | |
220 continue ; | |
221 } ; | |
222 | |
223 switch (retval) | |
224 { case -EAGAIN : | |
225 puts ("alsa_write_float: EAGAIN") ; | |
226 continue ; | |
227 break ; | |
228 | |
229 case -EPIPE : | |
230 if (epipe_count > 0) | |
231 { printf ("alsa_write_float: EPIPE %d\n", epipe_count) ; | |
232 if (epipe_count > 140) | |
233 return retval ; | |
234 } ; | |
235 epipe_count += 100 ; | |
236 | |
237 #if 0 | |
238 if (0) | |
239 { snd_pcm_status_t *status ; | |
240 | |
241 snd_pcm_status_alloca (&status) ; | |
242 if ((retval = snd_pcm_status (alsa_dev, status)) < 0) | |
243 fprintf (stderr, "alsa_out: xrun. can't determine length\n") ; | |
244 else if (snd_pcm_status_get_state (status) == SND_PCM_STATE_XRUN) | |
245 { struct timeval now, diff, tstamp ; | |
246 | |
247 gettimeofday (&now, 0) ; | |
248 snd_pcm_status_get_trigger_tstamp (status, &tstamp) ; | |
249 timersub (&now, &tstamp, &diff) ; | |
250 | |
251 fprintf (stderr, "alsa_write_float xrun: of at least %.3f msecs. resetting stream\n", | |
252 diff.tv_sec * 1000 + diff.tv_usec / 1000.0) ; | |
253 } | |
254 else | |
255 fprintf (stderr, "alsa_write_float: xrun. can't determine length\n") ; | |
256 } ; | |
257 #endif | |
258 | |
259 snd_pcm_prepare (alsa_dev) ; | |
260 break ; | |
261 | |
262 case -EBADFD : | |
263 fprintf (stderr, "alsa_write_float: Bad PCM state.n") ; | |
264 return 0 ; | |
265 break ; | |
266 | |
267 case -ESTRPIPE : | |
268 fprintf (stderr, "alsa_write_float: Suspend event.n") ; | |
269 return 0 ; | |
270 break ; | |
271 | |
272 case -EIO : | |
273 puts ("alsa_write_float: EIO") ; | |
274 return 0 ; | |
275 | |
276 default : | |
277 fprintf (stderr, "alsa_write_float: retval = %d\n", retval) ; | |
278 return 0 ; | |
279 break ; | |
280 } ; /* switch */ | |
281 } ; /* while */ | |
282 | |
283 return total ; | |
284 } /* alsa_write_float */ | |
285 | |
286 static void | |
287 alsa_close (AUDIO_OUT *audio_out) | |
288 { ALSA_AUDIO_OUT *alsa_out ; | |
289 | |
290 if ((alsa_out = (ALSA_AUDIO_OUT*) audio_out) == NULL) | |
291 { printf ("alsa_close : AUDIO_OUT is NULL.\n") ; | |
292 return ; | |
293 } ; | |
294 | |
295 if (alsa_out->magic != ALSA_MAGIC) | |
296 { printf ("alsa_close : Bad magic number.\n") ; | |
297 return ; | |
298 } ; | |
299 | |
300 memset (alsa_out, 0, sizeof (ALSA_AUDIO_OUT)) ; | |
301 | |
302 free (alsa_out) ; | |
303 | |
304 return ; | |
305 } /* alsa_close */ | |
306 | |
307 #endif /* HAVE_ALSA_ASOUNDLIB_H */ | |
308 | |
309 #include <fcntl.h> | |
310 #include <sys/ioctl.h> | |
311 #include <sys/soundcard.h> | |
312 | |
313 #define OSS_MAGIC MAKE_MAGIC ('L', 'i', 'n', 'u', 'x', 'O', 'S', 'S') | |
314 | |
315 typedef struct | |
316 { int magic ; | |
317 int fd ; | |
318 int channels ; | |
319 } OSS_AUDIO_OUT ; | |
320 | |
321 static AUDIO_OUT *opensoundsys_open (int channels, int samplerate) ; | |
322 static void opensoundsys_play (get_audio_callback_t callback, AUDIO_OUT *audio_out, void *callback_data) ; | |
323 static void opensoundsys_close (AUDIO_OUT *audio_out) ; | |
324 | |
325 | |
326 static AUDIO_OUT * | |
327 opensoundsys_open (int channels, int samplerate) | |
328 { OSS_AUDIO_OUT *opensoundsys_out ; | |
329 int stereo, fmt, error ; | |
330 | |
331 if ((opensoundsys_out = calloc (1, sizeof (OSS_AUDIO_OUT))) == NULL) | |
332 { perror ("opensoundsys_open : malloc ") ; | |
333 exit (1) ; | |
334 } ; | |
335 | |
336 opensoundsys_out->magic = OSS_MAGIC ; | |
337 opensoundsys_out->channels = channels ; | |
338 | |
339 if ((opensoundsys_out->fd = open ("/dev/dsp", O_WRONLY, 0)) == -1) | |
340 { perror ("opensoundsys_open : open ") ; | |
341 exit (1) ; | |
342 } ; | |
343 | |
344 stereo = 0 ; | |
345 if (ioctl (opensoundsys_out->fd, SNDCTL_DSP_STEREO, &stereo) == -1) | |
346 { /* Fatal error */ | |
347 perror ("opensoundsys_open : stereo ") ; | |
348 exit (1) ; | |
349 } ; | |
350 | |
351 if (ioctl (opensoundsys_out->fd, SNDCTL_DSP_RESET, 0)) | |
352 { perror ("opensoundsys_open : reset ") ; | |
353 exit (1) ; | |
354 } ; | |
355 | |
356 fmt = CPU_IS_BIG_ENDIAN ? AFMT_S16_BE : AFMT_S16_LE ; | |
357 if (ioctl (opensoundsys_out->fd, SNDCTL_DSP_SETFMT, &fmt) != 0) | |
358 { perror ("opensoundsys_open_dsp_device : set format ") ; | |
359 exit (1) ; | |
360 } ; | |
361 | |
362 if ((error = ioctl (opensoundsys_out->fd, SNDCTL_DSP_CHANNELS, &channels)) != 0) | |
363 { perror ("opensoundsys_open : channels ") ; | |
364 exit (1) ; | |
365 } ; | |
366 | |
367 if ((error = ioctl (opensoundsys_out->fd, SNDCTL_DSP_SPEED, &samplerate)) != 0) | |
368 { perror ("opensoundsys_open : sample rate ") ; | |
369 exit (1) ; | |
370 } ; | |
371 | |
372 if ((error = ioctl (opensoundsys_out->fd, SNDCTL_DSP_SYNC, 0)) != 0) | |
373 { perror ("opensoundsys_open : sync ") ; | |
374 exit (1) ; | |
375 } ; | |
376 | |
377 return (AUDIO_OUT*) opensoundsys_out ; | |
378 } /* opensoundsys_open */ | |
379 | |
380 static void | |
381 opensoundsys_play (get_audio_callback_t callback, AUDIO_OUT *audio_out, void *callback_data) | |
382 { OSS_AUDIO_OUT *opensoundsys_out ; | |
383 static float float_buffer [BUFFER_LEN] ; | |
384 static short buffer [BUFFER_LEN] ; | |
385 int k, read_frames ; | |
386 | |
387 if ((opensoundsys_out = (OSS_AUDIO_OUT*) audio_out) == NULL) | |
388 { printf ("opensoundsys_play : AUDIO_OUT is NULL.\n") ; | |
389 return ; | |
390 } ; | |
391 | |
392 if (opensoundsys_out->magic != OSS_MAGIC) | |
393 { printf ("opensoundsys_play : Bad magic number.\n") ; | |
394 return ; | |
395 } ; | |
396 | |
397 while ((read_frames = callback (callback_data, float_buffer, BUFFER_LEN / opensoundsys_out->channels))) | |
398 { for (k = 0 ; k < read_frames * opensoundsys_out->channels ; k++) | |
399 buffer [k] = lrint (32767.0 * float_buffer [k]) ; | |
400 (void) write (opensoundsys_out->fd, buffer, read_frames * opensoundsys_out->channels * sizeof (short)) ; | |
401 } ; | |
402 | |
403 return ; | |
404 } /* opensoundsys_play */ | |
405 | |
406 static void | |
407 opensoundsys_close (AUDIO_OUT *audio_out) | |
408 { OSS_AUDIO_OUT *opensoundsys_out ; | |
409 | |
410 if ((opensoundsys_out = (OSS_AUDIO_OUT*) audio_out) == NULL) | |
411 { printf ("opensoundsys_close : AUDIO_OUT is NULL.\n") ; | |
412 return ; | |
413 } ; | |
414 | |
415 if (opensoundsys_out->magic != OSS_MAGIC) | |
416 { printf ("opensoundsys_close : Bad magic number.\n") ; | |
417 return ; | |
418 } ; | |
419 | |
420 memset (opensoundsys_out, 0, sizeof (OSS_AUDIO_OUT)) ; | |
421 | |
422 free (opensoundsys_out) ; | |
423 | |
424 return ; | |
425 } /* opensoundsys_close */ | |
426 | |
427 #endif /* __linux__ */ | |
428 | |
429 /*------------------------------------------------------------------------------ | |
430 ** Mac OS X functions for playing a sound. | |
431 */ | |
432 | |
433 #if (defined (__MACH__) && defined (__APPLE__)) /* MacOSX */ | |
434 | |
435 #include <Carbon.h> | |
436 #include <CoreAudio/AudioHardware.h> | |
437 | |
438 #define MACOSX_MAGIC MAKE_MAGIC ('M', 'a', 'c', ' ', 'O', 'S', ' ', 'X') | |
439 | |
440 typedef struct | |
441 { int magic ; | |
442 AudioStreamBasicDescription format ; | |
443 | |
444 UInt32 buf_size ; | |
445 AudioDeviceID device ; | |
446 | |
447 int channels ; | |
448 int samplerate ; | |
449 int buffer_size ; | |
450 int done_playing ; | |
451 | |
452 get_audio_callback_t callback ; | |
453 | |
454 void *callback_data ; | |
455 } MACOSX_AUDIO_OUT ; | |
456 | |
457 static AUDIO_OUT *macosx_open (int channels, int samplerate) ; | |
458 static void macosx_play (get_audio_callback_t callback, AUDIO_OUT *audio_out, void *callback_data) ; | |
459 static void macosx_close (AUDIO_OUT *audio_out) ; | |
460 | |
461 static OSStatus | |
462 macosx_audio_out_callback (AudioDeviceID device, const AudioTimeStamp* current_time, | |
463 const AudioBufferList* data_in, const AudioTimeStamp* time_in, | |
464 AudioBufferList* data_out, const AudioTimeStamp* time_out, void* client_data) ; | |
465 | |
466 | |
467 static AUDIO_OUT * | |
468 macosx_open (int channels, int samplerate) | |
469 { MACOSX_AUDIO_OUT *macosx_out ; | |
470 OSStatus err ; | |
471 size_t count ; | |
472 | |
473 if ((macosx_out = calloc (1, sizeof (MACOSX_AUDIO_OUT))) == NULL) | |
474 { perror ("macosx_open : malloc ") ; | |
475 exit (1) ; | |
476 } ; | |
477 | |
478 macosx_out->magic = MACOSX_MAGIC ; | |
479 macosx_out->channels = channels ; | |
480 macosx_out->samplerate = samplerate ; | |
481 | |
482 macosx_out->device = kAudioDeviceUnknown ; | |
483 | |
484 /* get the default output device for the HAL */ | |
485 count = sizeof (AudioDeviceID) ; | |
486 if ((err = AudioHardwareGetProperty (kAudioHardwarePropertyDefaultOutputDevice, | |
487 &count, (void *) &(macosx_out->device))) != noErr) | |
488 { printf ("AudioHardwareGetProperty failed.\n") ; | |
489 free (macosx_out) ; | |
490 return NULL ; | |
491 } ; | |
492 | |
493 /* get the buffersize that the default device uses for IO */ | |
494 count = sizeof (UInt32) ; | |
495 if ((err = AudioDeviceGetProperty (macosx_out->device, 0, false, kAudioDevicePropertyBufferSize, | |
496 &count, &(macosx_out->buffer_size))) != noErr) | |
497 { printf ("AudioDeviceGetProperty (AudioDeviceGetProperty) failed.\n") ; | |
498 free (macosx_out) ; | |
499 return NULL ; | |
500 } ; | |
501 | |
502 /* get a description of the data format used by the default device */ | |
503 count = sizeof (AudioStreamBasicDescription) ; | |
504 if ((err = AudioDeviceGetProperty (macosx_out->device, 0, false, kAudioDevicePropertyStreamFormat, | |
505 &count, &(macosx_out->format))) != noErr) | |
506 { printf ("AudioDeviceGetProperty (kAudioDevicePropertyStreamFormat) failed.\n") ; | |
507 free (macosx_out) ; | |
508 return NULL ; | |
509 } ; | |
510 | |
511 macosx_out->format.mSampleRate = samplerate ; | |
512 macosx_out->format.mChannelsPerFrame = channels ; | |
513 | |
514 if ((err = AudioDeviceSetProperty (macosx_out->device, NULL, 0, false, kAudioDevicePropertyStreamFormat, | |
515 sizeof (AudioStreamBasicDescription), &(macosx_out->format))) != noErr) | |
516 { printf ("AudioDeviceSetProperty (kAudioDevicePropertyStreamFormat) failed.\n") ; | |
517 free (macosx_out) ; | |
518 return NULL ; | |
519 } ; | |
520 | |
521 /* we want linear pcm */ | |
522 if (macosx_out->format.mFormatID != kAudioFormatLinearPCM) | |
523 { free (macosx_out) ; | |
524 return NULL ; | |
525 } ; | |
526 | |
527 macosx_out->done_playing = 0 ; | |
528 | |
529 /* Fire off the device. */ | |
530 if ((err = AudioDeviceAddIOProc (macosx_out->device, macosx_audio_out_callback, | |
531 (void *) macosx_out)) != noErr) | |
532 { printf ("AudioDeviceAddIOProc failed.\n") ; | |
533 free (macosx_out) ; | |
534 return NULL ; | |
535 } ; | |
536 | |
537 return (MACOSX_AUDIO_OUT *) macosx_out ; | |
538 } /* macosx_open */ | |
539 | |
540 static void | |
541 macosx_play (get_audio_callback_t callback, AUDIO_OUT *audio_out, void *callback_data) | |
542 { MACOSX_AUDIO_OUT *macosx_out ; | |
543 OSStatus err ; | |
544 | |
545 if ((macosx_out = (MACOSX_AUDIO_OUT*) audio_out) == NULL) | |
546 { printf ("macosx_play : AUDIO_OUT is NULL.\n") ; | |
547 return ; | |
548 } ; | |
549 | |
550 if (macosx_out->magic != MACOSX_MAGIC) | |
551 { printf ("macosx_play : Bad magic number.\n") ; | |
552 return ; | |
553 } ; | |
554 | |
555 /* Set the callback function and callback data. */ | |
556 macosx_out->callback = callback ; | |
557 macosx_out->callback_data = callback_data ; | |
558 | |
559 err = AudioDeviceStart (macosx_out->device, macosx_audio_out_callback) ; | |
560 if (err != noErr) | |
561 printf ("AudioDeviceStart failed.\n") ; | |
562 | |
563 while (macosx_out->done_playing == SF_FALSE) | |
564 usleep (10 * 1000) ; /* 10 000 milliseconds. */ | |
565 | |
566 return ; | |
567 } /* macosx_play */ | |
568 | |
569 static void | |
570 macosx_close (AUDIO_OUT *audio_out) | |
571 { MACOSX_AUDIO_OUT *macosx_out ; | |
572 OSStatus err ; | |
573 | |
574 if ((macosx_out = (MACOSX_AUDIO_OUT*) audio_out) == NULL) | |
575 { printf ("macosx_close : AUDIO_OUT is NULL.\n") ; | |
576 return ; | |
577 } ; | |
578 | |
579 if (macosx_out->magic != MACOSX_MAGIC) | |
580 { printf ("macosx_close : Bad magic number.\n") ; | |
581 return ; | |
582 } ; | |
583 | |
584 | |
585 if ((err = AudioDeviceStop (macosx_out->device, macosx_audio_out_callback)) != noErr) | |
586 { printf ("AudioDeviceStop failed.\n") ; | |
587 return ; | |
588 } ; | |
589 | |
590 err = AudioDeviceRemoveIOProc (macosx_out->device, macosx_audio_out_callback) ; | |
591 if (err != noErr) | |
592 { printf ("AudioDeviceRemoveIOProc failed.\n") ; | |
593 return ; | |
594 } ; | |
595 | |
596 } /* macosx_close */ | |
597 | |
598 static OSStatus | |
599 macosx_audio_out_callback (AudioDeviceID device, const AudioTimeStamp* current_time, | |
600 const AudioBufferList* data_in, const AudioTimeStamp* time_in, | |
601 AudioBufferList* data_out, const AudioTimeStamp* time_out, void* client_data) | |
602 { MACOSX_AUDIO_OUT *macosx_out ; | |
603 int k, size, frame_count, read_count ; | |
604 float *buffer ; | |
605 | |
606 if ((macosx_out = (MACOSX_AUDIO_OUT*) client_data) == NULL) | |
607 { printf ("macosx_play : AUDIO_OUT is NULL.\n") ; | |
608 return 42 ; | |
609 } ; | |
610 | |
611 if (macosx_out->magic != MACOSX_MAGIC) | |
612 { printf ("macosx_play : Bad magic number.\n") ; | |
613 return 42 ; | |
614 } ; | |
615 | |
616 size = data_out->mBuffers [0].mDataByteSize ; | |
617 frame_count = size / sizeof (float) / macosx_out->channels ; | |
618 | |
619 buffer = (float*) data_out->mBuffers [0].mData ; | |
620 | |
621 read_count = macosx_out->callback (macosx_out->callback_data, buffer, frame_count) ; | |
622 | |
623 if (read_count < frame_count) | |
624 { memset (&(buffer [read_count]), 0, (frame_count - read_count) * sizeof (float)) ; | |
625 macosx_out->done_playing = 1 ; | |
626 } ; | |
627 | |
628 return noErr ; | |
629 } /* macosx_audio_out_callback */ | |
630 | |
631 #endif /* MacOSX */ | |
632 | |
633 | |
634 /*------------------------------------------------------------------------------ | |
635 ** Win32 functions for playing a sound. | |
636 ** | |
637 ** This API sucks. Its needlessly complicated and is *WAY* too loose with | |
638 ** passing pointers arounf in integers and and using char* pointers to | |
639 ** point to data instead of short*. It plain sucks! | |
640 */ | |
641 | |
642 #if (defined (_WIN32) || defined (WIN32)) | |
643 | |
644 #include <windows.h> | |
645 #include <mmsystem.h> | |
646 | |
647 #define WIN32_BUFFER_LEN (1<<15) | |
648 #define WIN32_MAGIC MAKE_MAGIC ('W', 'i', 'n', '3', '2', 's', 'u', 'x') | |
649 | |
650 typedef struct | |
651 { int magic ; | |
652 | |
653 HWAVEOUT hwave ; | |
654 WAVEHDR whdr [2] ; | |
655 | |
656 HANDLE Event ; | |
657 | |
658 short short_buffer [WIN32_BUFFER_LEN / sizeof (short)] ; | |
659 float float_buffer [WIN32_BUFFER_LEN / sizeof (short) / 2] ; | |
660 | |
661 int bufferlen, current ; | |
662 | |
663 int channels ; | |
664 | |
665 get_audio_callback_t callback ; | |
666 | |
667 void *callback_data ; | |
668 } WIN32_AUDIO_OUT ; | |
669 | |
670 static AUDIO_OUT *win32_open (int channels, int samplerate) ; | |
671 static void win32_play (get_audio_callback_t callback, AUDIO_OUT *audio_out, void *callback_data) ; | |
672 static void win32_close (AUDIO_OUT *audio_out) ; | |
673 | |
674 static DWORD CALLBACK | |
675 win32_audio_out_callback (HWAVEOUT hwave, UINT msg, DWORD data, DWORD param1, DWORD param2) ; | |
676 | |
677 static AUDIO_OUT* | |
678 win32_open (int channels, int samplerate) | |
679 { WIN32_AUDIO_OUT *win32_out ; | |
680 | |
681 WAVEFORMATEX wf ; | |
682 int error ; | |
683 | |
684 if ((win32_out = calloc (1, sizeof (WIN32_AUDIO_OUT))) == NULL) | |
685 { perror ("win32_open : malloc ") ; | |
686 exit (1) ; | |
687 } ; | |
688 | |
689 win32_out->magic = WIN32_MAGIC ; | |
690 win32_out->channels = channels ; | |
691 | |
692 win32_out->current = 0 ; | |
693 | |
694 win32_out->Event = CreateEvent (0, FALSE, FALSE, 0) ; | |
695 | |
696 wf.nChannels = channels ; | |
697 wf.nSamplesPerSec = samplerate ; | |
698 wf.nBlockAlign = channels * sizeof (short) ; | |
699 | |
700 wf.wFormatTag = WAVE_FORMAT_PCM ; | |
701 wf.cbSize = 0 ; | |
702 wf.wBitsPerSample = 16 ; | |
703 wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec ; | |
704 | |
705 error = waveOutOpen (&(win32_out->hwave), WAVE_MAPPER, &wf, (DWORD) win32_audio_out_callback, | |
706 (DWORD) win32_out, CALLBACK_FUNCTION) ; | |
707 if (error) | |
708 { puts ("waveOutOpen failed.") ; | |
709 free (win32_out) ; | |
710 return NULL ; | |
711 } ; | |
712 | |
713 waveOutPause (win32_out->hwave) ; | |
714 | |
715 return (WIN32_AUDIO_OUT *) win32_out ; | |
716 } /* win32_open */ | |
717 | |
718 static void | |
719 win32_play (get_audio_callback_t callback, AUDIO_OUT *audio_out, void *callback_data) | |
720 { WIN32_AUDIO_OUT *win32_out ; | |
721 int error ; | |
722 | |
723 if ((win32_out = (WIN32_AUDIO_OUT*) audio_out) == NULL) | |
724 { printf ("win32_play : AUDIO_OUT is NULL.\n") ; | |
725 return ; | |
726 } ; | |
727 | |
728 if (win32_out->magic != WIN32_MAGIC) | |
729 { printf ("win32_play : Bad magic number (%d %d).\n", win32_out->magic, WIN32_MAGIC) ; | |
730 return ; | |
731 } ; | |
732 | |
733 /* Set the callback function and callback data. */ | |
734 win32_out->callback = callback ; | |
735 win32_out->callback_data = callback_data ; | |
736 | |
737 win32_out->whdr [0].lpData = (char*) win32_out->short_buffer ; | |
738 win32_out->whdr [1].lpData = ((char*) win32_out->short_buffer) + sizeof (win32_out->short_buffer) / 2 ; | |
739 | |
740 win32_out->whdr [0].dwBufferLength = sizeof (win32_out->short_buffer) / 2 ; | |
741 win32_out->whdr [1].dwBufferLength = sizeof (win32_out->short_buffer) / 2 ; | |
742 | |
743 win32_out->bufferlen = sizeof (win32_out->short_buffer) / 2 / sizeof (short) ; | |
744 | |
745 /* Prepare the WAVEHDRs */ | |
746 if ((error = waveOutPrepareHeader (win32_out->hwave, &(win32_out->whdr [0]), sizeof (WAVEHDR)))) | |
747 { printf ("waveOutPrepareHeader [0] failed : %08X\n", error) ; | |
748 waveOutClose (win32_out->hwave) ; | |
749 return ; | |
750 } ; | |
751 | |
752 if ((error = waveOutPrepareHeader (win32_out->hwave, &(win32_out->whdr [1]), sizeof (WAVEHDR)))) | |
753 { printf ("waveOutPrepareHeader [1] failed : %08X\n", error) ; | |
754 waveOutUnprepareHeader (win32_out->hwave, &(win32_out->whdr [0]), sizeof (WAVEHDR)) ; | |
755 waveOutClose (win32_out->hwave) ; | |
756 return ; | |
757 } ; | |
758 | |
759 waveOutRestart (win32_out->hwave) ; | |
760 | |
761 /* Fake 2 calls to the callback function to queue up enough audio. */ | |
762 win32_audio_out_callback (0, MM_WOM_DONE, (DWORD) win32_out, 0, 0) ; | |
763 win32_audio_out_callback (0, MM_WOM_DONE, (DWORD) win32_out, 0, 0) ; | |
764 | |
765 /* Wait for playback to finish. The callback notifies us when all | |
766 ** wave data has been played. | |
767 */ | |
768 WaitForSingleObject (win32_out->Event, INFINITE) ; | |
769 | |
770 waveOutPause (win32_out->hwave) ; | |
771 waveOutReset (win32_out->hwave) ; | |
772 | |
773 waveOutUnprepareHeader (win32_out->hwave, &(win32_out->whdr [0]), sizeof (WAVEHDR)) ; | |
774 waveOutUnprepareHeader (win32_out->hwave, &(win32_out->whdr [1]), sizeof (WAVEHDR)) ; | |
775 | |
776 waveOutClose (win32_out->hwave) ; | |
777 win32_out->hwave = 0 ; | |
778 | |
779 return ; | |
780 } /* win32_play */ | |
781 | |
782 static void | |
783 win32_close (AUDIO_OUT *audio_out) | |
784 { WIN32_AUDIO_OUT *win32_out ; | |
785 | |
786 if ((win32_out = (WIN32_AUDIO_OUT*) audio_out) == NULL) | |
787 { printf ("win32_close : AUDIO_OUT is NULL.\n") ; | |
788 return ; | |
789 } ; | |
790 | |
791 if (win32_out->magic != WIN32_MAGIC) | |
792 { printf ("win32_close : Bad magic number.\n") ; | |
793 return ; | |
794 } ; | |
795 | |
796 memset (win32_out, 0, sizeof (WIN32_AUDIO_OUT)) ; | |
797 | |
798 free (win32_out) ; | |
799 } /* win32_close */ | |
800 | |
801 static DWORD CALLBACK | |
802 win32_audio_out_callback (HWAVEOUT hwave, UINT msg, DWORD data, DWORD param1, DWORD param2) | |
803 { WIN32_AUDIO_OUT *win32_out ; | |
804 int read_count, frame_count, k ; | |
805 short *sptr ; | |
806 | |
807 /* | |
808 ** I consider this technique of passing a pointer via an integer as | |
809 ** fundamentally broken but thats the way microsoft has defined the | |
810 ** interface. | |
811 */ | |
812 if ((win32_out = (WIN32_AUDIO_OUT*) data) == NULL) | |
813 { printf ("win32_audio_out_callback : AUDIO_OUT is NULL.\n") ; | |
814 return 1 ; | |
815 } ; | |
816 | |
817 if (win32_out->magic != WIN32_MAGIC) | |
818 { printf ("win32_audio_out_callback : Bad magic number (%d %d).\n", win32_out->magic, WIN32_MAGIC) ; | |
819 return 1 ; | |
820 } ; | |
821 | |
822 if (msg != MM_WOM_DONE) | |
823 return 0 ; | |
824 | |
825 /* Do the actual audio. */ | |
826 sample_count = win32_out->bufferlen ; | |
827 frame_count = sample_count / win32_out->channels ; | |
828 | |
829 read_count = win32_out->callback (win32_out->callback_data, win32_out->float_buffer, frame_count) ; | |
830 | |
831 sptr = (short*) win32_out->whdr [win32_out->current].lpData ; | |
832 | |
833 for (k = 0 ; k < read_count ; k++) | |
834 sptr [k] = lrint (32767.0 * win32_out->float_buffer [k]) ; | |
835 | |
836 if (read_count > 0) | |
837 { /* Fix buffer length is only a partial block. */ | |
838 if (read_count * sizeof (short) < win32_out->bufferlen) | |
839 win32_out->whdr [win32_out->current].dwBufferLength = read_count * sizeof (short) ; | |
840 | |
841 /* Queue the WAVEHDR */ | |
842 waveOutWrite (win32_out->hwave, (LPWAVEHDR) &(win32_out->whdr [win32_out->current]), sizeof (WAVEHDR)) ; | |
843 } | |
844 else | |
845 { /* Stop playback */ | |
846 waveOutPause (win32_out->hwave) ; | |
847 | |
848 SetEvent (win32_out->Event) ; | |
849 } ; | |
850 | |
851 win32_out->current = (win32_out->current + 1) % 2 ; | |
852 | |
853 return 0 ; | |
854 } /* win32_audio_out_callback */ | |
855 | |
856 #endif /* Win32 */ | |
857 | |
858 /*------------------------------------------------------------------------------ | |
859 ** Solaris. | |
860 */ | |
861 | |
862 #if (defined (sun) && defined (unix)) /* ie Solaris */ | |
863 | |
864 #include <fcntl.h> | |
865 #include <sys/ioctl.h> | |
866 #include <sys/audioio.h> | |
867 | |
868 #define SOLARIS_MAGIC MAKE_MAGIC ('S', 'o', 'l', 'a', 'r', 'i', 's', ' ') | |
869 | |
870 typedef struct | |
871 { int magic ; | |
872 int fd ; | |
873 int channels ; | |
874 int samplerate ; | |
875 } SOLARIS_AUDIO_OUT ; | |
876 | |
877 static AUDIO_OUT *solaris_open (int channels, int samplerate) ; | |
878 static void solaris_play (get_audio_callback_t callback, AUDIO_OUT *audio_out, void *callback_data) ; | |
879 static void solaris_close (AUDIO_OUT *audio_out) ; | |
880 | |
881 static AUDIO_OUT * | |
882 solaris_open (int channels, int samplerate) | |
883 { SOLARIS_AUDIO_OUT *solaris_out ; | |
884 audio_info_t audio_info ; | |
885 int error ; | |
886 | |
887 if ((solaris_out = calloc (1, sizeof (SOLARIS_AUDIO_OUT))) == NULL) | |
888 { perror ("solaris_open : malloc ") ; | |
889 exit (1) ; | |
890 } ; | |
891 | |
892 solaris_out->magic = SOLARIS_MAGIC ; | |
893 solaris_out->channels = channels ; | |
894 solaris_out->samplerate = channels ; | |
895 | |
896 /* open the audio device - write only, non-blocking */ | |
897 if ((solaris_out->fd = open ("/dev/audio", O_WRONLY | O_NONBLOCK)) < 0) | |
898 { perror ("open (/dev/audio) failed") ; | |
899 exit (1) ; | |
900 } ; | |
901 | |
902 /* Retrive standard values. */ | |
903 AUDIO_INITINFO (&audio_info) ; | |
904 | |
905 audio_info.play.sample_rate = samplerate ; | |
906 audio_info.play.channels = channels ; | |
907 audio_info.play.precision = 16 ; | |
908 audio_info.play.encoding = AUDIO_ENCODING_LINEAR ; | |
909 audio_info.play.gain = AUDIO_MAX_GAIN ; | |
910 audio_info.play.balance = AUDIO_MID_BALANCE ; | |
911 | |
912 if ((error = ioctl (solaris_out->fd, AUDIO_SETINFO, &audio_info))) | |
913 { perror ("ioctl (AUDIO_SETINFO) failed") ; | |
914 exit (1) ; | |
915 } ; | |
916 | |
917 return (AUDIO_OUT*) solaris_out ; | |
918 } /* solaris_open */ | |
919 | |
920 static void | |
921 solaris_play (get_audio_callback_t callback, AUDIO_OUT *audio_out, void *callback_data) | |
922 { SOLARIS_AUDIO_OUT *solaris_out ; | |
923 static float float_buffer [BUFFER_LEN] ; | |
924 static short buffer [BUFFER_LEN] ; | |
925 int k, read_frames ; | |
926 | |
927 if ((solaris_out = (SOLARIS_AUDIO_OUT*) audio_out) == NULL) | |
928 { printf ("solaris_play : AUDIO_OUT is NULL.\n") ; | |
929 return ; | |
930 } ; | |
931 | |
932 if (solaris_out->magic != SOLARIS_MAGIC) | |
933 { printf ("solaris_play : Bad magic number.\n") ; | |
934 return ; | |
935 } ; | |
936 | |
937 while ((read_frames = callback (callback_data, float_buffer, BUFFER_LEN / solaris_out->channels))) | |
938 { for (k = 0 ; k < read_frames * solaris_out->channels ; k++) | |
939 buffer [k] = lrint (32767.0 * float_buffer [k]) ; | |
940 write (solaris_out->fd, buffer, read_frames * solaris_out->channels * sizeof (short)) ; | |
941 } ; | |
942 | |
943 return ; | |
944 } /* solaris_play */ | |
945 | |
946 static void | |
947 solaris_close (AUDIO_OUT *audio_out) | |
948 { SOLARIS_AUDIO_OUT *solaris_out ; | |
949 | |
950 if ((solaris_out = (SOLARIS_AUDIO_OUT*) audio_out) == NULL) | |
951 { printf ("solaris_close : AUDIO_OUT is NULL.\n") ; | |
952 return ; | |
953 } ; | |
954 | |
955 if (solaris_out->magic != SOLARIS_MAGIC) | |
956 { printf ("solaris_close : Bad magic number.\n") ; | |
957 return ; | |
958 } ; | |
959 | |
960 memset (solaris_out, 0, sizeof (SOLARIS_AUDIO_OUT)) ; | |
961 | |
962 free (solaris_out) ; | |
963 | |
964 return ; | |
965 } /* solaris_close */ | |
966 | |
967 #endif /* Solaris */ | |
968 | |
969 /*============================================================================== | |
970 ** Main function. | |
971 */ | |
972 | |
973 AUDIO_OUT * | |
974 audio_open (int channels, int samplerate) | |
975 { | |
976 #if defined (__linux__) | |
977 #if HAVE_ALSA_ASOUNDLIB_H | |
978 if (access ("/proc/asound/cards", R_OK) == 0) | |
979 return alsa_open (channels, samplerate) ; | |
980 #endif | |
981 return opensoundsys_open (channels, samplerate) ; | |
982 #elif (defined (__MACH__) && defined (__APPLE__)) | |
983 return macosx_open (channels, samplerate) ; | |
984 #elif (defined (sun) && defined (unix)) | |
985 return solaris_open (channels, samplerate) ; | |
986 #elif (defined (_WIN32) || defined (WIN32)) | |
987 return win32_open (channels, samplerate) ; | |
988 #else | |
989 #warning "*** Playing sound not yet supported on this platform." | |
990 #warning "*** Please feel free to submit a patch." | |
991 printf ("Error : Playing sound not yet supported on this platform.\n") ; | |
992 return NULL ; | |
993 #endif | |
994 | |
995 | |
996 return NULL ; | |
997 } /* audio_open */ | |
998 | |
999 void | |
1000 audio_play (get_audio_callback_t callback, AUDIO_OUT *audio_out, void *callback_data) | |
1001 { | |
1002 | |
1003 if (callback == NULL) | |
1004 { printf ("Error : bad callback pointer.\n") ; | |
1005 return ; | |
1006 } ; | |
1007 | |
1008 if (audio_out == NULL) | |
1009 { printf ("Error : bad audio_out pointer.\n") ; | |
1010 return ; | |
1011 } ; | |
1012 | |
1013 if (callback_data == NULL) | |
1014 { printf ("Error : bad callback_data pointer.\n") ; | |
1015 return ; | |
1016 } ; | |
1017 | |
1018 #if defined (__linux__) | |
1019 #if HAVE_ALSA_ASOUNDLIB_H | |
1020 if (audio_out->magic == ALSA_MAGIC) | |
1021 alsa_play (callback, audio_out, callback_data) ; | |
1022 #endif | |
1023 opensoundsys_play (callback, audio_out, callback_data) ; | |
1024 #elif (defined (__MACH__) && defined (__APPLE__)) | |
1025 macosx_play (callback, audio_out, callback_data) ; | |
1026 #elif (defined (sun) && defined (unix)) | |
1027 solaris_play (callback, audio_out, callback_data) ; | |
1028 #elif (defined (_WIN32) || defined (WIN32)) | |
1029 win32_play (callback, audio_out, callback_data) ; | |
1030 #else | |
1031 #warning "*** Playing sound not yet supported on this platform." | |
1032 #warning "*** Please feel free to submit a patch." | |
1033 printf ("Error : Playing sound not yet supported on this platform.\n") ; | |
1034 return ; | |
1035 #endif | |
1036 | |
1037 return ; | |
1038 } /* audio_play */ | |
1039 | |
1040 void | |
1041 audio_close (AUDIO_OUT *audio_out) | |
1042 { | |
1043 #if defined (__linux__) | |
1044 #if HAVE_ALSA_ASOUNDLIB_H | |
1045 if (audio_out->magic == ALSA_MAGIC) | |
1046 alsa_close (audio_out) ; | |
1047 #endif | |
1048 opensoundsys_close (audio_out) ; | |
1049 #elif (defined (__MACH__) && defined (__APPLE__)) | |
1050 macosx_close (audio_out) ; | |
1051 #elif (defined (sun) && defined (unix)) | |
1052 solaris_close (audio_out) ; | |
1053 #elif (defined (_WIN32) || defined (WIN32)) | |
1054 win32_close (audio_out) ; | |
1055 #else | |
1056 #warning "*** Playing sound not yet supported on this platform." | |
1057 #warning "*** Please feel free to submit a patch." | |
1058 printf ("Error : Playing sound not yet supported on this platform.\n") ; | |
1059 return ; | |
1060 #endif | |
1061 | |
1062 return ; | |
1063 } /* audio_close */ | |
1064 | |
1065 #else /* (HAVE_SNDFILE == 0) */ | |
1066 | |
1067 /* Do not have libsndfile installed so just return. */ | |
1068 | |
1069 AUDIO_OUT * | |
1070 audio_open (int channels, int samplerate) | |
1071 { | |
1072 (void) channels ; | |
1073 (void) samplerate ; | |
1074 | |
1075 return NULL ; | |
1076 } /* audio_open */ | |
1077 | |
1078 void | |
1079 audio_play (get_audio_callback_t callback, AUDIO_OUT *audio_out, void *callback_data) | |
1080 { | |
1081 (void) callback ; | |
1082 (void) audio_out ; | |
1083 (void) callback_data ; | |
1084 | |
1085 return ; | |
1086 } /* audio_play */ | |
1087 | |
1088 void | |
1089 audio_close (AUDIO_OUT *audio_out) | |
1090 { | |
1091 audio_out = audio_out ; | |
1092 | |
1093 return ; | |
1094 } /* audio_close */ | |
1095 | |
1096 #endif /* HAVE_SNDFILE */ | |
1097 |