Merge pull request #42 from pljones/feature/channel-meters

Feature/channel meters
This commit is contained in:
corrados 2020-04-06 20:13:29 +02:00 committed by GitHub
commit 94ad067047
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 518 additions and 61 deletions

View file

@ -35,17 +35,29 @@ CChannelFader::CChannelFader ( QWidget* pNW,
// QWidget takes the ownership of the pMainGrid so that this only has
// to be created locally in this constructor)
pFrame = new QFrame ( pNW );
QVBoxLayout* pMainGrid = new QVBoxLayout ( pFrame );
pFader = new QSlider ( Qt::Vertical, pFrame );
pcbMute = new QCheckBox ( "Mute", pFrame );
pcbSolo = new QCheckBox ( "Solo", pFrame );
pLevelsBox = new QWidget ( pFrame );
plbrChannelLevel = new CMultiColorLEDBar ( pLevelsBox );
pFader = new QSlider ( Qt::Vertical, pLevelsBox );
pMuteSoloBox = new QWidget ( pFrame );
pcbMute = new QCheckBox ( "Mute", pMuteSoloBox );
pcbSolo = new QCheckBox ( "Solo", pMuteSoloBox );
pLabelInstBox = new QGroupBox ( pFrame );
plblLabel = new QLabel ( "", pFrame );
plblInstrument = new QLabel ( pFrame );
plblCountryFlag = new QLabel ( pFrame );
QVBoxLayout* pMainGrid = new QVBoxLayout ( pFrame );
QHBoxLayout* pLevelsGrid = new QHBoxLayout ( pLevelsBox );
QVBoxLayout* pMuteSoloGrid = new QVBoxLayout ( pMuteSoloBox );
QHBoxLayout* pLabelGrid = new QHBoxLayout ( pLabelInstBox );
QVBoxLayout* pLabelPictGrid = new QVBoxLayout ( );
// setup channel level
plbrChannelLevel->setContentsMargins( 0, 3, 2, 3 );
// setup slider
pFader->setPageStep ( 1 );
pFader->setTickPosition ( QSlider::TicksBothSides );
@ -62,18 +74,30 @@ CChannelFader::CChannelFader ( QWidget* pNW,
// set margins of the layouts to zero to get maximum space for the controls
pMainGrid->setContentsMargins ( 0, 0, 0, 0 );
pLevelsGrid->setContentsMargins ( 0, 0, 0, 0 );
pLevelsGrid->setSpacing ( 0 ); // only minimal space
pMuteSoloGrid->setContentsMargins ( 0, 0, 0, 0 );
pLabelGrid->setContentsMargins ( 0, 0, 0, 0 );
pLabelGrid->setSpacing ( 2 ); // only minimal space between picture and text
// add user controls to the grids
pLabelPictGrid->addWidget ( plblCountryFlag, 0, Qt::AlignHCenter );
pLabelPictGrid->addWidget ( plblInstrument, 0, Qt::AlignHCenter );
pLabelGrid->addLayout ( pLabelPictGrid );
pLabelGrid->addWidget ( plblLabel, 0, Qt::AlignVCenter );
pMainGrid->addWidget ( pFader, 0, Qt::AlignHCenter );
pMainGrid->addWidget ( pcbMute, 0, Qt::AlignLeft );
pMainGrid->addWidget ( pcbSolo, 0, Qt::AlignLeft );
pLevelsGrid->addWidget ( plbrChannelLevel, 0, Qt::AlignRight );
pLevelsGrid->addWidget ( pFader, 0, Qt::AlignLeft );
pMuteSoloGrid->addWidget ( pcbMute, 0, Qt::AlignLeft );
pMuteSoloGrid->addWidget ( pcbSolo, 0, Qt::AlignLeft );
pMainGrid->addWidget ( pLevelsBox, 0, Qt::AlignHCenter );
pMainGrid->addWidget ( pMuteSoloBox, 0, Qt::AlignHCenter );
pMainGrid->addWidget ( pLabelInstBox );
// add fader frame to audio mixer board layout
@ -83,17 +107,23 @@ CChannelFader::CChannelFader ( QWidget* pNW,
Reset();
// add help text to controls
plbrChannelLevel->setWhatsThis ( tr ( "<b>Channel Level:</b> Displays the "
"pre-fader audio level of this channel. All connected clients at the "
"server will be assigned an audio level, the same value for each client." ) );
plbrChannelLevel->setAccessibleName ( tr ( "Input level of the current audio "
"channel at the server" ) );
pFader->setWhatsThis ( tr ( "<b>Mixer Fader:</b> Adjusts the audio level of "
"this channel. All connected clients at the server will be assigned "
"an audio fader at each client." ) );
pFader->setAccessibleName ( tr ( "Mixer level setting of the connected client "
"at the server" ) );
"an audio fader at each client, adjusting the local mix." ) );
pFader->setAccessibleName ( tr ( "Local mix level setting of the current audio "
"channel at the server" ) );
pcbMute->setWhatsThis ( tr ( "<b>Mute:</b> With the Mute checkbox, the current "
pcbMute->setWhatsThis ( tr ( "<b>Mute:</b> With the Mute checkbox, the "
"audio channel can be muted." ) );
pcbMute->setAccessibleName ( tr ( "Mute button" ) );
pcbSolo->setWhatsThis ( tr ( "<b>Solo:</b> With the Solo checkbox, the current "
pcbSolo->setWhatsThis ( tr ( "<b>Solo:</b> With the Solo checkbox, the "
"audio channel can be set to solo which means that all other channels "
"except of the current channel are muted. It is possible to set more than "
"one channel to solo." ) );
@ -158,6 +188,11 @@ void CChannelFader::SetGUIDesign ( const EGUIDesign eNewDesign )
}
}
void CChannelFader::SetDisplayChannelLevel ( const bool eNDCL )
{
plbrChannelLevel->setHidden( !eNDCL );
}
void CChannelFader::SetupFaderTag ( const ESkillLevel eSkillLevel )
{
// setup group box for label/instrument picture: set a thick black border
@ -306,6 +341,11 @@ void CChannelFader::UpdateSoloState ( const bool bNewOtherSoloState )
}
}
void CChannelFader::SetChannelLevel ( const uint16_t iLevel )
{
plbrChannelLevel->setValue ( iLevel );
}
void CChannelFader::SetText ( const CChannelInfo& ChanInfo )
{
// store original received name
@ -634,6 +674,17 @@ void CAudioMixerBoard::SetGUIDesign ( const EGUIDesign eNewDesign )
}
}
void CAudioMixerBoard::SetDisplayChannelLevels ( const bool eNDCL )
{
bDisplayChannelLevels = eNDCL;
// apply preference to child GUI controls
for ( int i = 0; i < MAX_NUM_CHANNELS; i++ )
{
vecpChanFader[i]->SetDisplayChannelLevel ( bDisplayChannelLevels );
}
}
void CAudioMixerBoard::HideAll()
{
// make all controls invisible
@ -710,6 +761,9 @@ void CAudioMixerBoard::ApplyNewConClientList ( CVector<CChannelInfo>& vecChanInf
}
}
// At some future time a new level will arrive -- ???
vecpChanFader[i]->SetChannelLevel ( 0 );
// set the text in the fader
vecpChanFader[i]->SetText ( vecChanInfo[j] );
@ -857,3 +911,17 @@ bool CAudioMixerBoard::GetStoredFaderSettings ( const CChannelInfo& ChanInfo,
// return "not OK" since we did not find matching fader settings
return false;
}
void CAudioMixerBoard::SetChannelLevels ( const CVector<uint16_t>& vecChannelLevel )
{
const int iNumChannelLevels = vecChannelLevel.Size();
int i = 0;
for ( int iChId = 0; iChId < MAX_NUM_CHANNELS; iChId++ )
{
if ( vecpChanFader[iChId]->IsVisible() && i < iNumChannelLevels )
{
vecpChanFader[iChId]->SetChannelLevel ( vecChannelLevel[i++] );
}
}
}

View file

@ -36,6 +36,7 @@
#include <QHostAddress>
#include "global.h"
#include "util.h"
#include "multicolorledbar.h"
/* Classes ********************************************************************/
@ -55,6 +56,7 @@ public:
bool IsSolo() { return pcbSolo->isChecked(); }
bool IsMute() { return pcbMute->isChecked(); }
void SetGUIDesign ( const EGUIDesign eNewDesign );
void SetDisplayChannelLevel ( const bool eNDCL );
void UpdateSoloState ( const bool bNewOtherSoloState );
void SetFaderLevel ( const int iLevel );
@ -62,6 +64,7 @@ public:
void SetFaderIsMute ( const bool bIsMute );
int GetFaderLevel() { return pFader->value(); }
void Reset();
void SetChannelLevel ( const uint16_t iLevel );
protected:
double CalcFaderGain ( const int value );
@ -70,10 +73,16 @@ protected:
void SetupFaderTag ( const ESkillLevel eSkillLevel );
QFrame* pFrame;
QGroupBox* pLabelInstBox;
QWidget* pLevelsBox;
QWidget* pMuteSoloBox;
CMultiColorLEDBar* plbrChannelLevel;
QSlider* pFader;
QCheckBox* pcbMute;
QCheckBox* pcbSolo;
QGroupBox* pLabelInstBox;
QLabel* plblLabel;
QLabel* plblInstrument;
QLabel* plblCountryFlag;
@ -103,10 +112,13 @@ public:
void ApplyNewConClientList ( CVector<CChannelInfo>& vecChanInfo );
void SetServerName ( const QString& strNewServerName );
void SetGUIDesign ( const EGUIDesign eNewDesign );
void SetDisplayChannelLevels ( const bool eNDCL );
void SetFaderLevel ( const int iChannelIdx,
const int iValue );
void SetChannelLevels ( const CVector<uint16_t>& vecChannelLevel );
// settings
CVector<QString> vecStoredFaderTags;
CVector<int> vecStoredFaderLevels;
@ -129,6 +141,7 @@ protected:
CVector<CChannelFader*> vecpChanFader;
QGroupBox* pGroupBox;
QHBoxLayout* pMainLayout;
bool bDisplayChannelLevels;
bool bNoFaderVisible;
public slots:

View file

@ -102,6 +102,10 @@ qRegisterMetaType<CHostAddress> ( "CHostAddress" );
QObject::connect( &Protocol,
SIGNAL ( LicenceRequired ( ELicenceType ) ),
SIGNAL ( LicenceRequired ( ELicenceType ) ) );
QObject::connect ( &Protocol,
SIGNAL ( ReqChannelLevelList ( bool ) ),
this, SLOT ( OnReqChannelLevelList ( bool ) ) );
}
bool CChannel::ProtocolIsEnabled()

View file

@ -159,12 +159,18 @@ public:
void CreateReqConnClientsList() { Protocol.CreateReqConnClientsList(); }
void CreateChatTextMes ( const QString& strChatText ) { Protocol.CreateChatTextMes ( strChatText ); }
void CreateLicReqMes ( const ELicenceType eLicenceType ) { Protocol.CreateLicenceRequiredMes ( eLicenceType ); }
void CreateReqChannelLevelListMes ( bool bOptIn ) { Protocol.CreateReqChannelLevelListMes ( bOptIn ); }
void CreateConClientListMes ( const CVector<CChannelInfo>& vecChanInfo )
{ Protocol.CreateConClientListMes ( vecChanInfo ); }
CNetworkTransportProps GetNetworkTransportPropsFromCurrentSettings();
bool ChannelLevelsRequired() const { return bChannelLevelsRequired; }
double GetPrevLevel() const { return dPrevLevel; }
void SetPrevLevel ( const double nPL ) { dPrevLevel = nPL; }
protected:
bool ProtocolIsEnabled();
@ -177,6 +183,8 @@ protected:
iNetwFrameSizeFact = FRAME_SIZE_FACTOR_PREFERRED;
iNetwFrameSize = CELT_MINIMUM_NUM_BYTES;
iNumAudioChannels = 1; // mono
dPrevLevel = 0.0;
}
// connection parameters
@ -216,6 +224,9 @@ protected:
QMutex MutexSocketBuf;
QMutex MutexConvBuf;
bool bChannelLevelsRequired;
double dPrevLevel;
public slots:
void OnSendProtMessage ( CVector<uint8_t> vecMessage );
void OnJittBufSizeChange ( int iNewJitBufSize );
@ -249,6 +260,8 @@ public slots:
void OnNewConnection() { emit NewConnection(); }
void OnReqChannelLevelList ( bool bOptIn ) { bChannelLevelsRequired = bOptIn; }
signals:
void MessReadyForSending ( CVector<uint8_t> vecMessage );
void NewConnection();

View file

@ -65,6 +65,7 @@ CClient::CClient ( const quint16 iPortNumber,
bFraSiFactDefSupported ( false ),
bFraSiFactSafeSupported ( false ),
eGUIDesign ( GD_ORIGINAL ),
bDisplayChannelLevels ( true ),
bJitterBufferOK ( true ),
strCentralServerAddress ( "" ),
bUseDefaultCentralServerAddress ( true ),
@ -191,6 +192,10 @@ CClient::CClient ( const quint16 iPortNumber,
SIGNAL ( CLVersionAndOSReceived ( CHostAddress, COSUtil::EOpSystemType, QString ) ) );
#endif
QObject::connect ( &ConnLessProtocol,
SIGNAL ( CLChannelLevelListReceived ( CHostAddress, CVector<uint16_t> ) ),
this, SLOT ( OnCLChannelLevelListReceived ( CHostAddress, CVector<uint16_t> ) ) );
// other
QObject::connect ( &Sound, SIGNAL ( ReinitRequest ( int ) ),
this, SLOT ( OnSndCrdReinitRequest ( int ) ) );
@ -279,6 +284,9 @@ void CClient::OnNewConnection()
// Same problem is with the jitter buffer message.
Channel.CreateReqConnClientsList();
CreateServerJitterBufferMessage();
// send opt-in / out for Channel Level updates
Channel.CreateReqChannelLevelListMes ( bDisplayChannelLevels );
}
void CClient::CreateServerJitterBufferMessage()
@ -387,6 +395,14 @@ bool CClient::GetAndResetbJitterBufferOKFlag()
return bSocketJitBufOKFlag;
}
void CClient::SetDisplayChannelLevels ( const bool bNDCL )
{
bDisplayChannelLevels = bNDCL;
// tell any connected server about the change
Channel.CreateReqChannelLevelListMes ( bDisplayChannelLevels );
}
void CClient::SetSndCrdPrefFrameSizeFactor ( const int iNewFactor )
{
// first check new input parameter
@ -598,6 +614,12 @@ void CClient::OnSndCrdReinitRequest ( int iSndCrdResetType )
}
}
void CClient::OnCLChannelLevelListReceived ( CHostAddress InetAddr,
CVector<uint16_t> vecLevelList )
{
emit CLChannelLevelListReceived ( InetAddr, vecLevelList );
}
void CClient::Start()
{
// always use the OPUS codec

View file

@ -128,6 +128,9 @@ public:
EGUIDesign GetGUIDesign() const { return eGUIDesign; }
void SetGUIDesign ( const EGUIDesign eNGD ) { eGUIDesign = eNGD; }
bool GetDisplayChannelLevels() const { return bDisplayChannelLevels; }
void SetDisplayChannelLevels ( const bool bNDCL );
EAudioQuality GetAudioQuality() const { return eAudioQuality; }
void SetAudioQuality ( const EAudioQuality eNAudioQuality );
@ -359,6 +362,7 @@ protected:
int iStereoBlockSizeSam;
EGUIDesign eGUIDesign;
bool bDisplayChannelLevels;
bool bJitterBufferOK;
@ -398,6 +402,9 @@ public slots:
void OnSndCrdReinitRequest ( int iSndCrdResetType );
void OnCLChannelLevelListReceived ( CHostAddress InetAddr,
CVector<uint16_t> vecLevelList );
signals:
void ConClientListMesReceived ( CVector<CChannelInfo> vecChanInfo );
void ChatTextReceived ( QString strChatText );
@ -420,6 +427,9 @@ signals:
QString strVersion );
#endif
void CLChannelLevelListReceived ( CHostAddress InetAddr,
CVector<uint16_t> vecLevelList );
void Disconnected();
void ControllerInFaderLevel ( int iChannelIdx, int iValue );
};

View file

@ -185,6 +185,9 @@ CClientDlg::CClientDlg ( CClient* pNCliP,
// reset mixer board
MainMixerBoard->HideAll();
// restore channel level display preference
MainMixerBoard->SetDisplayChannelLevels ( pClient->GetDisplayChannelLevels() );
// restore fader settings
MainMixerBoard->vecStoredFaderTags = pClient->vecStoredFaderTags;
MainMixerBoard->vecStoredFaderLevels = pClient->vecStoredFaderLevels;
@ -483,6 +486,10 @@ CClientDlg::CClientDlg ( CClient* pNCliP,
SIGNAL ( ControllerInFaderLevel ( int, int ) ),
this, SLOT ( OnControllerInFaderLevel ( int, int ) ) );
QObject::connect ( pClient,
SIGNAL ( CLChannelLevelListReceived ( CHostAddress, CVector<uint16_t> ) ),
this, SLOT ( OnCLChannelLevelListReceived ( CHostAddress, CVector<uint16_t> ) ) );
#ifdef ENABLE_CLIENT_VERSION_AND_OS_DEBUGGING
QObject::connect ( pClient,
SIGNAL ( CLVersionAndOSReceived ( CHostAddress, COSUtil::EOpSystemType, QString ) ),
@ -495,6 +502,9 @@ CClientDlg::CClientDlg ( CClient* pNCliP,
QObject::connect ( &ClientSettingsDlg, SIGNAL ( GUIDesignChanged() ),
this, SLOT ( OnGUIDesignChanged() ) );
QObject::connect ( &ClientSettingsDlg, SIGNAL ( DisplayChannelLevelsChanged() ),
this, SLOT ( OnDisplayChannelLevelsChanged() ) );
QObject::connect ( &ClientSettingsDlg, SIGNAL ( AudioChannelsChanged() ),
this, SLOT ( OnAudioChannelsChanged() ) );
@ -906,7 +916,7 @@ void CClientDlg::OnTimerSigMet()
// linear transformation of the input level range to the progress-bar
// range
dCurSigLevelL -= LOW_BOUND_SIG_METER;
dCurSigLevelL *= NUM_STEPS_INP_LEV_METER /
dCurSigLevelL *= NUM_STEPS_LED_BAR /
( UPPER_BOUND_SIG_METER - LOW_BOUND_SIG_METER );
// lower bound the signal
@ -916,7 +926,7 @@ void CClientDlg::OnTimerSigMet()
}
dCurSigLevelR -= LOW_BOUND_SIG_METER;
dCurSigLevelR *= NUM_STEPS_INP_LEV_METER /
dCurSigLevelR *= NUM_STEPS_LED_BAR /
( UPPER_BOUND_SIG_METER - LOW_BOUND_SIG_METER );
// lower bound the signal

View file

@ -60,10 +60,6 @@
#define BUFFER_LED_UPDATE_TIME_MS 300 // ms
#define LED_BAR_UPDATE_TIME_MS 1000 // ms
// range for signal level meter
#define LOW_BOUND_SIG_METER ( -50.0 ) // dB
#define UPPER_BOUND_SIG_METER ( 0.0 ) // dB
// number of ping times > upper bound until error message is shown
#define NUM_HIGH_PINGS_UNTIL_ERROR 5
@ -196,12 +192,19 @@ public slots:
CVector<CChannelInfo> vecChanInfo )
{ ConnectDlg.SetConnClientsList ( InetAddr, vecChanInfo ); }
void OnCLChannelLevelListReceived ( CHostAddress /* unused */,
CVector<uint16_t> vecLevelList )
{ MainMixerBoard->SetChannelLevels ( vecLevelList ); }
void OnConnectDlgAccepted();
void OnDisconnected();
void OnGUIDesignChanged()
{ SetGUIDesign ( pClient->GetGUIDesign() ); }
void OnDisplayChannelLevelsChanged()
{ MainMixerBoard->SetDisplayChannelLevels ( pClient->GetDisplayChannelLevels() ); }
void OnAudioChannelsChanged() { UpdateRevSelection(); }
void OnNumClientsChanged ( int iNewNumClients );
void OnNewClientLevelChanged() { MainMixerBoard->iNewClientFaderLevel = pClient->iNewClientFaderLevel; }

View file

@ -189,6 +189,12 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent,
chbGUIDesignFancy->setAccessibleName ( tr ( "Fancy skin check box" ) );
// display channel levels
chbDisplayChannelLevels->setWhatsThis ( tr ( "<b>Display Channel Levels:</b> "
"If enabled, each client channel will display a pre-fader level bar." ) );
chbDisplayChannelLevels->setAccessibleName ( tr ( "Display channel levels check box" ) );
// audio channels
QString strAudioChannels = tr ( "<b>Audio Channels:</b> "
"Select the number of audio channels to be used. There are three "
@ -323,6 +329,9 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent,
chbGUIDesignFancy->setCheckState ( Qt::Checked );
}
// Display Channel Levels check box
chbDisplayChannelLevels->setCheckState ( pClient->GetDisplayChannelLevels() ? Qt::Checked : Qt::Unchecked );
// "Audio Channels" combo box
cbxAudioChannels->clear();
cbxAudioChannels->addItem ( "Mono" ); // CC_MONO
@ -386,6 +395,9 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent,
QObject::connect ( chbGUIDesignFancy, SIGNAL ( stateChanged ( int ) ),
this, SLOT ( OnGUIDesignFancyStateChanged ( int ) ) );
QObject::connect ( chbDisplayChannelLevels, SIGNAL ( stateChanged ( int ) ),
this, SLOT ( OnDisplayChannelLevelsStateChanged ( int ) ) );
QObject::connect ( chbAutoJitBuf, SIGNAL ( stateChanged ( int ) ),
this, SLOT ( OnAutoJitBufStateChanged ( int ) ) );
@ -704,6 +716,12 @@ void CClientSettingsDlg::OnGUIDesignFancyStateChanged ( int value )
UpdateDisplay();
}
void CClientSettingsDlg::OnDisplayChannelLevelsStateChanged ( int value )
{
pClient->SetDisplayChannelLevels ( value != Qt::Unchecked );
emit DisplayChannelLevelsChanged();
}
void CClientSettingsDlg::OnDefaultCentralServerStateChanged ( int value )
{
// apply new setting to the client

View file

@ -90,6 +90,7 @@ protected:
void OnSliderSndCrdBufferDelay ( int value );
void OnAutoJitBufStateChanged ( int value );
void OnGUIDesignFancyStateChanged ( int value );
void OnDisplayChannelLevelsStateChanged ( int value );
void OnDefaultCentralServerStateChanged ( int value );
void OnCentralServerAddressEditingFinished();
void OnNewClientLevelEditingFinished();
@ -105,6 +106,7 @@ protected:
signals:
void GUIDesignChanged();
void DisplayChannelLevelsChanged();
void AudioChannelsChanged();
void NewClientLevelChanged();
};

