Support 56kHz to 19.2kHz gain analysis (Patch v4)

This implementation uses decimation to generate an estimate of the
required ReplayGain adjustment for tracks sampled at high rates.

This approach avoids having to generate filters with commensurately more taps,
and also the subsequent effect on performance as these additional
taps are evaluated for high sample rate tracks.

Filter table entries with coefficients that are unchanged are
marked /* ORIGINAL */.

The remaining entries are new and have coefficient values obtained
from src/utils/loudness/loudness.sci. See:

        http://lists.xiph.org/pipermail/flac-dev/2012-February/003220.html

Because these filter coefficients can be generated from a known source,
they are preferred to the FooBar2000 coefficients whose provenance is
unknown.

Signed-off-by: Earl Chew <earl_chew@yahoo.com>
This commit is contained in:
Earl Chew 2012-02-22 16:57:54 -08:00 committed by Erik de Castro Lopo
parent 774e0776a5
commit 0554a4aee6
3 changed files with 252 additions and 123 deletions

View File

@ -47,8 +47,8 @@ typedef float Float_t; /* Type used for filtering */
extern Float_t ReplayGainReferenceLoudness; /* in dB SPL, currently == 89.0 */ extern Float_t ReplayGainReferenceLoudness; /* in dB SPL, currently == 89.0 */
int InitGainAnalysis ( long samplefreq ); int InitGainAnalysis ( long samplefreq );
int ValidGainFrequency ( long samplefreq );
int AnalyzeSamples ( const Float_t* left_samples, const Float_t* right_samples, size_t num_samples, int num_channels ); int AnalyzeSamples ( const Float_t* left_samples, const Float_t* right_samples, size_t num_samples, int num_channels );
int ResetSampleFrequency ( long samplefreq );
Float_t GetTitleGain ( void ); Float_t GetTitleGain ( void );
Float_t GetAlbumGain ( void ); Float_t GetAlbumGain ( void );

View File

@ -118,25 +118,7 @@ static FLAC__bool append_tag_(FLAC__StreamMetadata *block, const char *format, c
FLAC__bool grabbag__replaygain_is_valid_sample_frequency(unsigned sample_frequency) FLAC__bool grabbag__replaygain_is_valid_sample_frequency(unsigned sample_frequency)
{ {
static const unsigned valid_sample_rates[] = { return ValidGainFrequency( sample_frequency );
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]);
unsigned i;
for(i = 0; i < n_valid_sample_rates; i++)
if(sample_frequency == valid_sample_rates[i])
return true;
return false;
} }
FLAC__bool grabbag__replaygain_init(unsigned sample_frequency) FLAC__bool grabbag__replaygain_init(unsigned sample_frequency)

View File

