📄 sndfile-play.c
字号:
/*** Copyright (C) 1999-2004 Erik de Castro Lopo <erikd@mega-nerd.com>**** This program is free software; you can redistribute it and/or modify** it under the terms of the GNU General Public License as published by** the Free Software Foundation; either version 2 of the License, or** (at your option) any later version.**** This program is distributed in the hope that it will be useful,** but WITHOUT ANY WARRANTY; without even the implied warranty of** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the** GNU General Public License for more details.**** You should have received a copy of the GNU General Public License** along with this program; if not, write to the Free Software** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/#include "config.h"#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#if HAVE_UNISTD_H#include <unistd.h>#endif#if HAVE_ALSA_ASOUNDLIB_H #define ALSA_PCM_NEW_HW_PARAMS_API #define ALSA_PCM_NEW_SW_PARAMS_API #include <alsa/asoundlib.h> #include <sys/time.h>#endif#if defined (__linux__) #include <fcntl.h> #include <sys/ioctl.h> #include <sys/soundcard.h>#elif (defined (__MACH__) && defined (__APPLE__)) #include <Carbon.h> #include <CoreAudio/AudioHardware.h>#elif (defined (sun) && defined (unix)) #include <fcntl.h> #include <sys/ioctl.h> #include <sys/audioio.h>#elif (OS_IS_WIN32 == 1) #include <windows.h> #include <mmsystem.h>#endif#include <sndfile.h>#define SIGNED_SIZEOF(x) ((int) sizeof (x))#define BUFFER_LEN (2048)/*------------------------------------------------------------------------------** Linux/OSS functions for playing a sound.*/#if HAVE_ALSA_ASOUNDLIB_Hstatic snd_pcm_t * alsa_open (int channels, int srate) ;static int alsa_write_float (snd_pcm_t *alsa_dev, float *data, int frames, int channels) ;static voidalsa_play (int argc, char *argv []){ static float buffer [BUFFER_LEN] ; SNDFILE *sndfile ; SF_INFO sfinfo ; snd_pcm_t * alsa_dev ; int k, readcount, subformat ; for (k = 1 ; k < argc ; k++) { memset (&sfinfo, 0, sizeof (sfinfo)) ; printf ("Playing %s\n", argv [k]) ; if (! (sndfile = sf_open (argv [k], SFM_READ, &sfinfo))) { puts (sf_strerror (NULL)) ; continue ; } ; if (sfinfo.channels < 1 || sfinfo.channels > 2) { printf ("Error : channels = %d.\n", sfinfo.channels) ; continue ; } ; alsa_dev = alsa_open (sfinfo.channels, sfinfo.samplerate) ; subformat = sfinfo.format & SF_FORMAT_SUBMASK ; if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE) { double scale ; int m ; sf_command (sndfile, SFC_CALC_SIGNAL_MAX, &scale, sizeof (scale)) ; if (scale < 1e-10) scale = 1.0 ; else scale = 32700.0 / scale ; while ((readcount = sf_read_float (sndfile, buffer, BUFFER_LEN))) { for (m = 0 ; m < readcount ; m++) buffer [m] *= scale ; alsa_write_float (alsa_dev, buffer, BUFFER_LEN / sfinfo.channels, sfinfo.channels) ; } ; } else { while ((readcount = sf_read_float (sndfile, buffer, BUFFER_LEN))) alsa_write_float (alsa_dev, buffer, BUFFER_LEN / sfinfo.channels, sfinfo.channels) ; } ; snd_pcm_close (alsa_dev) ; sf_close (sndfile) ; } ; return ;} /* alsa_play */static snd_pcm_t *alsa_open (int channels, int samplerate){ const char * device = "plughw:0" ; snd_pcm_t *alsa_dev ; snd_pcm_hw_params_t *hw_params ; snd_pcm_uframes_t buffer_size, xfer_align, start_threshold ; snd_pcm_uframes_t alsa_period_size, alsa_buffer_frames ; snd_pcm_sw_params_t *sw_params ; int err ; alsa_period_size = 512 ; alsa_buffer_frames = 3 * alsa_period_size ; if ((err = snd_pcm_open (&alsa_dev, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { fprintf (stderr, "cannot open audio device \"%s\" (%s)\n", device, snd_strerror (err)) ; return NULL ; } ; snd_pcm_nonblock (alsa_dev, 0) ; if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) { fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n", snd_strerror (err)) ; return NULL ; } ; if ((err = snd_pcm_hw_params_any (alsa_dev, hw_params)) < 0) { fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n", snd_strerror (err)) ; return NULL ; } ; if ((err = snd_pcm_hw_params_set_access (alsa_dev, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { fprintf (stderr, "cannot set access type (%s)\n", snd_strerror (err)) ; return NULL ; } ; if ((err = snd_pcm_hw_params_set_format (alsa_dev, hw_params, SND_PCM_FORMAT_FLOAT)) < 0) { fprintf (stderr, "cannot set sample format (%s)\n", snd_strerror (err)) ; return NULL ; } ; if ((err = snd_pcm_hw_params_set_rate_near (alsa_dev, hw_params, &samplerate, 0)) < 0) { fprintf (stderr, "cannot set sample rate (%s)\n", snd_strerror (err)) ; return NULL ; } ; if ((err = snd_pcm_hw_params_set_channels (alsa_dev, hw_params, channels)) < 0) { fprintf (stderr, "cannot set channel count (%s)\n", snd_strerror (err)) ; return NULL ; } ; if ((err = snd_pcm_hw_params_set_buffer_size_near (alsa_dev, hw_params, &alsa_buffer_frames)) < 0) { fprintf (stderr, "cannot set buffer size (%s)\n", snd_strerror (err)) ; return NULL ; } ; if ((err = snd_pcm_hw_params_set_period_size_near (alsa_dev, hw_params, &alsa_period_size, 0)) < 0) { fprintf (stderr, "cannot set period size (%s)\n", snd_strerror (err)) ; return NULL ; } ; if ((err = snd_pcm_hw_params (alsa_dev, hw_params)) < 0) { fprintf (stderr, "cannot set parameters (%s)\n", snd_strerror (err)) ; return NULL ; } ; /* extra check: if we have only one period, this code won't work */ snd_pcm_hw_params_get_period_size (hw_params, &alsa_period_size, 0) ; snd_pcm_hw_params_get_buffer_size (hw_params, &buffer_size) ; if (alsa_period_size == buffer_size) { fprintf (stderr, "Can't use period equal to buffer size (%lu == %lu)", alsa_period_size, buffer_size) ; return NULL ; } ; snd_pcm_hw_params_free (hw_params) ; if ((err = snd_pcm_sw_params_malloc (&sw_params)) != 0) { fprintf (stderr, "%s: snd_pcm_sw_params_malloc: %s", __func__, snd_strerror (err)) ; return NULL ; } ; if ((err = snd_pcm_sw_params_current (alsa_dev, sw_params)) != 0) { fprintf (stderr, "%s: snd_pcm_sw_params_current: %s", __func__, snd_strerror (err)) ; return NULL ; } ; /* note: set start threshold to delay start until the ring buffer is full */ snd_pcm_sw_params_current (alsa_dev, sw_params) ; if ((err = snd_pcm_sw_params_get_xfer_align (sw_params, &xfer_align)) < 0) { fprintf (stderr, "cannot get xfer align (%s)\n", snd_strerror (err)) ; return NULL ; } ; /* round up to closest transfer boundary */ start_threshold = (buffer_size / xfer_align) * xfer_align ; if (start_threshold < 1) start_threshold = 1 ; if ((err = snd_pcm_sw_params_set_start_threshold (alsa_dev, sw_params, start_threshold)) < 0) { fprintf (stderr, "cannot set start threshold (%s)\n", snd_strerror (err)) ; return NULL ; } ; if ((err = snd_pcm_sw_params (alsa_dev, sw_params)) != 0) { fprintf (stderr, "%s: snd_pcm_sw_params: %s", __func__, snd_strerror (err)) ; return NULL ; } ; snd_pcm_sw_params_free (sw_params) ; snd_pcm_reset (alsa_dev) ; return alsa_dev ;} /* alsa_open */static intalsa_write_float (snd_pcm_t *alsa_dev, float *data, int frames, int channels){ static int epipe_count = 0 ; snd_pcm_status_t *status ; int total = 0 ; int retval ; if (epipe_count > 0) epipe_count -- ; while (total < frames) { retval = snd_pcm_writei (alsa_dev, data + total * channels, frames - total) ; if (retval >= 0) { total += retval ; if (total == frames) return total ; continue ; } ; switch (retval) { case -EAGAIN : puts ("alsa_write_float: EAGAIN") ; continue ; break ; case -EPIPE : if (epipe_count > 0) { printf ("alsa_write_float: EPIPE %d\n", epipe_count) ; if (epipe_count > 140) return retval ; } ; epipe_count += 100 ; if (0) { snd_pcm_status_alloca (&status) ; if ((retval = snd_pcm_status (alsa_dev, status)) < 0) fprintf (stderr, "alsa_out: xrun. can't determine length\n") ; else if (snd_pcm_status_get_state (status) == SND_PCM_STATE_XRUN) { struct timeval now, diff, tstamp ; gettimeofday (&now, 0) ; snd_pcm_status_get_trigger_tstamp (status, &tstamp) ; timersub (&now, &tstamp, &diff) ; fprintf (stderr, "alsa_write_float xrun: of at least %.3f msecs. resetting stream\n", diff.tv_sec * 1000 + diff.tv_usec / 1000.0) ; } else fprintf (stderr, "alsa_write_float: xrun. can't determine length\n") ; } ; snd_pcm_prepare (alsa_dev) ; break ; case -EBADFD : fprintf (stderr, "alsa_write_float: Bad PCM state.n") ; return 0 ; break ; case -ESTRPIPE : fprintf (stderr, "alsa_write_float: Suspend event.n") ; return 0 ; break ; case -EIO : puts ("alsa_write_float: EIO") ; return 0 ; default : fprintf (stderr, "alsa_write_float: retval = %d\n", retval) ; return 0 ; break ; } ; /* switch */ } ; /* while */ return total ;} /* alsa_write_float */#endif /* HAVE_ALSA_ASOUNDLIB_H *//*------------------------------------------------------------------------------** Linux/OSS functions for playing a sound.*/#if defined (__linux__)static int linux_open_dsp_device (int channels, int srate) ;static voidlinux_play (int argc, char *argv []){ static short buffer [BUFFER_LEN] ; SNDFILE *sndfile ; SF_INFO sfinfo ; int k, audio_device, readcount, subformat ; for (k = 1 ; k < argc ; k++) { memset (&sfinfo, 0, sizeof (sfinfo)) ; printf ("Playing %s\n", argv [k]) ; if (! (sndfile = sf_open (argv [k], SFM_READ, &sfinfo))) { puts (sf_strerror (NULL)) ; continue ; } ; if (sfinfo.channels < 1 || sfinfo.channels > 2) { printf ("Error : channels = %d.\n", sfinfo.channels) ; continue ; } ; audio_device = linux_open_dsp_device (sfinfo.channels, sfinfo.samplerate) ; subformat = sfinfo.format & SF_FORMAT_SUBMASK ; if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE) { static float float_buffer [BUFFER_LEN] ; double scale ; int m ; sf_command (sndfile, SFC_CALC_SIGNAL_MAX, &scale, sizeof (scale)) ; if (scale < 1e-10) scale = 1.0 ; else scale = 32700.0 / scale ; while ((readcount = sf_read_float (sndfile, float_buffer, BUFFER_LEN))) { for (m = 0 ; m < readcount ; m++) buffer [m] = scale * float_buffer [m] ; write (audio_device, buffer, readcount * sizeof (short)) ; } ; } else { while ((readcount = sf_read_short (sndfile, buffer, BUFFER_LEN))) write (audio_device, buffer, readcount * sizeof (short)) ; } ; close (audio_device) ; sf_close (sndfile) ; } ; return ;} /* linux_play */static intlinux_open_dsp_device (int channels, int srate){ int fd, stereo, temp, error ; if ((fd = open ("/dev/dsp", O_WRONLY, 0)) == -1 && (fd = open ("/dev/sound/dsp", O_WRONLY, 0)) == -1) { perror ("linux_open_dsp_device : open ") ; exit (1) ; } ; stereo = 0 ; if (ioctl (fd, SNDCTL_DSP_STEREO, &stereo) == -1) { /* Fatal error */ perror ("linux_open_dsp_device : stereo ") ; exit (1) ; } ; if (ioctl (fd, SNDCTL_DSP_RESET, 0)) { perror ("linux_open_dsp_device : reset ") ; exit (1) ; } ; temp = 16 ; if ((error = ioctl (fd, SOUND_PCM_WRITE_BITS, &temp)) != 0) { perror ("linux_open_dsp_device : bitwidth ") ; exit (1) ; } ; if ((error = ioctl (fd, SOUND_PCM_WRITE_CHANNELS, &channels)) != 0) { perror ("linux_open_dsp_device : channels ") ; exit (1) ; } ; if ((error = ioctl (fd, SOUND_PCM_WRITE_RATE, &srate)) != 0) { perror ("linux_open_dsp_device : sample rate ") ; exit (1) ; } ; if ((error = ioctl (fd, SNDCTL_DSP_SYNC, 0)) != 0) { perror ("linux_open_dsp_device : sync ") ; exit (1) ; } ; return fd ;} /* linux_open_dsp_device */#endif /* __linux__ *//*------------------------------------------------------------------------------** Mac OS X functions for playing a sound.*/#if (defined (__MACH__) && defined (__APPLE__)) /* MacOSX */typedef struct{ AudioStreamBasicDescription format ; UInt32 buf_size ; AudioDeviceID device ; SNDFILE *sndfile ; SF_INFO sfinfo ; int fake_stereo ; int done_playing ;} MacOSXAudioData ;#include <math.h>static OSStatusmacosx_audio_out_callback (AudioDeviceID device, const AudioTimeStamp* current_time, const AudioBufferList* data_in, const AudioTimeStamp* time_in, AudioBufferList* data_out, const AudioTimeStamp* time_out, void* client_data){ MacOSXAudioData *audio_data ; int size, sample_count, read_count, k ; float *buffer ; /* Prevent compiler warnings. */ device = device ; current_time = current_time ; data_in = data_in ; time_in = time_in ; time_out = time_out ;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -