Alexandre Lision | 7c6f4a6 | 2013-09-05 13:27:01 -0400 | [diff] [blame] | 1 | /* |
| 2 | ** Copyright (C) 1999-2011 Erik de Castro Lopo <erikd@mega-nerd.com> |
| 3 | ** Copyright (C) 2008 George Blood Audio |
| 4 | ** |
| 5 | ** All rights reserved. |
| 6 | ** |
| 7 | ** Redistribution and use in source and binary forms, with or without |
| 8 | ** modification, are permitted provided that the following conditions are |
| 9 | ** met: |
| 10 | ** |
| 11 | ** * Redistributions of source code must retain the above copyright |
| 12 | ** notice, this list of conditions and the following disclaimer. |
| 13 | ** * Redistributions in binary form must reproduce the above copyright |
| 14 | ** notice, this list of conditions and the following disclaimer in |
| 15 | ** the documentation and/or other materials provided with the |
| 16 | ** distribution. |
| 17 | ** * Neither the author nor the names of any contributors may be used |
| 18 | ** to endorse or promote products derived from this software without |
| 19 | ** specific prior written permission. |
| 20 | ** |
| 21 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 22 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
| 23 | ** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 24 | ** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| 25 | ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| 26 | ** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 27 | ** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| 28 | ** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| 29 | ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| 30 | ** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| 31 | ** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 32 | */ |
| 33 | |
| 34 | #include <stdio.h> |
| 35 | #include <stdlib.h> |
| 36 | #include <string.h> |
| 37 | #include <ctype.h> |
| 38 | #include <stdint.h> |
| 39 | |
| 40 | #include <sndfile.h> |
| 41 | |
| 42 | #include "common.h" |
| 43 | |
| 44 | #define BUFFER_LEN 4096 |
| 45 | |
| 46 | #define MIN(x,y) ((x) < (y) ? (x) : (y)) |
| 47 | |
| 48 | void |
| 49 | sfe_copy_data_fp (SNDFILE *outfile, SNDFILE *infile, int channels) |
| 50 | { static double data [BUFFER_LEN], max ; |
| 51 | int frames, readcount, k ; |
| 52 | |
| 53 | frames = BUFFER_LEN / channels ; |
| 54 | readcount = frames ; |
| 55 | |
| 56 | sf_command (infile, SFC_CALC_SIGNAL_MAX, &max, sizeof (max)) ; |
| 57 | |
| 58 | if (max < 1.0) |
| 59 | { while (readcount > 0) |
| 60 | { readcount = sf_readf_double (infile, data, frames) ; |
| 61 | sf_writef_double (outfile, data, readcount) ; |
| 62 | } ; |
| 63 | } |
| 64 | else |
| 65 | { sf_command (infile, SFC_SET_NORM_DOUBLE, NULL, SF_FALSE) ; |
| 66 | |
| 67 | while (readcount > 0) |
| 68 | { readcount = sf_readf_double (infile, data, frames) ; |
| 69 | for (k = 0 ; k < readcount * channels ; k++) |
| 70 | data [k] /= max ; |
| 71 | sf_writef_double (outfile, data, readcount) ; |
| 72 | } ; |
| 73 | } ; |
| 74 | |
| 75 | return ; |
| 76 | } /* sfe_copy_data_fp */ |
| 77 | |
| 78 | void |
| 79 | sfe_copy_data_int (SNDFILE *outfile, SNDFILE *infile, int channels) |
| 80 | { static int data [BUFFER_LEN] ; |
| 81 | int frames, readcount ; |
| 82 | |
| 83 | frames = BUFFER_LEN / channels ; |
| 84 | readcount = frames ; |
| 85 | |
| 86 | while (readcount > 0) |
| 87 | { readcount = sf_readf_int (infile, data, frames) ; |
| 88 | sf_writef_int (outfile, data, readcount) ; |
| 89 | } ; |
| 90 | |
| 91 | return ; |
| 92 | } /* sfe_copy_data_int */ |
| 93 | |
| 94 | /*============================================================================== |
| 95 | */ |
| 96 | |
| 97 | static int |
| 98 | merge_broadcast_info (SNDFILE * infile, SNDFILE * outfile, int format, const METADATA_INFO * info) |
| 99 | { SF_BROADCAST_INFO_2K binfo ; |
| 100 | int infileminor ; |
| 101 | |
| 102 | memset (&binfo, 0, sizeof (binfo)) ; |
| 103 | |
| 104 | if ((SF_FORMAT_TYPEMASK & format) != SF_FORMAT_WAV) |
| 105 | { printf ("Error : This is not a WAV file and hence broadcast info cannot be added to it.\n\n") ; |
| 106 | return 1 ; |
| 107 | } ; |
| 108 | |
| 109 | infileminor = SF_FORMAT_SUBMASK & format ; |
| 110 | |
| 111 | switch (infileminor) |
| 112 | { case SF_FORMAT_PCM_16 : |
| 113 | case SF_FORMAT_PCM_24 : |
| 114 | case SF_FORMAT_PCM_32 : |
| 115 | break ; |
| 116 | |
| 117 | default : |
| 118 | printf ( |
| 119 | "Warning : The EBU Technical Recommendation R68-2000 states that the only\n" |
| 120 | " allowed encodings are Linear PCM and MPEG3. This file is not in\n" |
| 121 | " the right format.\n\n" |
| 122 | ) ; |
| 123 | break ; |
| 124 | } ; |
| 125 | |
| 126 | if (sf_command (infile, SFC_GET_BROADCAST_INFO, &binfo, sizeof (binfo)) == 0) |
| 127 | { if (infile == outfile) |
| 128 | { printf ( |
| 129 | "Error : Attempting in-place broadcast info update, but file does not\n" |
| 130 | " have a 'bext' chunk to modify. The solution is to specify both\n" |
| 131 | " input and output files on the command line.\n\n" |
| 132 | ) ; |
| 133 | return 1 ; |
| 134 | } ; |
| 135 | } ; |
| 136 | |
| 137 | #define REPLACE_IF_NEW(x) \ |
| 138 | if (info->x != NULL) \ |
| 139 | { memset (binfo.x, 0, sizeof (binfo.x)) ; \ |
| 140 | memcpy (binfo.x, info->x, MIN (strlen (info->x), sizeof (binfo.x))) ; \ |
| 141 | } ; |
| 142 | |
| 143 | REPLACE_IF_NEW (description) ; |
| 144 | REPLACE_IF_NEW (originator) ; |
| 145 | REPLACE_IF_NEW (originator_reference) ; |
| 146 | REPLACE_IF_NEW (origination_date) ; |
| 147 | REPLACE_IF_NEW (origination_time) ; |
| 148 | REPLACE_IF_NEW (umid) ; |
| 149 | |
| 150 | /* Special case for Time Ref. */ |
| 151 | if (info->time_ref != NULL) |
| 152 | { uint64_t ts = atoll (info->time_ref) ; |
| 153 | |
| 154 | binfo.time_reference_high = (ts >> 32) ; |
| 155 | binfo.time_reference_low = (ts & 0xffffffff) ; |
| 156 | } ; |
| 157 | |
| 158 | /* Special case for coding_history because we may want to append. */ |
| 159 | if (info->coding_history != NULL) |
| 160 | { if (info->coding_hist_append) |
| 161 | { int slen = strlen (binfo.coding_history) ; |
| 162 | |
| 163 | while (slen > 1 && isspace (binfo.coding_history [slen - 1])) |
| 164 | slen -- ; |
| 165 | |
| 166 | memcpy (binfo.coding_history + slen, info->coding_history, sizeof (binfo.coding_history) - slen) ; |
| 167 | } |
| 168 | else |
| 169 | { size_t slen = MIN (strlen (info->coding_history), sizeof (binfo.coding_history)) ; |
| 170 | |
| 171 | memset (binfo.coding_history, 0, sizeof (binfo.coding_history)) ; |
| 172 | memcpy (binfo.coding_history, info->coding_history, slen) ; |
| 173 | binfo.coding_history_size = slen ; |
| 174 | } ; |
| 175 | } ; |
| 176 | |
| 177 | if (sf_command (outfile, SFC_SET_BROADCAST_INFO, &binfo, sizeof (binfo)) == 0) |
| 178 | { printf ("Error : Setting of broadcast info chunks failed.\n\n") ; |
| 179 | return 1 ; |
| 180 | } ; |
| 181 | |
| 182 | return 0 ; |
| 183 | } /* merge_broadcast_info*/ |
| 184 | |
| 185 | static void |
| 186 | update_strings (SNDFILE * outfile, const METADATA_INFO * info) |
| 187 | { |
| 188 | if (info->title != NULL) |
| 189 | sf_set_string (outfile, SF_STR_TITLE, info->title) ; |
| 190 | |
| 191 | if (info->copyright != NULL) |
| 192 | sf_set_string (outfile, SF_STR_COPYRIGHT, info->copyright) ; |
| 193 | |
| 194 | if (info->artist != NULL) |
| 195 | sf_set_string (outfile, SF_STR_ARTIST, info->artist) ; |
| 196 | |
| 197 | if (info->comment != NULL) |
| 198 | sf_set_string (outfile, SF_STR_COMMENT, info->comment) ; |
| 199 | |
| 200 | if (info->date != NULL) |
| 201 | sf_set_string (outfile, SF_STR_DATE, info->date) ; |
| 202 | |
| 203 | if (info->album != NULL) |
| 204 | sf_set_string (outfile, SF_STR_ALBUM, info->album) ; |
| 205 | |
| 206 | if (info->license != NULL) |
| 207 | sf_set_string (outfile, SF_STR_LICENSE, info->license) ; |
| 208 | |
| 209 | } /* update_strings */ |
| 210 | |
| 211 | |
| 212 | |
| 213 | void |
| 214 | sfe_apply_metadata_changes (const char * filenames [2], const METADATA_INFO * info) |
| 215 | { SNDFILE *infile = NULL, *outfile = NULL ; |
| 216 | SF_INFO sfinfo ; |
| 217 | METADATA_INFO tmpinfo ; |
| 218 | int error_code = 0 ; |
| 219 | |
| 220 | memset (&sfinfo, 0, sizeof (sfinfo)) ; |
| 221 | memset (&tmpinfo, 0, sizeof (tmpinfo)) ; |
| 222 | |
| 223 | if (filenames [1] == NULL) |
| 224 | infile = outfile = sf_open (filenames [0], SFM_RDWR, &sfinfo) ; |
| 225 | else |
| 226 | { infile = sf_open (filenames [0], SFM_READ, &sfinfo) ; |
| 227 | |
| 228 | /* Output must be WAV. */ |
| 229 | sfinfo.format = SF_FORMAT_WAV | (SF_FORMAT_SUBMASK & sfinfo.format) ; |
| 230 | outfile = sf_open (filenames [1], SFM_WRITE, &sfinfo) ; |
| 231 | } ; |
| 232 | |
| 233 | if (infile == NULL) |
| 234 | { printf ("Error : Not able to open input file '%s' : %s\n", filenames [0], sf_strerror (infile)) ; |
| 235 | error_code = 1 ; |
| 236 | goto cleanup_exit ; |
| 237 | } ; |
| 238 | |
| 239 | if (outfile == NULL) |
| 240 | { printf ("Error : Not able to open output file '%s' : %s\n", filenames [1], sf_strerror (outfile)) ; |
| 241 | error_code = 1 ; |
| 242 | goto cleanup_exit ; |
| 243 | } ; |
| 244 | |
| 245 | if (info->has_bext_fields && merge_broadcast_info (infile, outfile, sfinfo.format, info)) |
| 246 | { error_code = 1 ; |
| 247 | goto cleanup_exit ; |
| 248 | } ; |
| 249 | |
| 250 | if (infile != outfile) |
| 251 | { int infileminor = SF_FORMAT_SUBMASK & sfinfo.format ; |
| 252 | |
| 253 | /* If the input file is not the same as the output file, copy the data. */ |
| 254 | if ((infileminor == SF_FORMAT_DOUBLE) || (infileminor == SF_FORMAT_FLOAT)) |
| 255 | sfe_copy_data_fp (outfile, infile, sfinfo.channels) ; |
| 256 | else |
| 257 | sfe_copy_data_int (outfile, infile, sfinfo.channels) ; |
| 258 | } ; |
| 259 | |
| 260 | update_strings (outfile, info) ; |
| 261 | |
| 262 | cleanup_exit : |
| 263 | |
| 264 | if (outfile != NULL && outfile != infile) |
| 265 | sf_close (outfile) ; |
| 266 | |
| 267 | if (infile != NULL) |
| 268 | sf_close (infile) ; |
| 269 | |
| 270 | if (error_code) |
| 271 | exit (error_code) ; |
| 272 | |
| 273 | return ; |
| 274 | } /* sfe_apply_metadata_changes */ |
| 275 | |
| 276 | /*============================================================================== |
| 277 | */ |
| 278 | |
| 279 | typedef struct |
| 280 | { const char *ext ; |
| 281 | int len ; |
| 282 | int format ; |
| 283 | } OUTPUT_FORMAT_MAP ; |
| 284 | |
| 285 | static OUTPUT_FORMAT_MAP format_map [] = |
| 286 | { |
| 287 | { "aif", 3, SF_FORMAT_AIFF }, |
| 288 | { "wav", 0, SF_FORMAT_WAV }, |
| 289 | { "au", 0, SF_FORMAT_AU }, |
| 290 | { "caf", 0, SF_FORMAT_CAF }, |
| 291 | { "flac", 0, SF_FORMAT_FLAC }, |
| 292 | { "snd", 0, SF_FORMAT_AU }, |
| 293 | { "svx", 0, SF_FORMAT_SVX }, |
| 294 | { "paf", 0, SF_ENDIAN_BIG | SF_FORMAT_PAF }, |
| 295 | { "fap", 0, SF_ENDIAN_LITTLE | SF_FORMAT_PAF }, |
| 296 | { "gsm", 0, SF_FORMAT_RAW }, |
| 297 | { "nist", 0, SF_FORMAT_NIST }, |
| 298 | { "htk", 0, SF_FORMAT_HTK }, |
| 299 | { "ircam", 0, SF_FORMAT_IRCAM }, |
| 300 | { "sf", 0, SF_FORMAT_IRCAM }, |
| 301 | { "voc", 0, SF_FORMAT_VOC }, |
| 302 | { "w64", 0, SF_FORMAT_W64 }, |
| 303 | { "raw", 0, SF_FORMAT_RAW }, |
| 304 | { "mat4", 0, SF_FORMAT_MAT4 }, |
| 305 | { "mat5", 0, SF_FORMAT_MAT5 }, |
| 306 | { "mat", 0, SF_FORMAT_MAT4 }, |
| 307 | { "pvf", 0, SF_FORMAT_PVF }, |
| 308 | { "sds", 0, SF_FORMAT_SDS }, |
| 309 | { "sd2", 0, SF_FORMAT_SD2 }, |
| 310 | { "vox", 0, SF_FORMAT_RAW }, |
| 311 | { "xi", 0, SF_FORMAT_XI }, |
| 312 | { "wve", 0, SF_FORMAT_WVE }, |
| 313 | { "oga", 0, SF_FORMAT_OGG }, |
| 314 | { "mpc", 0, SF_FORMAT_MPC2K }, |
| 315 | { "rf64", 0, SF_FORMAT_RF64 }, |
| 316 | } ; /* format_map */ |
| 317 | |
| 318 | int |
| 319 | sfe_file_type_of_ext (const char *str, int format) |
| 320 | { char buffer [16], *cptr ; |
| 321 | int k ; |
| 322 | |
| 323 | format &= SF_FORMAT_SUBMASK ; |
| 324 | |
| 325 | if ((cptr = strrchr (str, '.')) == NULL) |
| 326 | return 0 ; |
| 327 | |
| 328 | strncpy (buffer, cptr + 1, 15) ; |
| 329 | buffer [15] = 0 ; |
| 330 | |
| 331 | for (k = 0 ; buffer [k] ; k++) |
| 332 | buffer [k] = tolower ((buffer [k])) ; |
| 333 | |
| 334 | if (strcmp (buffer, "gsm") == 0) |
| 335 | return SF_FORMAT_RAW | SF_FORMAT_GSM610 ; |
| 336 | |
| 337 | if (strcmp (buffer, "vox") == 0) |
| 338 | return SF_FORMAT_RAW | SF_FORMAT_VOX_ADPCM ; |
| 339 | |
| 340 | for (k = 0 ; k < (int) (sizeof (format_map) / sizeof (format_map [0])) ; k++) |
| 341 | { if (format_map [k].len > 0 && strncmp (buffer, format_map [k].ext, format_map [k].len) == 0) |
| 342 | return format_map [k].format | format ; |
| 343 | else if (strcmp (buffer, format_map [k].ext) == 0) |
| 344 | return format_map [k].format | format ; |
| 345 | } ; |
| 346 | |
| 347 | /* Default if all the above fails. */ |
| 348 | return (SF_FORMAT_WAV | SF_FORMAT_PCM_24) ; |
| 349 | } /* sfe_file_type_of_ext */ |
| 350 | |
| 351 | void |
| 352 | sfe_dump_format_map (void) |
| 353 | { SF_FORMAT_INFO info ; |
| 354 | int k ; |
| 355 | |
| 356 | for (k = 0 ; k < ARRAY_LEN (format_map) ; k++) |
| 357 | { info.format = format_map [k].format ; |
| 358 | sf_command (NULL, SFC_GET_FORMAT_INFO, &info, sizeof (info)) ; |
| 359 | printf (" %-10s : %s\n", format_map [k].ext, info.name == NULL ? "????" : info.name) ; |
| 360 | } ; |
| 361 | |
| 362 | } /* sfe_dump_format_map */ |
| 363 | |
| 364 | const char * |
| 365 | program_name (const char * argv0) |
| 366 | { const char * tmp ; |
| 367 | |
| 368 | tmp = strrchr (argv0, '/') ; |
| 369 | argv0 = tmp ? tmp + 1 : argv0 ; |
| 370 | |
| 371 | tmp = strrchr (argv0, '/') ; |
| 372 | argv0 = tmp ? tmp + 1 : argv0 ; |
| 373 | |
| 374 | /* Remove leading libtool name mangling. */ |
| 375 | if (strstr (argv0, "lt-") == argv0) |
| 376 | return argv0 + 3 ; |
| 377 | |
| 378 | return argv0 ; |
| 379 | } /* program_name */ |