Skip to content

Commit

Permalink
Loudness: add dual mono scanning mode
Browse files Browse the repository at this point in the history
implements reaper-oss#1209
  • Loading branch information
nofishonfriday committed Dec 3, 2019
1 parent 5db88c1 commit db8c1c6
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 21 deletions.
1 change: 1 addition & 0 deletions Breeder/BR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ static COMMAND_T g_commandTable[] =
{ { DEFACCEL, "SWS/BR: Normalize loudness of selected tracks to 0 LU" }, "BR_NORMALIZE_LOUDNESS_TRACKS_LU", NormalizeLoudness, NULL, -2, },

{ { DEFACCEL, "SWS/BR/NF: Toggle use high precision mode for loudness analyzing" }, "BR_NF_TOGGLE_LOUDNESS_HIGH_PREC", ToggleHighPrecisionOption, NULL, 0, IsHighPrecisionOptionEnabled},
{ { DEFACCEL, "SWS/BR/NF: Toggle use dual mono mode (for mono takes/channel modes) for loudness analyzing" }, "BR_NF_TOGGLE_LOUDNESS_DUAL_MONO", ToggleDualMonoOption, NULL, 0, IsDualMonoOptionEnabled },

/******************************************************************************
* MIDI editor - Item preview *
Expand Down
117 changes: 100 additions & 17 deletions Breeder/BR_Loudness.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ const int GO_TO_SHORTTERM = 0xF018;
const int GO_TO_MOMENTARY = 0xF019;
const int GO_TO_TRUE_PEAK = 0xF01A;
const int SET_DO_HIGH_PRECISION_MODE = 0xF01B;
const int SET_DO_DUAL_MONO_MODE = 0xF01C;

const int ANALYZE_TIMER = 1;
const int REANALYZE_TIMER = 2;
Expand Down Expand Up @@ -199,7 +200,8 @@ m_integratedOnly (false),
m_doTruePeak (true),
m_truePeakAnalyzed (false),
m_process (NULL),
m_doHighPrecisionMode (true)
m_doHighPrecisionMode (true),
m_doDualMonoMode (true)
{
}

Expand All @@ -221,7 +223,8 @@ m_integratedOnly (false),
m_doTruePeak (true),
m_truePeakAnalyzed (false),
m_process (NULL),
m_doHighPrecisionMode (true)
m_doHighPrecisionMode (true),
m_doDualMonoMode (true)
{
this->CheckSetAudioData();
}
Expand All @@ -244,7 +247,8 @@ m_integratedOnly (false),
m_doTruePeak (true),
m_truePeakAnalyzed (false),
m_process (NULL),
m_doHighPrecisionMode (true)
m_doHighPrecisionMode (true),
m_doDualMonoMode (true)
{
this->CheckSetAudioData();
}
Expand All @@ -256,12 +260,13 @@ BR_LoudnessObject::~BR_LoudnessObject ()
DestroyAudioAccessor(this->GetAudioData().audio);
}

bool BR_LoudnessObject::Analyze (bool integratedOnly, bool doTruePeak, bool doHighPrecisionMode)
bool BR_LoudnessObject::Analyze (bool integratedOnly, bool doTruePeak, bool doHighPrecisionMode, bool doDualMonoMode)
{
this->AbortAnalyze();
this->SetIntegratedOnly(integratedOnly);
this->SetDoTruePeak(doTruePeak);
this->SetDoHighPrecisionMode(doHighPrecisionMode);
this->SetDoDualMonoMode(doDualMonoMode);

// Is audio data still valid?
if (this->CheckSetAudioData())
Expand Down Expand Up @@ -432,7 +437,7 @@ void BR_LoudnessObject::SaveObject (ProjectStateContext* ctx)
ctx->AddLine(PROJ_OBJECT_KEY);
ctx->AddLine("%s %d %s", PROJ_OBJECT_KEY_TARGET, (this->GetTrack() ? 1 : 0), tmp);
ctx->AddLine("%s %lf %lf %lf %lf %lf %lf", PROJ_OBJECT_KEY_MEASUREMENTS, integrated, range, truePeak, truePeakPos, shortTermMax, momentaryMax);
ctx->AddLine("%s %d %d %d %d %d %d", PROJ_OBJECT_KEY_STATUS, this->GetDoTruePeak(), this->GetTruePeakAnalyzeStatus(), this->GetAnalyzedStatus(), this->GetIntegratedOnly(), VERSION, this->GetDoHighPrecisionMode());
ctx->AddLine("%s %d %d %d %d %d %d %d", PROJ_OBJECT_KEY_STATUS, this->GetDoTruePeak(), this->GetTruePeakAnalyzeStatus(), this->GetAnalyzedStatus(), this->GetIntegratedOnly(), VERSION, this->GetDoHighPrecisionMode(), this->GetDoDualMonoMode());

int count = 0;
WDL_FastString string;
Expand Down Expand Up @@ -470,7 +475,7 @@ void BR_LoudnessObject::SaveObject (ProjectStateContext* ctx)

bool BR_LoudnessObject::RestoreObject (ProjectStateContext* ctx)
{
bool doTruePeak = false, truePeakAnalyzed = false, analyzed = false, integratedOnly = false, doHighPrecision = false;
bool doTruePeak = false, truePeakAnalyzed = false, analyzed = false, integratedOnly = false, doHighPrecision = false, doDualMono = false;
int version = VERSION;

double integrated = NEGATIVE_INF;
Expand Down Expand Up @@ -514,6 +519,7 @@ bool BR_LoudnessObject::RestoreObject (ProjectStateContext* ctx)
integratedOnly = !!lp.gettoken_int(4);
version = lp.gettoken_int(5);
doHighPrecision = !!lp.gettoken_int(6);
doDualMono = !!lp.gettoken_int(7);
}
else if (!strcmp(lp.gettoken_str(0), PROJ_OBJECT_KEY_SHORT_TERM))
{
Expand Down Expand Up @@ -553,6 +559,7 @@ bool BR_LoudnessObject::RestoreObject (ProjectStateContext* ctx)
this->SetAnalyzedStatus(((version == VERSION) ? analyzed : false));
this->SetIntegratedOnly(integratedOnly);
this->SetDoHighPrecisionMode(doHighPrecision);
this->SetDoDualMonoMode(doDualMono);

return true;
}
Expand Down Expand Up @@ -1017,6 +1024,7 @@ unsigned WINAPI BR_LoudnessObject::AnalyzeData (void* loudnessObject)
const bool integratedOnly = _this->GetIntegratedOnly();
const bool doTruePeak = _this->GetDoTruePeak();
const bool doHighPrecisionMode = _this->GetDoHighPrecisionMode() && !integratedOnly;
const bool doDualMonoMode = _this->GetDoDualMonoMode();

/*
NF: fix for wrong results when analyzing item, item is not at pos 0.0 and contains take vol. env.
Expand All @@ -1041,10 +1049,20 @@ unsigned WINAPI BR_LoudnessObject::AnalyzeData (void* loudnessObject)
{
// Mono channel modes
if (data.channelMode <= 66)
{
ebur128_set_channel(loudnessState, 0, EBUR128_LEFT);
for (int i = 1; i <= data.channels; ++i)
ebur128_set_channel(loudnessState, i, EBUR128_UNUSED);
{
if (doDualMonoMode) {
ebur128_set_channel(loudnessState, 0, EBUR128_DUAL_MONO);
// Actually there's a check in ebur128_set_channel() for dual mono mode:
// if ebur128_init() is called with channels > 1 than dual mono mode is disabled.
// But it's ok because if source channels > 1 and set to mono channel modes,
// AudioAccessor returns dual mono anyway (what we want)
// so we can use default channel map in this case
}
else {
ebur128_set_channel(loudnessState, 0, EBUR128_LEFT);
for (int i = 1; i <= data.channels; ++i)
ebur128_set_channel(loudnessState, i, EBUR128_UNUSED);
}
}
// Stereo channel modes
else
Expand All @@ -1055,6 +1073,14 @@ unsigned WINAPI BR_LoudnessObject::AnalyzeData (void* loudnessObject)
ebur128_set_channel(loudnessState, i, EBUR128_UNUSED);
}
}
// Dual mono mode for mono takes
else if (doDualMonoMode && _this->m_take && data.channels == 1)
{
ebur128_set_channel(loudnessState, 0, EBUR128_DUAL_MONO);
for (int i = 1; i <= 4; ++i)
ebur128_set_channel(loudnessState, i, EBUR128_UNUSED);
}
// Normal channel mode
else
{
ebur128_set_channel(loudnessState, 0, EBUR128_LEFT);
Expand Down Expand Up @@ -1403,6 +1429,19 @@ bool BR_LoudnessObject::GetDoHighPrecisionMode()
return m_doHighPrecisionMode;
}

void BR_LoudnessObject::SetDoDualMonoMode(bool doDualMonoMode)
{
SWS_SectionLock lock(&m_mutex);
m_doDualMonoMode = doDualMonoMode;

}

bool BR_LoudnessObject::GetDoDualMonoMode()
{
SWS_SectionLock lock(&m_mutex);
return m_doDualMonoMode;
}

void BR_LoudnessObject::SetTruePeakAnalyzed (bool analyzed)
{
SWS_SectionLock lock(&m_mutex);
Expand Down Expand Up @@ -2096,8 +2135,9 @@ static WDL_DLGRET NormalizeProgressProc (HWND hwnd, UINT uMsg, WPARAM wParam, LP
doHighPrecisionMode = !!IsHighPrecisionOptionEnabled(NULL);

}

s_currentItem->Analyze(s_normalizeData->quickMode, false, doHighPrecisionMode);

bool doDualMonoMode = !!IsDualMonoOptionEnabled(NULL);
s_currentItem->Analyze(s_normalizeData->quickMode, false, doHighPrecisionMode, doDualMonoMode);
s_analyzeInProgress = true;
}
else
Expand Down Expand Up @@ -2283,6 +2323,7 @@ bool BR_AnalyzeLoudnessWnd::GetProperty (int propertySpecifier)
case USING_LU: return m_properties.usingLU;
case MIRROR_PROJ_SELECTION: return m_properties.mirrorProjSelection;
case DO_HIGH_PRECISION_MODE: return m_properties.doHighPrecisionMode;
case DO_DUAL_MONO_MODE: return m_properties.doDualMonoMode;
}
return false;
}
Expand All @@ -2296,6 +2337,7 @@ bool BR_AnalyzeLoudnessWnd::SetProperty(int propertySpecifier, bool newVal)
// case USING_LU: m_properties.usingLU = newVal; return true;
// case MIRROR_PROJ_SELECTION: m_properties.mirrorProjSelection = newVal; return true;
case DO_HIGH_PRECISION_MODE: m_properties.doHighPrecisionMode = newVal; return true;
case DO_DUAL_MONO_MODE: m_properties.doDualMonoMode = newVal; return true;
}
return false;
}
Expand Down Expand Up @@ -3391,6 +3433,14 @@ void BR_AnalyzeLoudnessWnd::OnCommand (WPARAM wParam, LPARAM lParam)
}
break;

case SET_DO_DUAL_MONO_MODE:
{
m_properties.doDualMonoMode = !m_properties.doDualMonoMode;
this->ClearList();
RefreshToolbar(NamedCommandLookup("_BR_NF_TOGGLE_LOUDNESS_DUAL_MONO"));
}
break;

case SET_UNIT_LUFS:
{
m_properties.usingLU = false;
Expand Down Expand Up @@ -3574,7 +3624,7 @@ void BR_AnalyzeLoudnessWnd::OnTimer (WPARAM wParam)
if ((s_currentObject = m_analyzeQueue.Get(0)))
{
s_currentObjectLen = s_currentObject->GetAudioLength();
s_currentObject->Analyze(false, m_properties.doTruePeak, m_properties.doHighPrecisionMode);
s_currentObject->Analyze(false, m_properties.doTruePeak, m_properties.doHighPrecisionMode, m_properties.doDualMonoMode);
m_analyzeInProgress = true;
}
else
Expand Down Expand Up @@ -3626,7 +3676,7 @@ void BR_AnalyzeLoudnessWnd::OnTimer (WPARAM wParam)
if ((s_currentObject = m_reanalyzeQueue.Get(0)))
{
s_currentObjectLen = s_currentObject->GetAudioLength();
s_currentObject->Analyze(false, m_properties.doTruePeak, m_properties.doHighPrecisionMode);
s_currentObject->Analyze(false, m_properties.doTruePeak, m_properties.doHighPrecisionMode, m_properties.doDualMonoMode);
m_analyzeInProgress = true;
}
else
Expand Down Expand Up @@ -3797,6 +3847,7 @@ HMENU BR_AnalyzeLoudnessWnd::OnContextMenu (int x, int y, bool* wantDefaultItems

AddToMenu((button ? menu : optionsMenu), __LOCALIZE("Measure true peak (slower)", "sws_DLG_174"), SET_DO_TRUE_PEAK, -1, false, m_properties.doTruePeak ? MF_CHECKED : MF_UNCHECKED);
AddToMenu((button ? menu : optionsMenu), __LOCALIZE("Use high precision mode (slower)", "sws_DLG_174"), SET_DO_HIGH_PRECISION_MODE, -1, false, m_properties.doHighPrecisionMode ? MF_CHECKED : MF_UNCHECKED);
AddToMenu((button ? menu : optionsMenu), __LOCALIZE("Use dual mono mode for mono takes/channel modes", "sws_DLG_174"), SET_DO_DUAL_MONO_MODE, -1, false, m_properties.doDualMonoMode ? MF_CHECKED : MF_UNCHECKED);
AddToMenu((button ? menu : optionsMenu), __LOCALIZE("Analyze after normalizing", "sws_DLG_174"), SET_ANALYZE_ON_NORMALIZE, -1, false, m_properties.analyzeOnNormalize ? MF_CHECKED : MF_UNCHECKED);
AddToMenu((button ? menu : optionsMenu), __LOCALIZE("Clear list when analyzing", "sws_DLG_174"), SET_CLEAR_ON_ANALYZE, -1, false, m_properties.clearAnalyzed ? MF_CHECKED : MF_UNCHECKED);
AddToMenu((button ? menu : optionsMenu), __LOCALIZE("Clear envelope when creating loudness graph", "sws_DLG_174"), SET_CLEAR_ENVELOPE, -1, false, m_properties.clearEnvelope ? MF_CHECKED : MF_UNCHECKED);
Expand Down Expand Up @@ -3851,7 +3902,8 @@ clearEnvelope (true),
clearAnalyzed (true),
doTruePeak (true),
usingLU (false),
doHighPrecisionMode (true)
doHighPrecisionMode (true),
doDualMonoMode (true)
{
}

Expand All @@ -3872,6 +3924,7 @@ void BR_AnalyzeLoudnessWnd::Properties::Load ()
doTruePeak = (lp.getnumtokens() > 7) ? !!lp.gettoken_int(7) : false;
usingLU = (lp.getnumtokens() > 8) ? !!lp.gettoken_int(8) : false;
doHighPrecisionMode = (lp.getnumtokens() > 9) ? !!lp.gettoken_int(9) : false;
doDualMonoMode = (lp.getnumtokens() > 10) ? !!lp.gettoken_int(10) : false;

GetPrivateProfileString("SWS", EXPORT_FORMAT_KEY, "$id - $target: $integrated, Range: $range, True peak: $truepeak", tmp, sizeof(tmp), get_ini_file());
exportFormat.Set(tmp);
Expand All @@ -3889,9 +3942,10 @@ void BR_AnalyzeLoudnessWnd::Properties::Save ()
int doTruePeakInt = doTruePeak;
int usingLUInt = usingLU;
int doHighPrecisionModeInt = doHighPrecisionMode;
int doDualMonoModeInt = doDualMonoMode;

char tmp[512];
snprintf(tmp, sizeof(tmp), "%d %d %d %d %d %d %d %d %d %d", analyzeTracksInt, analyzeOnNormalizeInt, mirrorProjSelectionInt, doubleClickGoToTargetInt, timeSelOverMaxInt, clearEnvelopeInt, clearAnalyzedInt, doTruePeakInt, usingLUInt, doHighPrecisionModeInt);
snprintf(tmp, sizeof(tmp), "%d %d %d %d %d %d %d %d %d %d %d", analyzeTracksInt, analyzeOnNormalizeInt, mirrorProjSelectionInt, doubleClickGoToTargetInt, timeSelOverMaxInt, clearEnvelopeInt, clearAnalyzedInt, doTruePeakInt, usingLUInt, doHighPrecisionModeInt, doDualMonoModeInt);
WritePrivateProfileString("SWS", LOUDNESS_KEY, tmp, get_ini_file());

WritePrivateProfileString("SWS", EXPORT_FORMAT_KEY, exportFormat.Get(), get_ini_file());
Expand Down Expand Up @@ -4129,6 +4183,18 @@ void ToggleHighPrecisionOption(COMMAND_T* ct)
RefreshToolbar(NamedCommandLookup("_BR_NF_TOGGLE_LOUDNESS_HIGH_PREC"));
}

void ToggleDualMonoOption(COMMAND_T* ct)
{
bool isDualMonoOptEnabled = !!IsDualMonoOptionEnabled(NULL);

BR_AnalyzeLoudnessWnd* dialog = g_loudnessWndManager.Get();
dialog->SetProperty(BR_AnalyzeLoudnessWnd::DO_DUAL_MONO_MODE, !isDualMonoOptEnabled);
dialog->SaveProperties();
dialog->ClearList();

RefreshToolbar(NamedCommandLookup("_BR_NF_TOGGLE_LOUDNESS_DUAL_MONO"));
}

/******************************************************************************
* Toggle states *
******************************************************************************/
Expand Down Expand Up @@ -4166,6 +4232,23 @@ int IsHighPrecisionOptionEnabled(COMMAND_T* ct)
return isHighPrecOptEnabled;
}