@ -110,43 +110,29 @@ typedef signed int Int32_t;
#define YULE_ORDER 10 #define YULE_ORDER 10
#define BUTTER_ORDER 2 #define BUTTER_ORDER 2
#define RMS_PERCENTILE 0.95 /* percentile which is louder than the proposed level */ #define RMS_PERCENTILE 0.95 /* percentile which is louder than the proposed level */
#define MAX_SAMP_FREQ 48000 /* maximum allowed sample frequency [Hz] */
#define RMS_WINDOW_TIME 50 /* Time slice size [ms] */ #define RMS_WINDOW_TIME 50 /* Time slice size [ms] */
#define STEPS_per_dB 100. /* Table entries per dB */ #define STEPS_per_dB 100. /* Table entries per dB */
#define MAX_dB 120. /* Table entries for 0...MAX_dB (normal max. values are 70...80 dB) */ #define MAX_dB 120. /* Table entries for 0...MAX_dB (normal max. values are 70...80 dB) */
#define MAX_ORDER (BUTTER_ORDER > YULE_ORDER ? BUTTER_ORDER : YULE_ORDER) #define MAX_ORDER (BUTTER_ORDER > YULE_ORDER ? BUTTER_ORDER : YULE_ORDER)
/* [JEC] the following was originally #defined as:
* (size_t) (MAX_SAMP_FREQ * RMS_WINDOW_TIME / 1000)
* but that seemed to fail to take into account the ceil() part of the
* sampleWindow calculation in ResetSampleFrequency(), and was causing
* buffer overflows for 48kHz analysis, hence the +1.
*/
/* [JEC] WATCHOUT: if MAX_SAMP_FREQ * RMS_WINDOW_TIME / 1000 is not an
* integer it must be manually rounded up. there is a limit on the
* kind of calculation that can be done in array size definition (e.g.
* Sun Forte compiler cannot handle any floating point terms).
*/
#define MAX_SAMPLES_PER_WINDOW (size_t) (MAX_SAMP_FREQ * RMS_WINDOW_TIME / 1000 + 1) /* max. Samples per Time slice */
#define PINK_REF 64.82 /* 298640883795 */ /* calibration value */ #define PINK_REF 64.82 /* 298640883795 */ /* calibration value */
static Float_t linprebuf [MAX_ORDER * 2]; static Float_t linprebuf [MAX_ORDER * 2];
static Float_t* linpre; /* left input samples, with pre-buffer */ static Float_t* linpre; /* left input samples, with pre-buffer */
static Float_t lstepbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER]; static Float_t* lstepbuf;
static Float_t* lstep; /* left "first step" (i.e. post first filter) samples */ static Float_t* lstep; /* left "first step" (i.e. post first filter) samples */
static Float_t loutbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER]; static Float_t* loutbuf;
static Float_t* lout; /* left "out" (i.e. post second filter) samples */ static Float_t* lout; /* left "out" (i.e. post second filter) samples */
static Float_t rinprebuf [MAX_ORDER * 2]; static Float_t rinprebuf [MAX_ORDER * 2];
static Float_t* rinpre; /* right input samples ... */ static Float_t* rinpre; /* right input samples ... */
static Float_t rstepbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER]; static Float_t* rstepbuf;
static Float_t* rstep; static Float_t* rstep;
static Float_t routbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER]; static Float_t* routbuf;
static Float_t* rout; static Float_t* rout;
static unsigned int sampleWindow; /* number of samples required to reach number of milliseconds required for RMS window */ static unsigned int sampleWindow; /* number of samples required to reach number of milliseconds required for RMS window */
static unsigned long totsamp; static unsigned long totsamp;
static double lsum; static double lsum;
static double rsum; static double rsum;
static int freqindex;
#if 0 #if 0
static Uint32_t A [(size_t)(STEPS_per_dB * MAX_dB)]; static Uint32_t A [(size_t)(STEPS_per_dB * MAX_dB)];
static Uint32_t B [(size_t)(STEPS_per_dB * MAX_dB)]; static Uint32_t B [(size_t)(STEPS_per_dB * MAX_dB)];
@ -156,59 +142,128 @@ static Uint32_t A [120 * 100];
static Uint32_t B [120 * 100]; static Uint32_t B [120 * 100];
#endif #endif
/* for each filter:
[0] 48 kHz, [1] 44.1 kHz, [2] 32 kHz, [3] 24 kHz, [4] 22050 Hz, [5] 16 kHz, [6] 12 kHz, [7] is 11025 Hz, [8] 8 kHz */
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning ( disable : 4305 ) #pragma warning ( disable : 4305 )
#endif #endif
static const Float_t AYule [9] [11] = { struct ReplayGainFilter {
{ 1., -3.84664617118067, 7.81501653005538,-11.34170355132042, 13.05504219327545,-12.28759895145294, 9.48293806319790, -5.87257861775999, 2.75465861874613, -0.86984376593551, 0.13919314567432 }, long rate;
{ 1., -3.47845948550071, 6.36317777566148, -8.54751527471874, 9.47693607801280, -8.81498681370155, 6.85401540936998, -4.39470996079559, 2.19611684890774, -0.75104302451432, 0.13149317958808 }, unsigned downsample;
{ 1., -2.37898834973084, 2.84868151156327, -2.64577170229825, 2.23697657451713, -1.67148153367602, 1.00595954808547, -0.45953458054983, 0.16378164858596, -0.05032077717131, 0.02347897407020 }, Float_t BYule[YULE_ORDER+1];
{ 1., -1.61273165137247, 1.07977492259970, -0.25656257754070, -0.16276719120440, -0.22638893773906, 0.39120800788284, -0.22138138954925, 0.04500235387352, 0.02005851806501, 0.00302439095741 }, Float_t AYule[YULE_ORDER+1];
{ 1., -1.49858979367799, 0.87350271418188, 0.12205022308084, -0.80774944671438, 0.47854794562326, -0.12453458140019, -0.04067510197014, 0.08333755284107, -0.04237348025746, 0.02977207319925 }, Float_t BButter[BUTTER_ORDER+1];
{ 1., -0.62820619233671, 0.29661783706366, -0.37256372942400, 0.00213767857124, -0.42029820170918, 0.22199650564824, 0.00613424350682, 0.06747620744683, 0.05784820375801, 0.03222754072173 }, Float_t AButter[BUTTER_ORDER+1];
{ 1., -1.04800335126349, 0.29156311971249, -0.26806001042947, 0.00819999645858, 0.45054734505008, -0.33032403314006, 0.06739368333110, -0.04784254229033, 0.01639907836189, 0.01807364323573 },
{ 1., -0.51035327095184, -0.31863563325245, -0.20256413484477, 0.14728154134330, 0.38952639978999, -0.23313271880868, -0.05246019024463, -0.02505961724053, 0.02442357316099, 0.01818801111503 },
{ 1., -0.25049871956020, -0.43193942311114, -0.03424681017675, -0.04678328784242, 0.26408300200955, 0.15113130533216, -0.17556493366449, -0.18823009262115, 0.05477720428674, 0.04704409688120 }
}; };
static const Float_t BYule [9] [11] = { static struct ReplayGainFilter *replaygainfilter;
static const struct ReplayGainFilter ReplayGainFilters[] = {
{
48000, 0, /* ORIGINAL */
{ 0.03857599435200, -0.02160367184185, -0.00123395316851, -0.00009291677959, -0.01655260341619, 0.02161526843274, -0.02074045215285, 0.00594298065125, 0.00306428023191, 0.00012025322027, 0.00288463683916 }, { 0.03857599435200, -0.02160367184185, -0.00123395316851, -0.00009291677959, -0.01655260341619, 0.02161526843274, -0.02074045215285, 0.00594298065125, 0.00306428023191, 0.00012025322027, 0.00288463683916 },
{ 0.05418656406430, -0.02911007808948, -0.00848709379851, -0.00851165645469, -0.00834990904936, 0.02245293253339, -0.02596338512915, 0.01624864962975, -0.00240879051584, 0.00674613682247, -0.00187763777362 }, { 1.00000000000000, -3.84664617118067, 7.81501653005538, -11.34170355132042, 13.05504219327545, -12.28759895145294, 9.48293806319790, -5.87257861775999, 2.75465861874613, -0.86984376593551, 0.13919314567432 },
{ 0.15457299681924, -0.09331049056315, -0.06247880153653, 0.02163541888798, -0.05588393329856, 0.04781476674921, 0.00222312597743, 0.03174092540049, -0.01390589421898, 0.00651420667831, -0.00881362733839 },
{ 0.30296907319327, -0.22613988682123, -0.08587323730772, 0.03282930172664, -0.00915702933434, -0.02364141202522, -0.00584456039913, 0.06276101321749, -0.00000828086748, 0.00205861885564, -0.02950134983287 },
{ 0.33642304856132, -0.25572241425570, -0.11828570177555, 0.11921148675203, -0.07834489609479, -0.00469977914380, -0.00589500224440, 0.05724228140351, 0.00832043980773, -0.01635381384540, -0.01760176568150 },
{ 0.44915256608450, -0.14351757464547, -0.22784394429749, -0.01419140100551, 0.04078262797139, -0.12398163381748, 0.04097565135648, 0.10478503600251, -0.01863887810927, -0.03193428438915, 0.00541907748707 },
{ 0.56619470757641, -0.75464456939302, 0.16242137742230, 0.16744243493672, -0.18901604199609, 0.30931782841830, -0.27562961986224, 0.00647310677246, 0.08647503780351, -0.03788984554840, -0.00588215443421 },
{ 0.58100494960553, -0.53174909058578, -0.14289799034253, 0.17520704835522, 0.02377945217615, 0.15558449135573, -0.25344790059353, 0.01628462406333, 0.06920467763959, -0.03721611395801, -0.00749618797172 },
{ 0.53648789255105, -0.42163034350696, -0.00275953611929, 0.04267842219415, -0.10214864179676, 0.14590772289388, -0.02459864859345, -0.11202315195388, -0.04060034127000, 0.04788665548180, -0.02217936801134 }
};
static const Float_t AButter [9] [3] = {
{ 1., -1.97223372919527, 0.97261396931306 },
{ 1., -1.96977855582618, 0.97022847566350 },
{ 1., -1.95835380975398, 0.95920349965459 },
{ 1., -1.95002759149878, 0.95124613669835 },
{ 1., -1.94561023566527, 0.94705070426118 },
{ 1., -1.92783286977036, 0.93034775234268 },
{ 1., -1.91858953033784, 0.92177618768381 },
{ 1., -1.91542108074780, 0.91885558323625 },
{ 1., -1.88903307939452, 0.89487434461664 }
};
static const Float_t BButter [9] [3] = {
{ 0.98621192462708, -1.97242384925416, 0.98621192462708 }, { 0.98621192462708, -1.97242384925416, 0.98621192462708 },
{ 1.00000000000000, -1.97223372919527, 0.97261396931306 },
},
{
44100, 0, /* ORIGINAL */
{ 0.05418656406430, -0.02911007808948, -0.00848709379851, -0.00851165645469, -0.00834990904936, 0.02245293253339, -0.02596338512915, 0.01624864962975, -0.00240879051584, 0.00674613682247, -0.00187763777362 },
{ 1.00000000000000, -3.47845948550071, 6.36317777566148, -8.54751527471874, 9.47693607801280, -8.81498681370155, 6.85401540936998, -4.39470996079559, 2.19611684890774, -0.75104302451432, 0.13149317958808 },
{ 0.98500175787242, -1.97000351574484, 0.98500175787242 }, { 0.98500175787242, -1.97000351574484, 0.98500175787242 },
{ 1.00000000000000, -1.96977855582618, 0.97022847566350 },
},
{
37800, 0,
{ 0.10296717174470, -0.04877975583256, -0.02878009075237, -0.03519509188311, 0.02888717172493, -0.00609872684844, 0.00209851217112, 0.00911704668543, 0.01154404718589, -0.00630293688700, 0.00107527155228 },
{ 1.00000000000000, -2.64848054923531, 3.58406058405771, -3.83794914179161, 3.90142345804575, -3.50179818637243, 2.67085284083076, -1.82581142372418, 1.09530368139801, -0.47689017820395, 0.11171431535905 },
{ 0.98252400815195, -1.96504801630391, 0.98252400815195 },
{ 1.00000000000000, -1.96474258269041, 0.96535344991740 },
},
{
36000, 0,
{ 0.11572297028613, -0.04120916051252, -0.04977731768022, -0.01047308680426, 0.00750863219157, 0.00055507694408, 0.00140344192886, 0.01286095246036, 0.00998223033885, -0.00725013810661, 0.00326503346879 },
{ 1.00000000000000, -2.43606802820871, 3.01907406973844, -2.90372016038192, 2.67947188094303, -2.17606479220391, 1.44912956803015, -0.87785765549050, 0.53592202672557, -0.26469344817509, 0.07495878059717 },
{ 0.98165826840326, -1.96331653680652, 0.98165826840326 },
{ 1.00000000000000, -1.96298008938934, 0.96365298422371 },
},
{
32000, 0, /* ORIGINAL */
{ 0.15457299681924, -0.09331049056315, -0.06247880153653, 0.02163541888798, -0.05588393329856, 0.04781476674921, 0.00222312597743, 0.03174092540049, -0.01390589421898, 0.00651420667831, -0.00881362733839 },
{ 1.00000000000000, -2.37898834973084, 2.84868151156327, -2.64577170229825, 2.23697657451713, -1.67148153367602, 1.00595954808547, -0.45953458054983, 0.16378164858596, -0.05032077717131, 0.02347897407020 },
{ 0.97938932735214, -1.95877865470428, 0.97938932735214 }, { 0.97938932735214, -1.95877865470428, 0.97938932735214 },
{ 1.00000000000000, -1.95835380975398, 0.95920349965459 },
},
{
28000, 0,
{ 0.23882392323383, -0.22007791534089, -0.06014581950332, 0.05004458058021, -0.03293111254977, 0.02348678189717, 0.04290549799671, -0.00938141862174, 0.00015095146303, -0.00712601540885, -0.00626520210162 },
{ 1.00000000000000, -2.06894080899139, 1.76944699577212, -0.81404732584187, 0.25418286850232, -0.30340791669762, 0.35616884070937, -0.14967310591258, -0.07024154183279, 0.11078404345174, -0.03551838002425 },
{ 0.97647981663949, -1.95295963327897, 0.97647981663949 },
{ 1.00000000000000, -1.95240635772520, 0.95351290883275 },
},
{
24000, 0, /* ORIGINAL */
{ 0.30296907319327, -0.22613988682123, -0.08587323730772, 0.03282930172664, -0.00915702933434, -0.02364141202522, -0.00584456039913, 0.06276101321749, -0.00000828086748, 0.00205861885564, -0.02950134983287 },
{ 1.00000000000000, -1.61273165137247, 1.07977492259970, -0.25656257754070, -0.16276719120440, -0.22638893773906, 0.39120800788284, -0.22138138954925, 0.04500235387352, 0.02005851806501, 0.00302439095741 },
{ 0.97531843204928, -1.95063686409857, 0.97531843204928 }, { 0.97531843204928, -1.95063686409857, 0.97531843204928 },
{ 1.00000000000000, -1.95002759149878, 0.95124613669835 },
},
{
22050, 0, /* ORIGINAL */
{ 0.33642304856132, -0.25572241425570, -0.11828570177555, 0.11921148675203, -0.07834489609479, -0.00469977914380, -0.00589500224440, 0.05724228140351, 0.00832043980773, -0.01635381384540, -0.01760176568150 },
{ 1.00000000000000, -1.49858979367799, 0.87350271418188, 0.12205022308084, -0.80774944671438, 0.47854794562326, -0.12453458140019, -0.04067510197014, 0.08333755284107, -0.04237348025746, 0.02977207319925 },
{ 0.97316523498161, -1.94633046996323, 0.97316523498161 }, { 0.97316523498161, -1.94633046996323, 0.97316523498161 },
{ 1.00000000000000, -1.94561023566527, 0.94705070426118 },
},
{
18900, 0,
{ 0.38412657295385, -0.44533729608120, 0.20426638066221, -0.28031676047946, 0.31484202614802, -0.26078311203207, 0.12925201224848, -0.01141164696062, 0.03036522115769, -0.03776339305406, 0.00692036603586 },
{ 1.00000000000000, -1.74403915585708, 1.96686095832499, -2.10081452941881, 1.90753918182846, -1.83814263754422, 1.36971352214969, -0.77883609116398, 0.39266422457649, -0.12529383592986, 0.05424760697665 },
{ 0.96535326815829, -1.93070653631658, 0.96535326815829 },
{ 1.00000000000000, -1.92950577983524, 0.93190729279793 },
},
{
16000, 0, /* ORIGINAL */
{ 0.44915256608450, -0.14351757464547, -0.22784394429749, -0.01419140100551, 0.04078262797139, -0.12398163381748, 0.04097565135648, 0.10478503600251, -0.01863887810927, -0.03193428438915, 0.00541907748707 },
{ 1.00000000000000, -0.62820619233671, 0.29661783706366, -0.37256372942400, 0.00213767857124, -0.42029820170918, 0.22199650564824, 0.00613424350682, 0.06747620744683, 0.05784820375801, 0.03222754072173 },
{ 0.96454515552826, -1.92909031105652, 0.96454515552826 }, { 0.96454515552826, -1.92909031105652, 0.96454515552826 },
{ 1.00000000000000, -1.92783286977036, 0.93034775234268 },
},
{
12000, 0, /* ORIGINAL */
{ 0.56619470757641, -0.75464456939302, 0.16242137742230, 0.16744243493672, -0.18901604199609, 0.30931782841830, -0.27562961986224, 0.00647310677246, 0.08647503780351, -0.03788984554840, -0.00588215443421 },
{ 1.00000000000000, -1.04800335126349, 0.29156311971249, -0.26806001042947, 0.00819999645858, 0.45054734505008, -0.33032403314006, 0.06739368333110, -0.04784254229033, 0.01639907836189, 0.01807364323573 },
{ 0.96009142950541, -1.92018285901082, 0.96009142950541 }, { 0.96009142950541, -1.92018285901082, 0.96009142950541 },
{ 1.00000000000000, -1.91858953033784, 0.92177618768381 },
},
{
11025, 0, /* ORIGINAL */
{ 0.58100494960553, -0.53174909058578, -0.14289799034253, 0.17520704835522, 0.02377945217615, 0.15558449135573, -0.25344790059353, 0.01628462406333, 0.06920467763959, -0.03721611395801, -0.00749618797172 },
{ 1.00000000000000, -0.51035327095184, -0.31863563325245, -0.20256413484477, 0.14728154134330, 0.38952639978999, -0.23313271880868, -0.05246019024463, -0.02505961724053, 0.02442357316099, 0.01818801111503 },
{ 0.95856916599601, -1.91713833199203, 0.95856916599601 }, { 0.95856916599601, -1.91713833199203, 0.95856916599601 },
{ 0.94597685600279, -1.89195371200558, 0.94597685600279 } { 1.00000000000000, -1.91542108074780, 0.91885558323625 },
},
{
8000, 0, /* ORIGINAL */
{ 0.53648789255105, -0.42163034350696, -0.00275953611929, 0.04267842219415, -0.10214864179676, 0.14590772289388, -0.02459864859345, -0.11202315195388, -0.04060034127000, 0.04788665548180, -0.02217936801134 },
{ 1.00000000000000, -0.25049871956020, -0.43193942311114, -0.03424681017675, -0.04678328784242, 0.26408300200955, 0.15113130533216, -0.17556493366449, -0.18823009262115, 0.05477720428674, 0.04704409688120 },
{ 0.94597685600279, -1.89195371200558, 0.94597685600279 },
{ 1.00000000000000, -1.88903307939452, 0.89487434461664 },
},
}; };
#ifdef _MSC_VER #ifdef _MSC_VER
@ -218,45 +273,113 @@ static const Float_t BButter [9] [3] = {
/* When calling this procedure, make sure that ip[-order] and op[-order] point to real data! */ /* When calling this procedure, make sure that ip[-order] and op[-order] point to real data! */
static void static void
filter ( const Float_t* input, Float_t* output, size_t nSamples, const Float_t* a, const Float_t* b, size_t order ) filter ( const Float_t* input, Float_t* output, size_t nSamples, const Float_t* a, const Float_t* b, size_t order, unsigned downsample )
{ {
double y; double y;
size_t i; size_t i;
size_t k; size_t k;
for ( i = 0; i < nSamples; i++ ) { const Float_t* input_head = input;
y = input[i] * b[0]; const Float_t* input_tail;
for ( k = 1; k <= order; k++ )
y += input[i-k] * b[k] - output[i-k] * a[k]; Float_t* output_head = output;
Float_t* output_tail;
for ( i = 0; i < nSamples; i++, input_head += downsample, ++output_head ) {
input_tail = input_head;
output_tail = output_head;
y = *input_head * b[0];
for ( k = 1; k <= order; k++ ) {
input_tail -= downsample;
--output_tail;
y += *input_tail * b[k] - *output_tail * a[k];
}
output[i] = (Float_t)y; output[i] = (Float_t)y;
} }
} }
/* returns a INIT_GAIN_ANALYSIS_OK if successful, INIT_GAIN_ANALYSIS_ERROR if not */ /* returns a INIT_GAIN_ANALYSIS_OK if successful, INIT_GAIN_ANALYSIS_ERROR if not */
int static struct ReplayGainFilter*
CreateGainFilter ( long samplefreq )
{
unsigned i;
long maxrate = 0;
unsigned downsample = 1;
struct ReplayGainFilter* gainfilter = malloc(sizeof(*gainfilter));
if ( !gainfilter )
return 0;
while (1) {
for ( i = 0; i < sizeof(ReplayGainFilters)/sizeof(ReplayGainFilters[0]); ++i ) {
if (maxrate < ReplayGainFilters[i].rate)
maxrate = ReplayGainFilters[i].rate;
if ( ReplayGainFilters[i].rate == samplefreq ) {
*gainfilter = ReplayGainFilters[i];
gainfilter->downsample = downsample;
return gainfilter;
}
}
if (samplefreq < maxrate)
break;
while (samplefreq > maxrate) {
downsample *= 2;
samplefreq /= 2;
}
}
free(gainfilter);
return 0;
}
static void*
ReallocateWindowBuffer(unsigned window_size, Float_t **window_buffer)
{
void *p = realloc(
*window_buffer, sizeof(**window_buffer) * (window_size + MAX_ORDER));
if (p)
*window_buffer = p;
return p;
}
static int
ResetSampleFrequency ( long samplefreq ) { ResetSampleFrequency ( long samplefreq ) {
int i; int i;
free(replaygainfilter);
replaygainfilter = CreateGainFilter( samplefreq );
if ( ! replaygainfilter)
return INIT_GAIN_ANALYSIS_ERROR;
sampleWindow = (int) ceil ((double)samplefreq * (double)RMS_WINDOW_TIME / 1000.0);
sampleWindow =
(replaygainfilter->rate * RMS_WINDOW_TIME + 1000-1) / 1000;
if ( ! ReallocateWindowBuffer(sampleWindow, &lstepbuf) ||
! ReallocateWindowBuffer(sampleWindow, &rstepbuf) ||
! ReallocateWindowBuffer(sampleWindow, &loutbuf) ||
! ReallocateWindowBuffer(sampleWindow, &routbuf) ) {
return INIT_GAIN_ANALYSIS_ERROR;
}
/* zero out initial values */ /* zero out initial values */
for ( i = 0; i < MAX_ORDER; i++ ) for ( i = 0; i < MAX_ORDER; i++ )
linprebuf[i] = lstepbuf[i] = loutbuf[i] = rinprebuf[i] = rstepbuf[i] = routbuf[i] = 0.; linprebuf[i] = lstepbuf[i] = loutbuf[i] = rinprebuf[i] = rstepbuf[i] = routbuf[i] = 0.;
switch ( (int)(samplefreq) ) {
case 48000: freqindex = 0; break;
case 44100: freqindex = 1; break;
case 32000: freqindex = 2; break;
case 24000: freqindex = 3; break;
case 22050: freqindex = 4; break;
case 16000: freqindex = 5; break;
case 12000: freqindex = 6; break;
case 11025: freqindex = 7; break;
case 8000: freqindex = 8; break;
default: return INIT_GAIN_ANALYSIS_ERROR;
}
sampleWindow = (int) ceil ((double)samplefreq * (double)RMS_WINDOW_TIME / 1000.0);
lsum = 0.; lsum = 0.;
rsum = 0.; rsum = 0.;
totsamp = 0; totsamp = 0;
@ -266,6 +389,16 @@ ResetSampleFrequency ( long samplefreq ) {
return INIT_GAIN_ANALYSIS_OK; return INIT_GAIN_ANALYSIS_OK;
} }
int
ValidGainFrequency ( long samplefreq )
{
struct ReplayGainFilter* gainfilter = CreateGainFilter( samplefreq );
free(gainfilter);
return gainfilter != 0;
}
int int
InitGainAnalysis ( long samplefreq ) InitGainAnalysis ( long samplefreq )
{ {
@ -290,13 +423,17 @@ InitGainAnalysis ( long samplefreq )
int int
AnalyzeSamples ( const Float_t* left_samples, const Float_t* right_samples, size_t num_samples, int num_channels ) AnalyzeSamples ( const Float_t* left_samples, const Float_t* right_samples, size_t num_samples, int num_channels )
{ {
unsigned downsample = replaygainfilter->downsample;
const Float_t* curleft; const Float_t* curleft;
const Float_t* curright; const Float_t* curright;
long prebufsamples;
long batchsamples; long batchsamples;
long cursamples; long cursamples;
long cursamplepos; long cursamplepos;
int i; int i;
num_samples /= downsample;
if ( num_samples == 0 ) if ( num_samples == 0 )
return GAIN_ANALYSIS_OK; return GAIN_ANALYSIS_OK;
@ -309,33 +446,35 @@ AnalyzeSamples ( const Float_t* left_samples, const Float_t* right_samples, size
default: return GAIN_ANALYSIS_ERROR; default: return GAIN_ANALYSIS_ERROR;
} }
if ( num_samples < MAX_ORDER ) { prebufsamples = MAX_ORDER;
memcpy ( linprebuf + MAX_ORDER, left_samples , num_samples * sizeof(Float_t) ); if (prebufsamples > num_samples)
memcpy ( rinprebuf + MAX_ORDER, right_samples, num_samples * sizeof(Float_t) ); prebufsamples = num_samples;
}
else { for ( i = 0; i < prebufsamples; ++i ) {
memcpy ( linprebuf + MAX_ORDER, left_samples, MAX_ORDER * sizeof(Float_t) ); linprebuf[i+MAX_ORDER] = left_samples [i * downsample];
memcpy ( rinprebuf + MAX_ORDER, right_samples, MAX_ORDER * sizeof(Float_t) ); rinprebuf[i+MAX_ORDER] = right_samples[i * downsample];
} }
while ( batchsamples > 0 ) { while ( batchsamples > 0 ) {
cursamples = batchsamples > (long)(sampleWindow-totsamp) ? (long)(sampleWindow - totsamp) : batchsamples; cursamples = batchsamples > (long)(sampleWindow-totsamp) ? (long)(sampleWindow - totsamp) : batchsamples;
if ( cursamplepos < MAX_ORDER ) { if ( cursamplepos < MAX_ORDER ) {
downsample = 1;
curleft = linpre+cursamplepos; curleft = linpre+cursamplepos;
curright = rinpre+cursamplepos; curright = rinpre+cursamplepos;
if (cursamples > MAX_ORDER - cursamplepos ) if (cursamples > MAX_ORDER - cursamplepos )
cursamples = MAX_ORDER - cursamplepos; cursamples = MAX_ORDER - cursamplepos;
} }
else { else {
curleft = left_samples + cursamplepos; downsample = replaygainfilter->downsample;
curright = right_samples + cursamplepos; curleft = left_samples + cursamplepos * downsample;
curright = right_samples + cursamplepos * downsample;
} }
filter ( curleft , lstep + totsamp, cursamples, AYule[freqindex], BYule[freqindex], YULE_ORDER ); filter ( curleft , lstep + totsamp, cursamples, replaygainfilter->AYule, replaygainfilter->BYule, YULE_ORDER, downsample );
filter ( curright, rstep + totsamp, cursamples, AYule[freqindex], BYule[freqindex], YULE_ORDER ); filter ( curright, rstep + totsamp, cursamples, replaygainfilter->AYule, replaygainfilter->BYule, YULE_ORDER, downsample );
filter ( lstep + totsamp, lout + totsamp, cursamples, AButter[freqindex], BButter[freqindex], BUTTER_ORDER ); filter ( lstep + totsamp, lout + totsamp, cursamples, replaygainfilter->AButter, replaygainfilter->BButter, BUTTER_ORDER, 1 );
filter ( rstep + totsamp, rout + totsamp, cursamples, AButter[freqindex], BButter[freqindex], BUTTER_ORDER ); filter ( rstep + totsamp, rout + totsamp, cursamples, replaygainfilter->AButter, replaygainfilter->BButter, BUTTER_ORDER, 1 );
for ( i = 0; i < cursamples; i++ ) { /* Get the squared values */ for ( i = 0; i < cursamples; i++ ) { /* Get the squared values */
lsum += lout [totsamp+i] * lout [totsamp+i]; lsum += lout [totsamp+i] * lout [totsamp+i];
@ -361,6 +500,7 @@ AnalyzeSamples ( const Float_t* left_samples, const Float_t* right_samples, size
if ( totsamp > sampleWindow ) /* somehow I really screwed up: Error in programming! Contact author about totsamp > sampleWindow */ if ( totsamp > sampleWindow ) /* somehow I really screwed up: Error in programming! Contact author about totsamp > sampleWindow */
return GAIN_ANALYSIS_ERROR; return GAIN_ANALYSIS_ERROR;
} }
if ( num_samples < MAX_ORDER ) { if ( num_samples < MAX_ORDER ) {
memmove ( linprebuf, linprebuf + num_samples, (MAX_ORDER-num_samples) * sizeof(Float_t) ); memmove ( linprebuf, linprebuf + num_samples, (MAX_ORDER-num_samples) * sizeof(Float_t) );
memmove ( rinprebuf, rinprebuf + num_samples, (MAX_ORDER-num_samples) * sizeof(Float_t) ); memmove ( rinprebuf, rinprebuf + num_samples, (MAX_ORDER-num_samples) * sizeof(Float_t) );
@ -368,8 +508,15 @@ AnalyzeSamples ( const Float_t* left_samples, const Float_t* right_samples, size
memcpy ( rinprebuf + MAX_ORDER - num_samples, right_samples, num_samples * sizeof(Float_t) ); memcpy ( rinprebuf + MAX_ORDER - num_samples, right_samples, num_samples * sizeof(Float_t) );
} }
else { else {
memcpy ( linprebuf, left_samples + num_samples - MAX_ORDER, MAX_ORDER * sizeof(Float_t) ); downsample = replaygainfilter->downsample;
memcpy ( rinprebuf, right_samples + num_samples - MAX_ORDER, MAX_ORDER * sizeof(Float_t) );
left_samples += (num_samples - MAX_ORDER) * downsample;
right_samples += (num_samples - MAX_ORDER) * downsample;
for ( i = 0; i < MAX_ORDER; ++i ) {
linprebuf[i] = left_samples [i * downsample];
rinprebuf[i] = right_samples[i * downsample];
}
} }
return GAIN_ANALYSIS_OK; return GAIN_ANALYSIS_OK;