View file

@ -529,6 +529,8 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout">
<item>
<widget class="QCheckBox" name="chbGUIDesignFancy">
<property name="text">
@ -536,6 +538,15 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="chbDisplayChannelLevels">
<property name="text">
<string>Display Channel Levels</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout">
<item>
@ -719,6 +730,7 @@
<tabstop>cbxAudioQuality</tabstop>
<tabstop>edtNewClientLevel</tabstop>
<tabstop>chbGUIDesignFancy</tabstop>
<tabstop>chbDisplayChannelFaders</tabstop>
<tabstop>chbDefaultCentralServer</tabstop>
<tabstop>edtCentralServerAddress</tabstop>
</tabstops>

View file

@ -169,10 +169,14 @@ LED bar: lbr
// maximum number of fader settings to be stored (together with the fader tags)
#define MAX_NUM_STORED_FADER_SETTINGS 100
// defines for LED input level meter
#define NUM_STEPS_INP_LEV_METER 8
#define RED_BOUND_INP_LEV_METER 7
#define YELLOW_BOUND_INP_LEV_METER 5
// defines for LED level meter CMultiColorLEDBar
#define NUM_STEPS_LED_BAR 8
#define RED_BOUND_LED_BAR 7
#define YELLOW_BOUND_LED_BAR 5
// range for signal level meter
#define LOW_BOUND_SIG_METER ( -50.0 ) // dB
#define UPPER_BOUND_SIG_METER ( 0.0 ) // dB
// Maximum number of connected clients at the server. If you want to change this
// paramter you have to modify the code on some places, too! The code tag
@ -195,6 +199,9 @@ LED bar: lbr
// list
#define PING_UPDATE_TIME_SERVER_LIST_MS 2000 // ms
// defines the interval between Channel Level updates from the server
#define CHANNEL_LEVEL_UPDATE_INTERVAL 100 // number of frames
// time-out until a registered server is deleted from the server list if no
// new registering was made in minutes
#define SERVLIST_TIME_OUT_MINUTES 60 // minutes

View file

