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