first twinges of replaygain support
authorJosh Coalson <jcoalson@users.sourceforce.net>
Fri, 25 Oct 2002 04:56:10 +0000 (04:56 +0000)
committerJosh Coalson <jcoalson@users.sourceforce.net>
Fri, 25 Oct 2002 04:56:10 +0000 (04:56 +0000)
src/flac/Makefile.lite
src/flac/Makefile.vc
src/flac/flac.dsp
src/metaflac/Makefile.lite
src/metaflac/Makefile.vc
src/metaflac/main.c
src/metaflac/metaflac.dsp

index 341ee1c..78d62a2 100644 (file)
@@ -24,16 +24,16 @@ topdir = ../..
 PROGRAM_NAME = flac
 ifeq ($(DARWIN_BUILD),yes)
 INCLUDES     = -I./include -I$(topdir)/include
-LIBS         = -lFLAC -lgain_analysis -lgetopt -lutf8 -lc -lm
+LIBS         = -lFLAC -lreplaygain -lgain_analysis -lgetopt -lutf8 -lc -lm
 else
 #@@@ TODO: conditionalize ogg includes, defines, and -logg
 ifeq ($(SOLARIS_BUILD),yes)
 INCLUDES     = -I./include -I$(topdir)/include -I$(HOME)/local/include -DFLAC__HAS_OGG
-LIBS         = -lOggFLAC -lFLAC -lgain_analysis -lgetopt -lutf8 -lm -L$(HOME)/local/lib -logg
+LIBS         = -lOggFLAC -lFLAC -lreplaygain -lgain_analysis -lgetopt -lutf8 -lm -L$(HOME)/local/lib -logg
 else
 #@@@ TODO: conditionalize ogg includes, defines, and -logg
 INCLUDES     = -I./include -I$(topdir)/include -I$(HOME)/local/include -DFLAC__HAS_OGG
-LIBS         = -lOggFLAC -lFLAC -lgain_analysis -lgetopt -lutf8 -lm -L$(HOME)/local/lib -logg
+LIBS         = -lOggFLAC -lFLAC -lreplaygain -lgain_analysis -lgetopt -lutf8 -lm -L$(HOME)/local/lib -logg
 endif
 endif
 
index 2deadcf..853ad6f 100644 (file)
@@ -40,7 +40,7 @@ OBJS= $(C_FILES:.c=.obj)
 all: flac.exe\r
 \r
 flac.exe: $(OBJS)\r
-       link.exe /libpath:"..\..\obj\lib" -out:../../obj/bin/$*.exe $(OBJS) libOggFLAC.lib libFLAC.lib ogg_static.lib gain_analysis.lib getopt.lib utf8.lib\r
+       link.exe /libpath:"..\..\obj\lib" -out:../../obj/bin/$*.exe $(OBJS) libOggFLAC.lib libFLAC.lib ogg_static.lib replaygain.lib gain_analysis.lib getopt.lib utf8.lib\r
 \r
 clean:\r
        -del *.obj *.pch\r
index ab5a082..6355a19 100644 (file)
@@ -51,7 +51,7 @@ BSC32=bscmake.exe
 # ADD BSC32 /nologo\r
 LINK32=link.exe\r
 # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386\r
-# ADD LINK32 ..\..\obj\lib\libOggFLAC.lib ..\..\obj\lib\libFLAC.lib ..\..\obj\lib\ogg_static.lib ..\..\obj\lib\getopt.lib ..\..\obj\lib\utf8.lib /nologo /subsystem:console /machine:I386\r
+# ADD LINK32 ..\..\obj\lib\libOggFLAC.lib ..\..\obj\lib\libFLAC.lib ..\..\obj\lib\ogg_static.lib ..\..\obj\lib\replaygain.lib ..\..\obj\lib\gain_analysis.lib ..\..\obj\lib\getopt.lib ..\..\obj\lib\utf8.lib /nologo /subsystem:console /machine:I386\r
 \r
 !ELSEIF  "$(CFG)" == "flac - Win32 Debug"\r
 \r
@@ -76,7 +76,7 @@ BSC32=bscmake.exe
 # ADD BSC32 /nologo\r
 LINK32=link.exe\r
 # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
-# ADD LINK32 ..\..\obj\lib\libOggFLAC.lib ..\..\obj\lib\libFLAC.lib ..\..\obj\lib\ogg_static.lib ..\..\obj\lib\getopt.lib ..\..\obj\lib\utf8.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 ..\..\obj\lib\libOggFLAC.lib ..\..\obj\lib\libFLAC.lib ..\..\obj\lib\ogg_static.lib ..\..\obj\lib\replaygain.lib ..\..\obj\lib\gain_analysis.lib ..\..\obj\lib\getopt.lib ..\..\obj\lib\utf8.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
 \r
 !ENDIF \r
 \r
