annotate src/libsndfile-1.0.27/programs/sndfile-play.c @ 84:08ae793730bd

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