Skip to content

Commit 6ddbb1d

Browse files
committedJun 1, 2017
Main developements from private repo.
1 parent 5527e9f commit 6ddbb1d

39 files changed

+1385
-354
lines changed
 

‎src/models/DynamicLoudnessCH2012.cpp

+102-45
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@
2828
#include "../modules/CompressSpectrum.h"
2929
#include "../modules/WeightSpectrum.h"
3030
#include "../modules/DoubleRoexBank.h"
31+
#include "../modules/MultiSourceDoubleRoexBank.h"
32+
#include "../modules/SpecificPartialLoudnessCHGM2011.h"
3133
#include "../modules/BinauralInhibitionMG2007.h"
3234
#include "../modules/InstantaneousLoudness.h"
3335
#include "../modules/ARAverager.h"
34-
#include "../modules/PeakFollower.h"
3536
#include "DynamicLoudnessCH2012.h"
3637

3738
namespace loudness{
@@ -74,11 +75,6 @@ namespace loudness{
7475
isPresentationDiotic_ = isPresentationDiotic;
7576
}
7677

77-
void DynamicLoudnessCH2012::setPeakSTLFollowerUsed(bool isPeakSTLFollowerUsed)
78-
{
79-
isPeakSTLFollowerUsed_ = isPeakSTLFollowerUsed;
80-
}
81-
8278
void DynamicLoudnessCH2012::setBinauralInhibitionUsed(bool isBinauralInhibitionUsed)
8379
{
8480
isBinauralInhibitionUsed_ = isBinauralInhibitionUsed;
@@ -124,24 +120,52 @@ namespace loudness{
124120
pathToFilterCoefs_ = pathToFilterCoefs;
125121
}
126122

123+
void DynamicLoudnessCH2012::setPartialLoudnessUsed (bool isPartialLoudnessUsed)
124+
{
125+
isPartialLoudnessUsed_ = isPartialLoudnessUsed;
126+
}
127+
128+
void DynamicLoudnessCH2012::setWindowSpecGM02 (bool isWindowSpecGM02)
129+
{
130+
isWindowSpecGM02_ = isWindowSpecGM02;
131+
}
132+
133+
void DynamicLoudnessCH2012::setScalingFactor (Real scalingFactor)
134+
{
135+
scalingFactor_ = scalingFactor;
136+
}
137+
138+
void DynamicLoudnessCH2012::setAttackTimeSTL (Real attackTimeSTL)
139+
{
140+
attackTimeSTL_ = attackTimeSTL;
141+
}
142+
143+
void DynamicLoudnessCH2012::setReleaseTimeSTL (Real releaseTimeSTL)
144+
{
145+
releaseTimeSTL_ = releaseTimeSTL;
146+
}
147+
127148
void DynamicLoudnessCH2012::configureModelParameters(const string& setName)
128149
{
129150
//common to all
130-
setRate(1000);
131-
setOuterEarFilter(OME::ANSIS342007_FREEFIELD);
132-
setMiddleEarFilter(OME::CHGM2011_MIDDLE_EAR);
133-
setSpectrumSampledUniformly(true);
134-
setHoppingGoertzelDFTUsed(false);
135-
setExcitationPatternInterpolated(false);
136-
setInterpolationCubic(true);
137-
setSpecificLoudnessOutput(true);
138-
setBinauralInhibitionUsed(true);
139-
setPresentationDiotic(true);
140-
setFirstSampleAtWindowCentre(true);
141-
setFilterSpacingInCams(0.1);
142-
setCompressionCriterionInCams(0.0);
143-
attackTimeSTL_ = 0.016;
144-
releaseTimeSTL_ = 0.032;
151+
setRate (1000);
152+
setOuterEarFilter (OME::ANSIS342007_FREEFIELD);
153+
setMiddleEarFilter (OME::CHGM2011_MIDDLE_EAR);
154+
setSpectrumSampledUniformly (true);
155+
setHoppingGoertzelDFTUsed (false);
156+
setExcitationPatternInterpolated (false);
157+
setInterpolationCubic (true);
158+
setPartialLoudnessUsed (false);
159+
setSpecificLoudnessOutput (true);
160+
setBinauralInhibitionUsed (true);
161+
setPresentationDiotic (true);
162+
setFirstSampleAtWindowCentre (true);
163+
setFilterSpacingInCams (0.1);
164+
setCompressionCriterionInCams (0.0);
165+
setWindowSpecGM02 (false);
166+
setScalingFactor (1.53e-8);
167+
setAttackTimeSTL (0.016);
168+
setReleaseTimeSTL (0.032);
145169
attackTimeLTL_ = 0.1;
146170
releaseTimeLTL_ = 2.0;
147171

@@ -205,7 +229,11 @@ namespace loudness{
205229
RealVec bandFreqsHz {10, 80, 500, 1250, 2540, 4050, 16001};
206230

207231
//window spec
208-
RealVec windowSizeSecs {0.128, 0.064, 0.032, 0.016, 0.008, 0.004};
232+
RealVec windowSizeSecs;
233+
if (isWindowSpecGM02_)
234+
windowSizeSecs = {0.064, 0.032, 0.016, 0.008, 0.004, 0.002};
235+
else
236+
windowSizeSecs = {0.128, 0.064, 0.032, 0.016, 0.008, 0.004};
209237
vector<int> windowSizeSamples(6,0);
210238
//round to nearest sample and force to be even such that centre samples
211239
//are aligned (using periodic Hann window)
@@ -264,6 +292,8 @@ namespace loudness{
264292
(new WeightSpectrum(middleEarFilter_, outerEarFilter_)));
265293
}
266294

295+
int lastSpectrumIdx = modules_.size()-1;
296+
267297
/*
268298
* Roex filters
269299
*/
@@ -272,18 +302,19 @@ namespace loudness{
272302
Real doubleRoexBankfactor, instantaneousLoudnessFactor;
273303
if (isSpecificLoudnessOutput_)
274304
{
275-
doubleRoexBankfactor = 1.53e-8;
305+
doubleRoexBankfactor = scalingFactor_;
276306
instantaneousLoudnessFactor = 1.0;
277307
LOUDNESS_DEBUG(name_ << ": Excitation pattern will be scaled for specific loudness");
278308
}
279309
else
280310
{
281311
doubleRoexBankfactor = 1.0;
282-
instantaneousLoudnessFactor = 1.53e-8;
312+
instantaneousLoudnessFactor = scalingFactor_;
283313
}
284314

285-
isBinauralInhibitionUsed_ = isBinauralInhibitionUsed_
286-
* (input.getNEars() == 2) * isSpecificLoudnessOutput_;
315+
isBinauralInhibitionUsed_ = isBinauralInhibitionUsed_ *
316+
(input.getNEars() == 2) *
317+
isSpecificLoudnessOutput_;
287318
if (isBinauralInhibitionUsed_)
288319
doubleRoexBankfactor /= 0.75;
289320

@@ -294,9 +325,6 @@ namespace loudness{
294325
isExcitationPatternInterpolated_,
295326
isInterpolationCubic_)));
296327

297-
/*
298-
* Binaural inhibition
299-
*/
300328
if (isBinauralInhibitionUsed_)
301329
{
302330
modules_.push_back(unique_ptr<Module>
@@ -307,39 +335,68 @@ namespace loudness{
307335
LOUDNESS_DEBUG(name_ << ": No binaural inhibition.");
308336
}
309337
outputModules_["SpecificLoudness"] = modules_.back().get();
310-
311-
/*
312-
* Instantaneous loudness
313-
*/
338+
314339
modules_.push_back(unique_ptr<Module>
315340
(new InstantaneousLoudness(instantaneousLoudnessFactor,
316341
isPresentationDiotic_)));
317342
outputModules_["InstantaneousLoudness"] = modules_.back().get();
318343

319-
/*
320-
* Short-term loudness
321-
*/
322344
modules_.push_back(unique_ptr<Module>
323345
(new ARAverager(attackTimeSTL_, releaseTimeSTL_)));
324346
outputModules_["ShortTermLoudness"] = modules_.back().get();
325347

326-
/*
327-
* Long-term loudness
328-
*/
329348
modules_.push_back(unique_ptr<Module>
330349
(new ARAverager(attackTimeLTL_, releaseTimeLTL_)));
331350
outputModules_["LongTermLoudness"] = modules_.back().get();
332351

333352
//configure targets
334353
configureLinearTargetModuleChain();
335354

336-
//Option to provide PeakFollower
337-
if (isPeakSTLFollowerUsed_)
355+
if ((input.getNSources() > 1) && (isPartialLoudnessUsed_))
338356
{
339-
modules_.push_back(unique_ptr<Module> (new PeakFollower(2.0)));
340-
outputModules_["PeakShortTermLoudness"] = modules_.back().get();
341-
outputModules_["ShortTermLoudness"] ->
342-
addTargetModule (*outputModules_["PeakShortTermLoudness"]);
357+
LOUDNESS_DEBUG(name_
358+
<< ": Setting up modules for partial loudness...");
359+
360+
modules_.push_back(unique_ptr<Module>
361+
(new MultiSourceDoubleRoexBank (1.5, 40.2,
362+
filterSpacingInCams_,
363+
doubleRoexBankfactor,
364+
false,
365+
false)));
366+
367+
int moduleIdx = modules_.size() - 1;
368+
369+
// Push spectrum to second excitation transformation stage
370+
Module* ptrToWeightedSpectrum = modules_[lastSpectrumIdx].get();
371+
ptrToWeightedSpectrum -> addTargetModule (*modules_.back().get());
372+
373+
outputModules_["MultiSourceExcitation"] = modules_.back().get();
374+
375+
modules_.push_back(unique_ptr<Module>
376+
(new SpecificPartialLoudnessCHGM2011()));
377+
378+
if (isBinauralInhibitionUsed_)
379+
{
380+
modules_.push_back(unique_ptr<Module>
381+
(new BinauralInhibitionMG2007));
382+
}
383+
384+
outputModules_["SpecificPartialLoudness"] = modules_.back().get();
385+
386+
modules_.push_back(unique_ptr<Module>
387+
(new InstantaneousLoudness(1.0, isPresentationDiotic_)));
388+
outputModules_["InstantaneousPartialLoudness"] = modules_.back().get();
389+
390+
modules_.push_back(unique_ptr<Module>
391+
(new ARAverager(attackTimeSTL_, releaseTimeSTL_)));
392+
outputModules_["ShortTermPartialLoudness"] = modules_.back().get();
393+
394+
modules_.push_back(unique_ptr<Module>
395+
(new ARAverager(attackTimeLTL_, releaseTimeLTL_)));
396+
outputModules_["LongTermPartialLoudness"] = modules_.back().get();
397+
398+
// configure targets for second (parallel) chain
399+
configureLinearTargetModuleChain(moduleIdx);
343400
}
344401

345402
return 1;

‎src/models/DynamicLoudnessCH2012.h

+11-6
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,11 @@ namespace loudness{
7272
* If you want this setExcitationPatternInterpolated(true); In mode `faster'
7373
* this is true;
7474
*
75-
* A peak follower can be applied to the short-term loudness using
76-
* setPeakSTLFollowerUsed(true) (default is false).
77-
*
7875
* OUTPUTS:
7976
* - "SpecificLoudness"
8077
* - "InstantaneousLoudness"
8178
* - "ShortTermLoudness"
8279
* - "LongTermLoudness"
83-
* - "PeakShortTermLoudness" (optional)
8480
*
8581
* REFERENCES:
8682
*
@@ -131,7 +127,7 @@ namespace loudness{
131127

132128
void setPresentationDiotic(bool isPresentationDiotic);
133129

134-
void setPeakSTLFollowerUsed(bool isPeakSTLFollowerUsed);
130+
void setPartialLoudnessUsed (bool isPartialLoudnessUsed);
135131

136132
void setBinauralInhibitionUsed(bool isBinauralInhibitionUsed);
137133

@@ -149,18 +145,27 @@ namespace loudness{
149145

150146
void setPathToFilterCoefs(string pathToFilterCoefs);
151147

148+
void setWindowSpecGM02 (bool isWindowSpecGM02);
149+
150+
void setScalingFactor (Real scalingFactor);
151+
152+
void setAttackTimeSTL (Real attackTimeSTL);
153+
void setReleaseTimeSTL (Real releaseTimeSTL);
154+
152155
private:
153156
virtual bool initializeInternal(const SignalBank &input);
154157

155158
string pathToFilterCoefs_;
156159
Real filterSpacingInCams_, compressionCriterionInCams_;
157160
Real attackTimeSTL_, releaseTimeSTL_;
158161
Real attackTimeLTL_, releaseTimeLTL_;
162+
Real scalingFactor_;
159163
bool isSpectrumSampledUniformly_, isHoppingGoertzelDFTUsed_;
160164
bool isExcitationPatternInterpolated_;
161165
bool isInterpolationCubic_, isPresentationDiotic_;
162166
bool isSpecificLoudnessOutput_, isBinauralInhibitionUsed_;
163-
bool isFirstSampleAtWindowCentre_, isPeakSTLFollowerUsed_;
167+
bool isFirstSampleAtWindowCentre_;
168+
bool isPartialLoudnessUsed_, isWindowSpecGM02_;
164169
OME::Filter outerEarFilter_, middleEarFilter_;
165170
};
166171
}

‎src/models/DynamicLoudnessGM2002.cpp

+4-18
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
#include "../modules/BinauralInhibitionMG2007.h"
3737
#include "../modules/InstantaneousLoudness.h"
3838
#include "../modules/ARAverager.h"
39-
#include "../modules/PeakFollower.h"
4039
#include "DynamicLoudnessGM2002.h"
4140

4241
namespace loudness{
@@ -68,11 +67,6 @@ namespace loudness{
6867
{
6968
isFirstSampleAtWindowCentre_ = isFirstSampleAtWindowCentre;
7069
}
71-
72-
void DynamicLoudnessGM2002::setPeakSTLFollowerUsed(bool isPeakSTLFollowerUsed)
73-
{
74-
isPeakSTLFollowerUsed_ = isPeakSTLFollowerUsed;
75-
}
7670

7771
void DynamicLoudnessGM2002::setAttackTimeSTL(Real attackTimeSTL)
7872
{
@@ -191,7 +185,6 @@ namespace loudness{
191185
{
192186
//common to all
193187
setRate(1000);
194-
setPeakSTLFollowerUsed(false);
195188
setOuterEarFilter(OME::ANSIS342007_FREEFIELD);
196189
setMiddleEarFilter(OME::ANSIS342007_MIDDLE_EAR_HPF);
197190
setSpectrumSampledUniformly(true);
@@ -208,7 +201,7 @@ namespace loudness{
208201
setBinauralInhibitionUsed(true);
209202
setPartialLoudnessUsed(true);
210203
configureSmoothingTimes("GM2002");
211-
204+
212205
if (setName != "GM2002")
213206
{
214207
if (setName == "Faster")
@@ -399,8 +392,10 @@ namespace loudness{
399392
modules_.push_back(unique_ptr<Module>
400393
(new RoexBankANSIS342007(1.8, 38.9, filterSpacingInCams_)));
401394
}
395+
396+
402397
outputModules_["Excitation"] = modules_.back().get();
403-
398+
404399
/*
405400
* Specific loudness
406401
*/
@@ -442,15 +437,6 @@ namespace loudness{
442437
//configure targets
443438
configureLinearTargetModuleChain();
444439

445-
//Option to provide PeakFollower
446-
if (isPeakSTLFollowerUsed_)
447-
{
448-
modules_.push_back(unique_ptr<Module> (new PeakFollower(2.0)));
449-
outputModules_["PeakShortTermLoudness"] = modules_.back().get();
450-
outputModules_["ShortTermLoudness"] ->
451-
addTargetModule (*outputModules_["PeakShortTermLoudness"]);
452-
}
453-
454440
// Masking conditions
455441
if ((input.getNSources() > 1) && (isPartialLoudnessUsed_))
456442
{

‎src/models/DynamicLoudnessGM2002.h

+1-6
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,6 @@ namespace loudness{
8181
* If you want this setExcitationPatternInterpolated(true). In `Faster'
8282
* modes, this is true.
8383
*
84-
* A peak follower can be applied to the short-term loudness using
85-
* setPeakSTLFollowerUsed(true) (default is false).
86-
*
8784
* OUTPUTS:
8885
* - "Excitation"
8986
* - "SpecificLoudness"
@@ -172,8 +169,6 @@ namespace loudness{
172169

173170
void setReleaseTimeLTL(Real releaseTimeLTL);
174171

175-
void setPeakSTLFollowerUsed(bool isPeakSTLFollowerUsed);
176-
177172
void setOuterEarFilter(const OME::Filter& outerEarFilter);
178173

179174
void setMiddleEarFilter(const OME::Filter& MiddleEarFilter);
@@ -203,7 +198,7 @@ namespace loudness{
203198
bool isSpectrumSampledUniformly_, isHoppingGoertzelDFTUsed_;
204199
bool isSpectralResolutionDoubled_, isBinauralInhibitionUsed_;
205200
bool isSpecificLoudnessANSIS342007_, isFirstSampleAtWindowCentre_;
206-
bool isPeakSTLFollowerUsed_, isPartialLoudnessUsed_;
201+
bool isPartialLoudnessUsed_;
207202
string pathToFilterCoefs_;
208203
OME::Filter outerEarFilter_, middleEarFilter_;
209204
};

‎src/models/StationaryLoudnessANSIS342007.cpp

+19-26
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
#include "../modules/MultiSourceRoexBank.h"
2323
#include "../modules/SpecificPartialLoudnessMGB1997.h"
2424
#include "../modules/SpecificLoudnessANSIS342007.h"
25-
#include "../modules/SpecificLoudnessModANSIS342007.h"
2625
#include "../modules/BinauralInhibitionMG2007.h"
2726
#include "../modules/InstantaneousLoudness.h"
2827
#include "StationaryLoudnessANSIS342007.h"
@@ -34,6 +33,7 @@ namespace loudness{
3433
{
3534
//Default parameters
3635
setOuterEarFilter (OME::Filter::ANSIS342007_FREEFIELD);
36+
setMiddleEarFilter (OME::Filter::ANSIS342007_MIDDLE_EAR);
3737
setFilterSpacingInCams (0.1);
3838
setPresentationDiotic (true);
3939
setBinauralInhibitionUsed (true);
@@ -64,6 +64,11 @@ namespace loudness{
6464
outerEarFilter_ = outerEarFilter;
6565
}
6666

67+
void StationaryLoudnessANSIS342007::setMiddleEarFilter(const OME::Filter middleEarFilter)
68+
{
69+
middleEarFilter_ = middleEarFilter;
70+
}
71+
6772
void StationaryLoudnessANSIS342007::setFilterSpacingInCams(Real filterSpacingInCams)
6873
{
6974
filterSpacingInCams_ = filterSpacingInCams;
@@ -74,18 +79,13 @@ namespace loudness{
7479
isSpecificLoudnessANSIS342007_ = isSpecificLoudnessANSIS342007;
7580
}
7681

77-
void StationaryLoudnessANSIS342007::setAlpha (const RealVec& alpha)
78-
{
79-
alpha_ = alpha;
80-
}
81-
8282
bool StationaryLoudnessANSIS342007::initializeInternal(const SignalBank &input)
8383
{
8484
/*
8585
* Weighting filter
8686
*/
8787
modules_.push_back(unique_ptr<Module>
88-
(new WeightSpectrum(OME::Filter::ANSIS342007_MIDDLE_EAR, outerEarFilter_)));
88+
(new WeightSpectrum(middleEarFilter_, outerEarFilter_)));
8989

9090
/*
9191
* Roex filters -> Specific loudness
@@ -96,20 +96,10 @@ namespace loudness{
9696
(new RoexBankANSIS342007(1.8, 38.9, filterSpacingInCams_)));
9797
outputModules_["Excitation"] = modules_.back().get();
9898

99-
if (!alpha_.empty())
100-
{
101-
modules_.push_back(unique_ptr<Module>
102-
(new SpecificLoudnessModANSIS342007(
103-
alpha_,
104-
isBinauralInhibitionUsed_)));
105-
}
106-
else
107-
{
108-
modules_.push_back(unique_ptr<Module>
109-
(new SpecificLoudnessANSIS342007(
110-
isSpecificLoudnessANSIS342007_,
111-
isBinauralInhibitionUsed_)));
112-
}
99+
modules_.push_back(unique_ptr<Module>
100+
(new SpecificLoudnessANSIS342007(
101+
isSpecificLoudnessANSIS342007_,
102+
isBinauralInhibitionUsed_)));
113103

114104
/*
115105
* Binaural inhibition
@@ -123,7 +113,7 @@ namespace loudness{
123113
*/
124114
modules_.push_back(unique_ptr<Module>
125115
(new InstantaneousLoudness(1.0, isPresentationDiotic_)));
126-
outputModules_["InstantaneousLoudness"] = modules_.back().get();
116+
outputModules_["Loudness"] = modules_.back().get();
127117

128118
//configure targets
129119
configureLinearTargetModuleChain();
@@ -136,13 +126,16 @@ namespace loudness{
136126
// Excitation transformation based on all sources
137127
modules_.push_back(unique_ptr<Module>
138128
(new MultiSourceRoexBank(filterSpacingInCams_)));
139-
outputModules_["MultiSourceExcitation"] = modules_.back().get();
129+
130+
/*
131+
modules_.push_back(unique_ptr<Module>
132+
(new RoexBankANSIS342007(1.8, 38.9, filterSpacingInCams_)));
133+
*/
140134
int moduleIdx = modules_.size() - 1;
141135

142136
// Push spectrum to second excitation transformation stage
143137
Module* ptrToWeightedSpectrum = modules_[0].get();
144-
ptrToWeightedSpectrum -> addTargetModule
145-
(*outputModules_["MultiSourceExcitation"]);
138+
ptrToWeightedSpectrum -> addTargetModule (*modules_.back().get());
146139

147140
// Partial loudness
148141
modules_.push_back(unique_ptr<Module>
@@ -156,7 +149,7 @@ namespace loudness{
156149

157150
modules_.push_back(unique_ptr<Module>
158151
(new InstantaneousLoudness(1.0, isPresentationDiotic_)));
159-
outputModules_["InstantaneousPartialLoudness"] = modules_.back().get();
152+
outputModules_["PartialLoudness"] = modules_.back().get();
160153

161154
// configure targets for second (parallel) chain
162155
configureLinearTargetModuleChain(moduleIdx);

‎src/models/StationaryLoudnessANSIS342007.h

+3-4
Original file line numberDiff line numberDiff line change
@@ -100,20 +100,19 @@ namespace loudness{
100100

101101
void setOuterEarFilter(const OME::Filter outerEarFilter);
102102

103+
void setMiddleEarFilter(const OME::Filter middleEarFilter);
104+
103105
void setFilterSpacingInCams(Real filterSpacingInCams);
104106

105107
void setSpecificLoudnessANSIS342007(bool isSpecificLoudnessANSIS342007_);
106108

107-
void setAlpha(const RealVec& alpha);
108-
109109
private:
110110
virtual bool initializeInternal(const SignalBank &input);
111111

112112
Real filterSpacingInCams_;
113113
bool isPresentationDiotic_, isPartialLoudnessUsed_, isBinauralInhibitionUsed_;
114114
bool isSpecificLoudnessANSIS342007_;
115-
OME::Filter outerEarFilter_;
116-
RealVec alpha_;
115+
OME::Filter outerEarFilter_, middleEarFilter_;
117116
};
118117
}
119118

‎src/models/StationaryLoudnessCHGM2011.cpp

+63-8
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
#include "../modules/WeightSpectrum.h"
2121
#include "../modules/DoubleRoexBank.h"
22+
#include "../modules/MultiSourceDoubleRoexBank.h"
23+
#include "../modules/SpecificPartialLoudnessCHGM2011.h"
2224
#include "../modules/BinauralInhibitionMG2007.h"
2325
#include "../modules/InstantaneousLoudness.h"
2426
#include "StationaryLoudnessCHGM2011.h"
@@ -29,11 +31,12 @@ namespace loudness{
2931
Model("StationaryLoudnessCHGM2011", false)
3032
{
3133
//Default parameters
32-
setOuterEarFilter(OME::Filter::ANSIS342007_FREEFIELD);
33-
setFilterSpacingInCams(0.1);
34-
setPresentationDiotic(true);
35-
setBinauralInhibitionUsed(true);
36-
setSpecificLoudnessOutput(true);
34+
setOuterEarFilter (OME::Filter::ANSIS342007_FREEFIELD);
35+
setFilterSpacingInCams (0.1);
36+
setPresentationDiotic (true);
37+
setBinauralInhibitionUsed (true);
38+
setSpecificLoudnessOutput (true);
39+
setPartialLoudnessUsed (true);
3740
}
3841

3942
StationaryLoudnessCHGM2011::~StationaryLoudnessCHGM2011()
@@ -44,6 +47,11 @@ namespace loudness{
4447
isPresentationDiotic_ = isPresentationDiotic;
4548
}
4649

50+
void StationaryLoudnessCHGM2011::setPartialLoudnessUsed(bool isPartialLoudnessUsed)
51+
{
52+
isPartialLoudnessUsed_ = isPartialLoudnessUsed;
53+
}
54+
4755
void StationaryLoudnessCHGM2011::setBinauralInhibitionUsed(bool isBinauralInhibitionUsed)
4856
{
4957
isBinauralInhibitionUsed_ = isBinauralInhibitionUsed;
@@ -70,7 +78,8 @@ namespace loudness{
7078
* Weighting filter
7179
*/
7280
modules_.push_back(unique_ptr<Module>
73-
(new WeightSpectrum(OME::CHGM2011_MIDDLE_EAR, outerEarFilter_)));
81+
(new WeightSpectrum (OME::CHGM2011_MIDDLE_EAR,
82+
outerEarFilter_)));
7483

7584
// Set up scaling factors depending on output config
7685
Real doubleRoexBankfactor, instantaneousLoudnessFactor;
@@ -86,7 +95,10 @@ namespace loudness{
8695
instantaneousLoudnessFactor = 1.53e-8;
8796
}
8897

89-
isBinauralInhibitionUsed_ = isBinauralInhibitionUsed_ * (input.getNEars() == 2) * isSpecificLoudnessOutput_;
98+
isBinauralInhibitionUsed_ = isBinauralInhibitionUsed_ *
99+
(input.getNEars() == 2) *
100+
isSpecificLoudnessOutput_;
101+
90102
if (isBinauralInhibitionUsed_)
91103
doubleRoexBankfactor /= 0.75;
92104

@@ -116,11 +128,54 @@ namespace loudness{
116128
modules_.push_back(unique_ptr<Module>
117129
(new InstantaneousLoudness(instantaneousLoudnessFactor,
118130
isPresentationDiotic_)));
119-
outputModules_["InstantaneousLoudness"] = modules_.back().get();
131+
outputModules_["Loudness"] = modules_.back().get();
120132

121133
//configure targets
122134
configureLinearTargetModuleChain();
123135

136+
// Masking conditions
137+
if ((input.getNSources() > 1) && (isPartialLoudnessUsed_))
138+
{
139+
LOUDNESS_DEBUG(name_
140+
<< ": Setting up modules for partial loudness...");
141+
142+
modules_.push_back(unique_ptr<Module>
143+
(new MultiSourceDoubleRoexBank (1.5, 40.2,
144+
filterSpacingInCams_,
145+
doubleRoexBankfactor,
146+
false,
147+
false)));
148+
149+
int moduleIdx = modules_.size() - 1;
150+
// Push spectrum to second excitation transformation stage
151+
Module* ptrToWeightedSpectrum = modules_[0].get();
152+
ptrToWeightedSpectrum -> addTargetModule (*modules_.back().get());
153+
154+
modules_.push_back(unique_ptr<Module>
155+
(new SpecificPartialLoudnessCHGM2011()));
156+
157+
/*
158+
int moduleIdx = modules_.size() - 1;
159+
Module* ptrToExcitation = modules_[1].get();
160+
ptrToExcitation -> addTargetModule (*modules_.back().get());
161+
*/
162+
163+
if (isBinauralInhibitionUsed_)
164+
{
165+
modules_.push_back(unique_ptr<Module>
166+
(new BinauralInhibitionMG2007));
167+
}
168+
169+
outputModules_["SpecificPartialLoudness"] = modules_.back().get();
170+
171+
modules_.push_back(unique_ptr<Module>
172+
(new InstantaneousLoudness(1.0, isPresentationDiotic_)));
173+
outputModules_["PartialLoudness"] = modules_.back().get();
174+
175+
// configure targets for second (parallel) chain
176+
configureLinearTargetModuleChain(moduleIdx);
177+
}
178+
124179
return 1;
125180
}
126181
}

‎src/models/StationaryLoudnessCHGM2011.h

+8-6
Original file line numberDiff line numberDiff line change
@@ -73,22 +73,24 @@ namespace loudness{
7373
StationaryLoudnessCHGM2011();
7474
virtual ~StationaryLoudnessCHGM2011();
7575

76-
void setPresentationDiotic(bool isPresentationDiotic);
76+
void setPresentationDiotic (bool isPresentationDiotic);
7777

78-
void setBinauralInhibitionUsed(bool isBinauralInhibitionUsed);
78+
void setBinauralInhibitionUsed (bool isBinauralInhibitionUsed);
7979

80-
void setOuterEarFilter(const OME::Filter outerEarFilter);
80+
void setOuterEarFilter (const OME::Filter outerEarFilter);
8181

82-
void setFilterSpacingInCams(Real filterSpacingInCams);
82+
void setFilterSpacingInCams (Real filterSpacingInCams);
8383

84-
void setSpecificLoudnessOutput(bool isSpecificLoudnessOutput);
84+
void setSpecificLoudnessOutput (bool isSpecificLoudnessOutput);
85+
86+
void setPartialLoudnessUsed (bool setPartialLoudnessUsed);
8587

8688
private:
8789
virtual bool initializeInternal(const SignalBank &input);
8890

8991
Real filterSpacingInCams_;
9092
bool isPresentationDiotic_, isBinauralInhibitionUsed_;
91-
bool isSpecificLoudnessOutput_;
93+
bool isSpecificLoudnessOutput_, isPartialLoudnessUsed_;
9294
OME::Filter outerEarFilter_;
9395
};
9496
}

‎src/models/StationaryLoudnessDIN456311991.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ namespace loudness{
5858
*/
5959
modules_.push_back(unique_ptr<Module>
6060
(new InstantaneousLoudnessDIN456311991));
61-
outputModules_["InstantaneousLoudness"] = modules_.back().get();
61+
outputModules_["Loudness"] = modules_.back().get();
6262

6363
//configure targets
6464
configureLinearTargetModuleChain();

‎src/modules/ARAverager.h

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
/*
2-
* Copyright (C) 2014 Dominic Ward <contactdominicward@gmail.com>
3-
*
42
* This file is part of Loudness
53
*
64
* Loudness is free software: you can redistribute it and/or modify
@@ -52,7 +50,7 @@ namespace loudness{
5250

5351
/** Constructs an ARAverager with attackTime and releaseTime in
5452
* seconds */
55-
ARAverager(Real attackTime, Real releaseTime);
53+
ARAverager(Real attackTime=0.1, Real releaseTime=0.1);
5654

5755
virtual ~ARAverager();
5856

‎src/modules/Biquad.cpp

+44-14
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,37 @@ namespace loudness{
3434
setACoefs(aCoefs);
3535
}
3636

37+
Biquad::Biquad(const string &type) :
38+
Module("Biquad"),
39+
Filter(2),
40+
type_ (type)
41+
{}
42+
3743
Biquad::~Biquad() {}
3844

3945
bool Biquad::initializeInternal(const SignalBank &input)
4046
{
47+
if (type_ == "RLB")
48+
{
49+
RealVec bCoefs = {1.0, -2.0, 1.0};
50+
RealVec aCoefs = {1.0, -1.99004745483398, 0.99007225036621};
51+
setBCoefs (bCoefs);
52+
setACoefs (aCoefs);
53+
setCoefficientFs (48000);
54+
}
55+
else if (type_ == "prefilter")
56+
{
57+
RealVec bCoefs = {1.53512485958697,
58+
-2.69169618940638,
59+
1.19839281085285};
60+
RealVec aCoefs = {1.0,
61+
-1.69065929318241,
62+
0.73248077421585};
63+
setBCoefs (bCoefs);
64+
setACoefs (aCoefs);
65+
setCoefficientFs (48000);
66+
}
67+
4168
LOUDNESS_ASSERT( bCoefs_.size() == 3 &&
4269
aCoefs_.size() == 3,
4370
"Filter coefficients do not satisfy filter order");
@@ -72,10 +99,19 @@ namespace loudness{
7299
bCoefs_[2] = (Vl*omegaSqrd - (Vb*omega/Q) + Vh) / denom;
73100
}
74101

102+
/*
103+
std::cout << "A" << std::endl;
104+
for (int i = 0; i < 3; ++i)
105+
std::cout << aCoefs_[i] << std::endl;
106+
std::cout << "B" << std::endl;
107+
for (int i = 0; i < 3; ++i)
108+
std::cout << bCoefs_[i] << std::endl;
109+
*/
110+
75111
delayLine_.initialize(input.getNSources(),
76112
input.getNEars(),
77113
input.getNChannels(),
78-
2 * order_,
114+
2,
79115
input.getFs());
80116

81117
//output SignalBank
@@ -101,22 +137,16 @@ namespace loudness{
101137

102138
for (int smp = 0; smp < input.getNSamples(); ++smp)
103139
{
104-
//input sample
105-
Real x = inputSignal[smp] * gain_;
106-
107140
//filter
108-
Real y = bCoefs_[0]*x + bCoefs_[1]*z[0] + bCoefs_[2]*z[1]
109-
- aCoefs_[1]*z[2] - aCoefs_[2]*z[3];
110-
111-
//update delay line
112-
z[3] = z[2];
113-
z[2] = y;
114-
z[1] = z[0];
115-
z[0] = x;
116-
117-
//output sample
141+
Real x = inputSignal[smp];
142+
Real y = bCoefs_[0] * x + z[0];
143+
z[0] = bCoefs_[1] * x - aCoefs_[1] * y + z[1];
144+
z[1] = bCoefs_[2] * x - aCoefs_[2] * y;
118145
outputSignal[smp] = y;
119146
}
147+
148+
killDenormal (z[0]);
149+
killDenormal (z[1]);
120150
}
121151
}
122152
}

‎src/modules/Biquad.h

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ namespace loudness{
3636

3737
/** Constructs a Biquad filter with no filter coefficients */
3838
Biquad();
39+
Biquad(const string &type_);
3940
/** Constructs a Biquad filter using the feedforward and feedback
4041
* coefficients bCoefs and aCoefs.
4142
* Both coefficient vectors should each be of size three.
@@ -60,6 +61,7 @@ namespace loudness{
6061
virtual void processInternal(){};
6162
virtual void resetInternal();
6263

64+
std::string type_;
6365
Real coefficientFs_ = 0;
6466
};
6567
}

‎src/modules/Butter.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ namespace loudness{
108108
for (int smp = 0; smp < input.getNSamples(); smp++)
109109
{
110110
//input sample
111-
Real x = inputSignal[smp] * gain_;
111+
Real x = inputSignal[smp];
112112

113113
//filter
114114
Real y = bCoefs_[0]*(x-z[2]) + bCoefs_[2]*
@@ -126,6 +126,9 @@ namespace loudness{
126126
//output sample
127127
outputSignal[smp] = y;
128128
}
129+
130+
for (int i = 3; i < 6; ++i)
131+
killDenormal (z[i]);
129132
}
130133
}
131134
}

‎src/modules/EMA.cpp

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright (C) 2014 Dominic Ward <contactdominicward@gmail.com>
3+
*
4+
* This file is part of Loudness
5+
*
6+
* Loudness is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* Loudness is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with Loudness. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#include "EMA.h"
21+
#include "../support/AuditoryTools.h"
22+
23+
namespace loudness{
24+
25+
EMA::EMA (Real timeConstant, bool asEffectiveAverageTime) :
26+
Module("EMA"),
27+
timeConstant_ (timeConstant),
28+
asEffectiveAverageTime_ (asEffectiveAverageTime)
29+
{
30+
LOUDNESS_DEBUG(name_ << ": Constructed.");
31+
};
32+
33+
EMA::~EMA()
34+
{};
35+
36+
bool EMA::initializeInternal(const SignalBank &input)
37+
{
38+
//filter coefficient
39+
if (asEffectiveAverageTime_)
40+
coef_ = 2.0 / (round (timeConstant_ * input.getFrameRate()) + 1);
41+
else
42+
coef_ = 1 - exp(-1.0 / (input.getFrameRate() * timeConstant_));
43+
44+
//output SignalBank
45+
output_.initialize(input);
46+
47+
return 1;
48+
}
49+
50+
void EMA::processInternal(const SignalBank &input)
51+
{
52+
int lastSampleIdx = input.getNSamples() - 1;
53+
54+
for (int src = 0; src < input.getNSources(); src++)
55+
{
56+
for (int ear = 0; ear < input.getNEars(); ++ear)
57+
{
58+
for (int chn = 0; chn < input.getNChannels(); ++chn)
59+
{
60+
const Real* x = input.getSignalReadPointer(src,
61+
ear, chn, 0);
62+
Real* y = output_.getSignalWritePointer(src,
63+
ear, chn, 0);
64+
killDenormal (y[lastSampleIdx]);
65+
Real yPrev = y[lastSampleIdx];
66+
67+
for (int smp = 0; smp < input.getNSamples(); ++smp)
68+
{
69+
y[smp] = coef_ * (x[smp] - yPrev) + yPrev;
70+
yPrev = y[smp];
71+
}
72+
}
73+
}
74+
}
75+
}
76+
77+
//output SignalBanks are cleared so not to worry about filter state
78+
void EMA::resetInternal()
79+
{}
80+
}

‎src/modules/EMA.h

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright (C) 2014 Dominic Ward <contactdominicward@gmail.com>
3+
*
4+
* This file is part of Loudness
5+
*
6+
* Loudness is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* Loudness is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with Loudness. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#ifndef EMA_H
21+
#define EMA_H
22+
23+
#include "../support/Module.h"
24+
25+
namespace loudness{
26+
27+
class EMA : public Module
28+
{
29+
public:
30+
31+
/** Constructs an EMA with attackTime and releaseTime in
32+
* seconds */
33+
EMA(Real timeConstant, bool asEffectiveAverageTime=false);
34+
35+
virtual ~EMA();
36+
37+
private:
38+
virtual bool initializeInternal(const SignalBank &input);
39+
virtual bool initializeInternal(){return 0;};
40+
virtual void processInternal(const SignalBank &input);
41+
virtual void processInternal(){};
42+
virtual void resetInternal();
43+
44+
Real timeConstant_, coef_;
45+
bool asEffectiveAverageTime_;
46+
};
47+
}
48+
49+
#endif

‎src/modules/FIR.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,7 @@ namespace loudness{
7070

7171
for (int smp = 0; smp < input.getNSamples(); ++smp)
7272
{
73-
//input sample
74-
Real x = inputSignal[smp] * gain_;
73+
Real x = inputSignal[smp];
7574

7675
//output sample
7776
outputSignal[smp] = bCoefs_[0] * x + z[0];

‎src/modules/FixedRoexBank.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,12 @@ namespace loudness{
6565
{
6666
cam = camLo_ + camStep_ * chn;
6767
fc = camToHertz (cam);
68+
output_.setCentreFreq (chn, fc);
69+
6870
erb = centreFreqToCambridgeERB (fc);
6971
pu = 4.0 * fc / erb;
72+
7073
t2 = 0.35 * (pu / p51_1k);
71-
output_.setCentreFreq (chn, fc);
7274

7375
int j = 0;
7476
while (j < input.getNChannels())

‎src/modules/ForwardMaskingPO1998.cpp

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Copyright (C) 2014 Dominic Ward <contactdominicward@gmail.com>
3+
*
4+
* This file is part of Loudness
5+
*
6+
* Loudness is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* Loudness is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with Loudness. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#include "ForwardMaskingPO1998.h"
21+
22+
namespace loudness{
23+
24+
ForwardMaskingPO1998::ForwardMaskingPO1998 (Real timeConstant1,
25+
Real timeConstant2,
26+
Real weight) :
27+
Module ("ForwardMaskingPO1998"),
28+
Filter (2),
29+
timeConstant1_ (timeConstant1),
30+
timeConstant2_ (timeConstant2),
31+
weight_ (weight)
32+
{
33+
LOUDNESS_DEBUG(name_ << ": Constructed.");
34+
}
35+
36+
ForwardMaskingPO1998::~ForwardMaskingPO1998()
37+
{}
38+
39+
bool ForwardMaskingPO1998::initializeInternal(const SignalBank &input)
40+
{
41+
Real weight2 = 1 - weight_;
42+
Real alpha1 = std::exp (-1.0 / (timeConstant1_ * input.getFrameRate()));
43+
Real alpha2 = std::exp (-1.0 / (timeConstant2_ * input.getFrameRate()));
44+
45+
// coefs for n-1
46+
RealVec bCoefs = {-(weight2 * alpha2 + weight_ * alpha1)};
47+
// coefs for n, n-1 and n-2
48+
RealVec aCoefs = {1.0, -alpha1 - alpha2, alpha1 * alpha2};
49+
50+
aCoefs[0] = 1.0 / ((1 - weight_) * timeConstant1_ +
51+
weight_ * timeConstant2_);
52+
aCoefs[0] /= input.getFrameRate();
53+
54+
setBCoefs (bCoefs);
55+
setACoefs (aCoefs);
56+
57+
delayLine_.initialize (
58+
input.getNSources(),
59+
input.getNEars(),
60+
input.getNChannels(),
61+
2,
62+
input.getFs());
63+
64+
65+
//output SignalBank
66+
output_.initialize(input);
67+
68+
return 1;
69+
}
70+
71+
void ForwardMaskingPO1998::processInternal(const SignalBank &input)
72+
{
73+
for (int src = 0; src < input.getNSources(); ++src)
74+
{
75+
for(int ear = 0; ear < input.getNEars(); ++ear)
76+
{
77+
for (int chn = 0; chn < input.getNChannels(); ++chn)
78+
{
79+
const Real* inputSignal = input.getSignalReadPointer
80+
(src, ear, chn);
81+
Real* outputSignal = output_.getSignalWritePointer
82+
(src, ear, chn);
83+
Real* z = delayLine_.getSignalWritePointer
84+
(src, ear, chn);
85+
86+
for (int smp = 0; smp < input.getNSamples(); ++smp)
87+
{
88+
//input sample
89+
Real x = inputSignal[smp];
90+
91+
//output sample
92+
outputSignal[smp] = x + z[0];
93+
94+
// b[n-1] ...
95+
z[0] = bCoefs_[0] * x + z[1];
96+
z[1] = 0.0;
97+
98+
// a[n-1] ...
99+
z[0] -= aCoefs_[1] * outputSignal[smp];
100+
101+
// a[n-2] ...
102+
z[1] -= aCoefs_[2] * outputSignal[smp];
103+
104+
outputSignal[smp] *= aCoefs_[0];
105+
106+
}
107+
108+
killDenormal (z[0]);
109+
killDenormal (z[1]);
110+
}
111+
}
112+
}
113+
}
114+
115+
//output SignalBanks are cleared so not to worry about filter state
116+
void ForwardMaskingPO1998::resetInternal()
117+
{}
118+
}

‎src/modules/ForwardMaskingPO1998.h

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (C) 2014 Dominic Ward <contactdominicward@gmail.com>
3+
*
4+
* This file is part of Loudness
5+
*
6+
* Loudness is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* Loudness is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with Loudness. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#ifndef FORWARDMASKINGPO1998_H
21+
#define FORWARDMASKINGPO1998_H
22+
23+
#include "../support/Filter.h"
24+
25+
namespace loudness{
26+
27+
class ForwardMaskingPO1998 : public Module, public Filter
28+
{
29+
public:
30+
ForwardMaskingPO1998(
31+
Real timeConstant1=4e-3,
32+
Real timeConstant2=29e-3,
33+
Real weight=0.0251);
34+
35+
virtual ~ForwardMaskingPO1998();
36+
37+
private:
38+
virtual bool initializeInternal(const SignalBank &input);
39+
virtual bool initializeInternal(){return 0;};
40+
virtual void processInternal(const SignalBank &input);
41+
virtual void processInternal(){};
42+
virtual void resetInternal();
43+
44+
Real timeConstant1_, timeConstant2_, weight_;
45+
};
46+
}
47+
48+
#endif

‎src/modules/HoppingGoertzelDFT.cpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,12 @@ namespace loudness{
245245
vPrev[k] = v;
246246
}
247247
}
248+
249+
for (int k = binIdxForGoertzels_[w][0]; k < binIdxForGoertzels_[w][1]; ++k)
250+
{
251+
killDenormal (vPrev[k]);
252+
killDenormal (vPrev2[k]);
253+
}
248254
}
249255
}
250256
}
@@ -299,7 +305,6 @@ namespace loudness{
299305
nSamplesUntilTrigger_ = largestWindowSize_ / 2;
300306
else
301307
nSamplesUntilTrigger_ = largestWindowSize_;
302-
nSamplesUntilTrigger_ = hopSize_;
303308

304309
for (int w = 0; w < nWindows_; ++w)
305310
{

‎src/modules/IIR.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ namespace loudness{
9494
for (int smp = 0; smp < input.getNSamples(); ++smp)
9595
{
9696
//input sample
97-
Real x = inputSignal[smp] * gain_;
97+
Real x = inputSignal[smp];
9898

9999
//output sample
100100
outputSignal[smp] = bCoefs_[0] * x + z[0];
@@ -108,6 +108,9 @@ namespace loudness{
108108
z[j-1] -= aCoefs_[j] * outputSignal[smp];
109109
z[orderMinus1_] -= aCoefs_[order_] * outputSignal[smp];
110110
}
111+
112+
for (int j = 0; j < order_; ++j)
113+
killDenormal (z[j]);
111114
}
112115
}
113116
}
+266
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
/*
2+
* Copyright (C) 2014 Dominic Ward <contactdominicward@gmail.com>
3+
*
4+
* This file is part of Loudness
5+
*
6+
* Loudness is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* Loudness is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with Loudness. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#include "MultiSourceDoubleRoexBank.h"
21+
#include "../support/AuditoryTools.h"
22+
23+
namespace loudness{
24+
25+
MultiSourceDoubleRoexBank::MultiSourceDoubleRoexBank(Real camLo,
26+
Real camHi,
27+
Real camStep,
28+
Real scalingFactor,
29+
bool isExcitationPatternInterpolated,
30+
bool isInterpolationCubic) :
31+
Module("MultiSourceDoubleRoexBank"),
32+
camLo_(camLo),
33+
camHi_(camHi),
34+
camStep_(camStep),
35+
scalingFactor_(scalingFactor),
36+
isExcitationPatternInterpolated_(isExcitationPatternInterpolated),
37+
isInterpolationCubic_(isInterpolationCubic)
38+
{}
39+
40+
MultiSourceDoubleRoexBank::~MultiSourceDoubleRoexBank() {}
41+
42+
bool MultiSourceDoubleRoexBank::initializeInternal(const SignalBank &input)
43+
{
44+
45+
if (camStep_ <= 0.1)
46+
isExcitationPatternInterpolated_ = false;
47+
if (isExcitationPatternInterpolated_)
48+
{
49+
camLo_ = 1.5;
50+
camHi_ = 40.2;
51+
}
52+
53+
//number of roex filters to use
54+
nFilters_ = std::floor((camHi_ - camLo_) / camStep_) + 1;
55+
56+
LOUDNESS_DEBUG(name_
57+
<< ": interpolation applied: " << isExcitationPatternInterpolated_
58+
<< " filter spacing in Cams: " << camStep_
59+
<< " Total number of filters: " << nFilters_);
60+
61+
//initialise output SignalBank
62+
if (isExcitationPatternInterpolated_)
63+
{
64+
//centre freqs in cams
65+
cams_.assign (nFilters_, 0.0);
66+
67+
//required for log interpolation
68+
logExcitation_.assign (nFilters_, 0.0);
69+
70+
//388 filters to cover [1.5, 40.2] see p. 3
71+
output_.initialize (input.getNSources(),
72+
input.getNEars(),
73+
388,
74+
1,
75+
input.getFs());
76+
output_.setChannelSpacingInCams (0.1);
77+
for (int i = 0; i < 388; ++i)
78+
output_.setCentreFreq (i, camToHertz (camLo_ + (i * 0.1)));
79+
}
80+
else
81+
{
82+
output_.initialize (
83+
input.getNSources(),
84+
input.getNEars(),
85+
nFilters_,
86+
1,
87+
input.getFs());
88+
output_.setChannelSpacingInCams (camStep_);
89+
}
90+
91+
output_.setFrameRate (input.getFrameRate());
92+
93+
//filter variables
94+
wPassive_.resize (nFilters_);
95+
wActive_.resize (nFilters_);
96+
maxGdB_.resize (nFilters_);
97+
thirdGainTerm_.resize (nFilters_);
98+
99+
//fill the above arrays
100+
for (int i = 0; i < nFilters_; ++i)
101+
{
102+
//filter frequency in Cams
103+
Real cam = camLo_ + (i * camStep_);
104+
//filter frequency in Hz
105+
Real fc = camToHertz (cam);
106+
107+
if (isExcitationPatternInterpolated_)
108+
cams_[i] = cam;
109+
else
110+
output_.setCentreFreq (i, fc);
111+
112+
//slopes
113+
Real tl = fc / (0.108 * fc + 2.33);
114+
Real tu = 15.6;
115+
Real pl = fc / (0.027 * fc + 5.44);
116+
Real pu = 27.9;
117+
118+
//precalculate some gain terms
119+
maxGdB_[i] = fc / (0.0191 * fc + 1.1);
120+
//Third term in Eq. 6
121+
thirdGainTerm_[i] = maxGdB_[i] / (1 + exp (0.05 * (100 - maxGdB_[i])));
122+
123+
//compute the fixed filters
124+
int j = 0;
125+
while (j < input.getNChannels())
126+
{
127+
Real pgPassive = 0.0, pgActive = 0.0;
128+
129+
//normalised deviation
130+
Real g = (input.getCentreFreq(j) - fc) / fc;
131+
132+
//Is g limited to 2 sufficient for the passive filter?
133+
if (g <= 2)
134+
{
135+
if (g < 0) //lower value
136+
{
137+
pgPassive = -tl * g;
138+
pgActive = -pl * g;
139+
}
140+
else //upper value
141+
{
142+
pgPassive = tu * g;
143+
pgActive = pu * g;
144+
}
145+
146+
//Eq. 4 and Eq. 7
147+
wPassive_[i].push_back ((1 + pgPassive) * exp (-pgPassive));
148+
wActive_[i].push_back ((1 + pgActive) * exp (-pgActive));
149+
}
150+
else
151+
break;
152+
j++;
153+
}
154+
}
155+
LOUDNESS_DEBUG(name_ << ": Passive and active filters configured.");
156+
LOUDNESS_DEBUG(name_ << ": Excitation pattern will be scaled by: "
157+
<< scalingFactor_);
158+
159+
return 1;
160+
}
161+
162+
163+
void MultiSourceDoubleRoexBank::processInternal(const SignalBank &input)
164+
{
165+
for (int ear = 0; ear < input.getNEars(); ++ear)
166+
{
167+
// First to passive filtering
168+
RealVec gainOut (nFilters_, 0.0);
169+
for (int src = 0; src < input.getNSources(); ++src)
170+
{
171+
const Real* inputSpectrum = input
172+
.getSingleSampleReadPointer
173+
(src, ear, 0);
174+
175+
Real* outputExcitation = output_
176+
.getSingleSampleWritePointer
177+
(src, ear, 0);
178+
179+
for (int i = 0; i < nFilters_; ++i)
180+
{
181+
Real excitationLinP = 0.0;
182+
183+
//passive filter output
184+
for (uint j = 0; j < wPassive_[i].size(); ++j)
185+
excitationLinP += wPassive_[i][j] * inputSpectrum[j];
186+
187+
outputExcitation[i] = excitationLinP;
188+
189+
// Accumulate power in each channel from all sources
190+
gainOut[i] += excitationLinP;
191+
}
192+
}
193+
194+
// Now gain calculation using total power from all inputs
195+
for (int i = 0; i < nFilters_; ++i)
196+
{
197+
//convert to dB
198+
Real excitationLog = powerToDecibels (gainOut[i]);
199+
200+
//compute gain (Complete Eq. 6 for <= 30)
201+
Real gain = maxGdB_[i] - (maxGdB_[i] /
202+
(1 + exp (-0.05*(excitationLog - (100 - maxGdB_[i]))))) +
203+
thirdGainTerm_[i];
204+
205+
//check for higher levels
206+
if (excitationLog > 30)
207+
{
208+
//complete Eq. 6 for > 30
209+
Real excitationLogMinus30 = excitationLog - 30;
210+
gain = gain - 0.003 * excitationLogMinus30 * excitationLogMinus30;
211+
}
212+
213+
//convert to linear gain
214+
gainOut[i] = decibelsToPower(gain);
215+
}
216+
217+
// Now the active filter for each source
218+
for (int src = 0; src < input.getNSources(); ++src)
219+
{
220+
const Real* inputSpectrum = input
221+
.getSingleSampleReadPointer
222+
(src, ear, 0);
223+
224+
Real* outputExcitation = output_.
225+
getSingleSampleWritePointer
226+
(src, ear, 0);
227+
228+
for (int i = 0; i < nFilters_; ++i)
229+
{
230+
// from above
231+
Real excitationLinP = outputExcitation[i];
232+
Real excitationLinA = 0.0;
233+
234+
//active filter output
235+
for (uint j = 0; j < wActive_[i].size(); ++j)
236+
excitationLinA += wActive_[i][j] * inputSpectrum[j];
237+
238+
// Use the gain derived from all inputs
239+
excitationLinA *= gainOut[i];
240+
241+
//excitation pattern
242+
Real excitation = scalingFactor_ *
243+
(excitationLinP + excitationLinA);
244+
245+
if (isExcitationPatternInterpolated_)
246+
logExcitation_[i] = log(excitation + 1e-10);
247+
else
248+
outputExcitation[i] = excitation;
249+
}
250+
251+
//Interpolate to estimate 0.1~Cam res excitation pattern
252+
if (isExcitationPatternInterpolated_)
253+
{
254+
spline_.set_points (cams_,
255+
logExcitation_,
256+
isInterpolationCubic_);
257+
258+
for (int i = 0; i < 388; ++i)
259+
outputExcitation[i] = exp (spline_ (camLo_ + i * 0.1));
260+
}
261+
}
262+
}
263+
}
264+
265+
void MultiSourceDoubleRoexBank::resetInternal(){};
266+
}
+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright (C) 2014 Dominic Ward <contactdominicward@gmail.com>
3+
*
4+
* This file is part of Loudness
5+
*
6+
* Loudness is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* Loudness is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with Loudness. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#ifndef MultiSourceDoubleRoexBank_H
21+
#define MultiSourceDoubleRoexBank_H
22+
23+
#include "../support/Module.h"
24+
#include "../thirdParty/spline/Spline.h"
25+
26+
namespace loudness{
27+
28+
/**
29+
* @class MultiSourceDoubleRoexBank
30+
*
31+
* @brief Applies a bank of double roex filters to an input power spectrum.
32+
*
33+
* This is an implementation of the double roex filter model proposed by
34+
* Chen et al (2011). The model makes use of a passive wideband filter and
35+
* an active narrowband filter. The output of the passive filter controls
36+
* the active gain of the narrowband filter.
37+
*
38+
* The centre frequencies of the filters are calculated according to the
39+
* Hertz to Cam equation given in ANSI S3.4:2007. Therefore, they will be
40+
* slightly different to those computed in Chen et al. (2011) as they use
41+
* the traditional equation with rounded coefficients. For example, 40.2
42+
* Cams ~= 17197 Hz (ANSI) and 40.2 Cams ~= 17070 Hz (Chen et al.).
43+
*
44+
* REFERENCES:
45+
*
46+
* Chen, Z., Hu, G., Glasberg, B. R., & Moore, B. C. J. (2011). A new method
47+
* of calculating auditory excitation patterns and loudness for steady
48+
* sounds. Hearing Research, 282(1-2), 204–15.
49+
*
50+
*/
51+
52+
class MultiSourceDoubleRoexBank : public Module
53+
{
54+
55+
public:
56+
57+
/** Constructs a MultiSourceDoubleRoexBank with double roex filters equally spaced
58+
* on the Cam scale.
59+
*
60+
* @param camLo Frequency of the first double roex filter in Cams.
61+
* @param camHi Frequency of the last double roex filter in Cams.
62+
* @param camStep Interval between adjacent filters on the cam Scale.
63+
* @param scalingFactor A scalar applied to the output excitation
64+
* pattern (default is 1).
65+
* @param isExcitationPatternInterpolated Set true to interpolate the
66+
* excitation pattern.
67+
* @param isInterpolationCubic Set true for cubic
68+
* interpolation, false for linear. Only applies if
69+
* isExcitationPatternInterpolated is true;
70+
71+
*/
72+
MultiSourceDoubleRoexBank(Real camLo = 1.5,
73+
Real camHi = 40.2,
74+
Real camStep = 0.1,
75+
Real scalingFactor = 1.0,
76+
bool isExcitationPatternInterpolated = false,
77+
bool isInterpolationCubic = true);
78+
79+
virtual ~MultiSourceDoubleRoexBank();
80+
81+
private:
82+
83+
virtual bool initializeInternal(const SignalBank &input);
84+
virtual bool initializeInternal(){return 0;};
85+
virtual void processInternal(const SignalBank &input);
86+
virtual void processInternal(){};
87+
virtual void resetInternal();
88+
89+
Real camLo_, camHi_, camStep_, scalingFactor_;
90+
bool isExcitationPatternInterpolated_, isInterpolationCubic_;
91+
int nFilters_;
92+
RealVec maxGdB_, thirdGainTerm_, cams_, logExcitation_;
93+
RealVecVec wPassive_, wActive_;
94+
spline spline_;
95+
};
96+
}
97+
98+
#endif

‎src/modules/PeakFollower.cpp

+16-13
Original file line numberDiff line numberDiff line change
@@ -52,25 +52,28 @@ namespace loudness{
5252
{
5353
int lastSampleIdx = input.getNSamples() - 1;
5454

55-
for (int ear = 0; ear < input.getNEars(); ++ear)
55+
for (int src = 0; src < input.getNSources(); ++src)
5656
{
57-
for (int chn = 0; chn < input.getNChannels(); ++chn)
57+
for (int ear = 0; ear < input.getNEars(); ++ear)
5858
{
59-
const Real* x = input.getSignalReadPointer(ear, chn, 0);
59+
for (int chn = 0; chn < input.getNChannels(); ++chn)
60+
{
61+
const Real* x = input.getSignalReadPointer(src, ear, chn, 0);
6062

61-
Real* y = output_.getSignalWritePointer(ear, chn, 0);
63+
Real* y = output_.getSignalWritePointer(src, ear, chn, 0);
6264

63-
Real yPrev = y[lastSampleIdx];
65+
Real yPrev = y[lastSampleIdx];
6466

65-
for (int smp = 0; smp < input.getNSamples(); ++smp)
66-
{
67-
Real absX = abs (x[smp]);
67+
for (int smp = 0; smp < input.getNSamples(); ++smp)
68+
{
69+
Real absX = abs (x[smp]);
6870

69-
if (absX >= yPrev)
70-
y[smp] = absX;
71-
else
72-
y[smp] = absX + coef_ * (yPrev - absX);
73-
yPrev = y[smp];
71+
if (absX >= yPrev)
72+
y[smp] = absX;
73+
else
74+
y[smp] = absX + coef_ * (yPrev - absX);
75+
yPrev = y[smp];
76+
}
7477
}
7578
}
7679
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright (C) 2014 Dominic Ward <contactdominicward@gmail.com>
3+
*
4+
* This file is part of Loudness
5+
*
6+
* Loudness is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* Loudness is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with Loudness. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#include "SpecificPartialLoudnessCHGM2011.h"
21+
#include "../support/AuditoryTools.h"
22+
23+
namespace loudness{
24+
25+
SpecificPartialLoudnessCHGM2011::SpecificPartialLoudnessCHGM2011() :
26+
Module("SpecificPartialLoudnessCHGM2011")
27+
{
28+
LOUDNESS_DEBUG(name_ << ": Constructed.");
29+
}
30+
31+
SpecificPartialLoudnessCHGM2011::~SpecificPartialLoudnessCHGM2011()
32+
{};
33+
34+
bool SpecificPartialLoudnessCHGM2011::initializeInternal(const SignalBank &input)
35+
{
36+
for (int chn = 0; chn < input.getNChannels(); ++chn)
37+
{
38+
Real fc = input.getCentreFreq (chn);
39+
Real ef = hertzToNormalisedCam (fc);
40+
// Table 1 of Chen et al., 2011
41+
Real kDB = 6.51 * ef*ef - 1.93;
42+
LOUDNESS_DEBUG(fc << ": " << kDB);
43+
k_.push_back (std::pow (10, kDB / 10.0));
44+
}
45+
46+
output_.initialize (input);
47+
48+
return 1;
49+
}
50+
51+
void SpecificPartialLoudnessCHGM2011::processInternal(const SignalBank &input)
52+
{
53+
for (int ear = 0; ear < input.getNEars(); ++ear)
54+
{
55+
/*
56+
* Should probably calculate excitation patterns using total power
57+
* from all sources? They don't specify this in the paper...
58+
* Just go simple linear approach for now.
59+
*/
60+
RealVec eTot (input.getNChannels(), 0.0);
61+
for (int src = 0; src < input.getNSources(); ++src)
62+
{
63+
const Real* inputExcitation = input
64+
.getSingleSampleReadPointer
65+
(src, ear, 0);
66+
67+
for (int chn = 0; chn < input.getNChannels(); ++chn)
68+
eTot[chn] += inputExcitation[chn];
69+
}
70+
71+
// Now do partial loudness calculation
72+
for (int src = 0; src < input.getNSources(); ++src)
73+
{
74+
const Real* inputExcitation = input
75+
.getSingleSampleReadPointer
76+
(src, ear, 0);
77+
78+
Real* outputExcitation = output_
79+
.getSingleSampleWritePointer
80+
(src, ear, 0);
81+
82+
for (int chn = 0; chn < input.getNChannels(); ++chn)
83+
{
84+
Real eNoise = eTot[chn] - inputExcitation[chn];
85+
Real threshold = k_[chn] * eNoise;
86+
if (inputExcitation[chn] > threshold)
87+
outputExcitation[chn] = inputExcitation[chn] - threshold;
88+
else
89+
outputExcitation[chn] = 0.0;
90+
}
91+
}
92+
}
93+
}
94+
95+
//output SignalBanks are cleared so not to worry about filter state
96+
void SpecificPartialLoudnessCHGM2011::resetInternal()
97+
{
98+
}
99+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (C) 2014 Dominic Ward <contactdominicward@gmail.com>
3+
*
4+
* This file is part of Loudness
5+
*
6+
* Loudness is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* Loudness is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with Loudness. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#ifndef SpecificPartialLoudnessCHGM2011_H
21+
#define SpecificPartialLoudnessCHGM2011_H
22+
23+
#include "../support/Module.h"
24+
25+
namespace loudness{
26+
27+
class SpecificPartialLoudnessCHGM2011 : public Module
28+
{
29+
public:
30+
31+
SpecificPartialLoudnessCHGM2011();
32+
33+
virtual ~SpecificPartialLoudnessCHGM2011();
34+
35+
private:
36+
virtual bool initializeInternal(const SignalBank &input);
37+
virtual bool initializeInternal(){return 0;};
38+
virtual void processInternal(const SignalBank &input);
39+
virtual void processInternal(){};
40+
virtual void resetInternal();
41+
42+
RealVec k_;
43+
};
44+
}
45+
#endif

‎src/modules/SpecificPartialLoudnessMGB1997.cpp

+45-41
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ namespace loudness{
183183
for (int chn = 0; chn < input.getNChannels(); ++chn)
184184
{
185185
Real eNoise = eTot[chn] - eSig[chn];
186-
Real eThrn = eNoise * kParam_[chn];
186+
Real eThrn = kParam_[chn] * eNoise + eThrqParam_[chn];
187187
Real nSig = 0.0;
188188

189189
if (eSig[chn] > 1e-10)
@@ -192,29 +192,32 @@ namespace loudness{
192192
{
193193
if (eSig[chn] >= eThrn) // Equation 19
194194
{
195-
nSig = parameterC2_ * std::pow (eTot[chn],
196-
yearExp_) - parameterC2_
197-
* (std::pow (eNoise + eThrn
198-
+ eThrqParam_[chn], yearExp_)
199-
- std::pow (eThrqParam_[chn] * gParam_[chn]
200-
+ aParam_[chn], alphaParam_[chn])
201-
+ std::pow (aParam_[chn],
202-
alphaParam_[chn])) * std::pow (eThrn
203-
/ eSig[chn], 0.3);
195+
nSig = parameterC2_ *
196+
std::pow (eTot[chn], yearExp_);
197+
198+
nSig -= parameterC2_ *
199+
(std::pow (eNoise + eThrn, yearExp_) -
200+
std::pow (eThrqParam_[chn] *
201+
gParam_[chn] + aParam_[chn],
202+
alphaParam_[chn]) +
203+
std::pow (aParam_[chn],
204+
alphaParam_[chn])) *
205+
std::pow (eThrn / eSig[chn], 0.3);
204206
}
205207
else // Equation 20
206208
{
207209
nSig = parameterC_ * std::pow (2.0 * eSig[chn]
208-
/ (eSig[chn] + eThrn), 1.5)
209-
* ((std::pow (eThrqParam_[chn]
210-
* gParam_[chn] + aParam_[chn],
211-
alphaParam_[chn])
212-
- std::pow (aParam_[chn], alphaParam_[chn]))
213-
/ (std::pow (eNoise + eThrn
214-
+ eThrqParam_[chn], yearExp_)
215-
- std::pow(eNoise, yearExp_)))
216-
* (std::pow (eTot[chn], yearExp_)
217-
- std::pow (eNoise, yearExp_));
210+
/ (eSig[chn] + eThrn), 1.5);
211+
212+
nSig *= (std::pow (eThrqParam_[chn] *
213+
gParam_[chn] + aParam_[chn],
214+
alphaParam_[chn]) -
215+
std::pow (aParam_[chn],
216+
alphaParam_[chn])) /
217+
(std::pow (eNoise + eThrn, yearExp_) -
218+
std::pow(eNoise, yearExp_));
219+
nSig *= std::pow (eTot[chn], yearExp_)
220+
- std::pow (eNoise, yearExp_);
218221
}
219222
}
220223
else // eTot <= 100 dB
@@ -224,33 +227,34 @@ namespace loudness{
224227
nSig = parameterC_ * (std::pow (eTot[chn]
225228
* gParam_[chn] + aParam_[chn],
226229
alphaParam_[chn]) - std::pow (
227-
aParam_[chn], alphaParam_[chn]))
228-
- parameterC_ * (std::pow (
229-
gParam_[chn] * (eNoise + eThrn
230-
+ eThrqParam_[chn]) + aParam_[chn],
231-
alphaParam_[chn]) - std::pow (
232-
eThrqParam_[chn] * gParam_[chn]
230+
aParam_[chn], alphaParam_[chn]));
231+
232+
nSig -= parameterC_ * (std::pow (
233+
gParam_[chn] * (eNoise + eThrn) +
234+
aParam_[chn], alphaParam_[chn]) -
235+
std::pow (eThrqParam_[chn] * gParam_[chn]
233236
+ aParam_[chn], alphaParam_[chn]))
234237
* std::pow (eThrn / eSig[chn], 0.3);
235238
}
236239
else // Equation 18
237240
{
238241
nSig = parameterC_ * std::pow (2.0 * eSig[chn]
239-
/ (eSig[chn] + eThrn), 1.5)
240-
* ((std::pow (eThrqParam_[chn]
241-
* gParam_[chn] + aParam_[chn],
242-
alphaParam_[chn]) - std::pow (
243-
aParam_[chn], alphaParam_[chn]))
244-
/ (std::pow ((eNoise + eThrn
245-
+ eThrqParam_[chn]) * gParam_[chn]
246-
+ aParam_[chn], alphaParam_[chn])
247-
- std::pow (eNoise * gParam_[chn]
248-
+ aParam_[chn], alphaParam_[chn])))
249-
* ( std::pow (eTot[chn]
250-
* gParam_[chn] + aParam_[chn],
251-
alphaParam_[chn]) - std::pow (eNoise
252-
* gParam_[chn] + aParam_[chn],
253-
alphaParam_[chn]));
242+
/ (eSig[chn] + eThrn), 1.5);
243+
244+
nSig *= (std::pow (eThrqParam_[chn] *
245+
gParam_[chn] + aParam_[chn],
246+
alphaParam_[chn]) - std::pow (
247+
aParam_[chn], alphaParam_[chn])) /
248+
(std::pow ((eNoise + eThrn) *
249+
gParam_[chn] + aParam_[chn],
250+
alphaParam_[chn]) - std::pow (
251+
eNoise * gParam_[chn] + aParam_[chn],
252+
alphaParam_[chn]));
253+
254+
nSig *= std::pow (eTot[chn] * gParam_[chn] +
255+
aParam_[chn], alphaParam_[chn]) -
256+
std::pow (eNoise * gParam_[chn] +
257+
aParam_[chn], alphaParam_[chn]);
254258
}
255259
}
256260
}

‎src/python/__init__.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import sound
2-
import extractors
3-
import iterators
4-
import predictors
5-
import spectra
1+
from . import sound
2+
from . import extractors
3+
from . import iterators
4+
from . import predictors
5+
from . import spectra

‎src/python/extractors.py

+125-67
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
class StationaryLoudnessExtractor:
99

10-
def __init__(self, model, outputs):
10+
def __init__(self, model, outputs, alwaysReinitialize=True):
1111

1212
if model.isDynamic():
1313
raise ValueError("Model cannot be dynamic")
@@ -23,6 +23,7 @@ def __init__(self, model, outputs):
2323
self.nEars = 0
2424
self.bank = ln.SignalBank()
2525
self.initialize = True
26+
self.alwaysReinitialize = alwaysReinitialize
2627

2728
def process(self, frequencies, intensityLevels):
2829

@@ -51,7 +52,8 @@ def process(self, frequencies, intensityLevels):
5152
self.outputDict['Frequencies'] = frequencies
5253
self.nEars = nEars
5354
self.model.initialize(self.bank)
54-
self.initialize = False
55+
if not self.alwaysReinitialize:
56+
self.initialize = False
5557

5658
self.bank.setSignals(intensities.T.reshape((1, nEars, nComponents, 1)))
5759

@@ -121,120 +123,176 @@ def __init__(self,
121123
self.hopSize,
122124
self.fs)
123125
self.outputs = outputs
124-
self.gainInDecibels = gainInDecibels
125126
if self.outputs is not None:
126-
if type(self.outputs) is not list:
127+
if isinstance(self.outputs, str):
127128
self.outputs = [self.outputs]
128-
self.model.setOutputsToAggregate(self.outputs)
129129
else:
130130
raise ValueError("Must specify outputs")
131131

132+
'''
133+
if aggregate:
134+
self.model.setOutputsToAggregate(self.outputs)
135+
'''
136+
132137
if not self.model.initialize(self.inputBuf):
133138
raise ValueError("Problem initialising the model!")
134139

135140
self.outputDict = {}
136-
self.nSamplesToPadStart = np.round(numSecondsToPadStartBy * self.fs)
137-
self.nSamplesToPadEnd = np.round(numSecondsToPadEndBy * self.fs)
141+
self.nSamplesToPadStart = int(
142+
np.round(numSecondsToPadStartBy * self.fs))
143+
self.nSamplesToPadEnd = int(
144+
np.round(numSecondsToPadEndBy * self.fs))
138145
self.frameTimeOffset = frameTimeOffset
139146
self.x = None
140147
self.processed = False
141148
self.loudness = None
142149
self.globalLoudness = None
143150

144-
def process(self, inputSignal):
145-
'''
146-
Process the numpy array `inputSignal' using a dynamic loudness
147-
model. For stereo signals, the input signal must have two dimensions
148-
with shape (nSamples x 2). For monophonic signals, the input signal
149-
can be 2D, i.e. (nSamples x 1), or one dimensional. For multiple
150-
sources, enter a list of inputSignals (easier than faffing with
151-
reshaping).
152-
'''
153-
# Input checks
154-
if type(inputSignal) is not list:
151+
def configureInput(self, inputSignal):
152+
153+
if not isinstance(inputSignal, list):
155154
if inputSignal.ndim == 1:
156-
self.inputSignal = inputSignal.reshape(1,
157-
1,
158-
1,
159-
inputSignal.size)
155+
sig = inputSignal.reshape(1,
156+
1,
157+
1,
158+
inputSignal.size)
160159
elif inputSignal.ndim < 3:
161-
self.inputSignal = inputSignal.T.reshape(1,
162-
inputSignal.shape[1],
163-
1,
164-
inputSignal.shape[0])
160+
sig = inputSignal.T.reshape(1,
161+
inputSignal.shape[1],
162+
1,
163+
inputSignal.shape[0])
165164
else:
166165
maxSamples = np.max([x.shape[0] for x in inputSignal])
167-
self.inputSignal = np.zeros((self.nInputSources,
168-
self.nInputEars,
169-
1,
170-
maxSamples))
171-
for i, sig in enumerate(inputSignal):
172-
self.inputSignal[i, :, 0, 0:sig.shape[0]] = sig.T
173-
174-
'''Pad end so that we can obtain analysis over the last sample,
175-
No neat way to do this at the moment so assume 0.2ms is enough.'''
176-
self.inputSignal = np.concatenate((
166+
sig = np.zeros((self.nInputSources,
167+
self.nInputEars,
168+
1,
169+
maxSamples))
170+
for i, isig in enumerate(inputSignal):
171+
sig[i, :, 0, :isig.shape[0]] = isig.T
172+
173+
sig = np.concatenate((
177174
np.zeros((self.nInputSources, self.nInputEars,
178175
1, self.nSamplesToPadStart)),
179-
self.inputSignal,
176+
sig,
180177
np.zeros((self.nInputSources, self.nInputEars,
181178
1, self.nSamplesToPadEnd))), 3)
182179

183-
# Apply any gain
184-
self.inputSignal *= 10 ** (self.gainInDecibels / 20.0)
185-
186180
# configure the number of output frames needed
187181
nOutputFrames = int(
188-
np.ceil(self.inputSignal.shape[3] / float(self.hopSize))
182+
np.ceil(sig.shape[3] / float(self.hopSize))
189183
)
190-
self.outputDict['FrameTimes'] = (
191-
self.frameTimeOffset + np.arange(nOutputFrames) *
192-
self.hopSize / float(self.fs)
184+
return sig, nOutputFrames
185+
186+
def outputToHDF5(self, inputSignal, nOutputFrames, hdf5Group):
187+
188+
hdf5Group.clear()
189+
hdf5Group.create_dataset(
190+
'FrameTime',
191+
data = self.frameTimeOffset + np.arange(nOutputFrames) *
192+
self.hopSize / float(self.fs),
193193
)
194194

195+
outputBanks = []
196+
datasets = []
197+
for name in self.outputs:
198+
bank = self.model.getOutput(name)
199+
outputBanks.append(bank)
200+
shape = (
201+
nOutputFrames,
202+
bank.getNSources(),
203+
bank.getNEars(),
204+
bank.getNChannels(),
205+
bank.getNSamples(),
206+
)
207+
208+
datasets.append(hdf5Group.create_dataset(name, shape, chunks=True))
209+
195210
# One process call every hop samples
196211
for frame in range(nOutputFrames):
197212

198-
# Any overlap is generated c++ side, so just fill the input
199-
# SignalBank with new blocks
200213
startIdx = frame * self.hopSize
201214
endIdx = startIdx + self.hopSize
202215
self.inputBuf.setSignals(
203-
self.inputSignal[:, :, :, startIdx:endIdx]
216+
inputSignal[:, :, :, startIdx:endIdx]
204217
)
205218

206219
# Process the input buffer
207220
self.model.process(self.inputBuf)
208221

209-
# get outputs
222+
# Store
223+
for bank, dataset in zip(outputBanks, datasets):
224+
dataset[frame] = bank.getSignals()
225+
226+
# Processing complete so clear internal states
227+
self.model.reset()
228+
self.processed = True
229+
230+
def outputToDictionary(self, inputSignal, nOutputFrames):
231+
232+
dic = {'FrameTime':
233+
self.frameTimeOffset + np.arange(nOutputFrames) *
234+
self.hopSize / float(self.fs),
235+
}
236+
237+
outputBanks = []
210238
for name in self.outputs:
211239
bank = self.model.getOutput(name)
212-
self.outputDict[name] = np.copy(
213-
np.squeeze(bank.getAggregatedSignals())
240+
outputBanks.append(bank)
241+
shape = (
242+
nOutputFrames,
243+
bank.getNSources(),
244+
bank.getNEars(),
245+
bank.getNChannels(),
246+
bank.getNSamples(),
214247
)
215-
if self.outputDict[name].ndim == 1:
216-
self.outputDict[name] = self.outputDict[name].reshape((-1, 1))
248+
dic[name] = np.squeeze(np.zeros(shape=shape))
249+
250+
# One process call every hop samples
251+
for frame in range(nOutputFrames):
252+
253+
startIdx = frame * self.hopSize
254+
endIdx = startIdx + self.hopSize
255+
self.inputBuf.setSignals(
256+
inputSignal[:, :, :, startIdx:endIdx]
257+
)
258+
259+
# Process the input buffer
260+
self.model.process(self.inputBuf)
261+
262+
# Store
263+
for bank, name in zip(outputBanks, self.outputs):
264+
dic[name][frame] = np.squeeze(bank.getSignals())
265+
266+
return dic
267+
268+
269+
def process(self, inputSignal, hdf5Group=None):
270+
'''
271+
Process the numpy array `inputSignal' using a dynamic loudness
272+
model. For stereo signals, the input signal must have two dimensions
273+
with shape (nSamples x 2). For monophonic signals, the input signal
274+
can be 2D, i.e. (nSamples x 1), or one dimensional. For multiple
275+
sources, enter a list of inputSignals (easier than faffing with
276+
reshaping).
277+
'''
278+
279+
if hdf5Group is None:
280+
sig, nOutputFrames = self.configureInput(inputSignal)
281+
dic = self.outputToDictionary(sig, nOutputFrames)
282+
elif isinstance(hdf5Group, h5py.Group):
283+
sig, nOutputFrames = self.configureInput(inputSignal)
284+
self.outputToHDF5(sig, nOutputFrames, hdf5Group)
217285

218286
# Processing complete so clear internal states
219287
self.model.reset()
220288
self.processed = True
221289

290+
if hdf5Group is None:
291+
return dic
292+
222293
def reinitializeModel(self):
223294
self.model.initialize(self.inputBuf)
224295

225-
def saveOutputToPickle(self, filename):
226-
'''
227-
Saves the complete output dictionary to a pickle file.
228-
'''
229-
if self.processed:
230-
print 'Saving to pickle file'
231-
filename, ext = os.path.splitext(filename)
232-
with open(filename + '.pickle', 'wb') as outfile:
233-
pickle.dump(
234-
self.outputDict,
235-
outfile,
236-
protocol=pickle.HIGHEST_PROTOCOL)
237-
238296

239297
class BatchWavFileProcessor:
240298
"""Class for processing multiple wav files using a given loudness model.
@@ -303,7 +361,7 @@ def process(self, model):
303361

304362
model.setOutputsToAggregate(self.outputs)
305363

306-
print "Output will be saved to ", self.filename
364+
print ("Output will be saved to ", self.filename)
307365
h5File = h5py.File(self.filename, 'w')
308366

309367
processor = ln.AudioFileProcessor(
@@ -315,7 +373,7 @@ def process(self, model):
315373

316374
for wavFile in self.wavFiles:
317375

318-
print("Processing file %s ..." % wavFile)
376+
print ("Processing file %s ..." % wavFile)
319377

320378
# Create new group
321379
wavFileGroup = h5File.create_group(wavFile)

‎src/python/iterators.py

+29-20
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import os
22
import numpy as np
33
import matplotlib.pyplot as plt
4-
from sound import Sound
5-
from extractors import DynamicLoudnessExtractor, StationaryLoudnessExtractor
4+
from .sound import Sound
5+
from .extractors import DynamicLoudnessExtractor, StationaryLoudnessExtractor
66

77

88
def asIs(x):
@@ -33,7 +33,8 @@ def __init__(self,
3333
nIters=10,
3434
alpha=1.0,
3535
nSecondsToPadStartBy=0.0,
36-
nSecondsToPadEndBy=0.2):
36+
nSecondsToPadEndBy=0.2,
37+
printResults=False):
3738

3839
if type(outputName) is list:
3940
outputName = outputName[0]
@@ -50,7 +51,7 @@ def __init__(self,
5051
self.tol = tol
5152
self.nIters = nIters
5253
self.alpha = alpha
53-
self.printResults = False
54+
self.printResults = printResults
5455

5556
if loudnessFunction is None:
5657
self.loudnessFunction = np.mean
@@ -59,9 +60,9 @@ def __init__(self,
5960

6061
def extractLoudness(self, signal, gainInDecibels=0.0):
6162

62-
self.extractor.gainInDecibels = gainInDecibels
63-
self.extractor.process(signal)
64-
timeSeries = self.extractor.outputDict[self.outputName]
63+
gain = 10 ** (gainInDecibels / 20.0)
64+
out = self.extractor.process(signal * gain)
65+
timeSeries = out[self.outputName]
6566
loudness = self.loudnessFunction(timeSeries)
6667
return loudness
6768

@@ -107,10 +108,12 @@ def __init__(self,
107108
loudnessFunction=None,
108109
tol=0.1,
109110
nIters=10,
110-
alpha=1.0):
111+
alpha=1.0,
112+
alwaysReinitialize=False):
111113

112114
self.outputName = outputName
113-
self.extractor = StationaryLoudnessExtractor(model, outputName)
115+
self.extractor = StationaryLoudnessExtractor(model, outputName,
116+
alwaysReinitialize)
114117
self.converged = False
115118
self.tol = tol
116119
self.nIters = nIters
@@ -184,29 +187,34 @@ def __init__(self,
184187
nIters=10,
185188
alpha=1.0,
186189
nSecondsToPadStartBy=0.0,
187-
nSecondsToPadEndBy=0.2):
190+
nSecondsToPadEndBy=0.2,
191+
printResults=False):
188192

189193
self.nSecondsToPadStartBy = nSecondsToPadStartBy
190194
self.nSecondsToPadEndBy = nSecondsToPadEndBy
191195
self.loudnessFunction = loudnessFunction
192196
self.tol = tol
193197
self.nIters = nIters
194198
self.alpha = alpha
199+
self.model = model
200+
self.fs = fs
201+
self.printResults = printResults
195202

196203
self.output = output
197204
if self.output is None or type(self.output) is list:
198205
raise ValueError("Only one model output allowed.")
199206

200-
def process(self, listOfInputSignals, targetLoudness):
207+
def process(self, listOfInputSignals, targetLoudness=None):
208+
print(self.printResults)
201209

202210
hasMultipleTargets = False
203-
if type(self.targetLoudness) in [np.ndarray, list]:
204-
if len(self.targetLoudness) == len(listOfInputSignals):
211+
if type(targetLoudness) in [np.ndarray, list]:
212+
if len(targetLoudness) == len(listOfInputSignals):
205213
hasMultipleTargets = True
206214

207-
self.gains = np.zeros(len(self.listOfInputSignals))
215+
gains = np.zeros(len(listOfInputSignals))
208216

209-
for i, signal in enumerate(self.listOfInputSignals):
217+
for i, signal in enumerate(listOfInputSignals):
210218

211219
if signal.ndim == 1:
212220
signal = signal.reshape((-1, 1))
@@ -222,22 +230,23 @@ def process(self, listOfInputSignals, targetLoudness):
222230
self.nIters,
223231
self.alpha,
224232
self.nSecondsToPadStartBy,
225-
self.nSecondsToPadEndBy)
233+
self.nSecondsToPadEndBy,
234+
self.printResults)
226235

227236
# Process the audio file
228237
print("Processing signal %d ..." % i)
229238

230239
if hasMultipleTargets:
231-
target = self.targetLoudness[i]
240+
target = targetLoudness[i]
232241
else:
233-
target = self.targetLoudness
242+
target = targetLoudness
234243

235244
gain = processor.process(signal, target)
236245

237246
# Get gain required for target loudness
238-
self.gains[i] = gain
247+
gains[i] = gain
239248

240-
return self.gains
249+
return gains
241250

242251

243252
class EqualDynamicLoudnessIterator():

‎src/python/predictors.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import numpy as np
22
import matplotlib.pyplot as plt
3-
from sound import Sound
4-
from iterators import StationaryLoudnessIterator, DynamicLoudnessIterator
5-
from extractors import StationaryLoudnessExtractor, DynamicLoudnessExtractor
6-
from scipy.interpolate import interp1d
3+
from .sound import Sound
4+
from .iterators import StationaryLoudnessIterator, DynamicLoudnessIterator
5+
from .extractors import StationaryLoudnessExtractor, DynamicLoudnessExtractor
6+
from scipy.interpolate import interp1d
77

88
# ISO 389-7 - free-field values
99
freqsISO389 = np.array([20.0, 25, 31.5, 40, 50, 63, 80, 100, 125, 160, 200,
@@ -101,7 +101,7 @@ def computeErrors(self):
101101
maxE = np.max(np.abs(error))
102102
return (error, rMSE, maxE)
103103
else:
104-
print 'Iterative process did not converge.'
104+
print ('Iterative process did not converge.')
105105
return (None, None, None)
106106

107107
def getResults(self):
@@ -219,7 +219,7 @@ def computeErrors(self):
219219
maxE = np.max(np.abs(error))
220220
return (error, rMSE, maxE)
221221
else:
222-
print 'Iterative process did not converge.'
222+
print ('Iterative process did not converge.')
223223
return (None, None, None)
224224

225225
def getResults(self):

‎src/python/sound.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ def applyRamp(self, dur):
188188
theta = np.pi * 0.5 * np.arange(d) / float(d)
189189
nSamplesmiddle = self.nSamples - 2 * d
190190
if nSamplesmiddle < 0:
191-
print "Duration too large"
191+
print ("Duration too large")
192192
else:
193193
self.data *= np.concatenate((
194194
np.cos(theta + np.pi * 1.5),

‎src/support/AuditoryTools.cpp

+54-31
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,10 @@ namespace loudness{
163163
void OME::getData()
164164
{
165165
//The data
166-
Real freqs[41] = {0, 20, 25, 31.5, 40, 50, 63, 80, 100, 125, 160, 200, 250,
167-
315, 400, 500, 630, 750, 800, 1000, 1250, 1500, 1600, 2000, 2500,
168-
3000, 3150, 4000, 5000, 6000, 6300, 8000, 9000, 10000, 11200, 12500,
169-
14000, 15000, 16000, 18000, 20000};
166+
Real freqs[41] = {0, 20, 25, 31.5, 40, 50, 63, 80, 100, 125, 160, 200,
167+
250, 315, 400, 500, 630, 750, 800, 1000, 1250, 1500, 1600, 2000,
168+
2500, 3000, 3150, 4000, 5000, 6000, 6300, 8000, 9000, 10000, 11200,
169+
12500, 14000, 15000, 16000, 18000, 20000};
170170

171171
Real freeANSI[41] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0.1, 0.3, 0.5, 0.9, 1.4,
172172
1.6, 1.7, 2.5, 2.7, 2.6, 2.6, 3.2, 5.2, 6.6, 12, 16.8, 15.3, 15.2,
@@ -176,11 +176,11 @@ namespace loudness{
176176
1.6, 1.7, 2.2, 2.7, 2.9, 3.8, 5.3, 6.8, 7.2, 10.2, 14.9, 14.5, 14.4,
177177
12.7, 10.8, 8.9, 8.7, 8.5, 6.2, 5, 4.5, 4, 3.3, 2.6, 2, 2, 2};
178178

179-
Real midANSI[41] = {-100, -39.6, -32, -25.85, -21.4, -18.5, -15.9, -14.1,
180-
-12.4, -11, -9.6, -8.3, -7.4, -6.2, -4.8, -3.8, -3.3, -2.9,
181-
-2.6, -2.6, -4.5, -5.4, -6.1, -8.5,-10.4, -7.3, -7, -6.6,
182-
-7, -9.2, -10.2, -12.2, -10.8, -10.1, -12.7, -15, -18.2,
183-
-23.8, -32.3, -45.5, -50};
179+
Real midANSI[41] = {-100, -39.6, -32, -25.85, -21.4, -18.5, -15.9,
180+
-14.1, -12.4, -11, -9.6, -8.3, -7.4, -6.2, -4.8, -3.8, -3.3, -2.9,
181+
-2.6, -2.6, -4.5, -5.4, -6.1, -8.5,-10.4, -7.3, -7, -6.6, -7, -9.2,
182+
-10.2, -12.2, -10.8, -10.1, -12.7, -15, -18.2, -23.8, -32.3, -45.5,
183+
-50};
184184

185185
Real midWard[41] = {-36.33, -36.33, -31.7, -27.24, -23.08, -19.61,
186186
-16.41, -13.4, -10.88, -8.67, -6.6, -4.88, -3.46, -2.23, -0.41,
@@ -196,25 +196,39 @@ namespace loudness{
196196
-21.8, -22.3, -23.2, -24};
197197

198198
//Beyerdynamic DT990 requires own sample points
199-
Real phoneFreqs[86] = {0, 5, 11, 17, 23, 29, 35, 41, 46, 52, 58, 64, 70,
200-
76, 82, 88, 101, 116, 133, 153, 176, 202, 232, 266, 306, 352, 404,
201-
464, 533, 612, 703, 808, 928, 1066, 1224, 1406, 1615, 1856, 2131,
202-
2448, 2813, 3231, 3711, 3928, 4144, 4396, 4897, 5215, 5437, 5625,
203-
5780, 6110, 6500, 6800, 7422, 7920, 8400, 8526, 8830, 9000, 9160,
204-
9230, 9360, 9650, 9900, 10500, 10900, 11250, 11700, 12200, 12500,
205-
12923, 13500, 14000, 14350, 14600, 14750, 14845, 15350, 16100,
206-
16700, 17200, 17750, 18420, 19587, 20000};
207-
208-
Real dt990[86] = {-6.45, -6.45, -7.48, -5.75, -3.69, -2.52, -1.61,
209-
-0.71, -0.42, 0.13, 0.24, -0.51, 0.56, 1.61, 2.02, 2.28, 2.6 ,
210-
2.79, 2.82, 2.69, 2.43, 2.09, 1.68, 1.31, 0.77, 0.15, -0.31,
211-
-0.64, -0.96, -1.36, -1.41, -1.16, -0.52, 0. , 1.24, 1.84, 2.8 ,
212-
4.28, 5.77, 7.5 , 8.39, 7.2 , 4.3 , 3.37, 4. , 4.52, 2.25,
213-
1.36, 2.26, 4.69, 7.01, 9.37, 9.6 , 9.66, 7.8 , 6.07, 8.25,
214-
8.9 , 7.84, 5.42, 2.24, 0.73, -0.08, 0.69, 1.23, 2.01, 1.47,
215-
0.8 , 0.54, 3.06, 3.5 , 1.79, -3.52, -7.86, -8.68, -9.81, -7.1 ,
216-
-5.09, -1.14, 0.02, 1.6 , 0.25, -2.8 , -3.97, -7.45, -6.8 };
217-
199+
Real phoneFreqs[88] = {0, 5, 11, 17, 23, 29, 35,
200+
41, 46, 52, 58, 64, 70, 76, 82, 88, 101,
201+
116, 133, 153, 176, 202, 232, 266, 306, 352, 404,
202+
464, 533, 612, 703, 808, 928, 1000, 1224, 1406, 1615,
203+
1856, 2131, 2448, 2813, 3231, 3711, 3928, 4144, 4396, 4897,
204+
5215, 5437, 5625, 5780, 5900, 6100, 6500, 7000, 7422, 7920,
205+
8400, 8526, 8830, 9000, 9160,9230, 9360, 9650, 9900, 10500, 10900,
206+
11300, 11700, 12000, 12200, 12700,13500,14000, 14350, 14600,14750,
207+
14845, 15350,16100, 16700,17000, 17200, 17750, 18420,19587,20000};
208+
209+
Real dt990[88] = {-6.40, -6.40, -7.62, -5.55, -3.56, -2.47, -1.60,
210+
-0.72, -0.39, 0.20, 0.22, -0.57, 0.61, 1.62, 2.00, 2.25, 2.56,
211+
2.78, 2.80, 2.69, 2.43, 2.13, 1.63, 1.32, 0.78, 0.14, -0.33, -0.64,
212+
-0.98, -1.37, -1.43, -1.18, -0.56, 0.00, 1.24, 1.84, 2.82, 4.31,
213+
5.82, 7.50, 8.37, 7.20, 4.32, 3.49, 3.99, 4.52, 2.26, 1.36, 2.25,
214+
4.67, 6.95, 8.37, 9.34, 9.63, 9.53, 7.98, 6.36, 8.78, 9.49, 8.85,
215+
6.96, 4.83, 3.87, 2.43, 0.83, 1.47, 2.81, 1.55, 0.74, 0.73, 1.97,
216+
3.25, 3.10, -3.52, -7.35, -8.66, -5.70, -3.47, -2.35, -0.35, 0.09,
217+
1.58, 1.29, 0.28, -2.79, -3.99, -7.43, -6.36};
218+
219+
// Based on MIT KEMAR ELEVATION=0 AZIMUTH=0
220+
Real kemarAvg00Freqs[42] = {100, 260, 300, 452, 581, 775, 904, 1080,
221+
1270, 1572, 1852, 2239, 2476, 2713, 2900, 3252, 3600, 4100, 4300,
222+
5017, 5211, 5491, 5900, 6072, 6309, 7321, 8300, 9173, 9539, 9755,
223+
10000, 10700, 11370, 12037, 13200, 13717, 15159, 16215, 16753,
224+
17162, 19251, 20000};
225+
226+
Real kemarAvg00[42] = {0.00, 1.00, 1.13, 0.99, 2.00, 2.02, 1.56, 2.22,
227+
2.03, 5.35, 10.24, 13.77, 12.80, 10.73, 10.08, 9.59, 10.30, 10.35,
228+
8.49, 5.98, 6.49, 6.57, 4.27, 2.75, 1.05, -3.83, -10.63, -5.24,
229+
-2.34, 0.34, 3.54, 4.24, 2.54, 2.92, 9.30, 10.01, 4.80, -4.14,
230+
-3.76, -4.44, -7.55, -12.19};
231+
218232
//frequencies to use for middle ear interpolation
219233
middleEarFreqPoints_.assign(freqs, freqs + 41);
220234

@@ -249,8 +263,13 @@ namespace loudness{
249263
outerEarFreqPoints_ = middleEarFreqPoints_;
250264
break;
251265
case BD_DT990:
252-
outerEardB_.assign(dt990, dt990 + 86);
253-
outerEarFreqPoints_.assign(phoneFreqs, phoneFreqs + 86);
266+
outerEardB_.assign(dt990, dt990 + 88);
267+
outerEarFreqPoints_.assign(phoneFreqs, phoneFreqs + 88);
268+
break;
269+
case LML_FREEFIELD:
270+
outerEardB_.assign(kemarAvg00, kemarAvg00 + 42);
271+
outerEarFreqPoints_.assign(kemarAvg00Freqs,
272+
kemarAvg00Freqs + 42);
254273
break;
255274
default:
256275
outerEarType_ = NONE;
@@ -307,13 +326,17 @@ namespace loudness{
307326
//outer ear
308327
if(outerEarType_ != NONE)
309328
{
329+
Real firstFreq = outerEarFreqPoints_.front();
330+
Real firstDataPoint = outerEardB_.front();
310331
Real lastFreq = outerEarFreqPoints_.back();
311332
Real lastDataPoint = outerEardB_.back();
312333
s.set_points(outerEarFreqPoints_, outerEardB_);
313334

314335
for(uint i = 0; i < freqs.size(); i++)
315336
{
316-
if(freqs[i] >= lastFreq)
337+
if(freqs[i] <= firstFreq)
338+
response_[i] += firstDataPoint;
339+
else if(freqs[i] >= lastFreq)
317340
response_[i] += lastDataPoint;
318341
else
319342
response_[i] += s(freqs[i]);

‎src/support/AuditoryTools.h

+12-1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,16 @@ namespace loudness{
6565
return (pow (10.0, (cam / 21.366)) - 1.0) / 4368e-6;
6666
}
6767

68+
inline Real hertzToNormalisedCam (Real freq)
69+
{
70+
return (hertzToCam(freq) / hertzToCam(1000.0)) - 1;
71+
}
72+
73+
inline Real normalisedCamToHertz (Real normalisedCam)
74+
{
75+
return (normalisedCam + 1) * hertzToCam(1000.0);
76+
}
77+
6878
/**
6979
* @brief Returns the ERB in Bark of the auditory filter for normally hearing
7080
* listeners at @a centreFreq in Hz.
@@ -213,7 +223,8 @@ namespace loudness{
213223
WARD_MIDDLE_EAR,
214224
ANSIS342007_FREEFIELD,
215225
ANSIS342007_DIFFUSEFIELD,
216-
BD_DT990
226+
BD_DT990,
227+
LML_FREEFIELD
217228
};
218229

219230
OME(){};

‎src/support/Common.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@
2222

2323
#include <memory>
2424
#include <vector>
25+
#include <cmath>
2526
#include <string>
2627
#include <algorithm>
2728
#include <map>
2829
#include "Debug.h"
29-
#include "UsefulFunctions.h"
3030

3131
/*
3232
* Expose objects

‎src/support/Filter.cpp

+1-12
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,10 @@
2222

2323
namespace loudness{
2424

25-
Filter::Filter() : gain_(1.0)
26-
{}
25+
Filter::Filter() {}
2726

2827
//Is this needed?
2928
Filter::Filter(int order) :
30-
gain_(1.0),
3129
order_(order)
3230
{}
3331

@@ -118,18 +116,9 @@ namespace loudness{
118116
return aCoefs_;
119117
}
120118

121-
Real Filter::getGain() const
122-
{
123-
return gain_;
124-
}
125-
126119
int Filter::getOrder() const
127120
{
128121
return order_;
129122
}
130123

131-
void Filter::setGain(Real gain)
132-
{
133-
gain_ = gain;
134-
}
135124
}

‎src/support/Filter.h

-10
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,6 @@ namespace loudness{
7979
const RealVec& getBCoefs() const;
8080
const RealVec& getACoefs() const;
8181

82-
/**
83-
* @brief Sets the linear gain of the digital filter.
84-
*/
85-
void setGain(Real gain);
86-
87-
/**
88-
* @brief Returns the linear gain of the digital filter.
89-
*/
90-
Real getGain() const;
91-
9282
/**
9383
* @brief Returns the order of the digital filter.
9484
*

‎src/support/SignalBank.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#define SIGNALBANK_H
2222

2323
#include "Common.h"
24+
#include "UsefulFunctions.h"
2425

2526
namespace loudness{
2627

@@ -243,7 +244,7 @@ namespace loudness{
243244
/** Get the value of a single sample in a given ear and channel.
244245
* watch your bounds.
245246
*/
246-
inline Real getSample(int source, int ear, int channel, int sample) const
247+
inline Real getSample(int source=0, int ear=0, int channel=0, int sample=0) const
247248
{
248249
LOUDNESS_ASSERT(
249250
isPositiveAndLessThanUpper(source, nSources_) &&

‎src/support/UsefulFunctions.h

+8-2
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,19 @@
2020
#ifndef USEFULFUNCTIONS_H
2121
#define USEFULFUNCTIONS_H
2222

23-
#include <cmath>
24-
#include <vector>
23+
#include "Common.h"
2524
#define PI 3.14159265358979323846264338327
2625
#define LOWER_LIMIT_DB -100
2726

2827
namespace loudness{
2928

29+
inline void killDenormal (Real& value)
30+
{
31+
static const Real antiDenormal = 1e-18;
32+
value += antiDenormal;
33+
value -= antiDenormal;
34+
}
35+
3036
/** Factorial of a number. */
3137
template <typename Type>
3238
inline Type factorial (Type const& value)

0 commit comments

Comments
 (0)
Please sign in to comment.