@ -33,7 +33,7 @@ CMultiColorLEDBar::CMultiColorLEDBar ( QWidget* parent, Qt::WindowFlags f )
: QFrame ( parent, f )
{
// set total number of LEDs
iNumLEDs = NUM_STEPS_INP_LEV_METER;
iNumLEDs = NUM_STEPS_LED_BAR;
// create layout and set spacing to zero
pMainLayout = new QVBoxLayout ( this );
@ -105,14 +105,14 @@ void CMultiColorLEDBar::setValue ( const int value )
if ( iLEDIdx < value )
{
// check which color we should use (green, yellow or red)
if ( iLEDIdx < YELLOW_BOUND_INP_LEV_METER )
if ( iLEDIdx < YELLOW_BOUND_LED_BAR )
{
// green region
vecpLEDs[iLEDIdx]->setColor ( cLED::RL_GREEN );
}
else
{
if ( iLEDIdx < RED_BOUND_INP_LEV_METER )
if ( iLEDIdx < RED_BOUND_LED_BAR )
{
// yellow region
vecpLEDs[iLEDIdx]->setColor ( cLED::RL_YELLOW );

View file

@ -153,6 +153,14 @@ MESSAGES (with connection)
| 1 byte licence type |
+---------------------+
- PROTMESSID_CLM_REQ_CHANNEL_LEVEL_LIST: Opt in or out of the channel level list
+---------------+
| 1 byte option |
+---------------+
option is boolean, true to opt in, false to opt out
// #### COMPATIBILITY OLD VERSION, TO BE REMOVED ####
- PROTMESSID_OPUS_SUPPORTED: Informs that OPUS codec is supported
@ -276,6 +284,27 @@ CONNECTION LESS MESSAGES
note: does not have any data -> n = 0
- PROTMESSID_CLM_CHANNEL_LEVEL_LIST: The channel level list
+----------------------------------+
| ( ( n + 1 ) / 2 ) * 4 bit values |
+----------------------------------+
n is number of connected clients
the values are the maximum channel levels for a client frame converted
to the range of CMultiColorLEDBar in 4 bits, two entries per byte
with the earlier channel in the lower half of the byte
where an odd number of clients is connected, there will be four unused
upper bits in the final byte, containing 0xF (which is out of range)
the server may compute them message when any client has used
PROTMESSID_CLM_REQ_CHANNEL_LEVEL_LIST to opt in
the server may issue to message only to a client that has used
PROTMESSID_CLM_REQ_CHANNEL_LEVEL_LIST to opt in
******************************************************************************
*
@ -549,6 +578,10 @@ if ( rand() < ( RAND_MAX / 2 ) ) return false;
case PROTMESSID_LICENCE_REQUIRED:
bRet = EvaluateLicenceRequiredMes ( vecbyMesBodyData );
break;
case PROTMESSID_REQ_CHANNEL_LEVEL_LIST:
bRet = EvaluateReqChannelLevelListMes ( vecbyMesBodyData );
break;
}
// immediately send acknowledge message
@ -634,6 +667,10 @@ if ( rand() < ( RAND_MAX / 2 ) ) return false;
case PROTMESSID_CLM_REQ_CONN_CLIENTS_LIST:
bRet = EvaluateCLReqConnClientsListMes ( InetAddr );
break;
case PROTMESSID_CLM_CHANNEL_LEVEL_LIST:
bRet = EvaluateCLChannelLevelListMes ( InetAddr, vecbyMesBodyData );
break;
}
}
else
@ -1234,6 +1271,40 @@ void CProtocol::CreateOpusSupportedMes()
CVector<uint8_t> ( 0 ) );
}
void CProtocol::CreateReqChannelLevelListMes ( const bool bRCL )
{
CVector<uint8_t> vecData ( 1 ); // 1 byte of data
int iPos = 0; // init position pointer
PutValOnStream ( vecData, iPos,
static_cast<uint32_t> ( bRCL ), 1 );
CreateAndSendMessage ( PROTMESSID_REQ_CHANNEL_LEVEL_LIST, vecData );
}
bool CProtocol::EvaluateReqChannelLevelListMes ( const CVector<uint8_t>& vecData )
{
int iPos = 0; // init position pointer
// check size
if ( vecData.Size() != 1 )
{
return true; // return error code
}
// extract opt in / out for channel levels
uint32_t val = GetValFromStream ( vecData, iPos, 1 );
if ( val != 0 && val != 1 )
{
return true; // return error code
}
// invoke message action
emit ReqChannelLevelList ( static_cast<bool> ( val ) );
return false; // no error
}
// Connection less messages ----------------------------------------------------
void CProtocol::CreateCLPingMes ( const CHostAddress& InetAddr, const int iMs )
@ -1936,6 +2007,70 @@ bool CProtocol::EvaluateCLReqConnClientsListMes ( const CHostAddress& InetAddr )
return false; // no error
}
void CProtocol::CreateCLChannelLevelListMes ( const CHostAddress& InetAddr,
const CVector<uint16_t>& vecLevelList,
const int iNumClients )
{
// This must be a multiple of bytes at four bits per client
const int iNumBytes = ( iNumClients + 1 ) / 2;
CVector<uint8_t> vecData( iNumBytes );
int iPos = 0; // init position pointer
for ( int i = 0, j = 0; i < iNumClients; i += 2 /* pack two per byte */, j++ )
{
uint16_t levelLo = vecLevelList[i] & 0x0F;
uint16_t levelHi = ( i + 1 < iNumClients ) ? vecLevelList[i + 1] & 0x0F : 0x0F;
uint8_t byte = static_cast<uint8_t> ( levelLo | ( levelHi << 4 ) );
PutValOnStream ( vecData, iPos,
static_cast<uint32_t> ( byte ), 1 );
}
CreateAndImmSendConLessMessage ( PROTMESSID_CLM_CHANNEL_LEVEL_LIST,
vecData,
InetAddr );
}
bool CProtocol::EvaluateCLChannelLevelListMes ( const CHostAddress& InetAddr,
const CVector<uint8_t>& vecData )
{
int iPos = 0; // init position pointer
const int iDataLen = vecData.Size(); // four bits per channel, 2 channels per byte
// may have one too many entries, last being 0xF
int iVecLen = iDataLen * 2; // one ushort per channel
if ( iVecLen > MAX_NUM_CHANNELS )
{
return true; // return error code
}
CVector<uint16_t> vecLevelList ( iVecLen );
for (int i = 0, j = 0; i < iDataLen; i++, j += 2 )
{
uint8_t byte = static_cast<uint8_t> ( GetValFromStream ( vecData, iPos, 1 ) );
uint16_t levelLo = byte & 0x0F;
uint16_t levelHi = ( byte >> 4 ) & 0x0F;
vecLevelList[j] = levelLo;
if ( levelHi != 0x0F )
{
vecLevelList[j + 1] = levelHi;
}
else
{
vecLevelList.resize ( iVecLen - 1 );
break;
}
}
// invoke message action
emit CLChannelLevelListReceived ( InetAddr, vecLevelList );
return false; // no error
}
/******************************************************************************\
* Message generation and parsing *

View file

@ -54,6 +54,7 @@
#define PROTMESSID_CHANNEL_INFOS 25 // set channel infos
#define PROTMESSID_OPUS_SUPPORTED 26 // tells that OPUS codec is supported
#define PROTMESSID_LICENCE_REQUIRED 27 // licence required
#define PROTMESSID_REQ_CHANNEL_LEVEL_LIST 28 // request the channel level list
// message IDs of connection less messages (CLM)
// DEFINITION -> start at 1000, end at 1999, see IsConnectionLessMessageID
@ -71,6 +72,7 @@
#define PROTMESSID_CLM_REQ_VERSION_AND_OS 1012 // request version number and operating system
#define PROTMESSID_CLM_CONN_CLIENTS_LIST 1013 // channel infos for connected clients
#define PROTMESSID_CLM_REQ_CONN_CLIENTS_LIST 1014 // request the connected clients list
#define PROTMESSID_CLM_CHANNEL_LEVEL_LIST 1015 // channel level list
// lengths of message as defined in protocol.cpp file
#define MESS_HEADER_LENGTH_BYTE 7 // TAG (2), ID (2), cnt (1), length (2)
@ -102,6 +104,7 @@ public:
void CreateReqNetwTranspPropsMes();
void CreateLicenceRequiredMes ( const ELicenceType eLicenceType );
void CreateOpusSupportedMes();
void CreateReqChannelLevelListMes ( const bool bRCL );
void CreateCLPingMes ( const CHostAddress& InetAddr, const int iMs );
void CreateCLPingWithNumClientsMes ( const CHostAddress& InetAddr,
@ -123,6 +126,9 @@ public:
void CreateCLConnClientsListMes ( const CHostAddress& InetAddr,
const CVector<CChannelInfo>& vecChanInfo );
void CreateCLReqConnClientsListMes ( const CHostAddress& InetAddr );
void CreateCLChannelLevelListMes ( const CHostAddress& InetAddr,
const CVector<uint16_t>& vecLevelList,
const int iNumClients );
static bool ParseMessageFrame ( const CVector<uint8_t>& vecbyData,
const int iNumBytesIn,
@ -216,6 +222,7 @@ protected:
bool EvaluateNetwTranspPropsMes ( const CVector<uint8_t>& vecData );
bool EvaluateReqNetwTranspPropsMes();
bool EvaluateLicenceRequiredMes ( const CVector<uint8_t>& vecData );
bool EvaluateReqChannelLevelListMes ( const CVector<uint8_t>& vecData );
bool EvaluateCLPingMes ( const CHostAddress& InetAddr,
const CVector<uint8_t>& vecData );
@ -236,6 +243,8 @@ protected:
bool EvaluateCLConnClientsListMes ( const CHostAddress& InetAddr,
const CVector<uint8_t>& vecData );
bool EvaluateCLReqConnClientsListMes ( const CHostAddress& InetAddr );
bool EvaluateCLChannelLevelListMes ( const CHostAddress& InetAddr,
const CVector<uint8_t>& vecData );
int iOldRecID;
int iOldRecCnt;
@ -270,6 +279,7 @@ signals:
void NetTranspPropsReceived ( CNetworkTransportProps NetworkTransportProps );
void ReqNetTranspProps();
void LicenceRequired ( ELicenceType eLicenceType );
void ReqChannelLevelList ( bool bOptIn );
void CLPingReceived ( CHostAddress InetAddr,
int iMs );
@ -291,4 +301,6 @@ signals:
void CLConnClientsListMesReceived ( CHostAddress InetAddr,
CVector<CChannelInfo> vecChanInfo );
void CLReqConnClientsList ( CHostAddress InetAddr );
void CLChannelLevelListReceived ( CHostAddress InetAddr,
CVector<uint16_t> vecLevelList );
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 60 KiB

View file

@ -81,11 +81,11 @@ The reverberation effect requires significant CPU so that it should only be used
level fader is set to minimum (which is the default setting), the reverberation effect is switched off and does
not cause any additional CPU usage.
### Local audio input fader
### Local audio pan / balance control
![Local audio input fader](audiofader.jpg)
![Local audio pan / balance control](audiofader.jpg)
With the audio fader, the relative levels of the left and right local audio channels can be changed. For a mono signal
With the balance control, the relative levels of the left and right local audio channels can be changed. For a mono signal
it acts like a panning between the two channels. If, e.g., a microphone is connected to the right input channel and
an instrument is connected to the left input channel which is much louder than the microphone, move the audio fader
in a direction where the label above the fader shows L -x, where x is the current attenuation indicator.
@ -94,11 +94,13 @@ in a direction where the label above the fader shows L -x, where x is the curren
![Audio faders](faders.jpg)
In the audio mixer frame, a fader for each connected client at the server is shown. This includes a fader for the own signal.
With the faders, the audio level of each client can be modified individually.
In the audio mixer frame, a fader is shown for each connected client at the server, including yourself.
The faders allow you to adjust the level of what you hear without affecting what others hear.
The VU meter shows the input level at the server - that is, what you are sending.
With the Mute checkbox, the current audio channel can be muted. With the Solo checkbox, the current audio channel can
be set to solo which means that all other channels except of the current channel are muted.
Using the Mute checkbox prevents the indicated channel being heard in your local mix.
The solo checkboxes allow you to hear only one, or several, channels, with those not soloed being muted.
Settings Window
---------------
@ -206,6 +208,12 @@ that client was already stored.
If enabled, a fancy skin will be applied to the main window.
### Display channel levels
![Display channel levels](displaychannellevels.png)
If enabled, the channel input level for each connected client will be displayed in the mixer.
### Central server address
![Central server address](centralserveraddress.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 71 KiB

View file

@ -335,6 +335,8 @@ CServer::CServer ( const int iNewMaxNumChan,
// allocate worst case memory for the coded data
vecbyCodedData.Init ( MAX_SIZE_BYTES_NETW_BUF );
// allocate worst case memory for the channel levels
vecChannelLevels.Init ( iMaxNumChannels );
// enable history graph (if requested)
if ( !strHistoryFileName.isEmpty() )
@ -864,6 +866,7 @@ JitterMeas.Measure();
// some inits
int iNumClients = 0; // init connected client counter
bool bChannelIsNowDisconnected = false;
bool bSendChannelLevels = false;
// Make put and get calls thread safe. Do not forget to unlock mutex
// afterwards!
@ -1007,6 +1010,29 @@ JitterMeas.Measure();
// one client is connected.
if ( iNumClients > 0 )
{
// Low frequency updates
if ( iFrameCount > CHANNEL_LEVEL_UPDATE_INTERVAL )
{
iFrameCount = 0;
// Calculate channel levels if any client has requested them
for ( int i = 0; i < iNumClients; i++ )
{
if ( vecChannels[ vecChanIDsCurConChan[i] ].ChannelLevelsRequired() )
{
bSendChannelLevels = true;
CreateLevelsForAllConChannels ( iNumClients,
vecNumAudioChannels,
vecvecsData,
vecChannelLevels );
break;
}
}
}
iFrameCount++;
for ( int i = 0; i < iNumClients; i++ )
{
// get actual ID of current channel
@ -1095,6 +1121,12 @@ opus_custom_encoder_ctl ( CurOpusEncoder,
// update socket buffer size
vecChannels[iCurChanID].UpdateSocketBufferSize();
// send channel levels
if ( bSendChannelLevels && vecChannels[iCurChanID].ChannelLevelsRequired() )
{
ConnLessProtocol.CreateCLChannelLevelListMes ( vecChannels[iCurChanID].GetAddress(), vecChannelLevels, iNumClients );
}
}
}
else
@ -1574,3 +1606,69 @@ void CServer::customEvent ( QEvent* pEvent )
}
}
}
/// @brief Compute frame peak level for each client
void CServer::CreateLevelsForAllConChannels ( const int iNumClients,
const CVector<int>& vecNumAudioChannels,
const CVector<CVector<int16_t> > vecvecsData,
CVector<uint16_t>& vecLevelsOut )
{
int i, j, k;
// init return vector with zeros since we mix all channels on that vector
vecLevelsOut.Reset ( 0 );
for ( j = 0; j < iNumClients; j++ )
{
// get a reference to the audio data
const CVector<int16_t>& vecsData = vecvecsData[j];
double dCurLevel = 0.0;
if ( vecNumAudioChannels[j] == 1 )
{
// mono
for ( i = 0; i < SYSTEM_FRAME_SIZE_SAMPLES; i += 3 )
{
dCurLevel = std::max ( dCurLevel, std::abs ( static_cast<double> ( vecsData[i] ) ) );
}
}
else
{
// stereo: apply stereo-to-mono attenuation
for ( i = 0, k = 0; i < SYSTEM_FRAME_SIZE_SAMPLES; i += 3, k += 6 )
{
double sMix = ( static_cast<double> ( vecsData[k] ) + vecsData[k + 1] ) / 2;
dCurLevel = std::max ( dCurLevel, std::abs ( sMix ) );
}
}
// smoothing
int iChId = vecChanIDsCurConChan [ j ];
dCurLevel = std::max ( dCurLevel, vecChannels[ iChId ].GetPrevLevel() * 0.5 );
vecChannels[ iChId ].SetPrevLevel ( dCurLevel );
// logarithmic measure
const double dNormChanLevel = dCurLevel / _MAXSHORT;
double dCurSigLevel;
if ( dNormChanLevel > 0 )
{
dCurSigLevel = 20.0 * log10 ( dNormChanLevel );
}
else
{
dCurSigLevel = -100000.0; // large negative value
}
// map to signal level meter
dCurSigLevel -= LOW_BOUND_SIG_METER;
dCurSigLevel *= NUM_STEPS_LED_BAR /
( UPPER_BOUND_SIG_METER - LOW_BOUND_SIG_METER );
if ( dCurSigLevel < 0 )
{
dCurSigLevel = 0;
}
vecLevelsOut[j] = static_cast<uint16_t> ( ceil ( dCurSigLevel ) );
}
}

View file

@ -28,6 +28,7 @@
#include <QTimer>
#include <QDateTime>
#include <QHostAddress>
#include <algorithm>
#ifdef USE_OPUS_SHARED_LIB
# include "opus/opus_custom.h"
#else
@ -225,6 +226,11 @@ protected:
// if server mode is normal or double system frame size
bool bUseDoubleSystemFrameSize;
void CreateLevelsForAllConChannels ( const int iNumClients,
const CVector<int>& vecNumAudioChannels,
const CVector<CVector<int16_t> > vecvecsData,
CVector<uint16_t>& vecLevelsOut );
// do not use the vector class since CChannel does not have appropriate
// copy constructor/operator
CChannel vecChannels[MAX_NUM_CHANNELS];
@ -255,12 +261,18 @@ protected:
CVector<int16_t> vecsSendData;
CVector<uint8_t> vecbyCodedData;
// Channel levels
CVector<uint16_t> vecChannelLevels;
// actual working objects
CHighPrioSocket Socket;
// logging
CServerLogging Logging;
// channel level update frame interval counter
uint16_t iFrameCount;
// recording thread
recorder::CJamRecorder JamRecorder;
bool bEnableRecording;

View file

@ -250,6 +250,12 @@ void CSettings::Load()
pClient->SetGUIDesign ( static_cast<EGUIDesign> ( iValue ) );
}
// display channel levels preference
if ( GetFlagIniSet ( IniXMLDocument, "client", "displaychannellevels", bValue ) )
{
pClient->SetDisplayChannelLevels ( bValue );
}
// audio channels
if ( GetNumericIniSet ( IniXMLDocument, "client", "audiochannels",
0, 2 /* CC_STEREO */, iValue ) )
@ -490,6 +496,10 @@ void CSettings::Save()
SetNumericIniSet ( IniXMLDocument, "client", "guidesign",
static_cast<int> ( pClient->GetGUIDesign() ) );
// display channel levels preference
SetFlagIniSet ( IniXMLDocument, "client", "displaychannellevels",
pClient->GetDisplayChannelLevels() );
// audio channels
SetNumericIniSet ( IniXMLDocument, "client", "audiochannels",
static_cast<int> ( pClient->GetAudioChannels() ) );