1/* 2** Copyright (C) 1999-2019 Erik de Castro Lopo <erikd@mega-nerd.com> 3** 4** All rights reserved. 5** 6** Redistribution and use in source and binary forms, with or without 7** modification, are permitted provided that the following conditions are 8** met: 9** 10** * Redistributions of source code must retain the above copyright 11** notice, this list of conditions and the following disclaimer. 12** * Redistributions in binary form must reproduce the above copyright 13** notice, this list of conditions and the following disclaimer in 14** the documentation and/or other materials provided with the 15** distribution. 16** * Neither the author nor the names of any contributors may be used 17** to endorse or promote products derived from this software without 18** specific prior written permission. 19** 20** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 27** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 28** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 29** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 30** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31*/ 32 33#include <stdio.h> 34#include <stdlib.h> 35#include <string.h> 36#include <inttypes.h> 37#include <ctype.h> 38#include <math.h> 39 40#include <sndfile.h> 41 42#include "common.h" 43 44#define BUFFER_LEN (1 << 16) 45 46#if (defined (WIN32) || defined (_WIN32)) 47#include <windows.h> 48#endif 49 50static void usage_exit (const char *progname) ; 51 52static void info_dump (const char *filename) ; 53static int instrument_dump (const char *filename) ; 54static int broadcast_dump (const char *filename) ; 55static int chanmap_dump (const char *filename) ; 56static int cart_dump (const char *filename) ; 57static void total_dump (void) ; 58 59static double total_seconds = 0.0 ; 60 61int 62main (int argc, char *argv []) 63{ int k ; 64 65 if (argc < 2 || strcmp (argv [1], "--help") == 0 || strcmp (argv [1], "-h") == 0) 66 usage_exit (program_name (argv [0])) ; 67 68 if (strcmp (argv [1], "--instrument") == 0) 69 { int error = 0 ; 70 71 for (k = 2 ; k < argc ; k++) 72 error += instrument_dump (argv [k]) ; 73 return error ; 74 } ; 75 76 if (strcmp (argv [1], "--broadcast") == 0) 77 { int error = 0 ; 78 79 for (k = 2 ; k < argc ; k++) 80 error += broadcast_dump (argv [k]) ; 81 return error ; 82 } ; 83 84 if (strcmp (argv [1], "--channel-map") == 0) 85 { int error = 0 ; 86 87 for (k = 2 ; k < argc ; k++) 88 error += chanmap_dump (argv [k]) ; 89 return error ; 90 } ; 91 92 if (strcmp (argv [1], "--cart") == 0) 93 { int error = 0 ; 94 95 for (k = 2 ; k < argc ; k++) 96 error += cart_dump (argv [k]) ; 97 return error ; 98 } ; 99 100 for (k = 1 ; k < argc ; k++) 101 info_dump (argv [k]) ; 102 103 if (argc > 2) 104 total_dump () ; 105 106 return 0 ; 107} /* main */ 108 109/*============================================================================== 110** Print version and usage. 111*/ 112 113static void 114usage_exit (const char *progname) 115{ printf ("Usage :\n %s <file> ...\n", progname) ; 116 printf (" Prints out information about one or more sound files.\n\n") ; 117 printf (" %s --instrument <file>\n", progname) ; 118 printf (" Prints out the instrument data for the given file.\n\n") ; 119 printf (" %s --broadcast <file>\n", progname) ; 120 printf (" Prints out the broadcast WAV info for the given file.\n\n") ; 121 printf (" %s --channel-map <file>\n", progname) ; 122 printf (" Prints out the channel map for the given file.\n\n") ; 123 printf (" %s --cart <file>\n", progname) ; 124 printf (" Prints out the cart chunk WAV info for the given file.\n\n") ; 125 126 printf ("Using %s.\n\n", sf_version_string ()) ; 127#if (defined (_WIN32) || defined (WIN32)) 128 printf ("This is a Unix style command line application which\n" 129 "should be run in a MSDOS box or Command Shell window.\n\n") ; 130 printf ("Sleeping for 5 seconds before exiting.\n\n") ; 131 fflush (stdout) ; 132 133 Sleep (5 * 1000) ; 134#endif 135 exit (1) ; 136} /* usage_exit */ 137 138/*============================================================================== 139** Dumping of sndfile info. 140*/ 141 142static double data [BUFFER_LEN] ; 143 144static double 145calc_decibels (SF_INFO * sfinfo, double max) 146{ double decibels ; 147 148 switch (sfinfo->format & SF_FORMAT_SUBMASK) 149 { case SF_FORMAT_PCM_U8 : 150 case SF_FORMAT_PCM_S8 : 151 decibels = max / 0x80 ; 152 break ; 153 154 case SF_FORMAT_PCM_16 : 155 decibels = max / 0x8000 ; 156 break ; 157 158 case SF_FORMAT_PCM_24 : 159 decibels = max / 0x800000 ; 160 break ; 161 162 case SF_FORMAT_PCM_32 : 163 decibels = max / 0x80000000 ; 164 break ; 165 166 case SF_FORMAT_FLOAT : 167 case SF_FORMAT_DOUBLE : 168 case SF_FORMAT_VORBIS : 169 case SF_FORMAT_OPUS : 170 decibels = max / 1.0 ; 171 break ; 172 173 default : 174 decibels = max / 0x8000 ; 175 break ; 176 } ; 177 178 return 20.0 * log10 (decibels) ; 179} /* calc_decibels */ 180 181static const char * 182format_duration_str (double seconds) 183{ static char str [128] ; 184 int hrs, min ; 185 double sec ; 186 187 memset (str, 0, sizeof (str)) ; 188 189 hrs = (int) (seconds / 3600.0) ; 190 min = (int) ((seconds - (hrs * 3600.0)) / 60.0) ; 191 sec = seconds - (hrs * 3600.0) - (min * 60.0) ; 192 193 snprintf (str, sizeof (str) - 1, "%02d:%02d:%06.3f", hrs, min, sec) ; 194 195 return str ; 196} /* format_duration_str */ 197 198static const char * 199generate_duration_str (SF_INFO *sfinfo) 200{ 201 double seconds ; 202 203 if (sfinfo->samplerate < 1) 204 return NULL ; 205 206 if (sfinfo->frames / sfinfo->samplerate > 0x7FFFFFFF) 207 return "unknown" ; 208 209 seconds = (1.0 * sfinfo->frames) / sfinfo->samplerate ; 210 211 /* Accumulate the total of all known file durations */ 212 total_seconds += seconds ; 213 214 return format_duration_str (seconds) ; 215} /* generate_duration_str */ 216 217static void 218info_dump (const char *filename) 219{ static char strbuffer [BUFFER_LEN] ; 220 SNDFILE *file ; 221 SF_INFO sfinfo ; 222 double signal_max, decibels ; 223 224 memset (&sfinfo, 0, sizeof (sfinfo)) ; 225 226 if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL) 227 { printf ("Error : Not able to open input file %s.\n", filename) ; 228 fflush (stdout) ; 229 memset (data, 0, sizeof (data)) ; 230 sf_command (file, SFC_GET_LOG_INFO, strbuffer, BUFFER_LEN) ; 231 puts (strbuffer) ; 232 puts (sf_strerror (NULL)) ; 233 return ; 234 } ; 235 236 printf ("========================================\n") ; 237 sf_command (file, SFC_GET_LOG_INFO, strbuffer, BUFFER_LEN) ; 238 puts (strbuffer) ; 239 printf ("----------------------------------------\n") ; 240 241 printf ("Sample Rate : %d\n", sfinfo.samplerate) ; 242 243 if (sfinfo.frames == SF_COUNT_MAX) 244 printf ("Frames : unknown\n") ; 245 else 246 printf ("Frames : %" PRId64 "\n", sfinfo.frames) ; 247 248 printf ("Channels : %d\n", sfinfo.channels) ; 249 printf ("Format : 0x%08X\n", sfinfo.format) ; 250 printf ("Sections : %d\n", sfinfo.sections) ; 251 printf ("Seekable : %s\n", (sfinfo.seekable ? "TRUE" : "FALSE")) ; 252 printf ("Duration : %s\n", generate_duration_str (&sfinfo)) ; 253 254 if (sfinfo.frames < 100 * 1024 * 1024) 255 { /* Do not use sf_signal_max because it doesn't work for non-seekable files . */ 256 sf_command (file, SFC_CALC_SIGNAL_MAX, &signal_max, sizeof (signal_max)) ; 257 decibels = calc_decibels (&sfinfo, signal_max) ; 258 printf ("Signal Max : %g (%4.2f dB)\n", signal_max, decibels) ; 259 } ; 260 putchar ('\n') ; 261 262 sf_close (file) ; 263 264} /* info_dump */ 265 266/*============================================================================== 267** Dumping of SF_INSTRUMENT data. 268*/ 269 270static const char * 271str_of_type (int mode) 272{ switch (mode) 273 { case SF_LOOP_NONE : return "none" ; 274 case SF_LOOP_FORWARD : return "fwd " ; 275 case SF_LOOP_BACKWARD : return "back" ; 276 case SF_LOOP_ALTERNATING : return "alt " ; 277 default : break ; 278 } ; 279 280 return "????" ; 281} /* str_of_mode */ 282 283static int 284instrument_dump (const char *filename) 285{ SNDFILE *file ; 286 SF_INFO sfinfo ; 287 SF_INSTRUMENT inst ; 288 int got_inst, k ; 289 290 memset (&sfinfo, 0, sizeof (sfinfo)) ; 291 292 if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL) 293 { printf ("Error : Not able to open input file %s.\n", filename) ; 294 fflush (stdout) ; 295 memset (data, 0, sizeof (data)) ; 296 puts (sf_strerror (NULL)) ; 297 return 1 ; 298 } ; 299 300 got_inst = sf_command (file, SFC_GET_INSTRUMENT, &inst, sizeof (inst)) ; 301 sf_close (file) ; 302 303 if (got_inst == SF_FALSE) 304 { printf ("Error : File '%s' does not contain instrument data.\n\n", filename) ; 305 return 1 ; 306 } ; 307 308 printf ("Instrument : %s\n\n", filename) ; 309 printf (" Gain : %d\n", inst.gain) ; 310 printf (" Base note : %d\n", inst.basenote) ; 311 printf (" Velocity : %d - %d\n", (int) inst.velocity_lo, (int) inst.velocity_hi) ; 312 printf (" Key : %d - %d\n", (int) inst.key_lo, (int) inst.key_hi) ; 313 printf (" Loop points : %d\n", inst.loop_count) ; 314 315 for (k = 0 ; k < inst.loop_count ; k++) 316 printf (" %-2d Mode : %s Start : %6" PRIu32 " End : %6" PRIu32 317 " Count : %6" PRIu32 "\n", k, str_of_type (inst.loops [k].mode), 318 inst.loops [k].start, inst.loops [k].end, inst.loops [k].count) ; 319 320 putchar ('\n') ; 321 return 0 ; 322} /* instrument_dump */ 323 324static int 325broadcast_dump (const char *filename) 326{ SNDFILE *file ; 327 SF_INFO sfinfo ; 328 SF_BROADCAST_INFO_2K bext ; 329 double time_ref_sec ; 330 int got_bext ; 331 332 memset (&sfinfo, 0, sizeof (sfinfo)) ; 333 334 if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL) 335 { printf ("Error : Not able to open input file %s.\n", filename) ; 336 fflush (stdout) ; 337 memset (data, 0, sizeof (data)) ; 338 puts (sf_strerror (NULL)) ; 339 return 1 ; 340 } ; 341 342 memset (&bext, 0, sizeof (SF_BROADCAST_INFO_2K)) ; 343 344 got_bext = sf_command (file, SFC_GET_BROADCAST_INFO, &bext, sizeof (bext)) ; 345 sf_close (file) ; 346 347 if (got_bext == SF_FALSE) 348 { printf ("Error : File '%s' does not contain broadcast information.\n\n", filename) ; 349 return 1 ; 350 } ; 351 352 /* 353 ** From : http://www.ebu.ch/en/technical/publications/userguides/bwf_user_guide.php 354 ** 355 ** Time Reference: 356 ** This field is a count from midnight in samples to the first sample 357 ** of the audio sequence. 358 */ 359 360 time_ref_sec = ((pow (2.0, 32) * bext.time_reference_high) + (1.0 * bext.time_reference_low)) / sfinfo.samplerate ; 361 362 printf ("Description : %.*s\n", (int) sizeof (bext.description), bext.description) ; 363 printf ("Originator : %.*s\n", (int) sizeof (bext.originator), bext.originator) ; 364 printf ("Origination ref : %.*s\n", (int) sizeof (bext.originator_reference), bext.originator_reference) ; 365 printf ("Origination date : %.*s\n", (int) sizeof (bext.origination_date), bext.origination_date) ; 366 printf ("Origination time : %.*s\n", (int) sizeof (bext.origination_time), bext.origination_time) ; 367 368 if (bext.time_reference_high == 0 && bext.time_reference_low == 0) 369 printf ("Time ref : 0\n") ; 370 else 371 printf ("Time ref : 0x%x%08x (%.6f seconds)\n", bext.time_reference_high, bext.time_reference_low, time_ref_sec) ; 372 373 printf ("BWF version : %d\n", bext.version) ; 374 375 if (bext.version >= 1) 376 printf ("UMID : %.*s\n", (int) sizeof (bext.umid), bext.umid) ; 377 378 if (bext.version >= 2) 379 { /* 0x7fff shall be used to designate an unused value */ 380 /* valid range: -99.99 .. 99.99 */ 381 printf ("Loudness value : %6.2f LUFS\n", bext.loudness_value / 100.0) ; 382 /* valid range: 0.00 .. 99.99 */ 383 printf ("Loudness range : %6.2f LU\n", bext.loudness_range / 100.0) ; 384 /* valid range: -99.99 .. 99.99 */ 385 printf ("Max. true peak level : %6.2f dBTP\n", bext.max_true_peak_level / 100.0) ; 386 printf ("Max. momentary loudness : %6.2f LUFS\n", bext.max_momentary_loudness / 100.0) ; 387 printf ("Max. short term loudness : %6.2f LUFS\n", bext.max_shortterm_loudness / 100.0) ; 388 } ; 389 390 printf ("Coding history : %.*s\n", bext.coding_history_size, bext.coding_history) ; 391 392 return 0 ; 393} /* broadcast_dump */ 394 395static int 396chanmap_dump (const char *filename) 397{ SNDFILE *file ; 398 SF_INFO sfinfo ; 399 int * channel_map ; 400 int got_chanmap, k ; 401 402 memset (&sfinfo, 0, sizeof (sfinfo)) ; 403 404 if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL) 405 { printf ("Error : Not able to open input file %s.\n", filename) ; 406 fflush (stdout) ; 407 memset (data, 0, sizeof (data)) ; 408 puts (sf_strerror (NULL)) ; 409 return 1 ; 410 } ; 411 412 if ((channel_map = calloc (sfinfo.channels, sizeof (int))) == NULL) 413 { printf ("Error : malloc failed.\n\n") ; 414 return 1 ; 415 } ; 416 417 got_chanmap = sf_command (file, SFC_GET_CHANNEL_MAP_INFO, channel_map, sfinfo.channels * sizeof (int)) ; 418 sf_close (file) ; 419 420 if (got_chanmap == SF_FALSE) 421 { printf ("Error : File '%s' does not contain channel map information.\n\n", filename) ; 422 free (channel_map) ; 423 return 1 ; 424 } ; 425 426 printf ("File : %s\n\n", filename) ; 427 428 puts (" Chan Position") ; 429 for (k = 0 ; k < sfinfo.channels ; k ++) 430 { const char * name ; 431 432#define CASE_NAME(x) case x : name = #x ; break ; 433 switch (channel_map [k]) 434 { CASE_NAME (SF_CHANNEL_MAP_INVALID) ; 435 CASE_NAME (SF_CHANNEL_MAP_MONO) ; 436 CASE_NAME (SF_CHANNEL_MAP_LEFT) ; 437 CASE_NAME (SF_CHANNEL_MAP_RIGHT) ; 438 CASE_NAME (SF_CHANNEL_MAP_CENTER) ; 439 CASE_NAME (SF_CHANNEL_MAP_FRONT_LEFT) ; 440 CASE_NAME (SF_CHANNEL_MAP_FRONT_RIGHT) ; 441 CASE_NAME (SF_CHANNEL_MAP_FRONT_CENTER) ; 442 CASE_NAME (SF_CHANNEL_MAP_REAR_CENTER) ; 443 CASE_NAME (SF_CHANNEL_MAP_REAR_LEFT) ; 444 CASE_NAME (SF_CHANNEL_MAP_REAR_RIGHT) ; 445 CASE_NAME (SF_CHANNEL_MAP_LFE) ; 446 CASE_NAME (SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER) ; 447 CASE_NAME (SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER) ; 448 CASE_NAME (SF_CHANNEL_MAP_SIDE_LEFT) ; 449 CASE_NAME (SF_CHANNEL_MAP_SIDE_RIGHT) ; 450 CASE_NAME (SF_CHANNEL_MAP_TOP_CENTER) ; 451 CASE_NAME (SF_CHANNEL_MAP_TOP_FRONT_LEFT) ; 452 CASE_NAME (SF_CHANNEL_MAP_TOP_FRONT_RIGHT) ; 453 CASE_NAME (SF_CHANNEL_MAP_TOP_FRONT_CENTER) ; 454 CASE_NAME (SF_CHANNEL_MAP_TOP_REAR_LEFT) ; 455 CASE_NAME (SF_CHANNEL_MAP_TOP_REAR_RIGHT) ; 456 CASE_NAME (SF_CHANNEL_MAP_TOP_REAR_CENTER) ; 457 CASE_NAME (SF_CHANNEL_MAP_MAX) ; 458 default : name = "default" ; 459 break ; 460 } ; 461 462 printf (" %3d %s\n", k, name) ; 463 } ; 464 465 putchar ('\n') ; 466 free (channel_map) ; 467 468 return 0 ; 469} /* chanmap_dump */ 470 471static int 472cart_dump (const char *filename) 473{ SNDFILE *file ; 474 SF_INFO sfinfo ; 475 SF_CART_INFO_VAR (1024) cart ; 476 int got_cart, k ; 477 478 memset (&sfinfo, 0, sizeof (sfinfo)) ; 479 memset (&cart, 0, sizeof (cart)) ; 480 481 if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL) 482 { printf ("Error : Not able to open input file %s.\n", filename) ; 483 fflush (stdout) ; 484 memset (data, 0, sizeof (data)) ; 485 puts (sf_strerror (NULL)) ; 486 return 1 ; 487 } ; 488 489 got_cart = sf_command (file, SFC_GET_CART_INFO, &cart, sizeof (cart)) ; 490 sf_close (file) ; 491 492 if (got_cart == SF_FALSE) 493 { printf ("Error : File '%s' does not contain cart information.\n\n", filename) ; 494 return 1 ; 495 } ; 496 497 printf ("Version : %.*s\n", (int) sizeof (cart.version), cart.version) ; 498 printf ("Title : %.*s\n", (int) sizeof (cart.title), cart.title) ; 499 printf ("Artist : %.*s\n", (int) sizeof (cart.artist), cart.artist) ; 500 printf ("Cut id : %.*s\n", (int) sizeof (cart.cut_id), cart.cut_id) ; 501 printf ("Category : %.*s\n", (int) sizeof (cart.category), cart.category) ; 502 printf ("Classification : %.*s\n", (int) sizeof (cart.classification), cart.classification) ; 503 printf ("Out cue : %.*s\n", (int) sizeof (cart.out_cue), cart.out_cue) ; 504 printf ("Start date : %.*s\n", (int) sizeof (cart.start_date), cart.start_date) ; 505 printf ("Start time : %.*s\n", (int) sizeof (cart.start_time), cart.start_time) ; 506 printf ("End date : %.*s\n", (int) sizeof (cart.end_date), cart.end_date) ; 507 printf ("End time : %.*s\n", (int) sizeof (cart.end_time), cart.end_time) ; 508 printf ("App id : %.*s\n", (int) sizeof (cart.producer_app_id), cart.producer_app_id) ; 509 printf ("App version : %.*s\n", (int) sizeof (cart.producer_app_version), cart.producer_app_version) ; 510 printf ("User defined : %.*s\n", (int) sizeof (cart.user_def), cart.user_def) ; 511 printf ("Level ref. : %d\n", cart.level_reference) ; 512 printf ("Post timers :\n") ; 513 514 for (k = 0 ; k < ARRAY_LEN (cart.post_timers) ; k++) 515 if (cart.post_timers [k].usage [0]) 516 printf (" %d %.*s %d\n", k, (int) sizeof (cart.post_timers [k].usage), cart.post_timers [k].usage, cart.post_timers [k].value) ; 517 518 printf ("Reserved : %.*s\n", (int) sizeof (cart.reserved), cart.reserved) ; 519 printf ("Url : %.*s\n", (int) sizeof (cart.url), cart.url) ; 520 printf ("Tag text : %.*s\n", cart.tag_text_size, cart.tag_text) ; 521 522 return 0 ; 523} /* cart_dump */ 524 525static void 526total_dump (void) 527{ printf ("========================================\n") ; 528 printf ("Total Duration : %s\n", format_duration_str (total_seconds)) ; 529} /* total_dump */ 530