index 0d897ca..7cddcce 100644 (file)
@@ -23,7 +23,7 @@ topdir = ../..
 
 PROGRAM_NAME = metaflac
 INCLUDES     = -I./include -I$(topdir)/include
-LIBS         = -lFLAC -lgain_analysis -lgetopt -lutf8 -lm
+LIBS         = -lFLAC -lreplaygain -lgain_analysis -lgetopt -lutf8 -lm
 
 OBJS = \
        main.o
index 252d566..4b78603 100644 (file)
@@ -33,7 +33,7 @@ OBJS= $(C_FILES:.c=.obj)
 all: metaflac.exe\r
 \r
 metaflac.exe: $(OBJS)\r
-       link.exe /libpath:"..\..\obj\lib" -out:../../obj/bin/$*.exe $(OBJS) libFLAC.lib gain_analysis.lib getopt.lib utf8.lib\r
+       link.exe /libpath:"..\..\obj\lib" -out:../../obj/bin/$*.exe $(OBJS) libFLAC.lib replaygain.lib gain_analysis.lib getopt.lib utf8.lib\r
 \r
 clean:\r
        -del *.obj *.pch\r
index 20e4c0c..fbfe63e 100644 (file)
@@ -27,6 +27,7 @@ more powerful operations yet to add:
 
 #include "FLAC/assert.h"
 #include "FLAC/metadata.h"
+#include "share/replaygain.h"
 #include "share/utf8.h"
 #include <ctype.h>
 #include <locale.h>
