cannam@85: /* cannam@85: ** Copyright (C) 1999-2011 Erik de Castro Lopo cannam@85: ** cannam@85: ** All rights reserved. cannam@85: ** cannam@85: ** Redistribution and use in source and binary forms, with or without cannam@85: ** modification, are permitted provided that the following conditions are cannam@85: ** met: cannam@85: ** cannam@85: ** * Redistributions of source code must retain the above copyright cannam@85: ** notice, this list of conditions and the following disclaimer. cannam@85: ** * Redistributions in binary form must reproduce the above copyright cannam@85: ** notice, this list of conditions and the following disclaimer in cannam@85: ** the documentation and/or other materials provided with the cannam@85: ** distribution. cannam@85: ** * Neither the author nor the names of any contributors may be used cannam@85: ** to endorse or promote products derived from this software without cannam@85: ** specific prior written permission. cannam@85: ** cannam@85: ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS cannam@85: ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED cannam@85: ** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR cannam@85: ** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR cannam@85: ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, cannam@85: ** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, cannam@85: ** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; cannam@85: ** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, cannam@85: ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR cannam@85: ** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF cannam@85: ** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cannam@85: */ cannam@85: cannam@85: #include cannam@85: #include cannam@85: #include cannam@85: #include cannam@85: #include cannam@85: #include cannam@85: cannam@85: #include cannam@85: cannam@85: #include "common.h" cannam@85: cannam@85: #define BUFFER_LEN (1 << 16) cannam@85: cannam@85: #if (defined (WIN32) || defined (_WIN32)) cannam@85: #include cannam@85: #endif cannam@85: cannam@85: static void print_version (void) ; cannam@85: static void usage_exit (const char *progname) ; cannam@85: cannam@85: static void info_dump (const char *filename) ; cannam@85: static int instrument_dump (const char *filename) ; cannam@85: static int broadcast_dump (const char *filename) ; cannam@85: static int chanmap_dump (const char *filename) ; cannam@85: static void total_dump (void) ; cannam@85: cannam@85: static double total_seconds = 0.0 ; cannam@85: cannam@85: int cannam@85: main (int argc, char *argv []) cannam@85: { int k ; cannam@85: cannam@85: print_version () ; cannam@85: cannam@85: if (argc < 2 || strcmp (argv [1], "--help") == 0 || strcmp (argv [1], "-h") == 0) cannam@85: { usage_exit (program_name (argv [0])) ; cannam@85: return 1 ; cannam@85: } ; cannam@85: cannam@85: if (strcmp (argv [1], "-i") == 0) cannam@85: { int error = 0 ; cannam@85: cannam@85: for (k = 2 ; k < argc ; k++) cannam@85: error += instrument_dump (argv [k]) ; cannam@85: return error ; cannam@85: } ; cannam@85: cannam@85: if (strcmp (argv [1], "-b") == 0) cannam@85: { int error = 0 ; cannam@85: cannam@85: for (k = 2 ; k < argc ; k++) cannam@85: error += broadcast_dump (argv [k]) ; cannam@85: return error ; cannam@85: } ; cannam@85: cannam@85: if (strcmp (argv [1], "-c") == 0) cannam@85: { int error = 0 ; cannam@85: cannam@85: for (k = 2 ; k < argc ; k++) cannam@85: error += chanmap_dump (argv [k]) ; cannam@85: return error ; cannam@85: } ; cannam@85: cannam@85: for (k = 1 ; k < argc ; k++) cannam@85: info_dump (argv [k]) ; cannam@85: cannam@85: if (argc > 2) cannam@85: total_dump () ; cannam@85: cannam@85: return 0 ; cannam@85: } /* main */ cannam@85: cannam@85: /*============================================================================== cannam@85: ** Print version and usage. cannam@85: */ cannam@85: cannam@85: static double data [BUFFER_LEN] ; cannam@85: cannam@85: static void cannam@85: print_version (void) cannam@85: { char buffer [256] ; cannam@85: cannam@85: sf_command (NULL, SFC_GET_LIB_VERSION, buffer, sizeof (buffer)) ; cannam@85: printf ("\nVersion : %s\n\n", buffer) ; cannam@85: } /* print_version */ cannam@85: cannam@85: cannam@85: static void cannam@85: usage_exit (const char *progname) cannam@85: { printf ("Usage :\n %s ...\n", progname) ; cannam@85: printf (" Prints out information about one or more sound files.\n\n") ; cannam@85: printf (" %s -i \n", progname) ; cannam@85: printf (" Prints out the instrument data for the given file.\n\n") ; cannam@85: printf (" %s -b \n", progname) ; cannam@85: printf (" Prints out the broadcast WAV info for the given file.\n\n") ; cannam@85: #if (defined (_WIN32) || defined (WIN32)) cannam@85: printf ("This is a Unix style command line application which\n" cannam@85: "should be run in a MSDOS box or Command Shell window.\n\n") ; cannam@85: printf ("Sleeping for 5 seconds before exiting.\n\n") ; cannam@85: fflush (stdout) ; cannam@85: cannam@85: /* This is the officially blessed by microsoft way but I can't get cannam@85: ** it to link. cannam@85: ** Sleep (15) ; cannam@85: ** Instead, use this: cannam@85: */ cannam@85: Sleep (5 * 1000) ; cannam@85: #endif cannam@85: printf ("Using %s.\n\n", sf_version_string ()) ; cannam@85: exit (0) ; cannam@85: } /* usage_exit */ cannam@85: cannam@85: /*============================================================================== cannam@85: ** Dumping of sndfile info. cannam@85: */ cannam@85: cannam@85: static double data [BUFFER_LEN] ; cannam@85: cannam@85: static double cannam@85: get_signal_max (SNDFILE *file) cannam@85: { double max ; cannam@85: cannam@85: sf_command (file, SFC_CALC_SIGNAL_MAX, &max, sizeof (max)) ; cannam@85: cannam@85: return max ; cannam@85: } /* get_signal_max */ cannam@85: cannam@85: static double cannam@85: calc_decibels (SF_INFO * sfinfo, double max) cannam@85: { double decibels ; cannam@85: cannam@85: switch (sfinfo->format & SF_FORMAT_SUBMASK) cannam@85: { case SF_FORMAT_PCM_U8 : cannam@85: case SF_FORMAT_PCM_S8 : cannam@85: decibels = max / 0x80 ; cannam@85: break ; cannam@85: cannam@85: case SF_FORMAT_PCM_16 : cannam@85: decibels = max / 0x8000 ; cannam@85: break ; cannam@85: cannam@85: case SF_FORMAT_PCM_24 : cannam@85: decibels = max / 0x800000 ; cannam@85: break ; cannam@85: cannam@85: case SF_FORMAT_PCM_32 : cannam@85: decibels = max / 0x80000000 ; cannam@85: break ; cannam@85: cannam@85: case SF_FORMAT_FLOAT : cannam@85: case SF_FORMAT_DOUBLE : cannam@85: decibels = max / 1.0 ; cannam@85: break ; cannam@85: cannam@85: default : cannam@85: decibels = max / 0x8000 ; cannam@85: break ; cannam@85: } ; cannam@85: cannam@85: return 20.0 * log10 (decibels) ; cannam@85: } /* calc_decibels */ cannam@85: cannam@85: static const char * cannam@85: format_duration_str (double seconds) cannam@85: { static char str [128] ; cannam@85: int hrs, min ; cannam@85: double sec ; cannam@85: cannam@85: memset (str, 0, sizeof (str)) ; cannam@85: cannam@85: hrs = (int) (seconds / 3600.0) ; cannam@85: min = (int) ((seconds - (hrs * 3600.0)) / 60.0) ; cannam@85: sec = seconds - (hrs * 3600.0) - (min * 60.0) ; cannam@85: cannam@85: snprintf (str, sizeof (str) - 1, "%02d:%02d:%06.3f", hrs, min, sec) ; cannam@85: cannam@85: return str ; cannam@85: } /* format_duration_str */ cannam@85: cannam@85: static const char * cannam@85: generate_duration_str (SF_INFO *sfinfo) cannam@85: { cannam@85: double seconds ; cannam@85: cannam@85: if (sfinfo->samplerate < 1) cannam@85: return NULL ; cannam@85: cannam@85: if (sfinfo->frames / sfinfo->samplerate > 0x7FFFFFFF) cannam@85: return "unknown" ; cannam@85: cannam@85: seconds = (1.0 * sfinfo->frames) / sfinfo->samplerate ; cannam@85: cannam@85: /* Accumulate the total of all known file durations */ cannam@85: total_seconds += seconds ; cannam@85: cannam@85: return format_duration_str (seconds) ; cannam@85: } /* generate_duration_str */ cannam@85: cannam@85: static void cannam@85: info_dump (const char *filename) cannam@85: { static char strbuffer [BUFFER_LEN] ; cannam@85: SNDFILE *file ; cannam@85: SF_INFO sfinfo ; cannam@85: double signal_max, decibels ; cannam@85: cannam@85: memset (&sfinfo, 0, sizeof (sfinfo)) ; cannam@85: cannam@85: if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL) cannam@85: { printf ("Error : Not able to open input file %s.\n", filename) ; cannam@85: fflush (stdout) ; cannam@85: memset (data, 0, sizeof (data)) ; cannam@85: sf_command (file, SFC_GET_LOG_INFO, strbuffer, BUFFER_LEN) ; cannam@85: puts (strbuffer) ; cannam@85: puts (sf_strerror (NULL)) ; cannam@85: return ; cannam@85: } ; cannam@85: cannam@85: printf ("========================================\n") ; cannam@85: sf_command (file, SFC_GET_LOG_INFO, strbuffer, BUFFER_LEN) ; cannam@85: puts (strbuffer) ; cannam@85: printf ("----------------------------------------\n") ; cannam@85: cannam@85: printf ("Sample Rate : %d\n", sfinfo.samplerate) ; cannam@85: cannam@85: if (sfinfo.frames == SF_COUNT_MAX) cannam@85: printf ("Frames : unknown\n") ; cannam@85: else cannam@85: printf ("Frames : %" PRId64 "\n", sfinfo.frames) ; cannam@85: cannam@85: printf ("Channels : %d\n", sfinfo.channels) ; cannam@85: printf ("Format : 0x%08X\n", sfinfo.format) ; cannam@85: printf ("Sections : %d\n", sfinfo.sections) ; cannam@85: printf ("Seekable : %s\n", (sfinfo.seekable ? "TRUE" : "FALSE")) ; cannam@85: printf ("Duration : %s\n", generate_duration_str (&sfinfo)) ; cannam@85: cannam@85: if (sfinfo.frames < 100 * 1024 * 1024) cannam@85: { /* Do not use sf_signal_max because it doesn't work for non-seekable files . */ cannam@85: signal_max = get_signal_max (file) ; cannam@85: decibels = calc_decibels (&sfinfo, signal_max) ; cannam@85: printf ("Signal Max : %g (%4.2f dB)\n", signal_max, decibels) ; cannam@85: } ; cannam@85: putchar ('\n') ; cannam@85: cannam@85: sf_close (file) ; cannam@85: cannam@85: } /* info_dump */ cannam@85: cannam@85: /*============================================================================== cannam@85: ** Dumping of SF_INSTRUMENT data. cannam@85: */ cannam@85: cannam@85: static const char * cannam@85: str_of_type (int mode) cannam@85: { switch (mode) cannam@85: { case SF_LOOP_NONE : return "none" ; cannam@85: case SF_LOOP_FORWARD : return "fwd " ; cannam@85: case SF_LOOP_BACKWARD : return "back" ; cannam@85: case SF_LOOP_ALTERNATING : return "alt " ; cannam@85: default : break ; cannam@85: } ; cannam@85: cannam@85: return "????" ; cannam@85: } /* str_of_mode */ cannam@85: cannam@85: static int cannam@85: instrument_dump (const char *filename) cannam@85: { SNDFILE *file ; cannam@85: SF_INFO sfinfo ; cannam@85: SF_INSTRUMENT inst ; cannam@85: int got_inst, k ; cannam@85: cannam@85: memset (&sfinfo, 0, sizeof (sfinfo)) ; cannam@85: cannam@85: if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL) cannam@85: { printf ("Error : Not able to open input file %s.\n", filename) ; cannam@85: fflush (stdout) ; cannam@85: memset (data, 0, sizeof (data)) ; cannam@85: puts (sf_strerror (NULL)) ; cannam@85: return 1 ; cannam@85: } ; cannam@85: cannam@85: got_inst = sf_command (file, SFC_GET_INSTRUMENT, &inst, sizeof (inst)) ; cannam@85: sf_close (file) ; cannam@85: cannam@85: if (got_inst == SF_FALSE) cannam@85: { printf ("Error : File '%s' does not contain instrument data.\n\n", filename) ; cannam@85: return 1 ; cannam@85: } ; cannam@85: cannam@85: printf ("Instrument : %s\n\n", filename) ; cannam@85: printf (" Gain : %d\n", inst.gain) ; cannam@85: printf (" Base note : %d\n", inst.basenote) ; cannam@85: printf (" Velocity : %d - %d\n", (int) inst.velocity_lo, (int) inst.velocity_hi) ; cannam@85: printf (" Key : %d - %d\n", (int) inst.key_lo, (int) inst.key_hi) ; cannam@85: printf (" Loop points : %d\n", inst.loop_count) ; cannam@85: cannam@85: for (k = 0 ; k < inst.loop_count ; k++) cannam@85: 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) ; cannam@85: cannam@85: putchar ('\n') ; cannam@85: return 0 ; cannam@85: } /* instrument_dump */ cannam@85: cannam@85: static int cannam@85: broadcast_dump (const char *filename) cannam@85: { SNDFILE *file ; cannam@85: SF_INFO sfinfo ; cannam@85: SF_BROADCAST_INFO_2K bext ; cannam@85: double time_ref_sec ; cannam@85: int got_bext ; cannam@85: cannam@85: memset (&sfinfo, 0, sizeof (sfinfo)) ; cannam@85: cannam@85: if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL) cannam@85: { printf ("Error : Not able to open input file %s.\n", filename) ; cannam@85: fflush (stdout) ; cannam@85: memset (data, 0, sizeof (data)) ; cannam@85: puts (sf_strerror (NULL)) ; cannam@85: return 1 ; cannam@85: } ; cannam@85: cannam@85: memset (&bext, 0, sizeof (SF_BROADCAST_INFO_2K)) ; cannam@85: cannam@85: got_bext = sf_command (file, SFC_GET_BROADCAST_INFO, &bext, sizeof (bext)) ; cannam@85: sf_close (file) ; cannam@85: cannam@85: if (got_bext == SF_FALSE) cannam@85: { printf ("Error : File '%s' does not contain broadcast information.\n\n", filename) ; cannam@85: return 1 ; cannam@85: } ; cannam@85: cannam@85: /* cannam@85: ** From : http://www.ebu.ch/en/technical/publications/userguides/bwf_user_guide.php cannam@85: ** cannam@85: ** Time Reference: cannam@85: ** This field is a count from midnight in samples to the first sample cannam@85: ** of the audio sequence. cannam@85: */ cannam@85: cannam@85: time_ref_sec = ((pow (2.0, 32) * bext.time_reference_high) + (1.0 * bext.time_reference_low)) / sfinfo.samplerate ; cannam@85: cannam@85: printf ("Description : %.*s\n", (int) sizeof (bext.description), bext.description) ; cannam@85: printf ("Originator : %.*s\n", (int) sizeof (bext.originator), bext.originator) ; cannam@85: printf ("Origination ref : %.*s\n", (int) sizeof (bext.originator_reference), bext.originator_reference) ; cannam@85: printf ("Origination date : %.*s\n", (int) sizeof (bext.origination_date), bext.origination_date) ; cannam@85: printf ("Origination time : %.*s\n", (int) sizeof (bext.origination_time), bext.origination_time) ; cannam@85: cannam@85: if (bext.time_reference_high == 0 && bext.time_reference_low == 0) cannam@85: printf ("Time ref : 0\n") ; cannam@85: else cannam@85: printf ("Time ref : 0x%x%08x (%.6f seconds)\n", bext.time_reference_high, bext.time_reference_low, time_ref_sec) ; cannam@85: cannam@85: printf ("BWF version : %d\n", bext.version) ; cannam@85: printf ("UMID : %.*s\n", (int) sizeof (bext.umid), bext.umid) ; cannam@85: printf ("Coding history : %.*s\n", bext.coding_history_size, bext.coding_history) ; cannam@85: cannam@85: return 0 ; cannam@85: } /* broadcast_dump */ cannam@85: cannam@85: static int cannam@85: chanmap_dump (const char *filename) cannam@85: { SNDFILE *file ; cannam@85: SF_INFO sfinfo ; cannam@85: int * channel_map ; cannam@85: int got_chanmap, k ; cannam@85: cannam@85: memset (&sfinfo, 0, sizeof (sfinfo)) ; cannam@85: cannam@85: if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL) cannam@85: { printf ("Error : Not able to open input file %s.\n", filename) ; cannam@85: fflush (stdout) ; cannam@85: memset (data, 0, sizeof (data)) ; cannam@85: puts (sf_strerror (NULL)) ; cannam@85: return 1 ; cannam@85: } ; cannam@85: cannam@85: if ((channel_map = calloc (sfinfo.channels, sizeof (int))) == NULL) cannam@85: { printf ("Error : malloc failed.\n\n") ; cannam@85: return 1 ; cannam@85: } ; cannam@85: cannam@85: got_chanmap = sf_command (file, SFC_GET_CHANNEL_MAP_INFO, channel_map, sfinfo.channels * sizeof (int)) ; cannam@85: sf_close (file) ; cannam@85: cannam@85: if (got_chanmap == SF_FALSE) cannam@85: { printf ("Error : File '%s' does not contain channel map information.\n\n", filename) ; cannam@85: free (channel_map) ; cannam@85: return 1 ; cannam@85: } ; cannam@85: cannam@85: printf ("File : %s\n\n", filename) ; cannam@85: cannam@85: puts (" Chan Position") ; cannam@85: for (k = 0 ; k < sfinfo.channels ; k ++) cannam@85: { const char * name ; cannam@85: cannam@85: #define CASE_NAME(x) case x : name = #x ; break ; cannam@85: switch (channel_map [k]) cannam@85: { CASE_NAME (SF_CHANNEL_MAP_INVALID) ; cannam@85: CASE_NAME (SF_CHANNEL_MAP_MONO) ; cannam@85: CASE_NAME (SF_CHANNEL_MAP_LEFT) ; cannam@85: CASE_NAME (SF_CHANNEL_MAP_RIGHT) ; cannam@85: CASE_NAME (SF_CHANNEL_MAP_CENTER) ; cannam@85: CASE_NAME (SF_CHANNEL_MAP_FRONT_LEFT) ; cannam@85: CASE_NAME (SF_CHANNEL_MAP_FRONT_RIGHT) ; cannam@85: CASE_NAME (SF_CHANNEL_MAP_FRONT_CENTER) ; cannam@85: CASE_NAME (SF_CHANNEL_MAP_REAR_CENTER) ; cannam@85: CASE_NAME (SF_CHANNEL_MAP_REAR_LEFT) ; cannam@85: CASE_NAME (SF_CHANNEL_MAP_REAR_RIGHT) ; cannam@85: CASE_NAME (SF_CHANNEL_MAP_LFE) ; cannam@85: CASE_NAME (SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER) ; cannam@85: CASE_NAME (SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER) ; cannam@85: CASE_NAME (SF_CHANNEL_MAP_SIDE_LEFT) ; cannam@85: CASE_NAME (SF_CHANNEL_MAP_SIDE_RIGHT) ; cannam@85: CASE_NAME (SF_CHANNEL_MAP_TOP_CENTER) ; cannam@85: CASE_NAME (SF_CHANNEL_MAP_TOP_FRONT_LEFT) ; cannam@85: CASE_NAME (SF_CHANNEL_MAP_TOP_FRONT_RIGHT) ; cannam@85: CASE_NAME (SF_CHANNEL_MAP_TOP_FRONT_CENTER) ; cannam@85: CASE_NAME (SF_CHANNEL_MAP_TOP_REAR_LEFT) ; cannam@85: CASE_NAME (SF_CHANNEL_MAP_TOP_REAR_RIGHT) ; cannam@85: CASE_NAME (SF_CHANNEL_MAP_TOP_REAR_CENTER) ; cannam@85: CASE_NAME (SF_CHANNEL_MAP_MAX) ; cannam@85: default : name = "default" ; cannam@85: break ; cannam@85: } ; cannam@85: cannam@85: printf (" %3d %s\n", k, name) ; cannam@85: } ; cannam@85: cannam@85: putchar ('\n') ; cannam@85: free (channel_map) ; cannam@85: cannam@85: return 0 ; cannam@85: } /* chanmap_dump */ cannam@85: cannam@85: static void cannam@85: total_dump (void) cannam@85: { printf ("========================================\n") ; cannam@85: printf ("Total Duration : %s\n", format_duration_str (total_seconds)) ; cannam@85: } /* total_dump */