1/* 2** Copyright (C) 2008-2016 Erik de Castro Lopo <erikd@mega-nerd.com> 3** Copyright (C) 2008-2010 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 <config.h> 35 36#include <stdio.h> 37#include <stdlib.h> 38#include <string.h> 39#include <ctype.h> 40#include <time.h> 41 42#include <sndfile.h> 43 44#include "common.h" 45 46#define BUFFER_LEN (1 << 16) 47 48 49static void usage_exit (const char *progname, int exit_code) ; 50static void missing_param (const char * option) ; 51static void read_localtime (struct tm * timedata) ; 52static int has_bext_fields_set (const METADATA_INFO * info) ; 53 54int 55main (int argc, char *argv []) 56{ METADATA_INFO info ; 57 struct tm timedata ; 58 const char *progname ; 59 const char * filenames [2] = { NULL, NULL } ; 60 char date [128], time [128] ; 61 int k ; 62 63 /* Store the program name. */ 64 progname = program_name (argv [0]) ; 65 66 /* Check if we've been asked for help. */ 67 if (argc < 3 || strcmp (argv [1], "--help") == 0 || strcmp (argv [1], "-h") == 0) 68 usage_exit (progname, 0) ; 69 70 /* Set all fields of the struct to zero bytes. */ 71 memset (&info, 0, sizeof (info)) ; 72 73 /* Get the time in case we need it later. */ 74 read_localtime (&timedata) ; 75 76 for (k = 1 ; k < argc ; k++) 77 { if (argv [k][0] != '-') 78 { if (filenames [0] == NULL) 79 filenames [0] = argv [k] ; 80 else if (filenames [1] == NULL) 81 filenames [1] = argv [k] ; 82 else 83 { printf ("Error : Already have two file names on the command line and then found '%s'.\n\n", argv [k]) ; 84 usage_exit (progname, 1) ; 85 } ; 86 continue ; 87 } ; 88 89#define HANDLE_BEXT_ARG(cmd, field) \ 90 if (strcmp (argv [k], cmd) == 0) \ 91 { k ++ ; \ 92 if (k == argc) missing_param (argv [k - 1]) ; \ 93 info.field = argv [k] ; \ 94 continue ; \ 95 } ; 96 97 HANDLE_BEXT_ARG ("--bext-description", description) ; 98 HANDLE_BEXT_ARG ("--bext-originator", originator) ; 99 HANDLE_BEXT_ARG ("--bext-orig-ref", originator_reference) ; 100 HANDLE_BEXT_ARG ("--bext-umid", umid) ; 101 HANDLE_BEXT_ARG ("--bext-orig-date", origination_date) ; 102 HANDLE_BEXT_ARG ("--bext-orig-time", origination_time) ; 103 HANDLE_BEXT_ARG ("--bext-loudness-value", loudness_value) ; 104 HANDLE_BEXT_ARG ("--bext-loudness-range", loudness_range) ; 105 HANDLE_BEXT_ARG ("--bext-max-truepeak", max_true_peak_level) ; 106 HANDLE_BEXT_ARG ("--bext-max-momentary", max_momentary_loudness) ; 107 HANDLE_BEXT_ARG ("--bext-max-shortterm", max_shortterm_loudness) ; 108 HANDLE_BEXT_ARG ("--bext-coding-hist", coding_history) ; 109 HANDLE_BEXT_ARG ("--bext-time-ref", time_ref) ; 110 111#define HANDLE_STR_ARG(cmd, field) \ 112 if (strcmp (argv [k], cmd) == 0) \ 113 { k ++ ; \ 114 if (k == argc) missing_param (argv [k - 1]) ; \ 115 info.field = argv [k] ; \ 116 continue ; \ 117 } ; 118 119 HANDLE_STR_ARG ("--str-comment", comment) ; 120 HANDLE_STR_ARG ("--str-title", title) ; 121 HANDLE_STR_ARG ("--str-copyright", copyright) ; 122 HANDLE_STR_ARG ("--str-artist", artist) ; 123 HANDLE_STR_ARG ("--str-date", date) ; 124 HANDLE_STR_ARG ("--str-album", album) ; 125 HANDLE_STR_ARG ("--str-license", license) ; 126 127 /* Following options do not take an argument. */ 128 if (strcmp (argv [k], "--bext-auto-time-date") == 0) 129 { snprintf (time, sizeof (time), "%02d:%02d:%02d", timedata.tm_hour, timedata.tm_min, timedata.tm_sec) ; 130 info.origination_time = time ; 131 132 snprintf (date, sizeof (date), "%04d-%02d-%02d", timedata.tm_year + 1900, timedata.tm_mon + 1, timedata.tm_mday) ; 133 info.origination_date = date ; 134 continue ; 135 } ; 136 137 if (strcmp (argv [k], "--bext-auto-time") == 0) 138 { snprintf (time, sizeof (time), "%02d:%02d:%02d", timedata.tm_hour, timedata.tm_min, timedata.tm_sec) ; 139 info.origination_time = time ; 140 continue ; 141 } ; 142 143 if (strcmp (argv [k], "--bext-auto-date") == 0) 144 { snprintf (date, sizeof (date), "%04d-%02d-%02d", timedata.tm_year + 1900, timedata.tm_mon + 1, timedata.tm_mday) ; 145 info.origination_date = strdup (date) ; 146 continue ; 147 } ; 148 149 if (strcmp (argv [k], "--str-auto-date") == 0) 150 { snprintf (date, sizeof (date), "%04d-%02d-%02d", timedata.tm_year + 1900, timedata.tm_mon + 1, timedata.tm_mday) ; 151 152 info.date = strdup (date) ; 153 continue ; 154 } ; 155 156 printf ("Error : Don't know what to do with command line arg '%s'.\n\n", argv [k]) ; 157 usage_exit (progname, 1) ; 158 } ; 159 160 /* Find out if any of the 'bext' fields are set. */ 161 info.has_bext_fields = has_bext_fields_set (&info) ; 162 163 if (filenames [0] == NULL) 164 { printf ("Error : No input file specificed.\n\n") ; 165 exit (1) ; 166 } ; 167 168 if (filenames [1] != NULL && strcmp (filenames [0], filenames [1]) == 0) 169 { printf ("Error : Input and output files are the same.\n\n") ; 170 exit (1) ; 171 } ; 172 173 if (info.coding_history != NULL && filenames [1] == NULL) 174 { printf ("\n" 175 "Error : Trying to update coding history of an existing file which unfortunately\n" 176 " is not supported. Instead, create a new file using :\n" 177 "\n" 178 " %s --bext-coding-hist \"Coding history\" old_file.wav new_file.wav\n" 179 "\n", 180 progname) ; 181 exit (1) ; 182 } ; 183 184 sfe_apply_metadata_changes (filenames, &info) ; 185 186 return 0 ; 187} /* main */ 188 189/*============================================================================== 190** Print version and usage. 191*/ 192 193static void 194usage_exit (const char *progname, int exit_code) 195{ printf ("\nUsage :\n\n" 196 " %s [options] <file>\n" 197 " %s [options] <input file> <output file>\n" 198 "\n", 199 progname, progname) ; 200 201 puts ( 202 "Where an option is made up of a pair of a field to set (one of\n" 203 "the 'bext' or metadata fields below) and a string. Fields are\n" 204 "as follows :\n" 205 ) ; 206 207 puts ( 208 " --bext-description Set the 'bext' description.\n" 209 " --bext-originator Set the 'bext' originator.\n" 210 " --bext-orig-ref Set the 'bext' originator reference.\n" 211 " --bext-umid Set the 'bext' UMID.\n" 212 " --bext-orig-date Set the 'bext' origination date.\n" 213 " --bext-orig-time Set the 'bext' origination time.\n" 214 " --bext-loudness-value Set the 'bext' loudness value.\n" 215 " --bext-loudness-range Set the 'bext' loudness range.\n" 216 " --bext-max-truepeak Set the 'bext' max. true peak level\n" 217 " --bext-max-momentary Set the 'bext' max. momentary loudness\n" 218 " --bext-max-shortterm Set the 'bext' max. short term loudness\n" 219 " --bext-coding-hist Set the 'bext' coding history.\n" 220 " --bext-time-ref Set the 'bext' Time ref.\n" 221 "\n" 222 " --str-comment Set the metadata comment.\n" 223 " --str-title Set the metadata title.\n" 224 " --str-copyright Set the metadata copyright.\n" 225 " --str-artist Set the metadata artist.\n" 226 " --str-date Set the metadata date.\n" 227 " --str-album Set the metadata album.\n" 228 " --str-license Set the metadata license.\n" 229 ) ; 230 231 puts ( 232 "There are also the following arguments which do not take a\n" 233 "parameter :\n\n" 234 " --bext-auto-time-date Set the 'bext' time and date to current time/date.\n" 235 " --bext-auto-time Set the 'bext' time to current time.\n" 236 " --bext-auto-date Set the 'bext' date to current date.\n" 237 " --str-auto-date Set the metadata date to current date.\n" 238 ) ; 239 240 puts ( 241 "Most of the above operations can be done in-place on an existing\n" 242 "file. If any operation cannot be performed, the application will\n" 243 "exit with an appropriate error message.\n" 244 ) ; 245 246 printf ("Using %s.\n\n", sf_version_string ()) ; 247 exit (exit_code) ; 248} /* usage_exit */ 249 250static void 251missing_param (const char * option) 252{ 253 printf ("Error : Option '%s' needs a parameter but doesn't seem to have one.\n\n", option) ; 254 exit (1) ; 255} /* missing_param */ 256 257/*============================================================================== 258*/ 259 260static int 261has_bext_fields_set (const METADATA_INFO * info) 262{ 263 if (info->description || info->originator || info->originator_reference) 264 return 1 ; 265 266 if (info->origination_date || info->origination_time || info->umid || info->coding_history || info->time_ref) 267 return 1 ; 268 269 if (info->loudness_value || info->loudness_range || info->max_true_peak_level || info->max_momentary_loudness || info->max_shortterm_loudness) 270 return 1 ; 271 272 return 0 ; 273} /* has_bext_fields_set */ 274 275static void 276read_localtime (struct tm * timedata) 277{ time_t current ; 278 279 time (¤t) ; 280 memset (timedata, 0, sizeof (struct tm)) ; 281 282#if defined (HAVE_LOCALTIME_R) 283 /* If the re-entrant version is available, use it. */ 284 localtime_r (¤t, timedata) ; 285#elif defined (HAVE_LOCALTIME) 286 { 287 struct tm *tmptr ; 288 /* Otherwise use the standard one and copy the data to local storage. */ 289 if ((tmptr = localtime (¤t)) != NULL) 290 memcpy (timedata, tmptr, sizeof (struct tm)) ; 291 } 292#endif 293 294 return ; 295} /* read_localtime */ 296