annotate src/libsndfile-1.0.27/programs/sndfile-play.c @ 83:ae30d91d2ffe

Replace these with versions built using an older toolset (so as to avoid ABI compatibilities when linking on Ubuntu 14.04 for packaging purposes)
author Chris Cannam
date Fri, 07 Feb 2020 11:51:13 +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