Emeric Vigier | eebea67 | 2012-08-06 17:36:30 -0400 | [diff] [blame] | 1 | /* |
| 2 | ** Copyright (C) 2002-2011 Erik de Castro Lopo <erikd@mega-nerd.com> |
| 3 | ** |
| 4 | ** This program is free software; you can redistribute it and/or modify |
| 5 | ** it under the terms of the GNU General Public License as published by |
| 6 | ** the Free Software Foundation; either version 2 of the License, or |
| 7 | ** (at your option) any later version. |
| 8 | ** |
| 9 | ** This program is distributed in the hope that it will be useful, |
| 10 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | ** GNU General Public License for more details. |
| 13 | ** |
| 14 | ** You should have received a copy of the GNU General Public License |
| 15 | ** along with this program; if not, write to the Free Software |
| 16 | ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. |
| 17 | */ |
| 18 | |
| 19 | #include <stdio.h> |
| 20 | #include <stdlib.h> |
| 21 | #include <unistd.h> |
| 22 | #include <string.h> |
| 23 | |
| 24 | #include "config.h" |
| 25 | |
| 26 | #include <float_cast.h> |
| 27 | |
| 28 | #if (HAVE_SNDFILE) |
| 29 | |
| 30 | #include <samplerate.h> |
| 31 | #include <sndfile.h> |
| 32 | |
| 33 | #include "audio_out.h" |
| 34 | |
| 35 | #define ARRAY_LEN(x) ((int) (sizeof (x) / sizeof ((x) [0]))) |
| 36 | |
| 37 | #define BUFFER_LEN 4096 |
| 38 | #define INPUT_FRAMES 100 |
| 39 | |
| 40 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) |
| 41 | |
| 42 | #define MAGIC_NUMBER ((int) ('S' << 16) + ('R' << 8) + ('C')) |
| 43 | |
| 44 | #ifndef M_PI |
| 45 | #define M_PI 3.14159265358979323846264338 |
| 46 | #endif |
| 47 | |
| 48 | |
| 49 | typedef struct |
| 50 | { int magic ; |
| 51 | |
| 52 | SNDFILE *sndfile ; |
| 53 | SF_INFO sfinfo ; |
| 54 | |
| 55 | SRC_STATE *src_state ; |
| 56 | SRC_DATA src_data ; |
| 57 | |
| 58 | int freq_point ; |
| 59 | int buffer_out_start, buffer_out_end ; |
| 60 | |
| 61 | float buffer_in [BUFFER_LEN] ; |
| 62 | float buffer_out [BUFFER_LEN] ; |
| 63 | } CALLBACK_DATA ; |
| 64 | |
| 65 | static int varispeed_get_data (CALLBACK_DATA *data, float *samples, int frames) ; |
| 66 | static void varispeed_play (const char *filename, int converter) ; |
| 67 | |
| 68 | int |
| 69 | main (int argc, char *argv []) |
| 70 | { const char *cptr, *progname, *filename ; |
| 71 | int k, converter ; |
| 72 | |
| 73 | converter = SRC_SINC_FASTEST ; |
| 74 | |
| 75 | progname = argv [0] ; |
| 76 | |
| 77 | if ((cptr = strrchr (progname, '/')) != NULL) |
| 78 | progname = cptr + 1 ; |
| 79 | |
| 80 | if ((cptr = strrchr (progname, '\\')) != NULL) |
| 81 | progname = cptr + 1 ; |
| 82 | |
| 83 | printf ("\n" |
| 84 | " %s\n" |
| 85 | "\n" |
| 86 | " This is a demo program which plays the given file at a slowly \n" |
| 87 | " varying speed. Lots of fun with drum loops and full mixes.\n" |
| 88 | "\n" |
| 89 | " It uses Secret Rabbit Code (aka libsamplerate) to perform the \n" |
| 90 | " vari-speeding and libsndfile for file I/O.\n" |
| 91 | "\n", progname) ; |
| 92 | |
| 93 | if (argc == 2) |
| 94 | filename = argv [1] ; |
| 95 | else if (argc == 4 && strcmp (argv [1], "-c") == 0) |
| 96 | { filename = argv [3] ; |
| 97 | converter = atoi (argv [2]) ; |
| 98 | } |
| 99 | else |
| 100 | { printf (" Usage :\n\n %s [-c <number>] <input file>\n\n", progname) ; |
| 101 | puts ( |
| 102 | " The optional -c argument allows the converter type to be chosen from\n" |
| 103 | " the following list :" |
| 104 | "\n" |
| 105 | ) ; |
| 106 | |
| 107 | for (k = 0 ; (cptr = src_get_name (k)) != NULL ; k++) |
| 108 | printf (" %d : %s\n", k, cptr) ; |
| 109 | |
| 110 | puts ("") ; |
| 111 | exit (1) ; |
| 112 | } ; |
| 113 | |
| 114 | varispeed_play (filename, converter) ; |
| 115 | |
| 116 | return 0 ; |
| 117 | } /* main */ |
| 118 | |
| 119 | /*============================================================================== |
| 120 | */ |
| 121 | |
| 122 | static void |
| 123 | varispeed_play (const char *filename, int converter) |
| 124 | { CALLBACK_DATA *data ; |
| 125 | AUDIO_OUT *audio_out ; |
| 126 | int error ; |
| 127 | |
| 128 | /* Allocate memory for the callback data. */ |
| 129 | if ((data = calloc (1, sizeof (CALLBACK_DATA))) == NULL) |
| 130 | { printf ("\n\n%s:%d Calloc failed!\n", __FILE__, __LINE__) ; |
| 131 | exit (1) ; |
| 132 | } ; |
| 133 | |
| 134 | data->magic = MAGIC_NUMBER ; |
| 135 | |
| 136 | if ((data->sndfile = sf_open (filename, SFM_READ, &data->sfinfo)) == NULL) |
| 137 | { puts (sf_strerror (NULL)) ; |
| 138 | exit (1) ; |
| 139 | } ; |
| 140 | |
| 141 | /* Initialize the sample rate converter. */ |
| 142 | if ((data->src_state = src_new (converter, data->sfinfo.channels, &error)) == NULL) |
| 143 | { printf ("\n\nError : src_new() failed : %s.\n\n", src_strerror (error)) ; |
| 144 | exit (1) ; |
| 145 | } ; |
| 146 | |
| 147 | printf ( |
| 148 | |
| 149 | " Playing : %s\n" |
| 150 | " Converter : %s\n" |
| 151 | "\n" |
| 152 | " Press <control-c> to exit.\n" |
| 153 | "\n", |
| 154 | filename, src_get_name (converter)) ; |
| 155 | |
| 156 | if ((audio_out = audio_open (data->sfinfo.channels, data->sfinfo.samplerate)) == NULL) |
| 157 | { printf ("\n\nError : audio_open () failed.\n") ; |
| 158 | exit (1) ; |
| 159 | } ; |
| 160 | |
| 161 | /* Set up sample rate converter info. */ |
| 162 | data->src_data.end_of_input = 0 ; /* Set this later. */ |
| 163 | |
| 164 | /* Start with zero to force load in while loop. */ |
| 165 | data->src_data.input_frames = 0 ; |
| 166 | data->src_data.data_in = data->buffer_in ; |
| 167 | |
| 168 | /* Start with output frames also zero. */ |
| 169 | data->src_data.output_frames_gen = 0 ; |
| 170 | |
| 171 | data->buffer_out_start = data->buffer_out_end = 0 ; |
| 172 | data->src_data.src_ratio = 1.0 ; |
| 173 | |
| 174 | /* Pass the data and the callbacl function to audio_play */ |
| 175 | audio_play ((get_audio_callback_t) varispeed_get_data, audio_out, data) ; |
| 176 | |
| 177 | /* Cleanup */ |
| 178 | audio_close (audio_out) ; |
| 179 | sf_close (data->sndfile) ; |
| 180 | src_delete (data->src_state) ; |
| 181 | |
| 182 | free (data) ; |
| 183 | |
| 184 | } /* varispeed_play */ |
| 185 | |
| 186 | /*============================================================================== |
| 187 | */ |
| 188 | |
| 189 | static int |
| 190 | varispeed_get_data (CALLBACK_DATA *data, float *samples, int frames) |
| 191 | { int error, readframes, frame_count, direct_out ; |
| 192 | |
| 193 | if (data->magic != MAGIC_NUMBER) |
| 194 | { printf ("\n\n%s:%d Eeeek, something really bad happened!\n", __FILE__, __LINE__) ; |
| 195 | exit (1) ; |
| 196 | } ; |
| 197 | |
| 198 | frame_count = 0 ; |
| 199 | |
| 200 | if (data->buffer_out_start < data->buffer_out_end) |
| 201 | { frame_count = MIN (data->buffer_out_end - data->buffer_out_start, frames) ; |
| 202 | memcpy (samples, data->buffer_out + data->sfinfo.channels * data->buffer_out_start, data->sfinfo.channels * frame_count * sizeof (float)) ; |
| 203 | data->buffer_out_start += frame_count ; |
| 204 | } ; |
| 205 | |
| 206 | data->buffer_out_start = data->buffer_out_end = 0 ; |
| 207 | |
| 208 | while (frame_count < frames) |
| 209 | { |
| 210 | /* Read INPUT_FRAMES frames worth looping at end of file. */ |
| 211 | for (readframes = 0 ; readframes < INPUT_FRAMES ; ) |
| 212 | { sf_count_t position ; |
| 213 | |
| 214 | readframes += sf_readf_float (data->sndfile, data->buffer_in + data->sfinfo.channels * readframes, INPUT_FRAMES - readframes) ; |
| 215 | |
| 216 | position = sf_seek (data->sndfile, 0, SEEK_CUR) ; |
| 217 | |
| 218 | if (position < 0 || position == data->sfinfo.frames) |
| 219 | sf_seek (data->sndfile, 0, SEEK_SET) ; |
| 220 | } ; |
| 221 | |
| 222 | data->src_data.input_frames = readframes ; |
| 223 | |
| 224 | data->src_data.src_ratio = 1.0 - 0.5 * sin (data->freq_point * 2 * M_PI / 20000) ; |
| 225 | data->freq_point ++ ; |
| 226 | |
| 227 | direct_out = (data->src_data.src_ratio * readframes < frames - frame_count) ? 1 : 0 ; |
| 228 | |
| 229 | if (direct_out) |
| 230 | { data->src_data.data_out = samples + frame_count * data->sfinfo.channels ; |
| 231 | data->src_data.output_frames = frames - frame_count ; |
| 232 | } |
| 233 | else |
| 234 | { data->src_data.data_out = data->buffer_out ; |
| 235 | data->src_data.output_frames = BUFFER_LEN / data->sfinfo.channels ; |
| 236 | } ; |
| 237 | |
| 238 | if ((error = src_process (data->src_state, &data->src_data))) |
| 239 | { printf ("\nError : %s\n\n", src_strerror (error)) ; |
| 240 | exit (1) ; |
| 241 | } ; |
| 242 | |
| 243 | if (direct_out) |
| 244 | { frame_count += data->src_data.output_frames_gen ; |
| 245 | continue ; |
| 246 | } ; |
| 247 | |
| 248 | memcpy (samples + frame_count * data->sfinfo.channels, data->buffer_out, (frames - frame_count) * data->sfinfo.channels * sizeof (float)) ; |
| 249 | |
| 250 | data->buffer_out_start = frames - frame_count ; |
| 251 | data->buffer_out_end = data->src_data.output_frames_gen ; |
| 252 | |
| 253 | frame_count += frames - frame_count ; |
| 254 | } ; |
| 255 | |
| 256 | return frame_count ; |
| 257 | } /* varispeed_get_data */ |
| 258 | |
| 259 | /*============================================================================== |
| 260 | */ |
| 261 | |
| 262 | #else /* (HAVE_SNFILE == 0) */ |
| 263 | |
| 264 | /* Alternative main function when libsndfile is not available. */ |
| 265 | |
| 266 | int |
| 267 | main (void) |
| 268 | { puts ( |
| 269 | "\n" |
| 270 | "****************************************************************\n" |
| 271 | " This example program was compiled without libsndfile \n" |
| 272 | " (http://www.zip.com.au/~erikd/libsndfile/).\n" |
| 273 | " It is therefore completely broken and non-functional.\n" |
| 274 | "****************************************************************\n" |
| 275 | "\n" |
| 276 | ) ; |
| 277 | |
| 278 | return 0 ; |
| 279 | } /* main */ |
| 280 | |
| 281 | #endif |
| 282 | |