@@ -81,6 +82,7 @@ static struct FLAC__share__option long_options_[] = {
        { "set-vc-field", 1, 0, 0 },
        { "import-vc-from", 1, 0, 0 },
        { "export-vc-to", 1, 0, 0 },
+       { "add-replay-gain", 0, 0, 0 },
        { "add-padding", 1, 0, 0 },
        /* major operations */
        { "help", 0, 0, 0 },
@@ -128,6 +130,7 @@ typedef enum {
        OP__SET_VC_FIELD,
        OP__IMPORT_VC_FROM,
        OP__EXPORT_VC_TO,
+       OP__ADD_REPLAY_GAIN,
        OP__ADD_PADDING,
        OP__LIST,
        OP__APPEND,
@@ -287,6 +290,9 @@ static FLAC__bool do_major_operation__remove_all(FLAC__Metadata_Chain *chain, co
 static FLAC__bool do_shorthand_operations(const CommandLineOptions *options);
 static FLAC__bool do_shorthand_operations_on_file(const char *filename, const CommandLineOptions *options);
 static FLAC__bool do_shorthand_operation(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write, FLAC__bool utf8_convert);
+static FLAC__bool do_shorthand_operation__add_replay_gain(char **filenames, unsigned num_files);
+static FLAC__bool do_replaygain_analyze_file(const char *filename, float *title_gain, float *title_peak);
+static FLAC__bool do_replaygain_store_to_file(const char *filename, float album_gain, float album_peak, float title_gain, float title_peak);
 static FLAC__bool do_shorthand_operation__add_padding(const char *filename, FLAC__Metadata_Chain *chain, unsigned length, FLAC__bool *needs_write);
 static FLAC__bool do_shorthand_operation__streaminfo(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write);
 static FLAC__bool do_shorthand_operation__vorbis_comment(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write, FLAC__bool raw);
@@ -613,6 +619,9 @@ FLAC__bool parse_option(int option_index, const char *option_argument, CommandLi
                        ok = false;
                }
        }
+       else if(0 == strcmp(opt, "add-replay-gain")) {
+               (void) append_shorthand_operation(options, OP__ADD_REPLAY_GAIN);
+       }
        else if(0 == strcmp(opt, "add-padding")) {
                op = append_shorthand_operation(options, OP__ADD_PADDING);
                FLAC__ASSERT(0 != option_argument);
@@ -951,6 +960,18 @@ int long_usage(const char *message, ...)
        fprintf(out, "--export-vc-to=file   Export Vorbis comments to a file.  Use '-' for stdin.\n");
        fprintf(out, "                      Each line will be of the form NAME=VALUE.  Specify\n");
        fprintf(out, "                      --no-utf8-convert if necessary.\n");
+       fprintf(out, "--add-replay-gain     Calculates the title and album gains/peaks of the given\n");
+       fprintf(out, "                      FLAC files as if all the files were part of one album,\n");
+       fprintf(out, "                      then stores them in the VORBIS_COMMENT block.  The tags\n");
+       fprintf(out, "                      are the same as those used by vorbisgain.  Existing\n");
+       fprintf(out, "                      ReplayGain tags will be replaced.  If only one FLAC file\n");
+       fprintf(out, "                      is given, the album and title gains will be the same.\n");
+       fprintf(out, "                      Since this operation requires two passes, it is always\n");
+       fprintf(out, "                      executed last, after all other operations have been\n");
+       fprintf(out, "                      completed and written to disk.  All FLAC files specified\n");
+       fprintf(out, "                      must have the same resolution, sample rate, and number\n");
+       fprintf(out, "                      of channels.  The sample rate must be one of 8, 11.025,\n");
+       fprintf(out, "                      12, 16, 22.05, 24, 32, 44.1, or 48 kHz.\n");
        fprintf(out, "--add-padding=length  Add a padding block of the given length (in bytes).\n");
        fprintf(out, "                      The overall length of the new block will be 4 + length;\n");
        fprintf(out, "                      the extra 4 bytes is for the metadata block header.\n");
@@ -1518,6 +1539,14 @@ FLAC__bool do_shorthand_operations(const CommandLineOptions *options)
        for(i = 0; i < options->num_files; i++)
                ok &= do_shorthand_operations_on_file(options->filenames[i], options);
 
+       /* check if OP__ADD_REPLAY_GAIN requested */
+       if(ok && options->num_files > 0) {
+               for(i = 0; i < options->ops.num_operations; i++) {
+                       if(options->ops.operations[i].type == OP__ADD_REPLAY_GAIN)
+                               ok = do_shorthand_operation__add_replay_gain(options->filenames, options->num_files);
+               }
+       }
+
        return ok;
 }
 
@@ -1597,6 +1626,10 @@ FLAC__bool do_shorthand_operation(const char *filename, FLAC__Metadata_Chain *ch
                case OP__EXPORT_VC_TO:
                        ok = do_shorthand_operation__vorbis_comment(filename, chain, operation, needs_write, !utf8_convert);
                        break;
+               case OP__ADD_REPLAY_GAIN:
+                       /* this command is always executed last */
+                       ok = true;
+                       break;
                case OP__ADD_PADDING:
                        ok = do_shorthand_operation__add_padding(filename, chain, operation->argument.add_padding.length, needs_write);
                        break;
@@ -1609,6 +1642,115 @@ FLAC__bool do_shorthand_operation(const char *filename, FLAC__Metadata_Chain *ch
        return ok;
 }
 
+FLAC__bool do_shorthand_operation__add_replay_gain(char **filenames, unsigned num_files)
+{
+       static const unsigned valid_sample_rates[] = {
+               8000,
+               11025,
+               12000,
+               16000,
+               22050,
+               24000,
+               32000,
+               44100,
+               48000
+       };
+       static const unsigned n_valid_sample_rates = sizeof(valid_sample_rates) / sizeof(valid_sample_rates[0]);
+
+       FLAC__StreamMetadata streaminfo;
+       float *title_gains = 0, *title_peaks = 0;
+       float album_gain = 0.0, album_peak = 0.0;
+       unsigned sample_rate = 0;
+       unsigned bits_per_sample = 0;
+       unsigned channels = 0;
+       unsigned i, j;
+       FLAC__bool first = true;
+
+       FLAC__ASSERT(num_files > 0);
+
+       for(i = 0; i < num_files; i++) {
+               FLAC__ASSERT(0 != filenames[i]);
+               if(!FLAC__metadata_get_streaminfo(filenames[i], &streaminfo)) {
+                       fprintf(stderr, "%s: ERROR: can't open file or get STREAMINFO block\n", filenames[i]);
+                       return false;
+               }
+               if(first) {
+                       first = false;
+                       sample_rate = streaminfo.data.stream_info.sample_rate;
+                       bits_per_sample = streaminfo.data.stream_info.bits_per_sample;
+                       channels = streaminfo.data.stream_info.channels;
+               }
+               else {
+                       if(sample_rate != streaminfo.data.stream_info.sample_rate) {
+                               fprintf(stderr, "%s: ERROR: sample rate of %u Hz does not match previous files' %u Hz\n", filenames[i], streaminfo.data.stream_info.sample_rate, sample_rate);
+                               return false;
+                       }
+                       if(bits_per_sample != streaminfo.data.stream_info.bits_per_sample) {
+                               fprintf(stderr, "%s: ERROR: resolution of %u bps does not match previous files' %u bps\n", filenames[i], streaminfo.data.stream_info.bits_per_sample, bits_per_sample);
+                               return false;
+                       }
+                       if(channels != streaminfo.data.stream_info.channels) {
+                               fprintf(stderr, "%s: ERROR: # channels (%u) does not match previous files' (%u)\n", filenames[i], streaminfo.data.stream_info.channels, channels);
+                               return false;
+                       }
+               }
+               for(j = 0; j < n_valid_sample_rates; j++)
+                       if(sample_rate == valid_sample_rates[j])
+                               break;
+               if(j == n_valid_sample_rates) {
+                       fprintf(stderr, "%s: ERROR: sample rate of %u Hz is not supported\n", filenames[i], sample_rate);
+                       return false;
+               }
+               if(channels != 1 && channels != 2) {
+                       fprintf(stderr, "%s: ERROR: # of channels (%u) is not supported, must be 1 or 2\n", filenames[i], channels);
+                       return false;
+               }
+       }
+       FLAC__ASSERT(bits_per_sample >= FLAC__MIN_BITS_PER_SAMPLE && bits_per_sample <= FLAC__MAX_BITS_PER_SAMPLE);
+
+       if(!FLAC__replaygain_init(sample_rate))
+               FLAC__ASSERT(0);
+
+       if(
+               0 == (title_gains = (float*)malloc(sizeof(float) * num_files)) ||
+               0 == (title_peaks = (float*)malloc(sizeof(float) * num_files))
+       )
+               die("out of memory allocating space for title gains/peaks");
+
+       for(i = 0; i < num_files; i++) {
+               if(!do_replaygain_analyze_file(filenames[i], title_gains+i, title_peaks+i)) {
+                       free(title_gains);
+                       free(title_peaks);
+                       return false;
+               }
+               if(title_peaks[i] > album_peak)
+                       album_peak = title_peaks[i];
+       }
+       album_gain = FLAC__replaygain_get_album_gain();
+
+       for(i = 0; i < num_files; i++) {
+               if(!do_replaygain_store_to_file(filenames[i], album_gain, album_peak, title_gains[i], title_peaks[i])) {
+                       free(title_gains);
+                       free(title_peaks);
+                       return false;
+               }
+       }
+
+       free(title_gains);
+       free(title_peaks);
+       return true;
+}
+
+FLAC__bool do_replaygain_analyze_file(const char *filename, float *title_gain, float *title_peak)
+{
+       return false;//@@@@
+}
+
+FLAC__bool do_replaygain_store_to_file(const char *filename, float album_gain, float album_peak, float title_gain, float title_peak)
+{
+       return false;//@@@@
+}
+
 FLAC__bool do_shorthand_operation__add_padding(const char *filename, FLAC__Metadata_Chain *chain, unsigned length, FLAC__bool *needs_write)
 {
        FLAC__StreamMetadata *padding = 0;
index 0aab586..9f4b5a3 100644 (file)
@@ -51,7 +51,7 @@ BSC32=bscmake.exe
 # ADD BSC32 /nologo\r
 LINK32=link.exe\r
 # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386\r
-# ADD LINK32 ..\..\obj\lib\libFLAC.lib ..\..\obj\lib\getopt.lib ..\..\obj\lib\utf8.lib /nologo /subsystem:console /machine:I386\r
+# ADD LINK32 ..\..\obj\lib\libFLAC.lib ..\..\obj\lib\replaygain.lib ..\..\obj\lib\gain_analysis.lib ..\..\obj\lib\getopt.lib ..\..\obj\lib\utf8.lib /nologo /subsystem:console /machine:I386\r
 \r
 !ELSEIF  "$(CFG)" == "metaflac - Win32 Debug"\r
 \r
@@ -76,7 +76,7 @@ BSC32=bscmake.exe
 # ADD BSC32 /nologo\r
 LINK32=link.exe\r
 # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
-# ADD LINK32 ..\..\obj\lib\libFLAC.lib ..\..\obj\lib\getopt.lib ..\..\obj\lib\utf8.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 ..\..\obj\lib\libFLAC.lib ..\..\obj\lib\replaygain.lib ..\..\obj\lib\gain_analysis.lib ..\..\obj\lib\getopt.lib ..\..\obj\lib\utf8.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
 \r
 !ENDIF \r
 \r