2 ** Copyright (C) 1999-2009 Erik de Castro Lopo <erikd@mega-nerd.com>
4 ** All rights reserved.
6 ** Redistribution and use in source and binary forms, with or without
7 ** modification, are permitted provided that the following conditions are
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
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.
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.
44 #define BUFFER_LEN (1 << 16)
46 #if (defined (WIN32) || defined (_WIN32))
50 static void print_version (void) ;
51 static void print_usage (const char *progname) ;
53 static void info_dump (const char *filename) ;
54 static int instrument_dump (const char *filename) ;
55 static int broadcast_dump (const char *filename) ;
56 static int chanmap_dump (const char *filename) ;
57 static void total_dump (void) ;
59 static double total_seconds = 0.0 ;
62 main (int argc, char *argv [])
67 if (argc < 2 || strcmp (argv [1], "--help") == 0 || strcmp (argv [1], "-h") == 0)
70 progname = strrchr (argv [0], '/') ;
71 progname = progname ? progname + 1 : argv [0] ;
73 print_usage (progname) ;
77 if (strcmp (argv [1], "-i") == 0)
80 for (k = 2 ; k < argc ; k++)
81 error += instrument_dump (argv [k]) ;
85 if (strcmp (argv [1], "-b") == 0)
88 for (k = 2 ; k < argc ; k++)
89 error += broadcast_dump (argv [k]) ;
93 if (strcmp (argv [1], "-c") == 0)
96 for (k = 2 ; k < argc ; k++)
97 error += chanmap_dump (argv [k]) ;
101 for (k = 1 ; k < argc ; k++)
102 info_dump (argv [k]) ;
110 /*==============================================================================
111 ** Print version and usage.
114 static double data [BUFFER_LEN] ;
118 { char buffer [256] ;
120 sf_command (NULL, SFC_GET_LIB_VERSION, buffer, sizeof (buffer)) ;
121 printf ("\nVersion : %s\n\n", buffer) ;
122 } /* print_version */
126 print_usage (const char *progname)
127 { printf ("Usage :\n %s <file> ...\n", progname) ;
128 printf (" Prints out information about one or more sound files.\n\n") ;
129 printf (" %s -i <file>\n", progname) ;
130 printf (" Prints out the instrument data for the given file.\n\n") ;
131 printf (" %s -b <file>\n", progname) ;
132 printf (" Prints out the broadcast WAV info for the given file.\n\n") ;
133 #if (defined (_WIN32) || defined (WIN32))
134 printf ("This is a Unix style command line application which\n"
135 "should be run in a MSDOS box or Command Shell window.\n\n") ;
136 printf ("Sleeping for 5 seconds before exiting.\n\n") ;
139 /* This is the officially blessed by microsoft way but I can't get
142 ** Instead, use this:
148 /*==============================================================================
149 ** Dumping of sndfile info.
152 static double data [BUFFER_LEN] ;
155 get_signal_max (SNDFILE *file)
158 sf_command (file, SFC_CALC_SIGNAL_MAX, &max, sizeof (max)) ;
161 } /* get_signal_max */
164 calc_decibels (SF_INFO * sfinfo, double max)
167 switch (sfinfo->format & SF_FORMAT_SUBMASK)
168 { case SF_FORMAT_PCM_U8 :
169 case SF_FORMAT_PCM_S8 :
170 decibels = max / 0x80 ;
173 case SF_FORMAT_PCM_16 :
174 decibels = max / 0x8000 ;
177 case SF_FORMAT_PCM_24 :
178 decibels = max / 0x800000 ;
181 case SF_FORMAT_PCM_32 :
182 decibels = max / 0x80000000 ;
185 case SF_FORMAT_FLOAT :
186 case SF_FORMAT_DOUBLE :
187 decibels = max / 1.0 ;
191 decibels = max / 0x8000 ;
195 return 20.0 * log10 (decibels) ;
196 } /* calc_decibels */
199 format_duration_str (double seconds)
200 { static char str [128] ;
204 memset (str, 0, sizeof (str)) ;
206 hrs = (int) (seconds / 3600.0) ;
207 min = (int) ((seconds - (hrs * 3600.0)) / 60.0) ;
208 sec = seconds - (hrs * 3600.0) - (min * 60.0) ;
210 snprintf (str, sizeof (str) - 1, "%02d:%02d:%06.3f", hrs, min, sec) ;
213 } /* format_duration_str */
216 generate_duration_str (SF_INFO *sfinfo)
220 if (sfinfo->samplerate < 1)
223 if (sfinfo->frames / sfinfo->samplerate > 0x7FFFFFFF)
226 seconds = (1.0 * sfinfo->frames) / sfinfo->samplerate ;
228 /* Accumulate the total of all known file durations */
229 total_seconds += seconds ;
231 return format_duration_str (seconds) ;
232 } /* generate_duration_str */
235 info_dump (const char *filename)
236 { static char strbuffer [BUFFER_LEN] ;
239 double signal_max, decibels ;
241 memset (&sfinfo, 0, sizeof (sfinfo)) ;
243 if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL)
244 { printf ("Error : Not able to open input file %s.\n", filename) ;
246 memset (data, 0, sizeof (data)) ;
247 sf_command (file, SFC_GET_LOG_INFO, strbuffer, BUFFER_LEN) ;
249 puts (sf_strerror (NULL)) ;
253 printf ("========================================\n") ;
254 sf_command (file, SFC_GET_LOG_INFO, strbuffer, BUFFER_LEN) ;
256 printf ("----------------------------------------\n") ;
258 printf ("Sample Rate : %d\n", sfinfo.samplerate) ;
259 printf ("Frames : %" PRId64 "\n", sfinfo.frames) ;
260 printf ("Channels : %d\n", sfinfo.channels) ;
261 printf ("Format : 0x%08X\n", sfinfo.format) ;
262 printf ("Sections : %d\n", sfinfo.sections) ;
263 printf ("Seekable : %s\n", (sfinfo.seekable ? "TRUE" : "FALSE")) ;
264 printf ("Duration : %s\n", generate_duration_str (&sfinfo)) ;
266 if (sfinfo.frames < 100 * 1024 * 1024)
267 { /* Do not use sf_signal_max because it doesn't work for non-seekable files . */
268 signal_max = get_signal_max (file) ;
269 decibels = calc_decibels (&sfinfo, signal_max) ;
270 printf ("Signal Max : %g (%4.2f dB)\n", signal_max, decibels) ;
278 /*==============================================================================
279 ** Dumping of SF_INSTRUMENT data.
283 str_of_type (int mode)
285 { case SF_LOOP_NONE : return "none" ;
286 case SF_LOOP_FORWARD : return "fwd " ;
287 case SF_LOOP_BACKWARD : return "back" ;
288 case SF_LOOP_ALTERNATING : return "alt " ;
296 instrument_dump (const char *filename)
302 memset (&sfinfo, 0, sizeof (sfinfo)) ;
304 if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL)
305 { printf ("Error : Not able to open input file %s.\n", filename) ;
307 memset (data, 0, sizeof (data)) ;
308 puts (sf_strerror (NULL)) ;
312 got_inst = sf_command (file, SFC_GET_INSTRUMENT, &inst, sizeof (inst)) ;
315 if (got_inst == SF_FALSE)
316 { printf ("Error : File '%s' does not contain instrument data.\n\n", filename) ;
320 printf ("Instrument : %s\n\n", filename) ;
321 printf (" Gain : %d\n", inst.gain) ;
322 printf (" Base note : %d\n", inst.basenote) ;
323 printf (" Velocity : %d - %d\n", (int) inst.velocity_lo, (int) inst.velocity_hi) ;
324 printf (" Key : %d - %d\n", (int) inst.key_lo, (int) inst.key_hi) ;
325 printf (" Loop points : %d\n", inst.loop_count) ;
327 for (k = 0 ; k < inst.loop_count ; k++)
328 printf (" %-2d Mode : %s Start : %6d End : %6d Count : %6d\n", k, str_of_type (inst.loops [k].mode), inst.loops [k].start, inst.loops [k].end, inst.loops [k].count) ;
332 } /* instrument_dump */
335 broadcast_dump (const char *filename)
338 SF_BROADCAST_INFO_2K bext ;
339 double time_ref_sec ;
342 memset (&sfinfo, 0, sizeof (sfinfo)) ;
344 if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL)
345 { printf ("Error : Not able to open input file %s.\n", filename) ;
347 memset (data, 0, sizeof (data)) ;
348 puts (sf_strerror (NULL)) ;
352 memset (&bext, 0, sizeof (SF_BROADCAST_INFO_2K)) ;
354 got_bext = sf_command (file, SFC_GET_BROADCAST_INFO, &bext, sizeof (bext)) ;
357 if (got_bext == SF_FALSE)
358 { printf ("Error : File '%s' does not contain broadcast information.\n\n", filename) ;
363 ** From : http://www.ebu.ch/en/technical/publications/userguides/bwf_user_guide.php
366 ** This field is a count from midnight in samples to the first sample
367 ** of the audio sequence.
370 time_ref_sec = ((pow (2.0, 32) * bext.time_reference_high) + (1.0 * bext.time_reference_low)) / sfinfo.samplerate ;
372 printf ("Description : %.*s\n", (int) sizeof (bext.description), bext.description) ;
373 printf ("Originator : %.*s\n", (int) sizeof (bext.originator), bext.originator) ;
374 printf ("Origination ref : %.*s\n", (int) sizeof (bext.originator_reference), bext.originator_reference) ;
375 printf ("Origination date : %.*s\n", (int) sizeof (bext.origination_date), bext.origination_date) ;
376 printf ("Origination time : %.*s\n", (int) sizeof (bext.origination_time), bext.origination_time) ;
378 if (bext.time_reference_high == 0 && bext.time_reference_low == 0)
379 printf ("Time ref : 0\n") ;
381 printf ("Time ref : 0x%x%08x (%.6f seconds)\n", bext.time_reference_high, bext.time_reference_low, time_ref_sec) ;
383 printf ("BWF version : %d\n", bext.version) ;
384 printf ("UMID : %.*s\n", (int) sizeof (bext.umid), bext.umid) ;
385 printf ("Coding history : %.*s\n", bext.coding_history_size, bext.coding_history) ;
388 } /* broadcast_dump */
391 chanmap_dump (const char *filename)
397 memset (&sfinfo, 0, sizeof (sfinfo)) ;
399 if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL)
400 { printf ("Error : Not able to open input file %s.\n", filename) ;
402 memset (data, 0, sizeof (data)) ;
403 puts (sf_strerror (NULL)) ;
407 if ((channel_map = calloc (sfinfo.channels, sizeof (int))) == NULL)
408 { printf ("Error : malloc failed.\n\n") ;
412 got_chanmap = sf_command (file, SFC_GET_CHANNEL_MAP_INFO, channel_map, sfinfo.channels * sizeof (int)) ;
415 if (got_chanmap == SF_FALSE)
416 { printf ("Error : File '%s' does not contain channel map information.\n\n", filename) ;
421 printf ("File : %s\n\n", filename) ;
423 puts (" Chan Position") ;
424 for (k = 0 ; k < sfinfo.channels ; k ++)
425 { const char * name ;
427 #define CASE_NAME(x) case x : name = #x ; break ;
428 switch (channel_map [k])
429 { CASE_NAME (SF_CHANNEL_MAP_INVALID) ;
430 CASE_NAME (SF_CHANNEL_MAP_MONO) ;
431 CASE_NAME (SF_CHANNEL_MAP_LEFT) ;
432 CASE_NAME (SF_CHANNEL_MAP_RIGHT) ;
433 CASE_NAME (SF_CHANNEL_MAP_CENTER) ;
434 CASE_NAME (SF_CHANNEL_MAP_FRONT_LEFT) ;
435 CASE_NAME (SF_CHANNEL_MAP_FRONT_RIGHT) ;
436 CASE_NAME (SF_CHANNEL_MAP_FRONT_CENTER) ;
437 CASE_NAME (SF_CHANNEL_MAP_REAR_CENTER) ;
438 CASE_NAME (SF_CHANNEL_MAP_REAR_LEFT) ;
439 CASE_NAME (SF_CHANNEL_MAP_REAR_RIGHT) ;
440 CASE_NAME (SF_CHANNEL_MAP_LFE) ;
441 CASE_NAME (SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER) ;
442 CASE_NAME (SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER) ;
443 CASE_NAME (SF_CHANNEL_MAP_SIDE_LEFT) ;
444 CASE_NAME (SF_CHANNEL_MAP_SIDE_RIGHT) ;
445 CASE_NAME (SF_CHANNEL_MAP_TOP_CENTER) ;
446 CASE_NAME (SF_CHANNEL_MAP_TOP_FRONT_LEFT) ;
447 CASE_NAME (SF_CHANNEL_MAP_TOP_FRONT_RIGHT) ;
448 CASE_NAME (SF_CHANNEL_MAP_TOP_FRONT_CENTER) ;
449 CASE_NAME (SF_CHANNEL_MAP_TOP_REAR_LEFT) ;
450 CASE_NAME (SF_CHANNEL_MAP_TOP_REAR_RIGHT) ;
451 CASE_NAME (SF_CHANNEL_MAP_TOP_REAR_CENTER) ;
452 CASE_NAME (SF_CHANNEL_MAP_MAX) ;
453 default : name = "default" ;
457 printf (" %3d %s\n", k, name) ;
468 { printf ("========================================\n") ;
469 printf ("Total Duration : %s\n", format_duration_str (total_seconds)) ;