Git init
[external/libsndfile.git] / programs / sndfile-info.c
1 /*
2 ** Copyright (C) 1999-2009 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
50 static void print_version (void) ;
51 static void print_usage (const char *progname) ;
52
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) ;
58
59 static double total_seconds = 0.0 ;
60
61 int
62 main (int argc, char *argv [])
63 {       int     k ;
64
65         print_version () ;
66
67         if (argc < 2 || strcmp (argv [1], "--help") == 0 || strcmp (argv [1], "-h") == 0)
68         {       char *progname ;
69
70                 progname = strrchr (argv [0], '/') ;
71                 progname = progname ? progname + 1 : argv [0] ;
72
73                 print_usage (progname) ;
74                 return 1 ;
75                 } ;
76
77         if (strcmp (argv [1], "-i") == 0)
78         {       int error = 0 ;
79
80                 for (k = 2 ; k < argc ; k++)
81                         error += instrument_dump (argv [k]) ;
82                 return error ;
83                 } ;
84
85         if (strcmp (argv [1], "-b") == 0)
86         {       int error = 0 ;
87
88                 for (k = 2 ; k < argc ; k++)
89                         error += broadcast_dump (argv [k]) ;
90                 return error ;
91                 } ;
92
93         if (strcmp (argv [1], "-c") == 0)
94         {       int error = 0 ;
95
96                 for (k = 2 ; k < argc ; k++)
97                         error += chanmap_dump (argv [k]) ;
98                 return error ;
99                 } ;
100
101         for (k = 1 ; k < argc ; k++)
102                 info_dump (argv [k]) ;
103
104         if (argc > 2)
105                 total_dump () ;
106
107         return 0 ;
108 } /* main */
109
110 /*==============================================================================
111 **      Print version and usage.
112 */
113
114 static double   data [BUFFER_LEN] ;
115
116 static void
117 print_version (void)
118 {       char buffer [256] ;
119
120         sf_command (NULL, SFC_GET_LIB_VERSION, buffer, sizeof (buffer)) ;
121         printf ("\nVersion : %s\n\n", buffer) ;
122 } /* print_version */
123
124
125 static void
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") ;
137                 fflush (stdout) ;
138
139                 /* This is the officially blessed by microsoft way but I can't get
140                 ** it to link.
141                 **     Sleep (15) ;
142                 ** Instead, use this:
143                 */
144                 Sleep (5 * 1000) ;
145 #endif
146 } /* print_usage */
147
148 /*==============================================================================
149 **      Dumping of sndfile info.
150 */
151
152 static double   data [BUFFER_LEN] ;
153
154 static double
155 get_signal_max (SNDFILE *file)
156 {       double  max ;
157
158         sf_command (file, SFC_CALC_SIGNAL_MAX, &max, sizeof (max)) ;
159
160         return max ;
161 } /* get_signal_max */
162
163 static double
164 calc_decibels (SF_INFO * sfinfo, double max)
165 {       double decibels ;
166
167         switch (sfinfo->format & SF_FORMAT_SUBMASK)
168         {       case SF_FORMAT_PCM_U8 :
169                 case SF_FORMAT_PCM_S8 :
170                         decibels = max / 0x80 ;
171                         break ;
172
173                 case SF_FORMAT_PCM_16 :
174                         decibels = max / 0x8000 ;
175                         break ;
176
177                 case SF_FORMAT_PCM_24 :
178                         decibels = max / 0x800000 ;
179                         break ;
180
181                 case SF_FORMAT_PCM_32 :
182                         decibels = max / 0x80000000 ;
183                         break ;
184
185                 case SF_FORMAT_FLOAT :
186                 case SF_FORMAT_DOUBLE :
187                         decibels = max / 1.0 ;
188                         break ;
189
190                 default :
191                         decibels = max / 0x8000 ;
192                         break ;
193                 } ;
194
195         return 20.0 * log10 (decibels) ;
196 } /* calc_decibels */
197
198 static const char *
199 format_duration_str (double seconds)
200 {       static char str [128] ;
201         int hrs, min ;
202         double sec ;
203
204         memset (str, 0, sizeof (str)) ;
205
206         hrs = (int) (seconds / 3600.0) ;
207         min = (int) ((seconds - (hrs * 3600.0)) / 60.0) ;
208         sec = seconds - (hrs * 3600.0) - (min * 60.0) ;
209
210         snprintf (str, sizeof (str) - 1, "%02d:%02d:%06.3f", hrs, min, sec) ;
211
212         return str ;
213 } /* format_duration_str */
214
215 static const char *
216 generate_duration_str (SF_INFO *sfinfo)
217 {
218         double seconds ;
219
220         if (sfinfo->samplerate < 1)
221                 return NULL ;
222
223         if (sfinfo->frames / sfinfo->samplerate > 0x7FFFFFFF)
224                 return "unknown" ;
225
226         seconds = (1.0 * sfinfo->frames) / sfinfo->samplerate ;
227
228         /* Accumulate the total of all known file durations */
229         total_seconds += seconds ;
230
231         return format_duration_str (seconds) ;
232 } /* generate_duration_str */
233
234 static void
235 info_dump (const char *filename)
236 {       static  char    strbuffer [BUFFER_LEN] ;
237         SNDFILE         *file ;
238         SF_INFO         sfinfo ;
239         double          signal_max, decibels ;
240
241         memset (&sfinfo, 0, sizeof (sfinfo)) ;
242
243         if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL)
244         {       printf ("Error : Not able to open input file %s.\n", filename) ;
245                 fflush (stdout) ;
246                 memset (data, 0, sizeof (data)) ;
247                 sf_command (file, SFC_GET_LOG_INFO, strbuffer, BUFFER_LEN) ;
248                 puts (strbuffer) ;
249                 puts (sf_strerror (NULL)) ;
250                 return ;
251                 } ;
252
253         printf ("========================================\n") ;
254         sf_command (file, SFC_GET_LOG_INFO, strbuffer, BUFFER_LEN) ;
255         puts (strbuffer) ;
256         printf ("----------------------------------------\n") ;
257
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)) ;
265
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) ;
271                 } ;
272         putchar ('\n') ;
273
274         sf_close (file) ;
275
276 } /* info_dump */
277
278 /*==============================================================================
279 **      Dumping of SF_INSTRUMENT data.
280 */
281
282 static const char *
283 str_of_type (int mode)
284 {       switch (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 " ;
289                 default : break ;
290                 } ;
291
292         return "????" ;
293 } /* str_of_mode */
294
295 static int
296 instrument_dump (const char *filename)
297 {       SNDFILE  *file ;
298         SF_INFO  sfinfo ;
299         SF_INSTRUMENT inst ;
300         int got_inst, k ;
301
302         memset (&sfinfo, 0, sizeof (sfinfo)) ;
303
304         if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL)
305         {       printf ("Error : Not able to open input file %s.\n", filename) ;
306                 fflush (stdout) ;
307                 memset (data, 0, sizeof (data)) ;
308                 puts (sf_strerror (NULL)) ;
309                 return 1 ;
310                 } ;
311
312         got_inst = sf_command (file, SFC_GET_INSTRUMENT, &inst, sizeof (inst)) ;
313         sf_close (file) ;
314
315         if (got_inst == SF_FALSE)
316         {       printf ("Error : File '%s' does not contain instrument data.\n\n", filename) ;
317                 return 1 ;
318                 } ;
319
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) ;
326
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) ;
329
330         putchar ('\n') ;
331         return 0 ;
332 } /* instrument_dump */
333
334 static int
335 broadcast_dump (const char *filename)
336 {       SNDFILE  *file ;
337         SF_INFO  sfinfo ;
338         SF_BROADCAST_INFO_2K bext ;
339         double time_ref_sec ;
340         int got_bext ;
341
342         memset (&sfinfo, 0, sizeof (sfinfo)) ;
343
344         if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL)
345         {       printf ("Error : Not able to open input file %s.\n", filename) ;
346                 fflush (stdout) ;
347                 memset (data, 0, sizeof (data)) ;
348                 puts (sf_strerror (NULL)) ;
349                 return 1 ;
350                 } ;
351
352         memset (&bext, 0, sizeof (SF_BROADCAST_INFO_2K)) ;
353
354         got_bext = sf_command (file, SFC_GET_BROADCAST_INFO, &bext, sizeof (bext)) ;
355         sf_close (file) ;
356
357         if (got_bext == SF_FALSE)
358         {       printf ("Error : File '%s' does not contain broadcast information.\n\n", filename) ;
359                 return 1 ;
360                 } ;
361
362         /*
363         **      From : http://www.ebu.ch/en/technical/publications/userguides/bwf_user_guide.php
364         **
365         **      Time Reference:
366         **              This field is a count from midnight in samples to the first sample
367         **              of the audio sequence.
368         */
369
370         time_ref_sec = ((pow (2.0, 32) * bext.time_reference_high) + (1.0 * bext.time_reference_low)) / sfinfo.samplerate ;
371
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) ;
377
378         if (bext.time_reference_high == 0 && bext.time_reference_low == 0)
379                 printf ("Time ref         : 0\n") ;
380         else
381                 printf ("Time ref         : 0x%x%08x (%.6f seconds)\n", bext.time_reference_high, bext.time_reference_low, time_ref_sec) ;
382
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) ;
386
387         return 0 ;
388 } /* broadcast_dump */
389
390 static int
391 chanmap_dump (const char *filename)
392 {       SNDFILE  *file ;
393         SF_INFO  sfinfo ;
394         int * channel_map ;
395         int got_chanmap, k ;
396
397         memset (&sfinfo, 0, sizeof (sfinfo)) ;
398
399         if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL)
400         {       printf ("Error : Not able to open input file %s.\n", filename) ;
401                 fflush (stdout) ;
402                 memset (data, 0, sizeof (data)) ;
403                 puts (sf_strerror (NULL)) ;
404                 return 1 ;
405                 } ;
406
407         if ((channel_map = calloc (sfinfo.channels, sizeof (int))) == NULL)
408         {       printf ("Error : malloc failed.\n\n") ;
409                 return 1 ;
410                 } ;
411
412         got_chanmap = sf_command (file, SFC_GET_CHANNEL_MAP_INFO, channel_map, sfinfo.channels * sizeof (int)) ;
413         sf_close (file) ;
414
415         if (got_chanmap == SF_FALSE)
416         {       printf ("Error : File '%s' does not contain channel map information.\n\n", filename) ;
417                 free (channel_map) ;
418                 return 1 ;
419                 } ;
420
421         printf ("File : %s\n\n", filename) ;
422
423         puts ("    Chan    Position") ;
424         for (k = 0 ; k < sfinfo.channels ; k ++)
425         {       const char * name ;
426
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" ;
454                                 break ;
455                         } ;
456
457                 printf ("    %3d     %s\n", k, name) ;
458                 } ;
459
460         putchar ('\n') ;
461         free (channel_map) ;
462
463         return 0 ;
464 } /* chanmap_dump */
465
466 static void
467 total_dump (void)
468 {       printf ("========================================\n") ;
469         printf ("Total Duration : %s\n", format_duration_str (total_seconds)) ;
470 } /* total_dump */