Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion ChangeLog
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@

### 3.7.0dev <- NOTE: the release version number will be 3.7.1 ###


- Automatic channel fader adjustment simplifies mixer setup by using the channel level meters (#1071).
(contributed by @JohannesBrx)

### 3.7.0 (2021-03-17) ###

Expand Down
171 changes: 171 additions & 0 deletions src/audiomixerboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -857,6 +857,8 @@ CAudioMixerBoard::CAudioMixerBoard ( QWidget* parent ) :
// create all mixer controls and make them invisible
vecpChanFader.Init ( MAX_NUM_CHANNELS );

vecAvgLevels.Init ( MAX_NUM_CHANNELS, 0.0f );

for ( int i = 0; i < MAX_NUM_CHANNELS; i++ )
{
vecpChanFader[i] = new CChannelFader ( this );
Expand Down Expand Up @@ -1145,6 +1147,7 @@ void CAudioMixerBoard::ApplyNewConClientList ( CVector<CChannelInfo>& vecChanInf
{
// the fader was not in use, reset everything for new client
vecpChanFader[i]->Reset();
vecAvgLevels[i] = 0.0f;

// check if this is my own fader and set fader property
if ( i == iMyChannelID )
Expand Down Expand Up @@ -1308,6 +1311,169 @@ void CAudioMixerBoard::SetAllFaderLevelsToNewClientLevel()
}
}

void CAudioMixerBoard::AutoAdjustAllFaderLevels()
{
QMutexLocker locker ( &Mutex );

// initialize variables used for statistics
float vecMaxLevel[MAX_NUM_FADER_GROUPS + 1];
int vecChannelsPerGroup[MAX_NUM_FADER_GROUPS + 1];
for ( int i = 0; i < MAX_NUM_FADER_GROUPS + 1; ++i )
{
vecMaxLevel[i] = LOW_BOUND_SIG_METER;
vecChannelsPerGroup[i] = 0;
}
CVector<CVector<float>> levels;
Comment thread
pljones marked this conversation as resolved.
levels.resize ( MAX_NUM_FADER_GROUPS + 1 );

// compute min/max level per group and number of channels per group
for ( int i = 0; i < MAX_NUM_CHANNELS; ++i )
{
// only apply to visible faders (and not to my own channel fader)
if ( vecpChanFader[i]->IsVisible() && ( i != iMyChannelID ) )
{
// map averaged meter output level to decibels
// (invert CStereoSignalLevelMeter::CalcLogResultForMeter)
float leveldB = vecAvgLevels[i] *
( UPPER_BOUND_SIG_METER - LOW_BOUND_SIG_METER ) /
NUM_STEPS_LED_BAR + LOW_BOUND_SIG_METER;

int group = vecpChanFader[i]->GetGroupID();
if ( group == INVALID_INDEX )
{
group = MAX_NUM_FADER_GROUPS;
}

Comment thread
pljones marked this conversation as resolved.
if ( leveldB >= AUTO_FADER_NOISE_THRESHOLD_DB )
{
vecMaxLevel[group] = fmax ( vecMaxLevel[group], leveldB );
levels[group].Add ( leveldB );
}
++vecChannelsPerGroup[group];
}
}

// sort levels for later median computation
Comment thread
pljones marked this conversation as resolved.
for ( int i = 0; i < MAX_NUM_FADER_GROUPS + 1; ++i )
{
std::sort ( levels[i].begin(), levels[i].end() );
}

// compute the number of active groups (at least one channel)
int cntActiveGroups = 0;
for ( int i = 0; i < MAX_NUM_FADER_GROUPS; ++i )
{
cntActiveGroups += vecChannelsPerGroup[i] > 0;
}

// only my channel is active, nothing to do
if ( cntActiveGroups == 0 &&
vecChannelsPerGroup[MAX_NUM_FADER_GROUPS] == 0 )
{
return;
}

// compute target level for each group
// (prevent clipping when each group contributes at maximum level)
float targetLevelPerGroup = -20.0f * log10 (
std::max ( cntActiveGroups, 1 ) );

// compute target levels for the channels of each group individually
float vecTargetChannelLevel[MAX_NUM_FADER_GROUPS + 1];
float levelOffset = 0.0f;
float minFader = 0.0f;
for ( int i = 0; i < MAX_NUM_FADER_GROUPS + 1; ++i )
{
// compute the target level for each channel in the current group
// (prevent clipping when each channel in this group contributes at
// the maximum level)
vecTargetChannelLevel[i] = vecChannelsPerGroup[i] > 0 ?
targetLevelPerGroup - 20.0f * log10 ( vecChannelsPerGroup[i] ) :
0.0f;

// get median level
int cntChannels = levels[i].Size();
if ( cntChannels == 0 )
{
continue;
Comment thread
pljones marked this conversation as resolved.
}
float refLevel = levels[i][cntChannels / 2];

// since we can only attenuate channels but not amplify, we have to
// check that the reference channel can be brought to the target
// level
if ( refLevel < vecTargetChannelLevel[i] )
{
// otherwise, we adjust the level offset in such a way that
// the level can be reached
levelOffset = fmin ( levelOffset,
refLevel - vecTargetChannelLevel[i] );

// compute the minimum necessary fader setting
minFader = fmin ( minFader, -vecMaxLevel[i] +
vecTargetChannelLevel[i] + levelOffset );
}
}

// take minimum fader value into account
// very weak channels would actually require strong channels to be
// attenuated to a large amount; however, the attenuation is limited by
// the faders
if ( minFader < -AUD_MIX_FADER_RANGE_DB )
{
levelOffset += -AUD_MIX_FADER_RANGE_DB - minFader;
Comment thread
ann0see marked this conversation as resolved.
}

// adjust all levels
for ( int i = 0; i < MAX_NUM_CHANNELS; ++i )
{
// only apply to visible faders (and not to my own channel fader)
if ( vecpChanFader[i]->IsVisible() && ( i != iMyChannelID ) )
{
// map averaged meter output level to decibels
// (invert CStereoSignalLevelMeter::CalcLogResultForMeter)
float leveldB = vecAvgLevels[i] *
( UPPER_BOUND_SIG_METER - LOW_BOUND_SIG_METER ) /
NUM_STEPS_LED_BAR + LOW_BOUND_SIG_METER;

int group = vecpChanFader[i]->GetGroupID();
if ( group == INVALID_INDEX )
{
if ( cntActiveGroups > 0 )
{
// do not adjust the channels without group in group mode
continue;
}
else
{
group = MAX_NUM_FADER_GROUPS;
}
}

// do not adjust channels with almost zero level to full level since
// the channel might simply be silent at the moment
if ( leveldB >= AUTO_FADER_NOISE_THRESHOLD_DB )
{
// compute new level
float newdBLevel = -leveldB + vecTargetChannelLevel[group] +
levelOffset;

// map range from decibels to fader level
// (this inverts MathUtils::CalcFaderGain)
float newFaderLevel = ( newdBLevel / AUD_MIX_FADER_RANGE_DB +
1.0f ) * AUD_MIX_FADER_MAX;

// limit fader
newFaderLevel = fmin ( fmax ( newFaderLevel, 0.0f),
float ( AUD_MIX_FADER_MAX ) );

// set fader level
vecpChanFader[i]->SetFaderLevel ( newFaderLevel, true );
}
}
}
}

void CAudioMixerBoard::StoreAllFaderSettings()
{
QMutexLocker locker ( &Mutex );
Expand Down Expand Up @@ -1511,6 +1677,11 @@ void CAudioMixerBoard::SetChannelLevels ( const CVector<uint16_t>& vecChannelLev
{
if ( vecpChanFader[iChId]->IsVisible() && ( i < iNumChannelLevels ) )
{
// compute exponential moving average
vecAvgLevels[iChId] =
(1.0f - AUTO_FADER_ADJUST_ALPHA) * vecAvgLevels[iChId] +
AUTO_FADER_ADJUST_ALPHA * vecChannelLevel[i];

vecpChanFader[iChId]->SetChannelLevel ( vecChannelLevel[i++] );

// show level only if we successfully received levels from the
Expand Down
2 changes: 2 additions & 0 deletions src/audiomixerboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ class CAudioMixerBoard :
void SetAllFaderLevelsToNewClientLevel();
void StoreAllFaderSettings();
void LoadAllFaderSettings();
void AutoAdjustAllFaderLevels();

protected:
class CMixerBoardScrollArea : public QScrollArea
Expand Down Expand Up @@ -276,6 +277,7 @@ class CAudioMixerBoard :
ERecorderState eRecorderState;
QMutex Mutex;
EChSortType eChSortType;
CVector<float> vecAvgLevels;

virtual void UpdateGainValue ( const int iChannelIdx,
const float fValue,
Expand Down
2 changes: 2 additions & 0 deletions src/clientdlg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,8 @@ CClientDlg::CClientDlg ( CClient* pNCliP,
pEditMenu->addAction ( tr ( "Set All Faders to New Client &Level" ), this,
SLOT ( OnSetAllFadersToNewClientLevel() ), QKeySequence ( Qt::CTRL + Qt::Key_L ) );

pEditMenu->addAction ( tr ( "Auto-Adjust all &Faders" ), this,
SLOT ( OnAutoAdjustAllFaderLevels() ), QKeySequence ( Qt::CTRL + Qt::Key_F ) );

// Main menu bar -----------------------------------------------------------
QMenuBar* pMenu = new QMenuBar ( this );
Expand Down
1 change: 1 addition & 0 deletions src/clientdlg.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ public slots:
void OnUseTowRowsForMixerPanel ( bool Checked ) { MainMixerBoard->SetNumMixerPanelRows ( Checked ? 2 : 1 ); }
void OnClearAllStoredSoloMuteSettings();
void OnSetAllFadersToNewClientLevel() { MainMixerBoard->SetAllFaderLevelsToNewClientLevel(); }
void OnAutoAdjustAllFaderLevels() { MainMixerBoard->AutoAdjustAllFaderLevels(); }

void OnSettingsStateChanged ( int value );
void OnChatStateChanged ( int value );
Expand Down
13 changes: 13 additions & 0 deletions src/global.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,19 @@ LED bar: lbr
#define AUD_MIX_FADER_MAX 100
#define AUD_MIX_PAN_MAX 100

// range of audio mixer fader
#define AUD_MIX_FADER_RANGE_DB 35.0f

// coefficient for averaging channel levels for automatic fader adjustment
#define AUTO_FADER_ADJUST_ALPHA 0.2f

// target level for auto fader adjustment in decibels
Comment thread
pljones marked this conversation as resolved.
#define AUTO_FADER_TARGET_LEVEL_DB -30.0f

// threshold in decibels below which the channel is considered as noise
// and not adjusted
#define AUTO_FADER_NOISE_THRESHOLD_DB -40.0f

// maximum number of fader groups (must be consistent to audiomixerboard implementation)
#define MAX_NUM_FADER_GROUPS 4

Expand Down
1 change: 1 addition & 0 deletions src/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,7 @@ CAboutDlg::CAboutDlg ( QWidget* parent ) : CBaseDlg ( parent )
"<p>David Kastrup (<a href=\"https://github.com/dakhubgit\">dakhubgit</a>)</p>"
"<p>Jordan Lum (<a href=\"https://github.com/mulyaj\">mulyaj</a>)</p>"
"<p>Noam Postavsky (<a href=\"https://github.com/npostavs\">npostavs</a>)</p>"
"<p>Johannes Brauers (<a href=\"https://github.com/JohannesBrx\">JohannesBrx</a>)</p>"
"<br>" + tr ( "For details on the contributions check out the " ) +
"<a href=\"https://github.com/jamulussoftware/jamulus/graphs/contributors\">" + tr ( "Github Contributors list" ) + "</a>." );

Expand Down
3 changes: 2 additions & 1 deletion src/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -1281,7 +1281,8 @@ class MathUtils
}
else
{
return powf ( 10.0f, ( fInValueRange0_1 * 35.0f - 35.0f ) / 20.0f );
return powf ( 10.0f, ( fInValueRange0_1 - 1.0f ) *
AUD_MIX_FADER_RANGE_DB / 20.0f );
}
}
};
Expand Down