2 ** Copyright (C) 1999-2011 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 usage_exit (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)
68 { usage_exit (program_name (argv
[0])) ;
72 if (strcmp (argv
[1], "-i") == 0)
75 for (k
= 2 ; k
< argc
; k
++)
76 error
+= instrument_dump (argv
[k
]) ;
80 if (strcmp (argv
[1], "-b") == 0)
83 for (k
= 2 ; k
< argc
; k
++)
84 error
+= broadcast_dump (argv
[k
]) ;
88 if (strcmp (argv
[1], "-c") == 0)
91 for (k
= 2 ; k
< argc
; k
++)
92 error
+= chanmap_dump (argv
[k
]) ;
96 for (k
= 1 ; k
< argc
; k
++)
97 info_dump (argv
[k
]) ;
105 /*==============================================================================
106 ** Print version and usage.
109 static double data
[BUFFER_LEN
] ;
113 { char buffer
[256] ;
115 sf_command (NULL
, SFC_GET_LIB_VERSION
, buffer
, sizeof (buffer
)) ;
116 printf ("\nVersion : %s\n\n", buffer
) ;
117 } /* print_version */
121 usage_exit (const char *progname
)
122 { printf ("Usage :\n %s <file> ...\n", progname
) ;
123 printf (" Prints out information about one or more sound files.\n\n") ;
124 printf (" %s -i <file>\n", progname
) ;
125 printf (" Prints out the instrument data for the given file.\n\n") ;
126 printf (" %s -b <file>\n", progname
) ;
127 printf (" Prints out the broadcast WAV info for the given file.\n\n") ;
128 #if (defined (_WIN32) || defined (WIN32))
129 printf ("This is a Unix style command line application which\n"
130 "should be run in a MSDOS box or Command Shell window.\n\n") ;
131 printf ("Sleeping for 5 seconds before exiting.\n\n") ;
134 /* This is the officially blessed by microsoft way but I can't get
137 ** Instead, use this:
141 printf ("Using %s.\n\n", sf_version_string ()) ;
145 /*==============================================================================
146 ** Dumping of sndfile info.
149 static double data
[BUFFER_LEN
] ;
152 get_signal_max (SNDFILE
*file
)
155 sf_command (file
, SFC_CALC_SIGNAL_MAX
, &max
, sizeof (max
)) ;
158 } /* get_signal_max */
161 calc_decibels (SF_INFO
* sfinfo
, double max
)
164 switch (sfinfo
->format
& SF_FORMAT_SUBMASK
)
165 { case SF_FORMAT_PCM_U8
:
166 case SF_FORMAT_PCM_S8
:
167 decibels
= max
/ 0x80 ;
170 case SF_FORMAT_PCM_16
:
171 decibels
= max
/ 0x8000 ;
174 case SF_FORMAT_PCM_24
:
175 decibels
= max
/ 0x800000 ;
178 case SF_FORMAT_PCM_32
:
179 decibels
= max
/ 0x80000000 ;
182 case SF_FORMAT_FLOAT
:
183 case SF_FORMAT_DOUBLE
:
184 decibels
= max
/ 1.0 ;
188 decibels
= max
/ 0x8000 ;
192 return 20.0 * log10 (decibels
) ;
193 } /* calc_decibels */
196 format_duration_str (double seconds
)
197 { static char str
[128] ;
201 memset (str
, 0, sizeof (str
)) ;
203 hrs
= (int) (seconds
/ 3600.0) ;
204 min
= (int) ((seconds
- (hrs
* 3600.0)) / 60.0) ;
205 sec
= seconds
- (hrs
* 3600.0) - (min
* 60.0) ;
207 snprintf (str
, sizeof (str
) - 1, "%02d:%02d:%06.3f", hrs
, min
, sec
) ;
210 } /* format_duration_str */
213 generate_duration_str (SF_INFO
*sfinfo
)
217 if (sfinfo
->samplerate
< 1)
220 if (sfinfo
->frames
/ sfinfo
->samplerate
> 0x7FFFFFFF)
223 seconds
= (1.0 * sfinfo
->frames
) / sfinfo
->samplerate
;
225 /* Accumulate the total of all known file durations */
226 total_seconds
+= seconds
;
228 return format_duration_str (seconds
) ;
229 } /* generate_duration_str */
232 info_dump (const char *filename
)
233 { static char strbuffer
[BUFFER_LEN
] ;
236 double signal_max
, decibels
;
238 memset (&sfinfo
, 0, sizeof (sfinfo
)) ;
240 if ((file
= sf_open (filename
, SFM_READ
, &sfinfo
)) == NULL
)
241 { printf ("Error : Not able to open input file %s.\n", filename
) ;
243 memset (data
, 0, sizeof (data
)) ;
244 sf_command (file
, SFC_GET_LOG_INFO
, strbuffer
, BUFFER_LEN
) ;
246 puts (sf_strerror (NULL
)) ;
250 printf ("========================================\n") ;
251 sf_command (file
, SFC_GET_LOG_INFO
, strbuffer
, BUFFER_LEN
) ;
253 printf ("----------------------------------------\n") ;
255 printf ("Sample Rate : %d\n", sfinfo
.samplerate
) ;
257 if (sfinfo
.frames
== SF_COUNT_MAX
)
258 printf ("Frames : unknown\n") ;
260 printf ("Frames : %" PRId64
"\n", sfinfo
.frames
) ;
262 printf ("Channels : %d\n", sfinfo
.channels
) ;
263 printf ("Format : 0x%08X\n", sfinfo
.format
) ;
264 printf ("Sections : %d\n", sfinfo
.sections
) ;
265 printf ("Seekable : %s\n", (sfinfo
.seekable
? "TRUE" : "FALSE")) ;
266 printf ("Duration : %s\n", generate_duration_str (&sfinfo
)) ;
268 if (sfinfo
.frames
< 100 * 1024 * 1024)
269 { /* Do not use sf_signal_max because it doesn't work for non-seekable files . */
270 signal_max
= get_signal_max (file
) ;
271 decibels
= calc_decibels (&sfinfo
, signal_max
) ;
272 printf ("Signal Max : %g (%4.2f dB)\n", signal_max
, decibels
) ;
280 /*==============================================================================
281 ** Dumping of SF_INSTRUMENT data.
285 str_of_type (int mode
)
287 { case SF_LOOP_NONE
: return "none" ;
288 case SF_LOOP_FORWARD
: return "fwd " ;
289 case SF_LOOP_BACKWARD
: return "back" ;
290 case SF_LOOP_ALTERNATING
: return "alt " ;
298 instrument_dump (const char *filename
)
304 memset (&sfinfo
, 0, sizeof (sfinfo
)) ;
306 if ((file
= sf_open (filename
, SFM_READ
, &sfinfo
)) == NULL
)
307 { printf ("Error : Not able to open input file %s.\n", filename
) ;
309 memset (data
, 0, sizeof (data
)) ;
310 puts (sf_strerror (NULL
)) ;
314 got_inst
= sf_command (file
, SFC_GET_INSTRUMENT
, &inst
, sizeof (inst
)) ;
317 if (got_inst
== SF_FALSE
)
318 { printf ("Error : File '%s' does not contain instrument data.\n\n", filename
) ;
322 printf ("Instrument : %s\n\n", filename
) ;
323 printf (" Gain : %d\n", inst
.gain
) ;
324 printf (" Base note : %d\n", inst
.basenote
) ;
325 printf (" Velocity : %d - %d\n", (int) inst
.velocity_lo
, (int) inst
.velocity_hi
) ;
326 printf (" Key : %d - %d\n", (int) inst
.key_lo
, (int) inst
.key_hi
) ;
327 printf (" Loop points : %d\n", inst
.loop_count
) ;
329 for (k
= 0 ; k
< inst
.loop_count
; k
++)
330 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
) ;
334 } /* instrument_dump */
337 broadcast_dump (const char *filename
)
340 SF_BROADCAST_INFO_2K bext
;
341 double time_ref_sec
;
344 memset (&sfinfo
, 0, sizeof (sfinfo
)) ;
346 if ((file
= sf_open (filename
, SFM_READ
, &sfinfo
)) == NULL
)
347 { printf ("Error : Not able to open input file %s.\n", filename
) ;
349 memset (data
, 0, sizeof (data
)) ;
350 puts (sf_strerror (NULL
)) ;
354 memset (&bext
, 0, sizeof (SF_BROADCAST_INFO_2K
)) ;
356 got_bext
= sf_command (file
, SFC_GET_BROADCAST_INFO
, &bext
, sizeof (bext
)) ;
359 if (got_bext
== SF_FALSE
)
360 { printf ("Error : File '%s' does not contain broadcast information.\n\n", filename
) ;
365 ** From : http://www.ebu.ch/en/technical/publications/userguides/bwf_user_guide.php
368 ** This field is a count from midnight in samples to the first sample
369 ** of the audio sequence.
372 time_ref_sec
= ((pow (2.0, 32) * bext
.time_reference_high
) + (1.0 * bext
.time_reference_low
)) / sfinfo
.samplerate
;
374 printf ("Description : %.*s\n", (int) sizeof (bext
.description
), bext
.description
) ;
375 printf ("Originator : %.*s\n", (int) sizeof (bext
.originator
), bext
.originator
) ;
376 printf ("Origination ref : %.*s\n", (int) sizeof (bext
.originator_reference
), bext
.originator_reference
) ;
377 printf ("Origination date : %.*s\n", (int) sizeof (bext
.origination_date
), bext
.origination_date
) ;
378 printf ("Origination time : %.*s\n", (int) sizeof (bext
.origination_time
), bext
.origination_time
) ;
380 if (bext
.time_reference_high
== 0 && bext
.time_reference_low
== 0)
381 printf ("Time ref : 0\n") ;
383 printf ("Time ref : 0x%x%08x (%.6f seconds)\n", bext
.time_reference_high
, bext
.time_reference_low
, time_ref_sec
) ;
385 printf ("BWF version : %d\n", bext
.version
) ;
386 printf ("UMID : %.*s\n", (int) sizeof (bext
.umid
), bext
.umid
) ;
387 printf ("Coding history : %.*s\n", bext
.coding_history_size
, bext
.coding_history
) ;
390 } /* broadcast_dump */
393 chanmap_dump (const char *filename
)
399 memset (&sfinfo
, 0, sizeof (sfinfo
)) ;
401 if ((file
= sf_open (filename
, SFM_READ
, &sfinfo
)) == NULL
)
402 { printf ("Error : Not able to open input file %s.\n", filename
) ;
404 memset (data
, 0, sizeof (data
)) ;
405 puts (sf_strerror (NULL
)) ;
409 if ((channel_map
= calloc (sfinfo
.channels
, sizeof (int))) == NULL
)
410 { printf ("Error : malloc failed.\n\n") ;
414 got_chanmap
= sf_command (file
, SFC_GET_CHANNEL_MAP_INFO
, channel_map
, sfinfo
.channels
* sizeof (int)) ;
417 if (got_chanmap
== SF_FALSE
)
418 { printf ("Error : File '%s' does not contain channel map information.\n\n", filename
) ;
423 printf ("File : %s\n\n", filename
) ;
425 puts (" Chan Position") ;
426 for (k
= 0 ; k
< sfinfo
.channels
; k
++)
427 { const char * name
;
429 #define CASE_NAME(x) case x : name = #x ; break ;
430 switch (channel_map
[k
])
431 { CASE_NAME (SF_CHANNEL_MAP_INVALID
) ;
432 CASE_NAME (SF_CHANNEL_MAP_MONO
) ;
433 CASE_NAME (SF_CHANNEL_MAP_LEFT
) ;
434 CASE_NAME (SF_CHANNEL_MAP_RIGHT
) ;
435 CASE_NAME (SF_CHANNEL_MAP_CENTER
) ;
436 CASE_NAME (SF_CHANNEL_MAP_FRONT_LEFT
) ;
437 CASE_NAME (SF_CHANNEL_MAP_FRONT_RIGHT
) ;
438 CASE_NAME (SF_CHANNEL_MAP_FRONT_CENTER
) ;
439 CASE_NAME (SF_CHANNEL_MAP_REAR_CENTER
) ;
440 CASE_NAME (SF_CHANNEL_MAP_REAR_LEFT
) ;
441 CASE_NAME (SF_CHANNEL_MAP_REAR_RIGHT
) ;
442 CASE_NAME (SF_CHANNEL_MAP_LFE
) ;
443 CASE_NAME (SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER
) ;
444 CASE_NAME (SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER
) ;
445 CASE_NAME (SF_CHANNEL_MAP_SIDE_LEFT
) ;
446 CASE_NAME (SF_CHANNEL_MAP_SIDE_RIGHT
) ;
447 CASE_NAME (SF_CHANNEL_MAP_TOP_CENTER
) ;
448 CASE_NAME (SF_CHANNEL_MAP_TOP_FRONT_LEFT
) ;
449 CASE_NAME (SF_CHANNEL_MAP_TOP_FRONT_RIGHT
) ;
450 CASE_NAME (SF_CHANNEL_MAP_TOP_FRONT_CENTER
) ;
451 CASE_NAME (SF_CHANNEL_MAP_TOP_REAR_LEFT
) ;
452 CASE_NAME (SF_CHANNEL_MAP_TOP_REAR_RIGHT
) ;
453 CASE_NAME (SF_CHANNEL_MAP_TOP_REAR_CENTER
) ;
454 CASE_NAME (SF_CHANNEL_MAP_MAX
) ;
455 default : name
= "default" ;
459 printf (" %3d %s\n", k
, name
) ;
470 { printf ("========================================\n") ;
471 printf ("Total Duration : %s\n", format_duration_str (total_seconds
)) ;