int IsDualMonoOptionEnabled(COMMAND_T* ct)
{
bool isDualMonoOptEnabled = false;
if (g_loudnessWndManager.Get())
isDualMonoOptEnabled = g_loudnessWndManager.Get()->GetProperty(BR_AnalyzeLoudnessWnd::DO_DUAL_MONO_MODE);
else
{
if (BR_AnalyzeLoudnessWnd * dialog = g_loudnessWndManager.Create())
{
dialog->LoadProperties();
isDualMonoOptEnabled = dialog->GetProperty(BR_AnalyzeLoudnessWnd::DO_DUAL_MONO_MODE);
}
}

return isDualMonoOptEnabled;
}


//////////////////////////////////////////////////////////////////
// //
Expand Down Expand Up @@ -4285,7 +4368,7 @@ static WDL_DLGRET NFAnalyzeLUFSProgressProc(HWND hwnd, UINT uMsg, WPARAM wParam,
if (!s_normalizeData->quickMode)
wantHighPrecisionMode = true;

s_currentItem->Analyze(s_normalizeData->quickMode, s_normalizeData->items->Get(s_currentItemId)->GetDoTruePeak(), wantHighPrecisionMode ? s_normalizeData->items->Get(s_currentItemId)->GetDoHighPrecisionMode() : false);
s_currentItem->Analyze(s_normalizeData->quickMode, s_normalizeData->items->Get(s_currentItemId)->GetDoTruePeak(), wantHighPrecisionMode ? s_normalizeData->items->Get(s_currentItemId)->GetDoHighPrecisionMode() : false, s_normalizeData->items->Get(s_currentItemId)->GetDoDualMonoMode());

s_analyzeInProgress = true;
}
Expand Down
13 changes: 9 additions & 4 deletions Breeder/BR_Loudness.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class BR_LoudnessObject
~BR_LoudnessObject ();

