sndfile-play.c
上传用户:shw771010
上传日期:2022-01-05
资源大小:991k
文件大小:27k
源码类别:

Audio

开发平台:

Unix_Linux

  1. /*
  2. ** Copyright (C) 1999-2009 Erik de Castro Lopo <erikd@mega-nerd.com>
  3. **
  4. ** All rights reserved.
  5. **
  6. ** Redistribution and use in source and binary forms, with or without
  7. ** modification, are permitted provided that the following conditions are
  8. ** met:
  9. **
  10. **     * Redistributions of source code must retain the above copyright
  11. **       notice, this list of conditions and the following disclaimer.
  12. **     * Redistributions in binary form must reproduce the above copyright
  13. **       notice, this list of conditions and the following disclaimer in
  14. **       the documentation and/or other materials provided with the
  15. **       distribution.
  16. **     * Neither the author nor the names of any contributors may be used
  17. **       to endorse or promote products derived from this software without
  18. **       specific prior written permission.
  19. **
  20. ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  22. ** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  23. ** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  24. ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  25. ** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  26. ** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  27. ** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  28. ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  29. ** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  30. ** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. */
  32. #include "sfconfig.h"
  33. #include <stdio.h>
  34. #include <stdlib.h>
  35. #include <string.h>
  36. #include <errno.h>
  37. #if HAVE_UNISTD_H
  38. #include <unistd.h>
  39. #endif
  40. #if HAVE_ALSA_ASOUNDLIB_H
  41. #define ALSA_PCM_NEW_HW_PARAMS_API
  42. #define ALSA_PCM_NEW_SW_PARAMS_API
  43. #include <alsa/asoundlib.h>
  44. #include <sys/time.h>
  45. #endif
  46. #if defined (__linux__) || defined (__FreeBSD_kernel__) || defined (__FreeBSD__)
  47. #include  <fcntl.h>
  48. #include  <sys/ioctl.h>
  49. #include  <sys/soundcard.h>
  50. #elif (defined (__MACH__) && defined (__APPLE__))
  51. #include <Carbon.h>
  52. #include <CoreAudio/AudioHardware.h>
  53. #elif (defined (sun) && defined (unix))
  54. #include <fcntl.h>
  55. #include <sys/ioctl.h>
  56. #include <sys/audioio.h>
  57. #elif (OS_IS_WIN32 == 1)
  58. #include <windows.h>
  59. #include <mmsystem.h>
  60. #endif
  61. #include <sndfile.h>
  62. #define SIGNED_SIZEOF(x) ((int) sizeof (x))
  63. #define BUFFER_LEN (2048)
  64. /*------------------------------------------------------------------------------
  65. ** Linux/OSS functions for playing a sound.
  66. */
  67. #if HAVE_ALSA_ASOUNDLIB_H
  68. static snd_pcm_t * alsa_open (int channels, unsigned srate, int realtime) ;
  69. static int alsa_write_float (snd_pcm_t *alsa_dev, float *data, int frames, int channels) ;
  70. static void
  71. alsa_play (int argc, char *argv [])
  72. { static float buffer [BUFFER_LEN] ;
  73. SNDFILE *sndfile ;
  74. SF_INFO sfinfo ;
  75. snd_pcm_t * alsa_dev ;
  76. int k, readcount, subformat ;
  77. for (k = 1 ; k < argc ; k++)
  78. { memset (&sfinfo, 0, sizeof (sfinfo)) ;
  79. printf ("Playing %sn", argv [k]) ;
  80. if (! (sndfile = sf_open (argv [k], SFM_READ, &sfinfo)))
  81. { puts (sf_strerror (NULL)) ;
  82. continue ;
  83. } ;
  84. if (sfinfo.channels < 1 || sfinfo.channels > 2)
  85. { printf ("Error : channels = %d.n", sfinfo.channels) ;
  86. continue ;
  87. } ;
  88. if ((alsa_dev = alsa_open (sfinfo.channels, (unsigned) sfinfo.samplerate, SF_FALSE)) == NULL)
  89. continue ;
  90. subformat = sfinfo.format & SF_FORMAT_SUBMASK ;
  91. if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE)
  92. { double scale ;
  93. int  m ;
  94. sf_command (sndfile, SFC_CALC_SIGNAL_MAX, &scale, sizeof (scale)) ;
  95. if (scale < 1e-10)
  96. scale = 1.0 ;
  97. else
  98. scale = 32700.0 / scale ;
  99. while ((readcount = sf_read_float (sndfile, buffer, BUFFER_LEN)))
  100. { for (m = 0 ; m < readcount ; m++)
  101. buffer [m] *= scale ;
  102. alsa_write_float (alsa_dev, buffer, BUFFER_LEN / sfinfo.channels, sfinfo.channels) ;
  103. } ;
  104. }
  105. else
  106. { while ((readcount = sf_read_float (sndfile, buffer, BUFFER_LEN)))
  107. alsa_write_float (alsa_dev, buffer, BUFFER_LEN / sfinfo.channels, sfinfo.channels) ;
  108. } ;
  109. snd_pcm_drain (alsa_dev) ;
  110. snd_pcm_close (alsa_dev) ;
  111. sf_close (sndfile) ;
  112. } ;
  113. return ;
  114. } /* alsa_play */
  115. static snd_pcm_t *
  116. alsa_open (int channels, unsigned samplerate, int realtime)
  117. { const char * device = "default" ;
  118. snd_pcm_t *alsa_dev = NULL ;
  119. snd_pcm_hw_params_t *hw_params ;
  120. snd_pcm_uframes_t buffer_size ;
  121. snd_pcm_uframes_t alsa_period_size, alsa_buffer_frames ;
  122. snd_pcm_sw_params_t *sw_params ;
  123. int err ;
  124. if (realtime)
  125. { alsa_period_size = 256 ;
  126. alsa_buffer_frames = 3 * alsa_period_size ;
  127. }
  128. else
  129. { alsa_period_size = 1024 ;
  130. alsa_buffer_frames = 4 * alsa_period_size ;
  131. } ;
  132. if ((err = snd_pcm_open (&alsa_dev, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0)
  133. { fprintf (stderr, "cannot open audio device "%s" (%s)n", device, snd_strerror (err)) ;
  134. goto catch_error ;
  135. } ;
  136. snd_pcm_nonblock (alsa_dev, 0) ;
  137. if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0)
  138. { fprintf (stderr, "cannot allocate hardware parameter structure (%s)n", snd_strerror (err)) ;
  139. goto catch_error ;
  140. } ;
  141. if ((err = snd_pcm_hw_params_any (alsa_dev, hw_params)) < 0)
  142. { fprintf (stderr, "cannot initialize hardware parameter structure (%s)n", snd_strerror (err)) ;
  143. goto catch_error ;
  144. } ;
  145. if ((err = snd_pcm_hw_params_set_access (alsa_dev, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
  146. { fprintf (stderr, "cannot set access type (%s)n", snd_strerror (err)) ;
  147. goto catch_error ;
  148. } ;
  149. if ((err = snd_pcm_hw_params_set_format (alsa_dev, hw_params, SND_PCM_FORMAT_FLOAT)) < 0)
  150. { fprintf (stderr, "cannot set sample format (%s)n", snd_strerror (err)) ;
  151. goto catch_error ;
  152. } ;
  153. if ((err = snd_pcm_hw_params_set_rate_near (alsa_dev, hw_params, &samplerate, 0)) < 0)
  154. { fprintf (stderr, "cannot set sample rate (%s)n", snd_strerror (err)) ;
  155. goto catch_error ;
  156. } ;
  157. if ((err = snd_pcm_hw_params_set_channels (alsa_dev, hw_params, channels)) < 0)
  158. { fprintf (stderr, "cannot set channel count (%s)n", snd_strerror (err)) ;
  159. goto catch_error ;
  160. } ;
  161. if ((err = snd_pcm_hw_params_set_buffer_size_near (alsa_dev, hw_params, &alsa_buffer_frames)) < 0)
  162. { fprintf (stderr, "cannot set buffer size (%s)n", snd_strerror (err)) ;
  163. goto catch_error ;
  164. } ;
  165. if ((err = snd_pcm_hw_params_set_period_size_near (alsa_dev, hw_params, &alsa_period_size, 0)) < 0)
  166. { fprintf (stderr, "cannot set period size (%s)n", snd_strerror (err)) ;
  167. goto catch_error ;
  168. } ;
  169. if ((err = snd_pcm_hw_params (alsa_dev, hw_params)) < 0)
  170. { fprintf (stderr, "cannot set parameters (%s)n", snd_strerror (err)) ;
  171. goto catch_error ;
  172. } ;
  173. /* extra check: if we have only one period, this code won't work */
  174. snd_pcm_hw_params_get_period_size (hw_params, &alsa_period_size, 0) ;
  175. snd_pcm_hw_params_get_buffer_size (hw_params, &buffer_size) ;
  176. if (alsa_period_size == buffer_size)
  177. { fprintf (stderr, "Can't use period equal to buffer size (%lu == %lu)", alsa_period_size, buffer_size) ;
  178. goto catch_error ;
  179. } ;
  180. snd_pcm_hw_params_free (hw_params) ;
  181. if ((err = snd_pcm_sw_params_malloc (&sw_params)) != 0)
  182. { fprintf (stderr, "%s: snd_pcm_sw_params_malloc: %s", __func__, snd_strerror (err)) ;
  183. goto catch_error ;
  184. } ;
  185. if ((err = snd_pcm_sw_params_current (alsa_dev, sw_params)) != 0)
  186. { fprintf (stderr, "%s: snd_pcm_sw_params_current: %s", __func__, snd_strerror (err)) ;
  187. goto catch_error ;
  188. } ;
  189. /* note: set start threshold to delay start until the ring buffer is full */
  190. snd_pcm_sw_params_current (alsa_dev, sw_params) ;
  191. if ((err = snd_pcm_sw_params_set_start_threshold (alsa_dev, sw_params, buffer_size)) < 0)
  192. { fprintf (stderr, "cannot set start threshold (%s)n", snd_strerror (err)) ;
  193. goto catch_error ;
  194. } ;
  195. if ((err = snd_pcm_sw_params (alsa_dev, sw_params)) != 0)
  196. { fprintf (stderr, "%s: snd_pcm_sw_params: %s", __func__, snd_strerror (err)) ;
  197. goto catch_error ;
  198. } ;
  199. snd_pcm_sw_params_free (sw_params) ;
  200. snd_pcm_reset (alsa_dev) ;
  201. catch_error :
  202. if (err < 0 && alsa_dev != NULL)
  203. { snd_pcm_close (alsa_dev) ;
  204. return NULL ;
  205. } ;
  206. return alsa_dev ;
  207. } /* alsa_open */
  208. static int
  209. alsa_write_float (snd_pcm_t *alsa_dev, float *data, int frames, int channels)
  210. { static int epipe_count = 0 ;
  211. int total = 0 ;
  212. int retval ;
  213. if (epipe_count > 0)
  214. epipe_count -- ;
  215. while (total < frames)
  216. { retval = snd_pcm_writei (alsa_dev, data + total * channels, frames - total) ;
  217. if (retval >= 0)
  218. { total += retval ;
  219. if (total == frames)
  220. return total ;
  221. continue ;
  222. } ;
  223. switch (retval)
  224. { case -EAGAIN :
  225. puts ("alsa_write_float: EAGAIN") ;
  226. continue ;
  227. break ;
  228. case -EPIPE :
  229. if (epipe_count > 0)
  230. { printf ("alsa_write_float: EPIPE %dn", epipe_count) ;
  231. if (epipe_count > 140)
  232. return retval ;
  233. } ;
  234. epipe_count += 100 ;
  235. #if 0
  236. if (0)
  237. { snd_pcm_status_t *status ;
  238. snd_pcm_status_alloca (&status) ;
  239. if ((retval = snd_pcm_status (alsa_dev, status)) < 0)
  240. fprintf (stderr, "alsa_out: xrun. can't determine lengthn") ;
  241. else if (snd_pcm_status_get_state (status) == SND_PCM_STATE_XRUN)
  242. { struct timeval now, diff, tstamp ;
  243. gettimeofday (&now, 0) ;
  244. snd_pcm_status_get_trigger_tstamp (status, &tstamp) ;
  245. timersub (&now, &tstamp, &diff) ;
  246. fprintf (stderr, "alsa_write_float xrun: of at least %.3f msecs. resetting streamn",
  247. diff.tv_sec * 1000 + diff.tv_usec / 1000.0) ;
  248. }
  249. else
  250. fprintf (stderr, "alsa_write_float: xrun. can't determine lengthn") ;
  251. } ;
  252. #endif
  253. snd_pcm_prepare (alsa_dev) ;
  254. break ;
  255. case -EBADFD :
  256. fprintf (stderr, "alsa_write_float: Bad PCM state.n") ;
  257. return 0 ;
  258. break ;
  259. case -ESTRPIPE :
  260. fprintf (stderr, "alsa_write_float: Suspend event.n") ;
  261. return 0 ;
  262. break ;
  263. case -EIO :
  264. puts ("alsa_write_float: EIO") ;
  265. return 0 ;
  266. default :
  267. fprintf (stderr, "alsa_write_float: retval = %dn", retval) ;
  268. return 0 ;
  269. break ;
  270. } ; /* switch */
  271. } ; /* while */
  272. return total ;
  273. } /* alsa_write_float */
  274. #endif /* HAVE_ALSA_ASOUNDLIB_H */
  275. /*------------------------------------------------------------------------------
  276. ** Linux/OSS functions for playing a sound.
  277. */
  278. #if defined (__linux__) || defined (__FreeBSD_kernel__) || defined (__FreeBSD__)
  279. static int opensoundsys_open_device (int channels, int srate) ;
  280. static void
  281. opensoundsys_play (int argc, char *argv [])
  282. { static short buffer [BUFFER_LEN] ;
  283. SNDFILE *sndfile ;
  284. SF_INFO sfinfo ;
  285. int k, audio_device, readcount, writecount, subformat ;
  286. for (k = 1 ; k < argc ; k++)
  287. { memset (&sfinfo, 0, sizeof (sfinfo)) ;
  288. printf ("Playing %sn", argv [k]) ;
  289. if (! (sndfile = sf_open (argv [k], SFM_READ, &sfinfo)))
  290. { puts (sf_strerror (NULL)) ;
  291. continue ;
  292. } ;
  293. if (sfinfo.channels < 1 || sfinfo.channels > 2)
  294. { printf ("Error : channels = %d.n", sfinfo.channels) ;
  295. continue ;
  296. } ;
  297. audio_device = opensoundsys_open_device (sfinfo.channels, sfinfo.samplerate) ;
  298. subformat = sfinfo.format & SF_FORMAT_SUBMASK ;
  299. if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE)
  300. { static float float_buffer [BUFFER_LEN] ;
  301. double scale ;
  302. int  m ;
  303. sf_command (sndfile, SFC_CALC_SIGNAL_MAX, &scale, sizeof (scale)) ;
  304. if (scale < 1e-10)
  305. scale = 1.0 ;
  306. else
  307. scale = 32700.0 / scale ;
  308. while ((readcount = sf_read_float (sndfile, float_buffer, BUFFER_LEN)))
  309. { for (m = 0 ; m < readcount ; m++)
  310. buffer [m] = scale * float_buffer [m] ;
  311. writecount = write (audio_device, buffer, readcount * sizeof (short)) ;
  312. } ;
  313. }
  314. else
  315. { while ((readcount = sf_read_short (sndfile, buffer, BUFFER_LEN)))
  316. writecount = write (audio_device, buffer, readcount * sizeof (short)) ;
  317. } ;
  318. if (ioctl (audio_device, SNDCTL_DSP_POST, 0) == -1)
  319. perror ("ioctl (SNDCTL_DSP_POST) ") ;
  320. if (ioctl (audio_device, SNDCTL_DSP_SYNC, 0) == -1)
  321. perror ("ioctl (SNDCTL_DSP_SYNC) ") ;
  322. close (audio_device) ;
  323. sf_close (sndfile) ;
  324. } ;
  325. return ;
  326. } /* opensoundsys_play */
  327. static int
  328. opensoundsys_open_device (int channels, int srate)
  329. { int fd, stereo, fmt ;
  330. if ((fd = open ("/dev/dsp", O_WRONLY, 0)) == -1 &&
  331. (fd = open ("/dev/sound/dsp", O_WRONLY, 0)) == -1)
  332. { perror ("opensoundsys_open_device : open ") ;
  333. exit (1) ;
  334. } ;
  335. stereo = 0 ;
  336. if (ioctl (fd, SNDCTL_DSP_STEREO, &stereo) == -1)
  337. {  /* Fatal error */
  338. perror ("opensoundsys_open_device : stereo ") ;
  339. exit (1) ;
  340. } ;
  341. if (ioctl (fd, SNDCTL_DSP_RESET, 0))
  342. { perror ("opensoundsys_open_device : reset ") ;
  343. exit (1) ;
  344. } ;
  345. fmt = CPU_IS_BIG_ENDIAN ? AFMT_S16_BE : AFMT_S16_LE ;
  346. if (ioctl (fd, SNDCTL_DSP_SETFMT, &fmt) != 0)
  347. { perror ("opensoundsys_open_device : set format ") ;
  348. exit (1) ;
  349.    } ;
  350. if (ioctl (fd, SNDCTL_DSP_CHANNELS, &channels) != 0)
  351. { perror ("opensoundsys_open_device : channels ") ;
  352. exit (1) ;
  353. } ;
  354. if (ioctl (fd, SNDCTL_DSP_SPEED, &srate) != 0)
  355. { perror ("opensoundsys_open_device : sample rate ") ;
  356. exit (1) ;
  357. } ;
  358. if (ioctl (fd, SNDCTL_DSP_SYNC, 0) != 0)
  359. { perror ("opensoundsys_open_device : sync ") ;
  360. exit (1) ;
  361. } ;
  362. return  fd ;
  363. } /* opensoundsys_open_device */
  364. #endif /* __linux__ */
  365. /*------------------------------------------------------------------------------
  366. ** Mac OS X functions for playing a sound.
  367. */
  368. #if (defined (__MACH__) && defined (__APPLE__)) /* MacOSX */
  369. typedef struct
  370. { AudioStreamBasicDescription format ;
  371. UInt32  buf_size ;
  372. AudioDeviceID  device ;
  373. SNDFILE  *sndfile ;
  374. SF_INFO  sfinfo ;
  375. int fake_stereo ;
  376. int done_playing ;
  377. } MacOSXAudioData ;
  378. #include <math.h>
  379. static OSStatus
  380. macosx_audio_out_callback (AudioDeviceID device, const AudioTimeStamp* current_time,
  381. const AudioBufferList* data_in, const AudioTimeStamp* time_in,
  382. AudioBufferList* data_out, const AudioTimeStamp* time_out,
  383. void* client_data)
  384. { MacOSXAudioData *audio_data ;
  385. int size, sample_count, read_count, k ;
  386. float *buffer ;
  387. /* Prevent compiler warnings. */
  388. device = device ;
  389. current_time = current_time ;
  390. data_in = data_in ;
  391. time_in = time_in ;
  392. time_out = time_out ;
  393. audio_data = (MacOSXAudioData*) client_data ;
  394. size = data_out->mBuffers [0].mDataByteSize ;
  395. sample_count = size / sizeof (float) ;
  396. buffer = (float*) data_out->mBuffers [0].mData ;
  397. if (audio_data->fake_stereo != 0)
  398. { read_count = sf_read_float (audio_data->sndfile, buffer, sample_count / 2) ;
  399. for (k = read_count - 1 ; k >= 0 ; k--)
  400. { buffer [2 * k ] = buffer [k] ;
  401. buffer [2 * k + 1] = buffer [k] ;
  402. } ;
  403. read_count *= 2 ;
  404. }
  405. else
  406. read_count = sf_read_float (audio_data->sndfile, buffer, sample_count) ;
  407. /* Fill the remainder with zeroes. */
  408. if (read_count < sample_count)
  409. { if (audio_data->fake_stereo == 0)
  410. memset (&(buffer [read_count]), 0, (sample_count - read_count) * sizeof (float)) ;
  411. /* Tell the main application to terminate. */
  412. audio_data->done_playing = SF_TRUE ;
  413. } ;
  414. return noErr ;
  415. } /* macosx_audio_out_callback */
  416. static void
  417. macosx_play (int argc, char *argv [])
  418. { MacOSXAudioData  audio_data ;
  419. OSStatus err ;
  420. UInt32 count, buffer_size ;
  421. int  k ;
  422. audio_data.fake_stereo = 0 ;
  423. audio_data.device = kAudioDeviceUnknown ;
  424. /*  get the default output device for the HAL */
  425. count = sizeof (AudioDeviceID) ;
  426. if ((err = AudioHardwareGetProperty (kAudioHardwarePropertyDefaultOutputDevice,
  427. &count, (void *) &(audio_data.device))) != noErr)
  428. { printf ("AudioHardwareGetProperty (kAudioDevicePropertyDefaultOutputDevice) failed.n") ;
  429. return ;
  430. } ;
  431. /*  get the buffersize that the default device uses for IO */
  432. count = sizeof (UInt32) ;
  433. if ((err = AudioDeviceGetProperty (audio_data.device, 0, false, kAudioDevicePropertyBufferSize,
  434. &count, &buffer_size)) != noErr)
  435. { printf ("AudioDeviceGetProperty (kAudioDevicePropertyBufferSize) failed.n") ;
  436. return ;
  437. } ;
  438. /*  get a description of the data format used by the default device */
  439. count = sizeof (AudioStreamBasicDescription) ;
  440. if ((err = AudioDeviceGetProperty (audio_data.device, 0, false, kAudioDevicePropertyStreamFormat,
  441. &count, &(audio_data.format))) != noErr)
  442. { printf ("AudioDeviceGetProperty (kAudioDevicePropertyStreamFormat) failed.n") ;
  443. return ;
  444. } ;
  445. /* Base setup completed. Now play files. */
  446. for (k = 1 ; k < argc ; k++)
  447. { printf ("Playing %sn", argv [k]) ;
  448. if (! (audio_data.sndfile = sf_open (argv [k], SFM_READ, &(audio_data.sfinfo))))
  449. { puts (sf_strerror (NULL)) ;
  450. continue ;
  451. } ;
  452. if (audio_data.sfinfo.channels < 1 || audio_data.sfinfo.channels > 2)
  453. { printf ("Error : channels = %d.n", audio_data.sfinfo.channels) ;
  454. continue ;
  455. } ;
  456. audio_data.format.mSampleRate = audio_data.sfinfo.samplerate ;
  457. if (audio_data.sfinfo.channels == 1)
  458. { audio_data.format.mChannelsPerFrame = 2 ;
  459. audio_data.fake_stereo = 1 ;
  460. }
  461. else
  462. audio_data.format.mChannelsPerFrame = audio_data.sfinfo.channels ;
  463. if ((err = AudioDeviceSetProperty (audio_data.device, NULL, 0, false, kAudioDevicePropertyStreamFormat,
  464. sizeof (AudioStreamBasicDescription), &(audio_data.format))) != noErr)
  465. { printf ("AudioDeviceSetProperty (kAudioDevicePropertyStreamFormat) failed.n") ;
  466. return ;
  467. } ;
  468. /*  we want linear pcm */
  469. if (audio_data.format.mFormatID != kAudioFormatLinearPCM)
  470. return ;
  471. /* Fire off the device. */
  472. if ((err = AudioDeviceAddIOProc (audio_data.device, macosx_audio_out_callback,
  473. (void *) &audio_data)) != noErr)
  474. { printf ("AudioDeviceAddIOProc failed.n") ;
  475. return ;
  476. } ;
  477. err = AudioDeviceStart (audio_data.device, macosx_audio_out_callback) ;
  478. if (err != noErr)
  479. return ;
  480. audio_data.done_playing = SF_FALSE ;
  481. while (audio_data.done_playing == SF_FALSE)
  482. usleep (10 * 1000) ; /* 10 000 milliseconds. */
  483. if ((err = AudioDeviceStop (audio_data.device, macosx_audio_out_callback)) != noErr)
  484. { printf ("AudioDeviceStop failed.n") ;
  485. return ;
  486. } ;
  487. err = AudioDeviceRemoveIOProc (audio_data.device, macosx_audio_out_callback) ;
  488. if (err != noErr)
  489. { printf ("AudioDeviceRemoveIOProc failed.n") ;
  490. return ;
  491. } ;
  492. sf_close (audio_data.sndfile) ;
  493. } ;
  494. return ;
  495. } /* macosx_play */
  496. #endif /* MacOSX */
  497. /*------------------------------------------------------------------------------
  498. ** Win32 functions for playing a sound.
  499. **
  500. ** This API sucks. Its needlessly complicated and is *WAY* too loose with
  501. ** passing pointers arounf in integers and and using char* pointers to
  502. **  point to data instead of short*. It plain sucks!
  503. */
  504. #if (OS_IS_WIN32 == 1)
  505. #define WIN32_BUFFER_LEN (1<<15)
  506. typedef struct
  507. { HWAVEOUT hwave ;
  508. WAVEHDR whdr [2] ;
  509. CRITICAL_SECTION mutex ; /* to control access to BuffersInUSe */
  510. HANDLE Event ; /* signal that a buffer is free */
  511. short buffer [WIN32_BUFFER_LEN / sizeof (short)] ;
  512. int current, bufferlen ;
  513. int BuffersInUse ;
  514. SNDFILE  *sndfile ;
  515. SF_INFO  sfinfo ;
  516. sf_count_t remaining ;
  517. } Win32_Audio_Data ;
  518. static void
  519. win32_play_data (Win32_Audio_Data *audio_data)
  520. { int thisread, readcount ;
  521. /* fill a buffer if there is more data and we can read it sucessfully */
  522. readcount = (audio_data->remaining > audio_data->bufferlen) ? audio_data->bufferlen : (int) audio_data->remaining ;
  523. thisread = (int) sf_read_short (audio_data->sndfile, (short *) (audio_data->whdr [audio_data->current].lpData), readcount) ;
  524. audio_data->remaining -= thisread ;
  525. if (thisread > 0)
  526. { /* Fix buffer length if this is only a partial block. */
  527. if (thisread < audio_data->bufferlen)
  528. audio_data->whdr [audio_data->current].dwBufferLength = thisread * sizeof (short) ;
  529. /* Queue the WAVEHDR */
  530. waveOutWrite (audio_data->hwave, (LPWAVEHDR) &(audio_data->whdr [audio_data->current]), sizeof (WAVEHDR)) ;
  531. /* count another buffer in use */
  532. EnterCriticalSection (&audio_data->mutex) ;
  533. audio_data->BuffersInUse ++ ;
  534. LeaveCriticalSection (&audio_data->mutex) ;
  535. /* use the other buffer next time */
  536. audio_data->current = (audio_data->current + 1) % 2 ;
  537. } ;
  538. return ;
  539. } /* win32_play_data */
  540. static void CALLBACK
  541. win32_audio_out_callback (HWAVEOUT hwave, UINT msg, DWORD_PTR data, DWORD param1, DWORD param2)
  542. { Win32_Audio_Data *audio_data ;
  543. /* Prevent compiler warnings. */
  544. hwave = hwave ;
  545. param1 = param2 ;
  546. if (data == 0)
  547. return ;
  548. /*
  549. ** I consider this technique of passing a pointer via an integer as
  550. ** fundamentally broken but thats the way microsoft has defined the
  551. ** interface.
  552. */
  553. audio_data = (Win32_Audio_Data*) data ;
  554. /* let main loop know a buffer is free */
  555. if (msg == MM_WOM_DONE)
  556. { EnterCriticalSection (&audio_data->mutex) ;
  557. audio_data->BuffersInUse -- ;
  558. LeaveCriticalSection (&audio_data->mutex) ;
  559. SetEvent (audio_data->Event) ;
  560. } ;
  561. return ;
  562. } /* win32_audio_out_callback */
  563. static void
  564. win32_play (int argc, char *argv [])
  565. { Win32_Audio_Data audio_data ;
  566. WAVEFORMATEX wf ;
  567. int k, error ;
  568. audio_data.sndfile = NULL ;
  569. audio_data.hwave = 0 ;
  570. for (k = 1 ; k < argc ; k++)
  571. { printf ("Playing %sn", argv [k]) ;
  572. if (! (audio_data.sndfile = sf_open (argv [k], SFM_READ, &(audio_data.sfinfo))))
  573. { puts (sf_strerror (NULL)) ;
  574. continue ;
  575. } ;
  576. audio_data.remaining = audio_data.sfinfo.frames * audio_data.sfinfo.channels ;
  577. audio_data.current = 0 ;
  578. InitializeCriticalSection (&audio_data.mutex) ;
  579. audio_data.Event = CreateEvent (0, FALSE, FALSE, 0) ;
  580. wf.nChannels = audio_data.sfinfo.channels ;
  581. wf.wFormatTag = WAVE_FORMAT_PCM ;
  582. wf.cbSize = 0 ;
  583. wf.wBitsPerSample = 16 ;
  584. wf.nSamplesPerSec = audio_data.sfinfo.samplerate ;
  585. wf.nBlockAlign = audio_data.sfinfo.channels * sizeof (short) ;
  586. wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec ;
  587. error = waveOutOpen (&(audio_data.hwave), WAVE_MAPPER, &wf, (DWORD_PTR) win32_audio_out_callback,
  588. (DWORD_PTR) &audio_data, CALLBACK_FUNCTION) ;
  589. if (error)
  590. { puts ("waveOutOpen failed.") ;
  591. audio_data.hwave = 0 ;
  592. continue ;
  593. } ;
  594. audio_data.whdr [0].lpData = (char*) audio_data.buffer ;
  595. audio_data.whdr [1].lpData = ((char*) audio_data.buffer) + sizeof (audio_data.buffer) / 2 ;
  596. audio_data.whdr [0].dwBufferLength = sizeof (audio_data.buffer) / 2 ;
  597. audio_data.whdr [1].dwBufferLength = sizeof (audio_data.buffer) / 2 ;
  598. audio_data.whdr [0].dwFlags = 0 ;
  599. audio_data.whdr [1].dwFlags = 0 ;
  600. /* length of each audio buffer in samples */
  601. audio_data.bufferlen = sizeof (audio_data.buffer) / 2 / sizeof (short) ;
  602. /* Prepare the WAVEHDRs */
  603. if ((error = waveOutPrepareHeader (audio_data.hwave, &(audio_data.whdr [0]), sizeof (WAVEHDR))))
  604. { printf ("waveOutPrepareHeader [0] failed : %08Xn", error) ;
  605. waveOutClose (audio_data.hwave) ;
  606. continue ;
  607. } ;
  608. if ((error = waveOutPrepareHeader (audio_data.hwave, &(audio_data.whdr [1]), sizeof (WAVEHDR))))
  609. { printf ("waveOutPrepareHeader [1] failed : %08Xn", error) ;
  610. waveOutUnprepareHeader (audio_data.hwave, &(audio_data.whdr [0]), sizeof (WAVEHDR)) ;
  611. waveOutClose (audio_data.hwave) ;
  612. continue ;
  613. } ;
  614. /* Fill up both buffers with audio data */
  615. audio_data.BuffersInUse = 0 ;
  616. win32_play_data (&audio_data) ;
  617. win32_play_data (&audio_data) ;
  618. /* loop until both buffers are released */
  619. while (audio_data.BuffersInUse > 0)
  620. {
  621. /* wait for buffer to be released */
  622. WaitForSingleObject (audio_data.Event, INFINITE) ;
  623. /* refill the buffer if there is more data to play */
  624. win32_play_data (&audio_data) ;
  625. } ;
  626. waveOutUnprepareHeader (audio_data.hwave, &(audio_data.whdr [0]), sizeof (WAVEHDR)) ;
  627. waveOutUnprepareHeader (audio_data.hwave, &(audio_data.whdr [1]), sizeof (WAVEHDR)) ;
  628. waveOutClose (audio_data.hwave) ;
  629. audio_data.hwave = 0 ;
  630. DeleteCriticalSection (&audio_data.mutex) ;
  631. sf_close (audio_data.sndfile) ;
  632. } ;
  633. } /* win32_play */
  634. #endif /* Win32 */
  635. /*------------------------------------------------------------------------------
  636. ** Solaris.
  637. */
  638. #if (defined (sun) && defined (unix)) /* ie Solaris */
  639. static void
  640. solaris_play (int argc, char *argv [])
  641. { static short  buffer [BUFFER_LEN] ;
  642. audio_info_t audio_info ;
  643. SNDFILE *sndfile ;
  644. SF_INFO sfinfo ;
  645. unsigned long delay_time ;
  646. long k, start_count, output_count, write_count, read_count ;
  647. int audio_fd, error, done ;
  648. for (k = 1 ; k < argc ; k++)
  649. { printf ("Playing %sn", argv [k]) ;
  650. if (! (sndfile = sf_open (argv [k], SFM_READ, &sfinfo)))
  651. { puts (sf_strerror (NULL)) ;
  652. continue ;
  653. } ;
  654. if (sfinfo.channels < 1 || sfinfo.channels > 2)
  655. { printf ("Error : channels = %d.n", sfinfo.channels) ;
  656. continue ;
  657. } ;
  658. /* open the audio device - write only, non-blocking */
  659. if ((audio_fd = open ("/dev/audio", O_WRONLY | O_NONBLOCK)) < 0)
  660. { perror ("open (/dev/audio) failed") ;
  661. return ;
  662. } ;
  663. /* Retrive standard values. */
  664. AUDIO_INITINFO (&audio_info) ;
  665. audio_info.play.sample_rate = sfinfo.samplerate ;
  666. audio_info.play.channels = sfinfo.channels ;
  667. audio_info.play.precision = 16 ;
  668. audio_info.play.encoding = AUDIO_ENCODING_LINEAR ;
  669. audio_info.play.gain = AUDIO_MAX_GAIN ;
  670. audio_info.play.balance = AUDIO_MID_BALANCE ;
  671. if ((error = ioctl (audio_fd, AUDIO_SETINFO, &audio_info)))
  672. { perror ("ioctl (AUDIO_SETINFO) failed") ;
  673. return ;
  674. } ;
  675. /* Delay time equal to 1/4 of a buffer in microseconds. */
  676. delay_time = (BUFFER_LEN * 1000000) / (audio_info.play.sample_rate * 4) ;
  677. done = 0 ;
  678. while (! done)
  679. { read_count = sf_read_short (sndfile, buffer, BUFFER_LEN) ;
  680. if (read_count < BUFFER_LEN)
  681. { memset (&(buffer [read_count]), 0, (BUFFER_LEN - read_count) * sizeof (short)) ;
  682. /* Tell the main application to terminate. */
  683. done = SF_TRUE ;
  684. } ;
  685. start_count = 0 ;
  686. output_count = BUFFER_LEN * sizeof (short) ;
  687. while (output_count > 0)
  688. { /* write as much data as possible */
  689. write_count = write (audio_fd, &(buffer [start_count]), output_count) ;
  690. if (write_count > 0)
  691. { output_count -= write_count ;
  692. start_count += write_count ;
  693. }
  694. else
  695. { /* Give the audio output time to catch up. */
  696. usleep (delay_time) ;
  697. } ;
  698. } ; /* while (outpur_count > 0) */
  699. } ; /* while (! done) */
  700. close (audio_fd) ;
  701. } ;
  702. return ;
  703. } /* solaris_play */
  704. #endif /* Solaris */
  705. /*==============================================================================
  706. ** Main function.
  707. */
  708. int
  709. main (int argc, char *argv [])
  710. {
  711. if (argc < 2)
  712. {
  713. printf ("nUsage : %s <input sound file>nn", argv [0]) ;
  714. #if (OS_IS_WIN32 == 1)
  715. printf ("This is a Unix style command line application whichn"
  716. "should be run in a MSDOS box or Command Shell window.nn") ;
  717. printf ("Sleeping for 5 seconds before exiting.nn") ;
  718. /* This is the officially blessed by microsoft way but I can't get
  719. ** it to link.
  720. **     Sleep (15) ;
  721. ** Instead, use this:
  722. */
  723. Sleep (5 * 1000) ;
  724. #endif
  725. return 1 ;
  726. } ;
  727. #if defined (__linux__)
  728. #if HAVE_ALSA_ASOUNDLIB_H
  729. if (access ("/proc/asound/cards", R_OK) == 0)
  730. alsa_play (argc, argv) ;
  731. else
  732. #endif
  733. opensoundsys_play (argc, argv) ;
  734. #elif defined (__FreeBSD_kernel__) || defined (__FreeBSD__)
  735. opensoundsys_play (argc, argv) ;
  736. #elif (defined (__MACH__) && defined (__APPLE__))
  737. macosx_play (argc, argv) ;
  738. #elif (defined (sun) && defined (unix))
  739. solaris_play (argc, argv) ;
  740. #elif (OS_IS_WIN32 == 1)
  741. win32_play (argc, argv) ;
  742. #elif defined (__BEOS__)
  743. printf ("This program cannot be compiled on BeOS.n") ;
  744. printf ("Instead, compile the file sfplay_beos.cpp.n") ;
  745. return 1 ;
  746. #else
  747. puts ("*** Playing sound not yet supported on this platform.") ;
  748. puts ("*** Please feel free to submit a patch.") ;
  749. return 1 ;
  750. #endif
  751. return 0 ;
  752. } /* main */