Chris@0: /* Chris@0: ** Copyright (C) 2002-2011 Erik de Castro Lopo Chris@0: ** Chris@0: ** This program is free software; you can redistribute it and/or modify Chris@0: ** it under the terms of the GNU General Public License as published by Chris@0: ** the Free Software Foundation; either version 2 of the License, or Chris@0: ** (at your option) any later version. Chris@0: ** Chris@0: ** This program is distributed in the hope that it will be useful, Chris@0: ** but WITHOUT ANY WARRANTY; without even the implied warranty of Chris@0: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Chris@0: ** GNU General Public License for more details. Chris@0: ** Chris@0: ** You should have received a copy of the GNU General Public License Chris@0: ** along with this program; if not, write to the Free Software Chris@0: ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. Chris@0: */ Chris@0: Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: Chris@0: #include "config.h" Chris@0: Chris@0: #include Chris@0: Chris@0: #if (HAVE_SNDFILE) Chris@0: Chris@0: #include Chris@0: #include Chris@0: Chris@0: #include "audio_out.h" Chris@0: Chris@0: #define ARRAY_LEN(x) ((int) (sizeof (x) / sizeof ((x) [0]))) Chris@0: Chris@0: #define BUFFER_LEN 4096 Chris@0: #define INPUT_FRAMES 100 Chris@0: Chris@0: #define MIN(a,b) ((a) < (b) ? (a) : (b)) Chris@0: Chris@0: #define MAGIC_NUMBER ((int) ('S' << 16) + ('R' << 8) + ('C')) Chris@0: Chris@0: #ifndef M_PI Chris@0: #define M_PI 3.14159265358979323846264338 Chris@0: #endif Chris@0: Chris@0: Chris@0: typedef struct Chris@0: { int magic ; Chris@0: Chris@0: SNDFILE *sndfile ; Chris@0: SF_INFO sfinfo ; Chris@0: Chris@0: SRC_STATE *src_state ; Chris@0: SRC_DATA src_data ; Chris@0: Chris@0: int freq_point ; Chris@0: int buffer_out_start, buffer_out_end ; Chris@0: Chris@0: float buffer_in [BUFFER_LEN] ; Chris@0: float buffer_out [BUFFER_LEN] ; Chris@0: } CALLBACK_DATA ; Chris@0: Chris@0: static int varispeed_get_data (CALLBACK_DATA *data, float *samples, int frames) ; Chris@0: static void varispeed_play (const char *filename, int converter) ; Chris@0: Chris@0: int Chris@0: main (int argc, char *argv []) Chris@0: { const char *cptr, *progname, *filename ; Chris@0: int k, converter ; Chris@0: Chris@0: converter = SRC_SINC_FASTEST ; Chris@0: Chris@0: progname = argv [0] ; Chris@0: Chris@0: if ((cptr = strrchr (progname, '/')) != NULL) Chris@0: progname = cptr + 1 ; Chris@0: Chris@0: if ((cptr = strrchr (progname, '\\')) != NULL) Chris@0: progname = cptr + 1 ; Chris@0: Chris@0: printf ("\n" Chris@0: " %s\n" Chris@0: "\n" Chris@0: " This is a demo program which plays the given file at a slowly \n" Chris@0: " varying speed. Lots of fun with drum loops and full mixes.\n" Chris@0: "\n" Chris@0: " It uses Secret Rabbit Code (aka libsamplerate) to perform the \n" Chris@0: " vari-speeding and libsndfile for file I/O.\n" Chris@0: "\n", progname) ; Chris@0: Chris@0: if (argc == 2) Chris@0: filename = argv [1] ; Chris@0: else if (argc == 4 && strcmp (argv [1], "-c") == 0) Chris@0: { filename = argv [3] ; Chris@0: converter = atoi (argv [2]) ; Chris@0: } Chris@0: else Chris@0: { printf (" Usage :\n\n %s [-c ] \n\n", progname) ; Chris@0: puts ( Chris@0: " The optional -c argument allows the converter type to be chosen from\n" Chris@0: " the following list :" Chris@0: "\n" Chris@0: ) ; Chris@0: Chris@0: for (k = 0 ; (cptr = src_get_name (k)) != NULL ; k++) Chris@0: printf (" %d : %s\n", k, cptr) ; Chris@0: Chris@0: puts ("") ; Chris@0: exit (1) ; Chris@0: } ; Chris@0: Chris@0: varispeed_play (filename, converter) ; Chris@0: Chris@0: return 0 ; Chris@0: } /* main */ Chris@0: Chris@0: /*============================================================================== Chris@0: */ Chris@0: Chris@0: static void Chris@0: varispeed_play (const char *filename, int converter) Chris@0: { CALLBACK_DATA *data ; Chris@0: AUDIO_OUT *audio_out ; Chris@0: int error ; Chris@0: Chris@0: /* Allocate memory for the callback data. */ Chris@0: if ((data = calloc (1, sizeof (CALLBACK_DATA))) == NULL) Chris@0: { printf ("\n\n%s:%d Calloc failed!\n", __FILE__, __LINE__) ; Chris@0: exit (1) ; Chris@0: } ; Chris@0: Chris@0: data->magic = MAGIC_NUMBER ; Chris@0: Chris@0: if ((data->sndfile = sf_open (filename, SFM_READ, &data->sfinfo)) == NULL) Chris@0: { puts (sf_strerror (NULL)) ; Chris@0: exit (1) ; Chris@0: } ; Chris@0: Chris@0: /* Initialize the sample rate converter. */ Chris@0: if ((data->src_state = src_new (converter, data->sfinfo.channels, &error)) == NULL) Chris@0: { printf ("\n\nError : src_new() failed : %s.\n\n", src_strerror (error)) ; Chris@0: exit (1) ; Chris@0: } ; Chris@0: Chris@0: printf ( Chris@0: Chris@0: " Playing : %s\n" Chris@0: " Converter : %s\n" Chris@0: "\n" Chris@0: " Press to exit.\n" Chris@0: "\n", Chris@0: filename, src_get_name (converter)) ; Chris@0: Chris@0: if ((audio_out = audio_open (data->sfinfo.channels, data->sfinfo.samplerate)) == NULL) Chris@0: { printf ("\n\nError : audio_open () failed.\n") ; Chris@0: exit (1) ; Chris@0: } ; Chris@0: Chris@0: /* Set up sample rate converter info. */ Chris@0: data->src_data.end_of_input = 0 ; /* Set this later. */ Chris@0: Chris@0: /* Start with zero to force load in while loop. */ Chris@0: data->src_data.input_frames = 0 ; Chris@0: data->src_data.data_in = data->buffer_in ; Chris@0: Chris@0: /* Start with output frames also zero. */ Chris@0: data->src_data.output_frames_gen = 0 ; Chris@0: Chris@0: data->buffer_out_start = data->buffer_out_end = 0 ; Chris@0: data->src_data.src_ratio = 1.0 ; Chris@0: Chris@0: /* Pass the data and the callbacl function to audio_play */ Chris@0: audio_play ((get_audio_callback_t) varispeed_get_data, audio_out, data) ; Chris@0: Chris@0: /* Cleanup */ Chris@0: audio_close (audio_out) ; Chris@0: sf_close (data->sndfile) ; Chris@0: src_delete (data->src_state) ; Chris@0: Chris@0: free (data) ; Chris@0: Chris@0: } /* varispeed_play */ Chris@0: Chris@0: /*============================================================================== Chris@0: */ Chris@0: Chris@0: static int Chris@0: varispeed_get_data (CALLBACK_DATA *data, float *samples, int frames) Chris@0: { int error, readframes, frame_count, direct_out ; Chris@0: Chris@0: if (data->magic != MAGIC_NUMBER) Chris@0: { printf ("\n\n%s:%d Eeeek, something really bad happened!\n", __FILE__, __LINE__) ; Chris@0: exit (1) ; Chris@0: } ; Chris@0: Chris@0: frame_count = 0 ; Chris@0: Chris@0: if (data->buffer_out_start < data->buffer_out_end) Chris@0: { frame_count = MIN (data->buffer_out_end - data->buffer_out_start, frames) ; Chris@0: memcpy (samples, data->buffer_out + data->sfinfo.channels * data->buffer_out_start, data->sfinfo.channels * frame_count * sizeof (float)) ; Chris@0: data->buffer_out_start += frame_count ; Chris@0: } ; Chris@0: Chris@0: data->buffer_out_start = data->buffer_out_end = 0 ; Chris@0: Chris@0: while (frame_count < frames) Chris@0: { Chris@0: /* Read INPUT_FRAMES frames worth looping at end of file. */ Chris@0: for (readframes = 0 ; readframes < INPUT_FRAMES ; ) Chris@0: { sf_count_t position ; Chris@0: Chris@0: readframes += sf_readf_float (data->sndfile, data->buffer_in + data->sfinfo.channels * readframes, INPUT_FRAMES - readframes) ; Chris@0: Chris@0: position = sf_seek (data->sndfile, 0, SEEK_CUR) ; Chris@0: Chris@0: if (position < 0 || position == data->sfinfo.frames) Chris@0: sf_seek (data->sndfile, 0, SEEK_SET) ; Chris@0: } ; Chris@0: Chris@0: data->src_data.input_frames = readframes ; Chris@0: Chris@0: data->src_data.src_ratio = 1.0 - 0.5 * sin (data->freq_point * 2 * M_PI / 20000) ; Chris@0: data->freq_point ++ ; Chris@0: Chris@0: direct_out = (data->src_data.src_ratio * readframes < frames - frame_count) ? 1 : 0 ; Chris@0: Chris@0: if (direct_out) Chris@0: { data->src_data.data_out = samples + frame_count * data->sfinfo.channels ; Chris@0: data->src_data.output_frames = frames - frame_count ; Chris@0: } Chris@0: else Chris@0: { data->src_data.data_out = data->buffer_out ; Chris@0: data->src_data.output_frames = BUFFER_LEN / data->sfinfo.channels ; Chris@0: } ; Chris@0: Chris@0: if ((error = src_process (data->src_state, &data->src_data))) Chris@0: { printf ("\nError : %s\n\n", src_strerror (error)) ; Chris@0: exit (1) ; Chris@0: } ; Chris@0: Chris@0: if (direct_out) Chris@0: { frame_count += data->src_data.output_frames_gen ; Chris@0: continue ; Chris@0: } ; Chris@0: Chris@0: memcpy (samples + frame_count * data->sfinfo.channels, data->buffer_out, (frames - frame_count) * data->sfinfo.channels * sizeof (float)) ; Chris@0: Chris@0: data->buffer_out_start = frames - frame_count ; Chris@0: data->buffer_out_end = data->src_data.output_frames_gen ; Chris@0: Chris@0: frame_count += frames - frame_count ; Chris@0: } ; Chris@0: Chris@0: return frame_count ; Chris@0: } /* varispeed_get_data */ Chris@0: Chris@0: /*============================================================================== Chris@0: */ Chris@0: Chris@0: #else /* (HAVE_SNFILE == 0) */ Chris@0: Chris@0: /* Alternative main function when libsndfile is not available. */ Chris@0: Chris@0: int Chris@0: main (void) Chris@0: { puts ( Chris@0: "\n" Chris@0: "****************************************************************\n" Chris@0: " This example program was compiled without libsndfile \n" Chris@0: " (http://www.zip.com.au/~erikd/libsndfile/).\n" Chris@0: " It is therefore completely broken and non-functional.\n" Chris@0: "****************************************************************\n" Chris@0: "\n" Chris@0: ) ; Chris@0: Chris@0: return 0 ; Chris@0: } /* main */ Chris@0: Chris@0: #endif Chris@0: