annotate src/libsndfile-1.0.27/programs/sndfile-play.c @ 148:b4bfdf10c4b3

Update Win64 capnp builds to v0.6
author Chris Cannam <cannam@all-day-breakfast.com>
date Mon, 22 May 2017 18:56:49 +0100
parents cd6cdf86811e
children
rev   line source
cannam@125 1 /*
cannam@125 2 ** Copyright (C) 1999-2015 Erik de Castro Lopo <erikd@mega-nerd.com>
cannam@125 3 **
cannam@125 4 ** All rights reserved.
cannam@125 5 **
cannam@125 6 ** Redistribution and use in source and binary forms, with or without
cannam@125 7 ** modification, are permitted provided that the following conditions are
cannam@125 8 ** met:
cannam@125 9 **
cannam@125 10 ** * Redistributions of source code must retain the above copyright
cannam@125 11 ** notice, this list of conditions and the following disclaimer.
cannam@125 12 ** * Redistributions in binary form must reproduce the above copyright
cannam@125 13 ** notice, this list of conditions and the following disclaimer in
cannam@125 14 ** the documentation and/or other materials provided with the
cannam@125 15 ** distribution.
cannam@125 16 ** * Neither the author nor the names of any contributors may be used
cannam@125 17 ** to endorse or promote products derived from this software without
cannam@125 18 ** specific prior written permission.
cannam@125 19 **
cannam@125 20 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
cannam@125 21 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
cannam@125 22 ** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
cannam@125 23 ** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
cannam@125 24 ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
cannam@125 25 ** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
cannam@125 26 ** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
cannam@125 27 ** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
cannam@125 28 ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
cannam@125 29 ** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
cannam@125 30 ** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
cannam@125 31 */
cannam@125 32
cannam@125 33 #include "sfconfig.h"
cannam@125 34
cannam@125 35 #include <stdio.h>
cannam@125 36 #include <stdlib.h>
cannam@125 37 #include <string.h>
cannam@125 38 #include <errno.h>
cannam@125 39
cannam@125 40 #if HAVE_UNISTD_H
cannam@125 41 #include <unistd.h>
cannam@125 42 #endif
cannam@125 43
cannam@125 44 #include <sndfile.h>
cannam@125 45
cannam@125 46 #include "common.h"
cannam@125 47
cannam@125 48 #if HAVE_ALSA_ASOUNDLIB_H
cannam@125 49 #define ALSA_PCM_NEW_HW_PARAMS_API
cannam@125 50 #define ALSA_PCM_NEW_SW_PARAMS_API
cannam@125 51 #include <alsa/asoundlib.h>
cannam@125 52 #include <sys/time.h>
cannam@125 53 #endif
cannam@125 54
cannam@125 55 #if defined (__ANDROID__)
cannam@125 56
cannam@125 57 #elif defined (__linux__) || defined (__FreeBSD_kernel__) || defined (__FreeBSD__)
cannam@125 58 #include <fcntl.h>
cannam@125 59 #include <sys/ioctl.h>
cannam@125 60 #include <sys/soundcard.h>
cannam@125 61
cannam@125 62 #elif (defined (__MACH__) && defined (__APPLE__))
cannam@125 63 #include <AvailabilityMacros.h>
cannam@125 64 #include <Availability.h>
cannam@125 65
cannam@125 66 #if (OSX_DARWIN_VERSION > 11)
cannam@125 67 /* Includes go here. */
cannam@125 68 #elif (OSX_DARWIN_VERSION == 11)
cannam@125 69 #include <AudioToolbox/AudioToolbox.h>
cannam@125 70 #elif (OSX_DARWIN_VERSION > 0 && OSX_DARWIN_VERSION <= 10)
cannam@125 71 #include <CoreAudio/AudioHardware.h>
cannam@125 72 #endif
cannam@125 73
cannam@125 74 #elif HAVE_SNDIO_H
cannam@125 75 #include <sndio.h>
cannam@125 76
cannam@125 77 #elif (defined (sun) && defined (unix))
cannam@125 78 #include <fcntl.h>
cannam@125 79 #include <sys/ioctl.h>
cannam@125 80 #include <sys/audioio.h>
cannam@125 81
cannam@125 82 #elif (OS_IS_WIN32 == 1)
cannam@125 83 #include <windows.h>
cannam@125 84 #include <mmsystem.h>
cannam@125 85
cannam@125 86 #endif
cannam@125 87
cannam@125 88 #define SIGNED_SIZEOF(x) ((int) sizeof (x))
cannam@125 89 #define BUFFER_LEN (2048)
cannam@125 90
cannam@125 91 /*------------------------------------------------------------------------------
cannam@125 92 ** Linux/OSS functions for playing a sound.
cannam@125 93 */
cannam@125 94
cannam@125 95 #if HAVE_ALSA_ASOUNDLIB_H
cannam@125 96
cannam@125 97 static snd_pcm_t * alsa_open (int channels, unsigned srate, int realtime) ;
cannam@125 98 static int alsa_write_float (snd_pcm_t *alsa_dev, float *data, int frames, int channels) ;
cannam@125 99
cannam@125 100 static void
cannam@125 101 alsa_play (int argc, char *argv [])
cannam@125 102 { static float buffer [BUFFER_LEN] ;
cannam@125 103 SNDFILE *sndfile ;
cannam@125 104 SF_INFO sfinfo ;
cannam@125 105 snd_pcm_t * alsa_dev ;
cannam@125 106 int k, readcount, subformat ;
cannam@125 107
cannam@125 108 for (k = 1 ; k < argc ; k++)
cannam@125 109 { memset (&sfinfo, 0, sizeof (sfinfo)) ;
cannam@125 110
cannam@125 111 printf ("Playing %s\n", argv [k]) ;
cannam@125 112 if (! (sndfile = sf_open (argv [k], SFM_READ, &sfinfo)))
cannam@125 113 { puts (sf_strerror (NULL)) ;
cannam@125 114 continue ;
cannam@125 115 } ;
cannam@125 116
cannam@125 117 if (sfinfo.channels < 1 || sfinfo.channels > 2)
cannam@125 118 { printf ("Error : channels = %d.\n", sfinfo.channels) ;
cannam@125 119 continue ;
cannam@125 120 } ;
cannam@125 121
cannam@125 122 if ((alsa_dev = alsa_open (sfinfo.channels, (unsigned) sfinfo.samplerate, SF_FALSE)) == NULL)
cannam@125 123 continue ;
cannam@125 124
cannam@125 125 subformat = sfinfo.format & SF_FORMAT_SUBMASK ;
cannam@125 126
cannam@125 127 if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE)
cannam@125 128 { double scale ;
cannam@125 129 int m ;
cannam@125 130
cannam@125 131 sf_command (sndfile, SFC_CALC_SIGNAL_MAX, &scale, sizeof (scale)) ;
cannam@125 132 if (scale < 1e-10)
cannam@125 133 scale = 1.0 ;
cannam@125 134 else
cannam@125 135 scale = 32700.0 / scale ;
cannam@125 136
cannam@125 137 while ((readcount = sf_read_float (sndfile, buffer, BUFFER_LEN)))
cannam@125 138 { for (m = 0 ; m < readcount ; m++)
cannam@125 139 buffer [m] *= scale ;
cannam@125 140 alsa_write_float (alsa_dev, buffer, BUFFER_LEN / sfinfo.channels, sfinfo.channels) ;
cannam@125 141 } ;
cannam@125 142 }
cannam@125 143 else
cannam@125 144 { while ((readcount = sf_read_float (sndfile, buffer, BUFFER_LEN)))
cannam@125 145 alsa_write_float (alsa_dev, buffer, BUFFER_LEN / sfinfo.channels, sfinfo.channels) ;
cannam@125 146 } ;
cannam@125 147
cannam@125 148 snd_pcm_drain (alsa_dev) ;
cannam@125 149 snd_pcm_close (alsa_dev) ;
cannam@125 150
cannam@125 151 sf_close (sndfile) ;
cannam@125 152 } ;
cannam@125 153
cannam@125 154 return ;
cannam@125 155 } /* alsa_play */
cannam@125 156
cannam@125 157 static snd_pcm_t *
cannam@125 158 alsa_open (int channels, unsigned samplerate, int realtime)
cannam@125 159 { const char * device = "default" ;
cannam@125 160 snd_pcm_t *alsa_dev = NULL ;
cannam@125 161 snd_pcm_hw_params_t *hw_params ;
cannam@125 162 snd_pcm_uframes_t buffer_size ;
cannam@125 163 snd_pcm_uframes_t alsa_period_size, alsa_buffer_frames ;
cannam@125 164 snd_pcm_sw_params_t *sw_params ;
cannam@125 165
cannam@125 166 int err ;
cannam@125 167
cannam@125 168 if (realtime)
cannam@125 169 { alsa_period_size = 256 ;
cannam@125 170 alsa_buffer_frames = 3 * alsa_period_size ;
cannam@125 171 }
cannam@125 172 else
cannam@125 173 { alsa_period_size = 1024 ;
cannam@125 174 alsa_buffer_frames = 4 * alsa_period_size ;
cannam@125 175 } ;
cannam@125 176
cannam@125 177 if ((err = snd_pcm_open (&alsa_dev, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0)
cannam@125 178 { fprintf (stderr, "cannot open audio device \"%s\" (%s)\n", device, snd_strerror (err)) ;
cannam@125 179 goto catch_error ;
cannam@125 180 } ;
cannam@125 181
cannam@125 182 snd_pcm_nonblock (alsa_dev, 0) ;
cannam@125 183
cannam@125 184 if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0)
cannam@125 185 { fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n", snd_strerror (err)) ;
cannam@125 186 goto catch_error ;
cannam@125 187 } ;
cannam@125 188
cannam@125 189 if ((err = snd_pcm_hw_params_any (alsa_dev, hw_params)) < 0)
cannam@125 190 { fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n", snd_strerror (err)) ;
cannam@125 191 goto catch_error ;
cannam@125 192 } ;
cannam@125 193
cannam@125 194 if ((err = snd_pcm_hw_params_set_access (alsa_dev, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
cannam@125 195 { fprintf (stderr, "cannot set access type (%s)\n", snd_strerror (err)) ;
cannam@125 196 goto catch_error ;
cannam@125 197 } ;
cannam@125 198
cannam@125 199 if ((err = snd_pcm_hw_params_set_format (alsa_dev, hw_params, SND_PCM_FORMAT_FLOAT)) < 0)
cannam@125 200 { fprintf (stderr, "cannot set sample format (%s)\n", snd_strerror (err)) ;
cannam@125 201 goto catch_error ;
cannam@125 202 } ;
cannam@125 203
cannam@125 204 if ((err = snd_pcm_hw_params_set_rate_near (alsa_dev, hw_params, &samplerate, 0)) < 0)
cannam@125 205 { fprintf (stderr, "cannot set sample rate (%s)\n", snd_strerror (err)) ;
cannam@125 206 goto catch_error ;
cannam@125 207 } ;
cannam@125 208
cannam@125 209 if ((err = snd_pcm_hw_params_set_channels (alsa_dev, hw_params, channels)) < 0)
cannam@125 210 { fprintf (stderr, "cannot set channel count (%s)\n", snd_strerror (err)) ;
cannam@125 211 goto catch_error ;
cannam@125 212 } ;
cannam@125 213
cannam@125 214 if ((err = snd_pcm_hw_params_set_buffer_size_near (alsa_dev, hw_params, &alsa_buffer_frames)) < 0)
cannam@125 215 { fprintf (stderr, "cannot set buffer size (%s)\n", snd_strerror (err)) ;
cannam@125 216 goto catch_error ;
cannam@125 217 } ;
cannam@125 218
cannam@125 219 if ((err = snd_pcm_hw_params_set_period_size_near (alsa_dev, hw_params, &alsa_period_size, 0)) < 0)
cannam@125 220 { fprintf (stderr, "cannot set period size (%s)\n", snd_strerror (err)) ;
cannam@125 221 goto catch_error ;
cannam@125 222 } ;
cannam@125 223
cannam@125 224 if ((err = snd_pcm_hw_params (alsa_dev, hw_params)) < 0)
cannam@125 225 { fprintf (stderr, "cannot set parameters (%s)\n", snd_strerror (err)) ;
cannam@125 226 goto catch_error ;
cannam@125 227 } ;
cannam@125 228
cannam@125 229 /* extra check: if we have only one period, this code won't work */
cannam@125 230 snd_pcm_hw_params_get_period_size (hw_params, &alsa_period_size, 0) ;
cannam@125 231 snd_pcm_hw_params_get_buffer_size (hw_params, &buffer_size) ;
cannam@125 232 if (alsa_period_size == buffer_size)
cannam@125 233 { fprintf (stderr, "Can't use period equal to buffer size (%lu == %lu)", alsa_period_size, buffer_size) ;
cannam@125 234 goto catch_error ;
cannam@125 235 } ;
cannam@125 236
cannam@125 237 snd_pcm_hw_params_free (hw_params) ;
cannam@125 238
cannam@125 239 if ((err = snd_pcm_sw_params_malloc (&sw_params)) != 0)
cannam@125 240 { fprintf (stderr, "%s: snd_pcm_sw_params_malloc: %s", __func__, snd_strerror (err)) ;
cannam@125 241 goto catch_error ;
cannam@125 242 } ;
cannam@125 243
cannam@125 244 if ((err = snd_pcm_sw_params_current (alsa_dev, sw_params)) != 0)
cannam@125 245 { fprintf (stderr, "%s: snd_pcm_sw_params_current: %s", __func__, snd_strerror (err)) ;
cannam@125 246 goto catch_error ;
cannam@125 247 } ;
cannam@125 248
cannam@125 249 /* note: set start threshold to delay start until the ring buffer is full */
cannam@125 250 snd_pcm_sw_params_current (alsa_dev, sw_params) ;
cannam@125 251
cannam@125 252 if ((err = snd_pcm_sw_params_set_start_threshold (alsa_dev, sw_params, buffer_size)) < 0)
cannam@125 253 { fprintf (stderr, "cannot set start threshold (%s)\n", snd_strerror (err)) ;
cannam@125 254 goto catch_error ;
cannam@125 255 } ;
cannam@125 256
cannam@125 257 if ((err = snd_pcm_sw_params (alsa_dev, sw_params)) != 0)
cannam@125 258 { fprintf (stderr, "%s: snd_pcm_sw_params: %s", __func__, snd_strerror (err)) ;
cannam@125 259 goto catch_error ;
cannam@125 260 } ;
cannam@125 261
cannam@125 262 snd_pcm_sw_params_free (sw_params) ;
cannam@125 263
cannam@125 264 snd_pcm_reset (alsa_dev) ;
cannam@125 265
cannam@125 266 catch_error :
cannam@125 267
cannam@125 268 if (err < 0 && alsa_dev != NULL)
cannam@125 269 { snd_pcm_close (alsa_dev) ;
cannam@125 270 return NULL ;
cannam@125 271 } ;
cannam@125 272
cannam@125 273 return alsa_dev ;
cannam@125 274 } /* alsa_open */
cannam@125 275
cannam@125 276 static int
cannam@125 277 alsa_write_float (snd_pcm_t *alsa_dev, float *data, int frames, int channels)
cannam@125 278 { static int epipe_count = 0 ;
cannam@125 279
cannam@125 280 int total = 0 ;
cannam@125 281 int retval ;
cannam@125 282
cannam@125 283 if (epipe_count > 0)
cannam@125 284 epipe_count -- ;
cannam@125 285
cannam@125 286 while (total < frames)
cannam@125 287 { retval = snd_pcm_writei (alsa_dev, data + total * channels, frames - total) ;
cannam@125 288
cannam@125 289 if (retval >= 0)
cannam@125 290 { total += retval ;
cannam@125 291 if (total == frames)
cannam@125 292 return total ;
cannam@125 293
cannam@125 294 continue ;
cannam@125 295 } ;
cannam@125 296
cannam@125 297 switch (retval)
cannam@125 298 { case -EAGAIN :
cannam@125 299 puts ("alsa_write_float: EAGAIN") ;
cannam@125 300 continue ;
cannam@125 301 break ;
cannam@125 302
cannam@125 303 case -EPIPE :
cannam@125 304 if (epipe_count > 0)
cannam@125 305 { printf ("alsa_write_float: EPIPE %d\n", epipe_count) ;
cannam@125 306 if (epipe_count > 140)
cannam@125 307 return retval ;
cannam@125 308 } ;
cannam@125 309 epipe_count += 100 ;
cannam@125 310
cannam@125 311 #if 0
cannam@125 312 if (0)
cannam@125 313 { snd_pcm_status_t *status ;
cannam@125 314
cannam@125 315 snd_pcm_status_alloca (&status) ;
cannam@125 316 if ((retval = snd_pcm_status (alsa_dev, status)) < 0)
cannam@125 317 fprintf (stderr, "alsa_out: xrun. can't determine length\n") ;
cannam@125 318 else if (snd_pcm_status_get_state (status) == SND_PCM_STATE_XRUN)
cannam@125 319 { struct timeval now, diff, tstamp ;
cannam@125 320
cannam@125 321 gettimeofday (&now, 0) ;
cannam@125 322 snd_pcm_status_get_trigger_tstamp (status, &tstamp) ;
cannam@125 323 timersub (&now, &tstamp, &diff) ;
cannam@125 324
cannam@125 325 fprintf (stderr, "alsa_write_float xrun: of at least %.3f msecs. resetting stream\n",
cannam@125 326 diff.tv_sec * 1000 + diff.tv_usec / 1000.0) ;
cannam@125 327 }
cannam@125 328 else
cannam@125 329 fprintf (stderr, "alsa_write_float: xrun. can't determine length\n") ;
cannam@125 330 } ;
cannam@125 331 #endif
cannam@125 332
cannam@125 333 snd_pcm_prepare (alsa_dev) ;
cannam@125 334 break ;
cannam@125 335
cannam@125 336 case -EBADFD :
cannam@125 337 fprintf (stderr, "alsa_write_float: Bad PCM state.n") ;
cannam@125 338 return 0 ;
cannam@125 339 break ;
cannam@125 340
cannam@125 341 case -ESTRPIPE :
cannam@125 342 fprintf (stderr, "alsa_write_float: Suspend event.n") ;
cannam@125 343 return 0 ;
cannam@125 344 break ;
cannam@125 345
cannam@125 346 case -EIO :
cannam@125 347 puts ("alsa_write_float: EIO") ;
cannam@125 348 return 0 ;
cannam@125 349
cannam@125 350 default :
cannam@125 351 fprintf (stderr, "alsa_write_float: retval = %d\n", retval) ;
cannam@125 352 return 0 ;
cannam@125 353 break ;
cannam@125 354 } ; /* switch */
cannam@125 355 } ; /* while */
cannam@125 356
cannam@125 357 return total ;
cannam@125 358 } /* alsa_write_float */
cannam@125 359
cannam@125 360 #endif /* HAVE_ALSA_ASOUNDLIB_H */
cannam@125 361
cannam@125 362 /*------------------------------------------------------------------------------
cannam@125 363 ** Linux/OSS functions for playing a sound.
cannam@125 364 */
cannam@125 365
cannam@125 366 #if !defined (__ANDROID__) && (defined (__linux__) || defined (__FreeBSD_kernel__) || defined (__FreeBSD__))
cannam@125 367
cannam@125 368 static int opensoundsys_open_device (int channels, int srate) ;
cannam@125 369
cannam@125 370 static int
cannam@125 371 opensoundsys_play (int argc, char *argv [])
cannam@125 372 { static short buffer [BUFFER_LEN] ;
cannam@125 373 SNDFILE *sndfile ;
cannam@125 374 SF_INFO sfinfo ;
cannam@125 375 int k, audio_device, readcount, writecount, subformat ;
cannam@125 376
cannam@125 377 for (k = 1 ; k < argc ; k++)
cannam@125 378 { memset (&sfinfo, 0, sizeof (sfinfo)) ;
cannam@125 379
cannam@125 380 printf ("Playing %s\n", argv [k]) ;
cannam@125 381 if (! (sndfile = sf_open (argv [k], SFM_READ, &sfinfo)))
cannam@125 382 { puts (sf_strerror (NULL)) ;
cannam@125 383 continue ;
cannam@125 384 } ;
cannam@125 385
cannam@125 386 if (sfinfo.channels < 1 || sfinfo.channels > 2)
cannam@125 387 { printf ("Error : channels = %d.\n", sfinfo.channels) ;
cannam@125 388 continue ;
cannam@125 389 } ;
cannam@125 390
cannam@125 391 audio_device = opensoundsys_open_device (sfinfo.channels, sfinfo.samplerate) ;
cannam@125 392
cannam@125 393 subformat = sfinfo.format & SF_FORMAT_SUBMASK ;
cannam@125 394
cannam@125 395 if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE)
cannam@125 396 { static float float_buffer [BUFFER_LEN] ;
cannam@125 397 double scale ;
cannam@125 398 int m ;
cannam@125 399
cannam@125 400 sf_command (sndfile, SFC_CALC_SIGNAL_MAX, &scale, sizeof (scale)) ;
cannam@125 401 if (scale < 1e-10)
cannam@125 402 scale = 1.0 ;
cannam@125 403 else
cannam@125 404 scale = 32700.0 / scale ;
cannam@125 405
cannam@125 406 while ((readcount = sf_read_float (sndfile, float_buffer, BUFFER_LEN)))
cannam@125 407 { for (m = 0 ; m < readcount ; m++)
cannam@125 408 buffer [m] = scale * float_buffer [m] ;
cannam@125 409 writecount = write (audio_device, buffer, readcount * sizeof (short)) ;
cannam@125 410 } ;
cannam@125 411 }
cannam@125 412 else
cannam@125 413 { while ((readcount = sf_read_short (sndfile, buffer, BUFFER_LEN)))
cannam@125 414 writecount = write (audio_device, buffer, readcount * sizeof (short)) ;
cannam@125 415 } ;
cannam@125 416
cannam@125 417 if (ioctl (audio_device, SNDCTL_DSP_POST, 0) == -1)
cannam@125 418 perror ("ioctl (SNDCTL_DSP_POST) ") ;
cannam@125 419
cannam@125 420 if (ioctl (audio_device, SNDCTL_DSP_SYNC, 0) == -1)
cannam@125 421 perror ("ioctl (SNDCTL_DSP_SYNC) ") ;
cannam@125 422
cannam@125 423 close (audio_device) ;
cannam@125 424
cannam@125 425 sf_close (sndfile) ;
cannam@125 426 } ;
cannam@125 427
cannam@125 428 return writecount ;
cannam@125 429 } /* opensoundsys_play */
cannam@125 430
cannam@125 431 static int
cannam@125 432 opensoundsys_open_device (int channels, int srate)
cannam@125 433 { int fd, stereo, fmt ;
cannam@125 434
cannam@125 435 if ((fd = open ("/dev/dsp", O_WRONLY, 0)) == -1 &&
cannam@125 436 (fd = open ("/dev/sound/dsp", O_WRONLY, 0)) == -1)
cannam@125 437 { perror ("opensoundsys_open_device : open ") ;
cannam@125 438 exit (1) ;
cannam@125 439 } ;
cannam@125 440
cannam@125 441 stereo = 0 ;
cannam@125 442 if (ioctl (fd, SNDCTL_DSP_STEREO, &stereo) == -1)
cannam@125 443 { /* Fatal error */
cannam@125 444 perror ("opensoundsys_open_device : stereo ") ;
cannam@125 445 exit (1) ;
cannam@125 446 } ;
cannam@125 447
cannam@125 448 if (ioctl (fd, SNDCTL_DSP_RESET, 0))
cannam@125 449 { perror ("opensoundsys_open_device : reset ") ;
cannam@125 450 exit (1) ;
cannam@125 451 } ;
cannam@125 452
cannam@125 453 fmt = CPU_IS_BIG_ENDIAN ? AFMT_S16_BE : AFMT_S16_LE ;
cannam@125 454 if (ioctl (fd, SNDCTL_DSP_SETFMT, &fmt) != 0)
cannam@125 455 { perror ("opensoundsys_open_device : set format ") ;
cannam@125 456 exit (1) ;
cannam@125 457 } ;
cannam@125 458
cannam@125 459 if (ioctl (fd, SNDCTL_DSP_CHANNELS, &channels) != 0)
cannam@125 460 { perror ("opensoundsys_open_device : channels ") ;
cannam@125 461 exit (1) ;
cannam@125 462 } ;
cannam@125 463
cannam@125 464 if (ioctl (fd, SNDCTL_DSP_SPEED, &srate) != 0)
cannam@125 465 { perror ("opensoundsys_open_device : sample rate ") ;
cannam@125 466 exit (1) ;
cannam@125 467 } ;
cannam@125 468
cannam@125 469 if (ioctl (fd, SNDCTL_DSP_SYNC, 0) != 0)
cannam@125 470 { perror ("opensoundsys_open_device : sync ") ;
cannam@125 471 exit (1) ;
cannam@125 472 } ;
cannam@125 473
cannam@125 474 return fd ;
cannam@125 475 } /* opensoundsys_open_device */
cannam@125 476
cannam@125 477 #endif /* __linux__ */
cannam@125 478
cannam@125 479 /*------------------------------------------------------------------------------
cannam@125 480 ** Mac OS X functions for playing a sound.
cannam@125 481 */
cannam@125 482
cannam@125 483 #if (OSX_DARWIN_VERSION > 11)
cannam@125 484 /* MacOSX 10.8 use a new Audio API. Someone needs to write some code for it. */
cannam@125 485 #endif /* OSX_DARWIN_VERSION > 11 */
cannam@125 486
cannam@125 487 #if (OSX_DARWIN_VERSION == 11)
cannam@125 488 /* MacOSX 10.7 use AudioQueue API */
cannam@125 489
cannam@125 490 #define kBytesPerAudioBuffer (1024 * 8)
cannam@125 491 #define kNumberOfAudioBuffers 4
cannam@125 492
cannam@125 493 typedef struct
cannam@125 494 { AudioStreamBasicDescription format ;
cannam@125 495
cannam@125 496 AudioQueueRef queue ;
cannam@125 497 AudioQueueBufferRef queueBuffer [kNumberOfAudioBuffers] ;
cannam@125 498
cannam@125 499 UInt32 buf_size ;
cannam@125 500
cannam@125 501 SNDFILE *sndfile ;
cannam@125 502 SF_INFO sfinfo ;
cannam@125 503
cannam@125 504 int done_playing ;
cannam@125 505 } MacOSXAudioData ;
cannam@125 506
cannam@125 507
cannam@125 508 static void
cannam@125 509 macosx_fill_buffer (MacOSXAudioData *audio_data, AudioQueueBufferRef audio_buffer)
cannam@125 510 { int size, sample_count, read_count ;
cannam@125 511 short *buffer ;
cannam@125 512
cannam@125 513 size = audio_buffer->mAudioDataBytesCapacity ;
cannam@125 514 sample_count = size / sizeof (short) ;
cannam@125 515
cannam@125 516 buffer = (short*) audio_buffer->mAudioData ;
cannam@125 517
cannam@125 518 read_count = sf_read_short (audio_data->sndfile, buffer, sample_count) ;
cannam@125 519
cannam@125 520 if (read_count > 0)
cannam@125 521 { audio_buffer->mAudioDataByteSize = read_count * sizeof (short) ;
cannam@125 522 AudioQueueEnqueueBuffer (audio_data->queue, audio_buffer, 0, NULL) ;
cannam@125 523 }
cannam@125 524 else
cannam@125 525 AudioQueueStop (audio_data->queue, false) ;
cannam@125 526
cannam@125 527 } /* macosx_fill_buffer */
cannam@125 528
cannam@125 529
cannam@125 530 static void
cannam@125 531 macosx_audio_out_callback (void *user_data, AudioQueueRef audio_queue, AudioQueueBufferRef audio_buffer)
cannam@125 532 { MacOSXAudioData *audio_data = (MacOSXAudioData *) user_data ;
cannam@125 533
cannam@125 534 if (audio_data->queue == audio_queue)
cannam@125 535 macosx_fill_buffer (audio_data, audio_buffer) ;
cannam@125 536
cannam@125 537 } /* macosx_audio_out_callback */
cannam@125 538
cannam@125 539
cannam@125 540 static void
cannam@125 541 macosx_audio_out_property_callback (void *user_data, AudioQueueRef audio_queue, AudioQueuePropertyID prop)
cannam@125 542 { MacOSXAudioData *audio_data = (MacOSXAudioData *) user_data ;
cannam@125 543
cannam@125 544 if (prop == kAudioQueueProperty_IsRunning)
cannam@125 545 { UInt32 is_running = 0 ;
cannam@125 546 UInt32 is_running_size = sizeof (is_running) ;
cannam@125 547
cannam@125 548 AudioQueueGetProperty (audio_queue, kAudioQueueProperty_IsRunning, &is_running, &is_running_size) ;
cannam@125 549
cannam@125 550 if (!is_running)
cannam@125 551 { audio_data->done_playing = SF_TRUE ;
cannam@125 552 CFRunLoopStop (CFRunLoopGetCurrent ()) ;
cannam@125 553 } ;
cannam@125 554 } ;
cannam@125 555 } /* macosx_audio_out_property_callback */
cannam@125 556
cannam@125 557
cannam@125 558
cannam@125 559 static void
cannam@125 560 macosx_play (int argc, char *argv [])
cannam@125 561 { MacOSXAudioData audio_data ;
cannam@125 562 OSStatus err ;
cannam@125 563 int i ;
cannam@125 564 int k ;
cannam@125 565
cannam@125 566 memset (&audio_data, 0x55, sizeof (audio_data)) ;
cannam@125 567
cannam@125 568 for (k = 1 ; k < argc ; k++)
cannam@125 569 { memset (&(audio_data.sfinfo), 0, sizeof (audio_data.sfinfo)) ;
cannam@125 570
cannam@125 571 printf ("Playing %s\n", argv [k]) ;
cannam@125 572 if (! (audio_data.sndfile = sf_open (argv [k], SFM_READ, &(audio_data.sfinfo))))
cannam@125 573 { puts (sf_strerror (NULL)) ;
cannam@125 574 continue ;
cannam@125 575 } ;
cannam@125 576
cannam@125 577 if (audio_data.sfinfo.channels < 1 || audio_data.sfinfo.channels > 2)
cannam@125 578 { printf ("Error : channels = %d.\n", audio_data.sfinfo.channels) ;
cannam@125 579 continue ;
cannam@125 580 } ;
cannam@125 581
cannam@125 582 /* fill ASBD */
cannam@125 583 audio_data.format.mSampleRate = audio_data.sfinfo.samplerate ;
cannam@125 584 audio_data.format.mChannelsPerFrame = audio_data.sfinfo.channels ;
cannam@125 585 audio_data.format.mFormatID = kAudioFormatLinearPCM ;
cannam@125 586 audio_data.format.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked ;
cannam@125 587 audio_data.format.mBytesPerPacket = audio_data.format.mChannelsPerFrame * 2 ;
cannam@125 588 audio_data.format.mFramesPerPacket = 1 ;
cannam@125 589 audio_data.format.mBytesPerFrame = audio_data.format.mBytesPerPacket ;
cannam@125 590 audio_data.format.mBitsPerChannel = 16 ;
cannam@125 591 audio_data.format.mReserved = 0 ;
cannam@125 592
cannam@125 593 /* create the queue */
cannam@125 594 if ((err = AudioQueueNewOutput (&(audio_data.format), macosx_audio_out_callback, &audio_data,
cannam@125 595 CFRunLoopGetCurrent (), kCFRunLoopCommonModes, 0, &(audio_data.queue))) != noErr)
cannam@125 596 { printf ("AudioQueueNewOutput failed\n") ;
cannam@125 597 return ;
cannam@125 598 } ;
cannam@125 599
cannam@125 600 /* add property listener */
cannam@125 601 if ((err = AudioQueueAddPropertyListener (audio_data.queue, kAudioQueueProperty_IsRunning, macosx_audio_out_property_callback, &audio_data)) != noErr)
cannam@125 602 { printf ("AudioQueueAddPropertyListener failed\n") ;
cannam@125 603 return ;
cannam@125 604 } ;
cannam@125 605
cannam@125 606 /* create the buffers */
cannam@125 607 for (i = 0 ; i < kNumberOfAudioBuffers ; i++)
cannam@125 608 { if ((err = AudioQueueAllocateBuffer (audio_data.queue, kBytesPerAudioBuffer, &audio_data.queueBuffer [i])) != noErr)
cannam@125 609 { printf ("AudioQueueAllocateBuffer failed\n") ;
cannam@125 610 return ;
cannam@125 611 } ;
cannam@125 612
cannam@125 613 macosx_fill_buffer (&audio_data, audio_data.queueBuffer [i]) ;
cannam@125 614 } ;
cannam@125 615
cannam@125 616 audio_data.done_playing = SF_FALSE ;
cannam@125 617
cannam@125 618 /* start queue */
cannam@125 619 if ((err = AudioQueueStart (audio_data.queue, NULL)) != noErr)
cannam@125 620 { printf ("AudioQueueStart failed\n") ;
cannam@125 621 return ;
cannam@125 622 } ;
cannam@125 623
cannam@125 624 while (audio_data.done_playing == SF_FALSE)
cannam@125 625 CFRunLoopRun () ;
cannam@125 626
cannam@125 627 /* free the buffers */
cannam@125 628 for (i = 0 ; i < kNumberOfAudioBuffers ; i++)
cannam@125 629 { if ((err = AudioQueueFreeBuffer (audio_data.queue, audio_data.queueBuffer [i])) != noErr)
cannam@125 630 { printf ("AudioQueueFreeBuffer failed\n") ;
cannam@125 631 return ;
cannam@125 632 } ;
cannam@125 633 } ;
cannam@125 634
cannam@125 635 /* free the queue */
cannam@125 636 if ((err = AudioQueueDispose (audio_data.queue, true)) != noErr)
cannam@125 637 { printf ("AudioQueueDispose failed\n") ;
cannam@125 638 return ;
cannam@125 639 } ;
cannam@125 640
cannam@125 641 sf_close (audio_data.sndfile) ;
cannam@125 642 } ;
cannam@125 643
cannam@125 644 return ;
cannam@125 645 } /* macosx_play, AudioQueue implementation */
cannam@125 646
cannam@125 647 #endif /* OSX_DARWIN_VERSION == 11 */
cannam@125 648
cannam@125 649 #if (OSX_DARWIN_VERSION > 0 && OSX_DARWIN_VERSION <= 10)
cannam@125 650 /* MacOSX 10.6 or earlier, use AudioHardware API */
cannam@125 651
cannam@125 652 typedef struct
cannam@125 653 { AudioStreamBasicDescription format ;
cannam@125 654
cannam@125 655 UInt32 buf_size ;
cannam@125 656 AudioDeviceID device ;
cannam@125 657
cannam@125 658 SNDFILE *sndfile ;
cannam@125 659 SF_INFO sfinfo ;
cannam@125 660
cannam@125 661 int fake_stereo ;
cannam@125 662 int done_playing ;
cannam@125 663 } MacOSXAudioData ;
cannam@125 664
cannam@125 665 #include <math.h>
cannam@125 666
cannam@125 667 static OSStatus
cannam@125 668 macosx_audio_out_callback (AudioDeviceID device, const AudioTimeStamp* current_time,
cannam@125 669 const AudioBufferList* data_in, const AudioTimeStamp* time_in,
cannam@125 670 AudioBufferList* data_out, const AudioTimeStamp* time_out,
cannam@125 671 void* client_data)
cannam@125 672 { MacOSXAudioData *audio_data ;
cannam@125 673 int size, sample_count, read_count, k ;
cannam@125 674 float *buffer ;
cannam@125 675
cannam@125 676 /* Prevent compiler warnings. */
cannam@125 677 device = device ;
cannam@125 678 current_time = current_time ;
cannam@125 679 data_in = data_in ;
cannam@125 680 time_in = time_in ;
cannam@125 681 time_out = time_out ;
cannam@125 682
cannam@125 683 audio_data = (MacOSXAudioData*) client_data ;
cannam@125 684
cannam@125 685 size = data_out->mBuffers [0].mDataByteSize ;
cannam@125 686 sample_count = size / sizeof (float) ;
cannam@125 687
cannam@125 688 buffer = (float*) data_out->mBuffers [0].mData ;
cannam@125 689
cannam@125 690 if (audio_data->fake_stereo != 0)
cannam@125 691 { read_count = sf_read_float (audio_data->sndfile, buffer, sample_count / 2) ;
cannam@125 692
cannam@125 693 for (k = read_count - 1 ; k >= 0 ; k--)
cannam@125 694 { buffer [2 * k ] = buffer [k] ;
cannam@125 695 buffer [2 * k + 1] = buffer [k] ;
cannam@125 696 } ;
cannam@125 697 read_count *= 2 ;
cannam@125 698 }
cannam@125 699 else
cannam@125 700 read_count = sf_read_float (audio_data->sndfile, buffer, sample_count) ;
cannam@125 701
cannam@125 702 /* Fill the remainder with zeroes. */
cannam@125 703 if (read_count < sample_count)
cannam@125 704 { if (audio_data->fake_stereo == 0)
cannam@125 705 memset (&(buffer [read_count]), 0, (sample_count - read_count) * sizeof (float)) ;
cannam@125 706 /* Tell the main application to terminate. */
cannam@125 707 audio_data->done_playing = SF_TRUE ;
cannam@125 708 } ;
cannam@125 709
cannam@125 710 return noErr ;
cannam@125 711 } /* macosx_audio_out_callback */
cannam@125 712
cannam@125 713 static void
cannam@125 714 macosx_play (int argc, char *argv [])
cannam@125 715 { MacOSXAudioData audio_data ;
cannam@125 716 OSStatus err ;
cannam@125 717 UInt32 count, buffer_size ;
cannam@125 718 int k ;
cannam@125 719
cannam@125 720 audio_data.fake_stereo = 0 ;
cannam@125 721 audio_data.device = kAudioDeviceUnknown ;
cannam@125 722
cannam@125 723 /* get the default output device for the HAL */
cannam@125 724 count = sizeof (AudioDeviceID) ;
cannam@125 725 if ((err = AudioHardwareGetProperty (kAudioHardwarePropertyDefaultOutputDevice,
cannam@125 726 &count, (void *) &(audio_data.device))) != noErr)
cannam@125 727 { printf ("AudioHardwareGetProperty (kAudioDevicePropertyDefaultOutputDevice) failed.\n") ;
cannam@125 728 return ;
cannam@125 729 } ;
cannam@125 730
cannam@125 731 /* get the buffersize that the default device uses for IO */
cannam@125 732 count = sizeof (UInt32) ;
cannam@125 733 if ((err = AudioDeviceGetProperty (audio_data.device, 0, false, kAudioDevicePropertyBufferSize,
cannam@125 734 &count, &buffer_size)) != noErr)
cannam@125 735 { printf ("AudioDeviceGetProperty (kAudioDevicePropertyBufferSize) failed.\n") ;
cannam@125 736 return ;
cannam@125 737 } ;
cannam@125 738
cannam@125 739 /* get a description of the data format used by the default device */
cannam@125 740 count = sizeof (AudioStreamBasicDescription) ;
cannam@125 741 if ((err = AudioDeviceGetProperty (audio_data.device, 0, false, kAudioDevicePropertyStreamFormat,
cannam@125 742 &count, &(audio_data.format))) != noErr)
cannam@125 743 { printf ("AudioDeviceGetProperty (kAudioDevicePropertyStreamFormat) failed.\n") ;
cannam@125 744 return ;
cannam@125 745 } ;
cannam@125 746
cannam@125 747 /* Base setup completed. Now play files. */
cannam@125 748 for (k = 1 ; k < argc ; k++)
cannam@125 749 { memset (&(audio_data.sfinfo), 0, sizeof (audio_data.sfinfo)) ;
cannam@125 750
cannam@125 751 printf ("Playing %s\n", argv [k]) ;
cannam@125 752 if (! (audio_data.sndfile = sf_open (argv [k], SFM_READ, &(audio_data.sfinfo))))
cannam@125 753 { puts (sf_strerror (NULL)) ;
cannam@125 754 continue ;
cannam@125 755 } ;
cannam@125 756
cannam@125 757 if (audio_data.sfinfo.channels < 1 || audio_data.sfinfo.channels > 2)
cannam@125 758 { printf ("Error : channels = %d.\n", audio_data.sfinfo.channels) ;
cannam@125 759 continue ;
cannam@125 760 } ;
cannam@125 761
cannam@125 762 audio_data.format.mSampleRate = audio_data.sfinfo.samplerate ;
cannam@125 763
cannam@125 764 if (audio_data.sfinfo.channels == 1)
cannam@125 765 { audio_data.format.mChannelsPerFrame = 2 ;
cannam@125 766 audio_data.fake_stereo = 1 ;
cannam@125 767 }
cannam@125 768 else
cannam@125 769 audio_data.format.mChannelsPerFrame = audio_data.sfinfo.channels ;
cannam@125 770
cannam@125 771 if ((err = AudioDeviceSetProperty (audio_data.device, NULL, 0, false, kAudioDevicePropertyStreamFormat,
cannam@125 772 sizeof (AudioStreamBasicDescription), &(audio_data.format))) != noErr)
cannam@125 773 { printf ("AudioDeviceSetProperty (kAudioDevicePropertyStreamFormat) failed.\n") ;
cannam@125 774 return ;
cannam@125 775 } ;
cannam@125 776
cannam@125 777 /* we want linear pcm */
cannam@125 778 if (audio_data.format.mFormatID != kAudioFormatLinearPCM)
cannam@125 779 return ;
cannam@125 780
cannam@125 781 /* Fire off the device. */
cannam@125 782 if ((err = AudioDeviceAddIOProc (audio_data.device, macosx_audio_out_callback,
cannam@125 783 (void *) &audio_data)) != noErr)
cannam@125 784 { printf ("AudioDeviceAddIOProc failed.\n") ;
cannam@125 785 return ;
cannam@125 786 } ;
cannam@125 787
cannam@125 788 err = AudioDeviceStart (audio_data.device, macosx_audio_out_callback) ;
cannam@125 789 if (err != noErr)
cannam@125 790 return ;
cannam@125 791
cannam@125 792 audio_data.done_playing = SF_FALSE ;
cannam@125 793
cannam@125 794 while (audio_data.done_playing == SF_FALSE)
cannam@125 795 usleep (10 * 1000) ; /* 10 000 milliseconds. */
cannam@125 796
cannam@125 797 if ((err = AudioDeviceStop (audio_data.device, macosx_audio_out_callback)) != noErr)
cannam@125 798 { printf ("AudioDeviceStop failed.\n") ;
cannam@125 799 return ;
cannam@125 800 } ;
cannam@125 801
cannam@125 802 err = AudioDeviceRemoveIOProc (audio_data.device, macosx_audio_out_callback) ;
cannam@125 803 if (err != noErr)
cannam@125 804 { printf ("AudioDeviceRemoveIOProc failed.\n") ;
cannam@125 805 return ;
cannam@125 806 } ;
cannam@125 807
cannam@125 808 sf_close (audio_data.sndfile) ;
cannam@125 809 } ;
cannam@125 810
cannam@125 811 return ;
cannam@125 812 } /* macosx_play, AudioHardware implementation */
cannam@125 813
cannam@125 814 #endif /* OSX_DARWIN_VERSION > 0 && OSX_DARWIN_VERSION <= 10 */
cannam@125 815
cannam@125 816 /*------------------------------------------------------------------------------
cannam@125 817 ** Win32 functions for playing a sound.
cannam@125 818 **
cannam@125 819 ** This API sucks. Its needlessly complicated and is *WAY* too loose with
cannam@125 820 ** passing pointers around in integers and using char* pointers to
cannam@125 821 ** point to data instead of short*. It plain sucks!
cannam@125 822 */
cannam@125 823
cannam@125 824 #if (OS_IS_WIN32 == 1)
cannam@125 825
cannam@125 826 #define WIN32_BUFFER_LEN (1 << 15)
cannam@125 827
cannam@125 828 typedef struct
cannam@125 829 { HWAVEOUT hwave ;
cannam@125 830 WAVEHDR whdr [2] ;
cannam@125 831
cannam@125 832 CRITICAL_SECTION mutex ; /* to control access to BuffersInUSe */
cannam@125 833 HANDLE Event ; /* signal that a buffer is free */
cannam@125 834
cannam@125 835 short buffer [WIN32_BUFFER_LEN / sizeof (short)] ;
cannam@125 836 int current, bufferlen ;
cannam@125 837 int BuffersInUse ;
cannam@125 838
cannam@125 839 SNDFILE *sndfile ;
cannam@125 840 SF_INFO sfinfo ;
cannam@125 841
cannam@125 842 sf_count_t remaining ;
cannam@125 843 } Win32_Audio_Data ;
cannam@125 844
cannam@125 845
cannam@125 846 static void
cannam@125 847 win32_play_data (Win32_Audio_Data *audio_data)
cannam@125 848 { int thisread, readcount ;
cannam@125 849
cannam@125 850 /* fill a buffer if there is more data and we can read it sucessfully */
cannam@125 851 readcount = (audio_data->remaining > audio_data->bufferlen) ? audio_data->bufferlen : (int) audio_data->remaining ;
cannam@125 852
cannam@125 853 thisread = (int) sf_read_short (audio_data->sndfile, (short *) (audio_data->whdr [audio_data->current].lpData), readcount) ;
cannam@125 854
cannam@125 855 audio_data->remaining -= thisread ;
cannam@125 856
cannam@125 857 if (thisread > 0)
cannam@125 858 { /* Fix buffer length if this is only a partial block. */
cannam@125 859 if (thisread < audio_data->bufferlen)
cannam@125 860 audio_data->whdr [audio_data->current].dwBufferLength = thisread * sizeof (short) ;
cannam@125 861
cannam@125 862 /* Queue the WAVEHDR */
cannam@125 863 waveOutWrite (audio_data->hwave, (LPWAVEHDR) &(audio_data->whdr [audio_data->current]), sizeof (WAVEHDR)) ;
cannam@125 864
cannam@125 865 /* count another buffer in use */
cannam@125 866 EnterCriticalSection (&audio_data->mutex) ;
cannam@125 867 audio_data->BuffersInUse ++ ;
cannam@125 868 LeaveCriticalSection (&audio_data->mutex) ;
cannam@125 869
cannam@125 870 /* use the other buffer next time */
cannam@125 871 audio_data->current = (audio_data->current + 1) % 2 ;
cannam@125 872 } ;
cannam@125 873
cannam@125 874 return ;
cannam@125 875 } /* win32_play_data */
cannam@125 876
cannam@125 877 static void CALLBACK
cannam@125 878 win32_audio_out_callback (HWAVEOUT hwave, UINT msg, DWORD_PTR data, DWORD param1, DWORD param2)
cannam@125 879 { Win32_Audio_Data *audio_data ;
cannam@125 880
cannam@125 881 /* Prevent compiler warnings. */
cannam@125 882 (void) hwave ;
cannam@125 883 (void) param1 ;
cannam@125 884 (void) param2 ;
cannam@125 885
cannam@125 886 if (data == 0)
cannam@125 887 return ;
cannam@125 888
cannam@125 889 /*
cannam@125 890 ** I consider this technique of passing a pointer via an integer as
cannam@125 891 ** fundamentally broken but thats the way microsoft has defined the
cannam@125 892 ** interface.
cannam@125 893 */
cannam@125 894 audio_data = (Win32_Audio_Data*) data ;
cannam@125 895
cannam@125 896 /* let main loop know a buffer is free */
cannam@125 897 if (msg == MM_WOM_DONE)
cannam@125 898 { EnterCriticalSection (&audio_data->mutex) ;
cannam@125 899 audio_data->BuffersInUse -- ;
cannam@125 900 LeaveCriticalSection (&audio_data->mutex) ;
cannam@125 901 SetEvent (audio_data->Event) ;
cannam@125 902 } ;
cannam@125 903
cannam@125 904 return ;
cannam@125 905 } /* win32_audio_out_callback */
cannam@125 906
cannam@125 907 static void
cannam@125 908 win32_play (int argc, char *argv [])
cannam@125 909 { Win32_Audio_Data audio_data ;
cannam@125 910
cannam@125 911 WAVEFORMATEX wf ;
cannam@125 912 int k, error ;
cannam@125 913
cannam@125 914 audio_data.sndfile = NULL ;
cannam@125 915 audio_data.hwave = 0 ;
cannam@125 916
cannam@125 917 for (k = 1 ; k < argc ; k++)
cannam@125 918 { printf ("Playing %s\n", argv [k]) ;
cannam@125 919
cannam@125 920 if (! (audio_data.sndfile = sf_open (argv [k], SFM_READ, &(audio_data.sfinfo))))
cannam@125 921 { puts (sf_strerror (NULL)) ;
cannam@125 922 continue ;
cannam@125 923 } ;
cannam@125 924
cannam@125 925 audio_data.remaining = audio_data.sfinfo.frames * audio_data.sfinfo.channels ;
cannam@125 926 audio_data.current = 0 ;
cannam@125 927
cannam@125 928 InitializeCriticalSection (&audio_data.mutex) ;
cannam@125 929 audio_data.Event = CreateEvent (0, FALSE, FALSE, 0) ;
cannam@125 930
cannam@125 931 wf.nChannels = audio_data.sfinfo.channels ;
cannam@125 932 wf.wFormatTag = WAVE_FORMAT_PCM ;
cannam@125 933 wf.cbSize = 0 ;
cannam@125 934 wf.wBitsPerSample = 16 ;
cannam@125 935
cannam@125 936 wf.nSamplesPerSec = audio_data.sfinfo.samplerate ;
cannam@125 937
cannam@125 938 wf.nBlockAlign = audio_data.sfinfo.channels * sizeof (short) ;
cannam@125 939
cannam@125 940 wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec ;
cannam@125 941
cannam@125 942 error = waveOutOpen (&(audio_data.hwave), WAVE_MAPPER, &wf, (DWORD_PTR) win32_audio_out_callback,
cannam@125 943 (DWORD_PTR) &audio_data, CALLBACK_FUNCTION) ;
cannam@125 944 if (error)
cannam@125 945 { puts ("waveOutOpen failed.") ;
cannam@125 946 audio_data.hwave = 0 ;
cannam@125 947 continue ;
cannam@125 948 } ;
cannam@125 949
cannam@125 950 audio_data.whdr [0].lpData = (char*) audio_data.buffer ;
cannam@125 951 audio_data.whdr [1].lpData = ((char*) audio_data.buffer) + sizeof (audio_data.buffer) / 2 ;
cannam@125 952
cannam@125 953 audio_data.whdr [0].dwBufferLength = sizeof (audio_data.buffer) / 2 ;
cannam@125 954 audio_data.whdr [1].dwBufferLength = sizeof (audio_data.buffer) / 2 ;
cannam@125 955
cannam@125 956 audio_data.whdr [0].dwFlags = 0 ;
cannam@125 957 audio_data.whdr [1].dwFlags = 0 ;
cannam@125 958
cannam@125 959 /* length of each audio buffer in samples */
cannam@125 960 audio_data.bufferlen = sizeof (audio_data.buffer) / 2 / sizeof (short) ;
cannam@125 961
cannam@125 962 /* Prepare the WAVEHDRs */
cannam@125 963 if ((error = waveOutPrepareHeader (audio_data.hwave, &(audio_data.whdr [0]), sizeof (WAVEHDR))))
cannam@125 964 { printf ("waveOutPrepareHeader [0] failed : %08X\n", error) ;
cannam@125 965 waveOutClose (audio_data.hwave) ;
cannam@125 966 continue ;
cannam@125 967 } ;
cannam@125 968
cannam@125 969 if ((error = waveOutPrepareHeader (audio_data.hwave, &(audio_data.whdr [1]), sizeof (WAVEHDR))))
cannam@125 970 { printf ("waveOutPrepareHeader [1] failed : %08X\n", error) ;
cannam@125 971 waveOutUnprepareHeader (audio_data.hwave, &(audio_data.whdr [0]), sizeof (WAVEHDR)) ;
cannam@125 972 waveOutClose (audio_data.hwave) ;
cannam@125 973 continue ;
cannam@125 974 } ;
cannam@125 975
cannam@125 976 /* Fill up both buffers with audio data */
cannam@125 977 audio_data.BuffersInUse = 0 ;
cannam@125 978 win32_play_data (&audio_data) ;
cannam@125 979 win32_play_data (&audio_data) ;
cannam@125 980
cannam@125 981 /* loop until both buffers are released */
cannam@125 982 while (audio_data.BuffersInUse > 0)
cannam@125 983 {
cannam@125 984 /* wait for buffer to be released */
cannam@125 985 WaitForSingleObject (audio_data.Event, INFINITE) ;
cannam@125 986
cannam@125 987 /* refill the buffer if there is more data to play */
cannam@125 988 win32_play_data (&audio_data) ;
cannam@125 989 } ;
cannam@125 990
cannam@125 991 waveOutUnprepareHeader (audio_data.hwave, &(audio_data.whdr [0]), sizeof (WAVEHDR)) ;
cannam@125 992 waveOutUnprepareHeader (audio_data.hwave, &(audio_data.whdr [1]), sizeof (WAVEHDR)) ;
cannam@125 993
cannam@125 994 waveOutClose (audio_data.hwave) ;
cannam@125 995 audio_data.hwave = 0 ;
cannam@125 996
cannam@125 997 DeleteCriticalSection (&audio_data.mutex) ;
cannam@125 998
cannam@125 999 sf_close (audio_data.sndfile) ;
cannam@125 1000 } ;
cannam@125 1001
cannam@125 1002 } /* win32_play */
cannam@125 1003
cannam@125 1004 #endif /* Win32 */
cannam@125 1005
cannam@125 1006 /*------------------------------------------------------------------------------
cannam@125 1007 ** OpenBSD's sndio.
cannam@125 1008 */
cannam@125 1009
cannam@125 1010 #if HAVE_SNDIO_H
cannam@125 1011
cannam@125 1012 static void
cannam@125 1013 sndio_play (int argc, char *argv [])
cannam@125 1014 { struct sio_hdl *hdl ;
cannam@125 1015 struct sio_par par ;
cannam@125 1016 short buffer [BUFFER_LEN] ;
cannam@125 1017 SNDFILE *sndfile ;
cannam@125 1018 SF_INFO sfinfo ;
cannam@125 1019 int k, readcount ;
cannam@125 1020
cannam@125 1021 for (k = 1 ; k < argc ; k++)
cannam@125 1022 { printf ("Playing %s\n", argv [k]) ;
cannam@125 1023 if (! (sndfile = sf_open (argv [k], SFM_READ, &sfinfo)))
cannam@125 1024 { puts (sf_strerror (NULL)) ;
cannam@125 1025 continue ;
cannam@125 1026 } ;
cannam@125 1027
cannam@125 1028 if (sfinfo.channels < 1 || sfinfo.channels > 2)
cannam@125 1029 { printf ("Error : channels = %d.\n", sfinfo.channels) ;
cannam@125 1030 continue ;
cannam@125 1031 } ;
cannam@125 1032
cannam@125 1033 if ((hdl = sio_open (NULL, SIO_PLAY, 0)) == NULL)
cannam@125 1034 { fprintf (stderr, "open sndio device failed") ;
cannam@125 1035 return ;
cannam@125 1036 } ;
cannam@125 1037
cannam@125 1038 sio_initpar (&par) ;
cannam@125 1039 par.rate = sfinfo.samplerate ;
cannam@125 1040 par.pchan = sfinfo.channels ;
cannam@125 1041 par.bits = 16 ;
cannam@125 1042 par.sig = 1 ;
cannam@125 1043 par.le = SIO_LE_NATIVE ;
cannam@125 1044
cannam@125 1045 if (! sio_setpar (hdl, &par) || ! sio_getpar (hdl, &par))
cannam@125 1046 { fprintf (stderr, "set sndio params failed") ;
cannam@125 1047 return ;
cannam@125 1048 } ;
cannam@125 1049
cannam@125 1050 if (! sio_start (hdl))
cannam@125 1051 { fprintf (stderr, "sndio start failed") ;
cannam@125 1052 return ;
cannam@125 1053 } ;
cannam@125 1054
cannam@125 1055 while ((readcount = sf_read_short (sndfile, buffer, BUFFER_LEN)))
cannam@125 1056 sio_write (hdl, buffer, readcount * sizeof (short)) ;
cannam@125 1057
cannam@125 1058 sio_close (hdl) ;
cannam@125 1059 } ;
cannam@125 1060
cannam@125 1061 return ;
cannam@125 1062 } /* sndio_play */
cannam@125 1063
cannam@125 1064 #endif /* sndio */
cannam@125 1065
cannam@125 1066 /*------------------------------------------------------------------------------
cannam@125 1067 ** Solaris.
cannam@125 1068 */
cannam@125 1069
cannam@125 1070 #if (defined (sun) && defined (unix)) /* ie Solaris */
cannam@125 1071
cannam@125 1072 static void
cannam@125 1073 solaris_play (int argc, char *argv [])
cannam@125 1074 { static short buffer [BUFFER_LEN] ;
cannam@125 1075 audio_info_t audio_info ;
cannam@125 1076 SNDFILE *sndfile ;
cannam@125 1077 SF_INFO sfinfo ;
cannam@125 1078 unsigned long delay_time ;
cannam@125 1079 long k, start_count, output_count, write_count, read_count ;
cannam@125 1080 int audio_fd, error, done ;
cannam@125 1081
cannam@125 1082 for (k = 1 ; k < argc ; k++)
cannam@125 1083 { printf ("Playing %s\n", argv [k]) ;
cannam@125 1084 if (! (sndfile = sf_open (argv [k], SFM_READ, &sfinfo)))
cannam@125 1085 { puts (sf_strerror (NULL)) ;
cannam@125 1086 continue ;
cannam@125 1087 } ;
cannam@125 1088
cannam@125 1089 if (sfinfo.channels < 1 || sfinfo.channels > 2)
cannam@125 1090 { printf ("Error : channels = %d.\n", sfinfo.channels) ;
cannam@125 1091 continue ;
cannam@125 1092 } ;
cannam@125 1093
cannam@125 1094 /* open the audio device - write only, non-blocking */
cannam@125 1095 if ((audio_fd = open ("/dev/audio", O_WRONLY | O_NONBLOCK)) < 0)
cannam@125 1096 { perror ("open (/dev/audio) failed") ;
cannam@125 1097 return ;
cannam@125 1098 } ;
cannam@125 1099
cannam@125 1100 /* Retrive standard values. */
cannam@125 1101 AUDIO_INITINFO (&audio_info) ;
cannam@125 1102
cannam@125 1103 audio_info.play.sample_rate = sfinfo.samplerate ;
cannam@125 1104 audio_info.play.channels = sfinfo.channels ;
cannam@125 1105 audio_info.play.precision = 16 ;
cannam@125 1106 audio_info.play.encoding = AUDIO_ENCODING_LINEAR ;
cannam@125 1107 audio_info.play.gain = AUDIO_MAX_GAIN ;
cannam@125 1108 audio_info.play.balance = AUDIO_MID_BALANCE ;
cannam@125 1109
cannam@125 1110 if ((error = ioctl (audio_fd, AUDIO_SETINFO, &audio_info)))
cannam@125 1111 { perror ("ioctl (AUDIO_SETINFO) failed") ;
cannam@125 1112 return ;
cannam@125 1113 } ;
cannam@125 1114
cannam@125 1115 /* Delay time equal to 1/4 of a buffer in microseconds. */
cannam@125 1116 delay_time = (BUFFER_LEN * 1000000) / (audio_info.play.sample_rate * 4) ;
cannam@125 1117
cannam@125 1118 done = 0 ;
cannam@125 1119 while (! done)
cannam@125 1120 { read_count = sf_read_short (sndfile, buffer, BUFFER_LEN) ;
cannam@125 1121 if (read_count < BUFFER_LEN)
cannam@125 1122 { memset (&(buffer [read_count]), 0, (BUFFER_LEN - read_count) * sizeof (short)) ;
cannam@125 1123 /* Tell the main application to terminate. */
cannam@125 1124 done = SF_TRUE ;
cannam@125 1125 } ;
cannam@125 1126
cannam@125 1127 start_count = 0 ;
cannam@125 1128 output_count = BUFFER_LEN * sizeof (short) ;
cannam@125 1129
cannam@125 1130 while (output_count > 0)
cannam@125 1131 { /* write as much data as possible */
cannam@125 1132 write_count = write (audio_fd, &(buffer [start_count]), output_count) ;
cannam@125 1133 if (write_count > 0)
cannam@125 1134 { output_count -= write_count ;
cannam@125 1135 start_count += write_count ;
cannam@125 1136 }
cannam@125 1137 else
cannam@125 1138 { /* Give the audio output time to catch up. */
cannam@125 1139 usleep (delay_time) ;
cannam@125 1140 } ;
cannam@125 1141 } ; /* while (outpur_count > 0) */
cannam@125 1142 } ; /* while (! done) */
cannam@125 1143
cannam@125 1144 close (audio_fd) ;
cannam@125 1145 } ;
cannam@125 1146
cannam@125 1147 return ;
cannam@125 1148 } /* solaris_play */
cannam@125 1149
cannam@125 1150 #endif /* Solaris */
cannam@125 1151
cannam@125 1152 /*==============================================================================
cannam@125 1153 ** Main function.
cannam@125 1154 */
cannam@125 1155
cannam@125 1156 int
cannam@125 1157 main (int argc, char *argv [])
cannam@125 1158 {
cannam@125 1159 if (argc < 2)
cannam@125 1160 {
cannam@125 1161 printf ("\nUsage : %s <input sound file>\n\n", program_name (argv [0])) ;
cannam@125 1162 printf ("Using %s.\n\n", sf_version_string ()) ;
cannam@125 1163 #if (OS_IS_WIN32 == 1)
cannam@125 1164 printf ("This is a Unix style command line application which\n"
cannam@125 1165 "should be run in a MSDOS box or Command Shell window.\n\n") ;
cannam@125 1166 printf ("Sleeping for 5 seconds before exiting.\n\n") ;
cannam@125 1167
cannam@125 1168 Sleep (5 * 1000) ;
cannam@125 1169 #endif
cannam@125 1170 return 1 ;
cannam@125 1171 } ;
cannam@125 1172
cannam@125 1173 #if defined (__ANDROID__)
cannam@125 1174 puts ("*** Playing sound not yet supported on Android.") ;
cannam@125 1175 puts ("*** Please feel free to submit a patch.") ;
cannam@125 1176 return 1 ;
cannam@125 1177 #elif defined (__linux__)
cannam@125 1178 #if HAVE_ALSA_ASOUNDLIB_H
cannam@125 1179 if (access ("/proc/asound/cards", R_OK) == 0)
cannam@125 1180 alsa_play (argc, argv) ;
cannam@125 1181 else
cannam@125 1182 #endif
cannam@125 1183 opensoundsys_play (argc, argv) ;
cannam@125 1184 #elif defined (__FreeBSD_kernel__) || defined (__FreeBSD__)
cannam@125 1185 opensoundsys_play (argc, argv) ;
cannam@125 1186 #elif (defined (__MACH__) && defined (__APPLE__) && OSX_DARWIN_VERSION <= 11)
cannam@125 1187 macosx_play (argc, argv) ;
cannam@125 1188 #elif HAVE_SNDIO_H
cannam@125 1189 sndio_play (argc, argv) ;
cannam@125 1190 #elif (defined (sun) && defined (unix))
cannam@125 1191 solaris_play (argc, argv) ;
cannam@125 1192 #elif (OS_IS_WIN32 == 1)
cannam@125 1193 win32_play (argc, argv) ;
cannam@125 1194 #elif (defined (__MACH__) && defined (__APPLE__) && OSX_DARWIN_VERSION > 11)
cannam@125 1195 printf ("OS X 10.8 and later have a new Audio API.\n") ;
cannam@125 1196 printf ("Someone needs to write code to use that API.\n") ;
cannam@125 1197 return 1 ;
cannam@125 1198 #elif defined (__BEOS__)
cannam@125 1199 printf ("This program cannot be compiled on BeOS.\n") ;
cannam@125 1200 printf ("Instead, compile the file sfplay_beos.cpp.\n") ;
cannam@125 1201 return 1 ;
cannam@125 1202 #else
cannam@125 1203 puts ("*** Playing sound not yet supported on this platform.") ;
cannam@125 1204 puts ("*** Please feel free to submit a patch.") ;
cannam@125 1205 return 1 ;
cannam@125 1206 #endif
cannam@125 1207
cannam@125 1208 return 0 ;
cannam@125 1209 } /* main */
cannam@125 1210