/* Analyze */
bool Analyze (bool integratedOnly, bool doTruePeak, bool doHighPrecisionMode);
bool Analyze (bool integratedOnly, bool doTruePeak, bool doHighPrecisionMode, bool doDualMonoMode);
void AbortAnalyze ();
bool IsRunning ();
double GetProgress ();
Expand Down Expand Up @@ -84,6 +84,8 @@ class BR_LoudnessObject
// see https://github.com/jiixyj/libebur128/issues/93#issuecomment-429043548
void SetDoHighPrecisionMode(bool doHighPrecisionMode);
bool GetDoHighPrecisionMode();
void SetDoDualMonoMode(bool doDualMonoMode);
bool GetDoDualMonoMode();

private:
struct AudioData
Expand Down Expand Up @@ -132,7 +134,7 @@ class BR_LoudnessObject
GUID m_guid;
double m_integrated, m_truePeak, m_truePeakPos, m_shortTermMax, m_momentaryMax, m_range;
double m_progress;
bool m_running, m_analyzed, m_killFlag, m_integratedOnly, m_doTruePeak, m_truePeakAnalyzed, m_doHighPrecisionMode;
bool m_running, m_analyzed, m_killFlag, m_integratedOnly, m_doTruePeak, m_truePeakAnalyzed, m_doHighPrecisionMode, m_doDualMonoMode;
HANDLE m_process;
SWS_Mutex m_mutex;
vector<double> m_shortTermValues;
Expand Down Expand Up @@ -236,8 +238,8 @@ class BR_AnalyzeLoudnessWnd : public SWS_DockWnd
void Update (bool updateList = true);
void KillNormalizeDlg ();
bool GetProperty (int propertySpecifier);
enum PropertySpecifier { TIME_SEL_OVER_MAX, DOUBLECLICK_GOTO_TARGET, USING_LU, MIRROR_PROJ_SELECTION, DO_HIGH_PRECISION_MODE };
// NF: for setting 'use high precision mode' (or other Loudness options) from actions (or ReaScript in future)
enum PropertySpecifier { TIME_SEL_OVER_MAX, DOUBLECLICK_GOTO_TARGET, USING_LU, MIRROR_PROJ_SELECTION, DO_HIGH_PRECISION_MODE, DO_DUAL_MONO_MODE };
// NF: for setting 'use high precision mode' / other Loudness options from actions (or ReaScript in future)
// returns true if Property is set successfully
bool SetProperty (int propertySpecifier, bool newVal);
void LoadProperties();
Expand Down Expand Up @@ -280,6 +282,7 @@ class BR_AnalyzeLoudnessWnd : public SWS_DockWnd
bool doTruePeak;
bool usingLU;
bool doHighPrecisionMode;
bool doDualMonoMode;
WDL_FastString exportFormat;
Properties ();
void Load ();
Expand Down Expand Up @@ -309,6 +312,7 @@ void NormalizeLoudness (COMMAND_T*);
void AnalyzeLoudness (COMMAND_T*);
void ToggleLoudnessPref (COMMAND_T*);
void ToggleHighPrecisionOption(COMMAND_T*);
void ToggleDualMonoOption(COMMAND_T*);

// #880
bool NFDoAnalyzeTakeLoudness_IntegratedOnly(MediaItem_Take*, double* lufsIntegrated);
Expand All @@ -323,3 +327,4 @@ int IsNormalizeLoudnessVisible (COMMAND_T*);
int IsAnalyzeLoudnessVisible (COMMAND_T*);
int IsLoudnessPrefVisible (COMMAND_T*);
int IsHighPrecisionOptionEnabled(COMMAND_T*);
int IsDualMonoOptionEnabled(COMMAND_T*);
5 changes: 5 additions & 0 deletions whatsnew.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
Loudness:
+Add dual mono scanning mode (Issue 1209)
When this mode is enabled, mono takes and takes set to one of the mono channel modes result in +3dB. Useful when exporting to a stereo mix.
More info https://forum.cockos.com/showthread.php?p=2192186#post2192186|here| (posts #2656, #2659) and https://github.com/jiixyj/libebur128/issues/42#issuecomment-213695127|here|.

!v2.11.0 pre-release build
<strong>Reminder: this new SWS version requires REAPER v5.979+!</strong>

Expand Down

0 comments on commit db8c1c6

Please sign in to comment.