From 037cdeaf83d03f2db351fee88653bf2b4f1772ff Mon Sep 17 00:00:00 2001 From: Volker Fischer Date: Sat, 28 May 2011 20:07:57 +0000 Subject: [PATCH] added two buttons on the main window --- src/audiomixerboard.cpp | 26 +- src/clientsettingsdlgbase.ui | 3 + src/llconclientdlg.cpp | 1973 +++++++++++++++++----------------- src/llconclientdlg.h | 9 +- src/llconclientdlgbase.ui | 18 +- src/util.h | 2 +- 6 files changed, 1036 insertions(+), 995 deletions(-) diff --git a/src/audiomixerboard.cpp b/src/audiomixerboard.cpp index bf7e74db..9b78c85b 100755 --- a/src/audiomixerboard.cpp +++ b/src/audiomixerboard.cpp @@ -122,37 +122,17 @@ void CChannelFader::SetGUIDesign ( const EGUIDesign eNewDesign ) "QSlider::handle { image: url(:/png/fader/res/faderhandle.png); }" ); // mute button - pcbMute->setStyleSheet ( - "QCheckBox::indicator { width: 38px;" - " height: 21px; }" - "QCheckBox::indicator:unchecked {" - " image: url(:/png/fader/res/ledbuttonnotpressed.png); }" - "QCheckBox::indicator:checked {" - " image: url(:/png/fader/res/ledbuttonpressed.png); }" - "QCheckBox { color: rgb(148, 148, 148);" - " font: bold; }" ); pcbMute->setText ( "MUTE" ); // solo button - pcbSolo->setStyleSheet ( - "QCheckBox::indicator { width: 38px;" - " height: 21px; }" - "QCheckBox::indicator:unchecked {" - " image: url(:/png/fader/res/ledbuttonnotpressed.png); }" - "QCheckBox::indicator:checked {" - " image: url(:/png/fader/res/ledbuttonpressed.png); }" - "QCheckBox { color: rgb(148, 148, 148);" - " font: bold; }" ); pcbSolo->setText ( "SOLO" ); break; default: // reset style sheet and set original paramters - pFader->setStyleSheet ( "" ); - pcbMute->setStyleSheet ( "" ); - pcbSolo->setStyleSheet ( "" ); - pcbMute->setText ( "Mute" ); - pcbSolo->setText ( "Solo" ); + pFader->setStyleSheet ( "" ); + pcbMute->setText ( "Mute" ); + pcbSolo->setText ( "Solo" ); break; } } diff --git a/src/clientsettingsdlgbase.ui b/src/clientsettingsdlgbase.ui index c8e73935..6c90b251 100755 --- a/src/clientsettingsdlgbase.ui +++ b/src/clientsettingsdlgbase.ui @@ -237,6 +237,9 @@ Driver Setup + + false + diff --git a/src/llconclientdlg.cpp b/src/llconclientdlg.cpp index fa9774f9..2e4438be 100755 --- a/src/llconclientdlg.cpp +++ b/src/llconclientdlg.cpp @@ -1,967 +1,1006 @@ -/******************************************************************************\ - * Copyright (c) 2004-2011 - * - * Author(s): - * Volker Fischer - * - ****************************************************************************** - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -\******************************************************************************/ - -#include "llconclientdlg.h" - - -/* Implementation *************************************************************/ -CLlconClientDlg::CLlconClientDlg ( CClient* pNCliP, - CSettings* pNSetP, - const bool bNewConnectOnStartup, - const bool bNewDisalbeLEDs, - QWidget* parent, - Qt::WindowFlags f ) : - QDialog ( parent, f ), - pClient ( pNCliP ), - pSettings ( pNSetP ), - bUnreadChatMessage ( false ), - ClientSettingsDlg ( pNCliP, parent, Qt::Window ), - ChatDlg ( parent, Qt::Window ), - ConnectDlg ( parent, Qt::Dialog ) -{ - setupUi ( this ); - - - // Add help text to controls ----------------------------------------------- - // input level meter - QString strInpLevH = tr ( "Input Level Meter: The input level " - "indicators show the current input level of the two stereo channels " - "of the current selected audio input. The upper level display belongs " - "to the left channel and the lower level display to the right channel " - "of the audio input.
" - "Make sure not to clip the input signal to avoid distortions of the " - "audio signal." ); - - QString strInpLevHTT = tr ( "If the llcon software is connected and " - "you play your instrument/sing in the microphone, the LED level " - "meter should flicker. If this is not the case, you have " - "probably selected the wrong input channel (e.g. line in instead " - "of the microphone input) or set the input gain too low in the " - "(Windows) audio mixer.
For a proper usage of the llcon software, " - "you should not hear your singing/instrument in the loudspeaker or " - "your headphone when the llcon software is not connected. This can " - "be achieved by muting your input audio channel in the Playback " - "mixer (not the Recording mixer!)." ) + TOOLTIP_COM_END_TEXT; - - QString strInpLevHAccText = tr ( "Input level meter" ); - QString strInpLevHAccDescr = tr ( "Simulates an analog LED level meter." ); - - lblInputLEDMeter->setWhatsThis ( strInpLevH ); - lblLevelMeterLeft->setWhatsThis ( strInpLevH ); - lblLevelMeterRight->setWhatsThis ( strInpLevH ); - lbrInputLevelL->setWhatsThis ( strInpLevH ); - lbrInputLevelL->setAccessibleName ( strInpLevHAccText ); - lbrInputLevelL->setAccessibleDescription ( strInpLevHAccDescr ); - lbrInputLevelL->setToolTip ( strInpLevHTT ); - lbrInputLevelR->setWhatsThis ( strInpLevH ); - lbrInputLevelR->setAccessibleName ( strInpLevHAccText ); - lbrInputLevelR->setAccessibleDescription ( strInpLevHAccDescr ); - lbrInputLevelR->setToolTip ( strInpLevHTT ); - - // connect/disconnect button - butConnect->setWhatsThis ( tr ( "Connect / Disconnect Button:" - " Push this button to connect the server. A valid IP address has " - "to be specified before. If the client is connected, pressing this " - "button will disconnect the connection." ) ); - - butConnect->setAccessibleName ( - tr ( "Connect and disconnect toggle button" ) ); - - butConnect->setAccessibleDescription ( tr ( "Clicking on this " - "button changes the caption of the button from Connect to " - "Disconnect, i.e., it implements a toggle functionality for connecting " - "and disconnecting the llcon software." ) ); - - // fader tag - QString strFaderTag = tr ( "Fader Tag: The fader tag of the local " - "client is set in the fader tag edit box. This tag will appear " - "at your fader on the mixer board when you are connected to a llcon " - "server. This tag will also show up at each client which is connected " - "to the same server as the local client. If the fader tag is empty, " - "the IP address of the client is displayed instead." ); - - QString strFaderTagTT = tr ( "Set your name and/or instrument and/or " - "pseoudonym here so that the other musicians can identify you." ) + - TOOLTIP_COM_END_TEXT; - - lblServerTag->setWhatsThis ( strFaderTag ); - lblServerTag->setToolTip ( strFaderTagTT ); - edtFaderTag->setWhatsThis ( strFaderTag ); - edtFaderTag->setToolTip ( strFaderTagTT ); - - edtFaderTag->setAccessibleName ( tr ( "Fader tag edit box" ) ); - - // local audio input fader - QString strAudFader = tr ( "Local Audio Input Fader: With the " - "audio fader, the relative levels of the left and right local audio " - "channels can be changed. 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 indication. " ); - - lblAudioPan->setWhatsThis ( strAudFader ); - lblAudioPanValue->setWhatsThis ( strAudFader ); - sldAudioPan->setWhatsThis ( strAudFader ); - - sldAudioPan->setAccessibleName ( tr ( "Local audio input fader (left/right)" ) ); - - // reverberation level - QString strAudReverb = tr ( "Reverberation Level: A reverberation " - "effect can be applied to one local audio channel. The channel " - "selection and the reverberation level can be modified. If, e.g., " - "the microphone signal is fed into the right audio channel of the " - "sound card and a reverberation effect shall be applied, set the " - "channel selector to right and move the fader upwards until the " - "desired reverberation level is reached.
" - "The reverberation effect requires significant CPU so that it should " - "only be used on fast PCs. If the reverberation 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." ); - - lblAudioReverb->setWhatsThis ( strAudReverb ); - sldAudioReverb->setWhatsThis ( strAudReverb ); - - sldAudioReverb->setAccessibleName ( tr ( "Reverberation effect level setting" ) ); - - // reverberation channel selection - QString strRevChanSel = tr ( "Reverberation Channel Selection: " - "With these radio buttons the audio input channel on which the " - "reverberation effect is applied can be chosen. Either the left " - "or right input channel can be selected." ); - - rbtReverbSelL->setWhatsThis ( strRevChanSel ); - rbtReverbSelL->setAccessibleName ( tr ( "Left channel selection for reverberation" ) ); - rbtReverbSelR->setWhatsThis ( strRevChanSel ); - rbtReverbSelR->setAccessibleName ( tr ( "Right channel selection for reverberation" ) ); - - // connection LED - QString strLEDConnection = tr ( "Connection Status LED: " - "The connection status LED indicator shows the current connection " - "status. If the light is green, a successful connection to the " - "server is established. If the light blinks red right after " - "pressing the connect button, the server address is invalid. " - "If the light turns red and stays red, the connection to the " - "server is lost." ); - - lblConnection->setWhatsThis ( strLEDConnection ); - ledConnection->setWhatsThis ( strLEDConnection ); - - ledConnection->setAccessibleName ( tr ( "Connection status LED indicator" ) ); - - // delay LED - QString strLEDDelay = tr ( "Delay Status LED: " - "The delay status LED indicator shows the current audio delay " - "status. If the light is green, the delay is perfect for a jam " - "session. If the ligth is yellow, a session is still possible but " - "it may be harder to play. If the light is red, the delay is too " - "large for jamming." ); - - lblDelay->setWhatsThis ( strLEDDelay ); - ledDelay->setWhatsThis ( strLEDDelay ); - ledDelay->setToolTip ( tr ( "If this LED indicator turns red, " - "you will not have much fun using the llcon software." ) + - TOOLTIP_COM_END_TEXT ); - - ledDelay->setAccessibleName ( tr ( "Delay status LED indicator" ) ); - - // buffers LED - QString strLEDBuffers = tr ( "Buffers Status LED: " - "The buffers status LED indicator shows the current audio/streaming " - "status. If the light is green, there are no buffer overruns/underruns " - "and the audio stream is not interrupted. If the light is red, the " - "audio stream is interrupted caused by one of the following problems:" - "" ); - - lblBuffers->setWhatsThis ( strLEDBuffers ); - ledBuffers->setWhatsThis ( strLEDBuffers ); - - ledBuffers->setAccessibleName ( tr ( "Buffers status LED indicator" ) ); - - // chat LED - QString strLEDChat = tr ( "Chat Status LED: " - "If the option Open Chat on New Message is not activated, this " - "status LED will turn green on a new received chat message." ); - - lblChat->setWhatsThis ( strLEDChat ); - ledChat->setWhatsThis ( strLEDChat ); - - ledBuffers->setAccessibleName ( tr ( "Chat status LED indicator" ) ); - - - // init GUI design - SetGUIDesign ( pClient->GetGUIDesign() ); - - // reset mixer board - MainMixerBoard->HideAll(); - - // init fader tag line edit - edtFaderTag->setText ( pClient->strName ); - - // init status label - OnTimerStatus(); - - // init connection button text - butConnect->setText ( CON_BUT_CONNECTTEXT ); - - // init input level meter bars - lbrInputLevelL->setValue ( 0 ); - lbrInputLevelR->setValue ( 0 ); - - // init status LEDs - ledConnection->SetUpdateTime ( 2 * LED_BAR_UPDATE_TIME_MS ); - ledChat->SetUpdateTime ( 2 * LED_BAR_UPDATE_TIME_MS ); - ledDelay->SetUpdateTime ( 2 * PING_UPDATE_TIME_MS ); - ledDelay->Reset(); - - - // init slider controls --- - // audio in fader - sldAudioPan->setRange ( AUD_FADER_IN_MIN, AUD_FADER_IN_MAX ); - sldAudioPan->setTickInterval ( AUD_FADER_IN_MAX / 5 ); - UpdateAudioFaderSlider(); - - // audio reverberation - sldAudioReverb->setRange ( 0, AUD_REVERB_MAX ); - const int iCurAudReverb = pClient->GetReverbLevel(); - sldAudioReverb->setValue ( iCurAudReverb ); - sldAudioReverb->setTickInterval ( AUD_REVERB_MAX / 5 ); - - // init reverb channel - UpdateRevSelection(); - - - // connect on startup --- - if ( bNewConnectOnStartup ) - { - // since the software starts up right now, the previous state was - // "not connected" so that a call to "OnConnectDisconBut()" will - // start the connection - OnConnectDisconBut(); - } - - - // disable controls on request --- - // disable LEDs in main window if requested - if ( bNewDisalbeLEDs ) - { - lbrInputLevelL->setEnabled ( false ); - lbrInputLevelR->setEnabled ( false ); - ledConnection->setEnabled ( false ); - ledBuffers->setEnabled ( false ); - ledDelay->setEnabled ( false ); - ledChat->setEnabled ( false ); - butConnect->setFocus(); - } - - - // Mac Workaround: - // If the connect button is the default button, on Mac it is highlighted - // by fading in and out a blue backgroud color. This operation consumes so - // much CPU that we get audio interruptions. - // Better solution: increase thread priority of worker thread (since the - // user can always highlight the button manually, too) -> TODO -#if defined ( __APPLE__ ) || defined ( __MACOSX ) - butConnect->setDefault ( false ); -#endif - - - // View menu -------------------------------------------------------------- - pViewMenu = new QMenu ( "&View", this ); - - pViewMenu->addAction ( tr ( "&Chat..." ), this, - SLOT ( OnOpenChatDialog() ) ); - - pViewMenu->addAction ( tr ( "&General Settings..." ), this, - SLOT ( OnOpenGeneralSettings() ) ); - - pViewMenu->addSeparator(); - - pViewMenu->addAction ( tr ( "E&xit" ), this, - SLOT ( close() ), QKeySequence ( Qt::CTRL + Qt::Key_Q ) ); - - - // Main menu bar ----------------------------------------------------------- - pMenu = new QMenuBar ( this ); - - pMenu->addMenu ( pViewMenu ); - pMenu->addMenu ( new CLlconHelpMenu ( this ) ); - - // Now tell the layout about the menu - layout()->setMenuBar ( pMenu ); - - - // Connections ------------------------------------------------------------- - // push-buttons - QObject::connect ( butConnect, SIGNAL ( clicked() ), - this, SLOT ( OnConnectDisconBut() ) ); - - // timers - QObject::connect ( &TimerSigMet, SIGNAL ( timeout() ), - this, SLOT ( OnTimerSigMet() ) ); - - QObject::connect ( &TimerStatus, SIGNAL ( timeout() ), - this, SLOT ( OnTimerStatus() ) ); - - QObject::connect ( &TimerPing, SIGNAL ( timeout() ), - this, SLOT ( OnTimerPing() ) ); - - // sliders - QObject::connect ( sldAudioPan, SIGNAL ( valueChanged ( int ) ), - this, SLOT ( OnAudioPanValueChanged ( int ) ) ); - - QObject::connect ( sldAudioReverb, SIGNAL ( valueChanged ( int ) ), - this, SLOT ( OnAudioReverbValueChanged ( int ) ) ); - - // radio buttons - QObject::connect ( rbtReverbSelL, SIGNAL ( clicked() ), - this, SLOT ( OnReverbSelLClicked() ) ); - - QObject::connect ( rbtReverbSelR, SIGNAL ( clicked() ), - this, SLOT ( OnReverbSelRClicked() ) ); - - // line edits - QObject::connect ( edtFaderTag, SIGNAL ( textChanged ( const QString& ) ), - this, SLOT ( OnFaderTagTextChanged ( const QString& ) ) ); - - // other - QObject::connect ( pClient, - SIGNAL ( ConClientListMesReceived ( CVector ) ), - this, SLOT ( OnConClientListMesReceived ( CVector ) ) ); - - QObject::connect ( pClient, - SIGNAL ( Disconnected() ), - this, SLOT ( OnDisconnected() ) ); - - QObject::connect ( pClient, - SIGNAL ( ChatTextReceived ( QString ) ), - this, SLOT ( OnChatTextReceived ( QString ) ) ); - - QObject::connect ( pClient, SIGNAL ( PingTimeReceived ( int ) ), - this, SLOT ( OnPingTimeResult ( int ) ) ); - - QObject::connect ( pClient, - SIGNAL ( CLServerListReceived ( CHostAddress, CVector ) ), - this, SLOT ( OnCLServerListReceived ( CHostAddress, CVector ) ) ); - - QObject::connect ( pClient, - SIGNAL ( CLPingTimeWithNumClientsReceived ( CHostAddress, int, int ) ), - this, SLOT ( OnCLPingTimeWithNumClientsReceived ( CHostAddress, int, int ) ) ); - - QObject::connect ( QCoreApplication::instance(), SIGNAL ( aboutToQuit() ), - this, SLOT ( OnAboutToQuit() ) ); - - QObject::connect ( &ClientSettingsDlg, SIGNAL ( GUIDesignChanged() ), - this, SLOT ( OnGUIDesignChanged() ) ); - - QObject::connect ( &ClientSettingsDlg, SIGNAL ( StereoCheckBoxChanged() ), - this, SLOT ( OnStereoCheckBoxChanged() ) ); - - QObject::connect ( MainMixerBoard, SIGNAL ( ChangeChanGain ( int, double ) ), - this, SLOT ( OnChangeChanGain ( int, double ) ) ); - - QObject::connect ( MainMixerBoard, SIGNAL ( NumClientsChanged ( int ) ), - this, SLOT ( OnNumClientsChanged ( int ) ) ); - - QObject::connect ( &ChatDlg, SIGNAL ( NewLocalInputText ( QString ) ), - this, SLOT ( OnNewLocalInputText ( QString ) ) ); - - QObject::connect ( &ConnectDlg, SIGNAL ( ReqServerListQuery ( CHostAddress ) ), - this, SLOT ( OnReqServerListQuery ( CHostAddress ) ) ); - - QObject::connect ( &ConnectDlg, SIGNAL ( CreateCLServerListPingMes ( CHostAddress ) ), - this, SLOT ( OnCreateCLServerListPingMes ( CHostAddress ) ) ); - - - // Timers ------------------------------------------------------------------ - // start timer for status bar - TimerStatus.start ( LED_BAR_UPDATE_TIME_MS ); -} - -void CLlconClientDlg::closeEvent ( QCloseEvent* Event ) -{ - // if settings/connect dialog or chat dialog is open, close it - ClientSettingsDlg.close(); - ConnectDlg.close(); - ChatDlg.close(); - - // if connected, terminate connection - if ( pClient->IsRunning() ) - { - pClient->Stop(); - } - - // store fader tag - pClient->strName = edtFaderTag->text(); - - // default implementation of this event handler routine - Event->accept(); -} - -void CLlconClientDlg::UpdateAudioFaderSlider() -{ - // update slider and label of audio fader - const int iCurAudInFader = pClient->GetAudioInFader(); - sldAudioPan->setValue ( iCurAudInFader ); - - // show in label the center position and what channel is - // attenuated - if ( iCurAudInFader == AUD_FADER_IN_MIDDLE ) - { - lblAudioPanValue->setText ( "Center" ); - } - else - { - if ( iCurAudInFader > AUD_FADER_IN_MIDDLE ) - { - // attenuation on right channel - lblAudioPanValue->setText ( "R -" + - QString().setNum ( iCurAudInFader - AUD_FADER_IN_MIDDLE ) ); - } - else - { - // attenuation on left channel - lblAudioPanValue->setText ( "L -" + - QString().setNum ( AUD_FADER_IN_MIDDLE - iCurAudInFader ) ); - } - } -} - -void CLlconClientDlg::UpdateRevSelection() -{ - if ( pClient->GetUseStereo() ) - { - // for stereo make channel selection invisible since - // reverberation effect is always applied to both channels - rbtReverbSelL->setVisible ( false ); - rbtReverbSelR->setVisible ( false ); - } - else - { - // make radio buttons visible - rbtReverbSelL->setVisible ( true ); - rbtReverbSelR->setVisible ( true ); - - // update value - if ( pClient->IsReverbOnLeftChan() ) - { - rbtReverbSelL->setChecked ( true ); - } - else - { - rbtReverbSelR->setChecked ( true ); - } - } -} - -void CLlconClientDlg::OnAudioPanValueChanged ( int value ) -{ - pClient->SetAudioInFader ( value ); - UpdateAudioFaderSlider(); -} - -void CLlconClientDlg::OnConnectDisconBut() -{ - // the connect/disconnect button implements a toggle functionality - // -> apply inverted running state - ConnectDisconnect ( !pClient->IsRunning() ); -} - -void CLlconClientDlg::OnOpenGeneralSettings() -{ - // open general settings dialog - ClientSettingsDlg.show(); - - // make sure dialog is upfront and has focus - ClientSettingsDlg.raise(); - ClientSettingsDlg.activateWindow(); -} - -void CLlconClientDlg::OnChatTextReceived ( QString strChatText ) -{ - // init flag (will maybe overwritten later in this function) - bUnreadChatMessage = false; - - ChatDlg.AddChatText ( strChatText ); - - // if requested, open window - if ( pClient->GetOpenChatOnNewMessage() ) - { - ShowChatWindow(); - } - else - { - if ( !ChatDlg.isVisible() ) - { - bUnreadChatMessage = true; - } - } - - UpdateDisplay(); -} - -void CLlconClientDlg::OnDisconnected() -{ - // channel is now disconnected, clear mixer board (remove all faders) - MainMixerBoard->HideAll(); - - UpdateDisplay(); -} - -void CLlconClientDlg::OnConClientListMesReceived ( CVector vecChanInfo ) -{ - // update mixer board - MainMixerBoard->ApplyNewConClientList ( vecChanInfo ); -} - -void CLlconClientDlg::OnNumClientsChanged ( int iNewNumClients ) -{ - // update window title - SetMyWindowTitle ( iNewNumClients ); -} - -void CLlconClientDlg::SetMyWindowTitle ( const int iNumClients ) -{ - // show number of connected clients in window title (and therefore also in - // the task bar of the OS) - if ( iNumClients == 0 ) - { - // only application name - setWindowTitle ( APP_NAME ); - } - else - { - if ( iNumClients == 1 ) - { - setWindowTitle ( QString ( APP_NAME ) + " (1 user)" ); - } - else - { - setWindowTitle ( QString ( APP_NAME ) + - QString ( " (%1 users)" ).arg ( iNumClients ) ); - } - } -} - -void CLlconClientDlg::ShowChatWindow() -{ - // open chat dialog - ChatDlg.show(); - - // make sure dialog is upfront and has focus - ChatDlg.raise(); - ChatDlg.activateWindow(); - - // chat dialog is opened, reset unread message flag - bUnreadChatMessage = false; - - UpdateDisplay(); -} - -void CLlconClientDlg::OnFaderTagTextChanged ( const QString& strNewName ) -{ - // check length - if ( strNewName.length() <= MAX_LEN_FADER_TAG ) - { - // refresh internal name parameter - pClient->strName = strNewName; - - // update name at server - pClient->SetRemoteName(); - } - else - { - // text is too long, update control with shortend text - edtFaderTag->setText ( strNewName.left ( MAX_LEN_FADER_TAG ) ); - } -} - -void CLlconClientDlg::OnTimerSigMet() -{ - // get current input levels - double dCurSigLevelL = pClient->MicLevelL(); - double dCurSigLevelR = pClient->MicLevelR(); - - // linear transformation of the input level range to the progress-bar - // range - dCurSigLevelL -= LOW_BOUND_SIG_METER; - dCurSigLevelL *= NUM_STEPS_INP_LEV_METER / - ( UPPER_BOUND_SIG_METER - LOW_BOUND_SIG_METER ); - - // lower bound the signal - if ( dCurSigLevelL < 0 ) - { - dCurSigLevelL = 0; - } - - dCurSigLevelR -= LOW_BOUND_SIG_METER; - dCurSigLevelR *= NUM_STEPS_INP_LEV_METER / - ( UPPER_BOUND_SIG_METER - LOW_BOUND_SIG_METER ); - - // lower bound the signal - if ( dCurSigLevelR < 0 ) - { - dCurSigLevelR = 0; - } - - // show current level - lbrInputLevelL->setValue ( (int) ceil ( dCurSigLevelL ) ); - lbrInputLevelR->setValue ( (int) ceil ( dCurSigLevelR ) ); -} - -void CLlconClientDlg::OnTimerPing() -{ - // send ping message to the server - pClient->CreateCLPingMes(); -} - -void CLlconClientDlg::OnPingTimeResult ( int iPingTime ) -{ - // calculate overall delay - const int iOverallDelayMs = pClient->EstimatedOverallDelay ( iPingTime ); - - // color definition: <= 40 ms green, <= 65 ms yellow, otherwise red - int iOverallDelayLEDColor; - if ( iOverallDelayMs <= 40 ) - { - iOverallDelayLEDColor = MUL_COL_LED_GREEN; - } - else - { - if ( iOverallDelayMs <= 65 ) - { - iOverallDelayLEDColor = MUL_COL_LED_YELLOW; - } - else - { - iOverallDelayLEDColor = MUL_COL_LED_RED; - } - } - - // only update delay information on settings dialog if it is visible to - // avoid CPU load on working thread if not necessary - if ( ClientSettingsDlg.isVisible() ) - { - // set ping time result to general settings dialog - ClientSettingsDlg.SetPingTimeResult ( iPingTime, - iOverallDelayMs, - iOverallDelayLEDColor ); - } - - // update delay LED on the main window - ledDelay->SetLight ( iOverallDelayLEDColor ); -} - -void CLlconClientDlg::OnCLPingTimeWithNumClientsReceived ( CHostAddress InetAddr, - int iPingTime, - int iNumClients ) -{ - // color definition: <= 25 ms green, <= 50 ms yellow, otherwise red - int iPingTimeLEDColor; - if ( iPingTime <= 25 ) - { - iPingTimeLEDColor = MUL_COL_LED_GREEN; - } - else - { - if ( iPingTime <= 50 ) - { - iPingTimeLEDColor = MUL_COL_LED_YELLOW; - } - else - { - iPingTimeLEDColor = MUL_COL_LED_RED; - } - } - - // update connection dialog server list - ConnectDlg.SetPingTimeAndNumClientsResult ( InetAddr, - iPingTime, - iPingTimeLEDColor, - iNumClients ); -} - -void CLlconClientDlg::ConnectDisconnect ( const bool bDoStart ) -{ - // start/stop client, set button text - if ( bDoStart ) - { - // get the central server address string - const QString strCurCentServAddr = - SELECT_SERVER_ADDRESS ( pClient->GetUseDefaultCentralServerAddress(), - pClient->GetServerListCentralServerAddress() ); - - // init the connect dialog and execute it (modal dialog) - ConnectDlg.Init ( strCurCentServAddr, pClient->vstrIPAddress ); - ConnectDlg.exec(); - - // check if state is OK (e.g., no Cancel was pressed) - if ( ConnectDlg.GetStateOK() ) - { - const QString strSelectedAddress = ConnectDlg.GetSelectedAddress(); - - // only store new host address in our data base if the address is - // not empty and it was not a server list item (only the addresses - // typed in manually are stored by definition) - if ( !strSelectedAddress.isEmpty() && - !ConnectDlg.GetServerListItemWasChosen() ) - { - CVector vstrTempList ( MAX_NUM_SERVER_ADDR_ITEMS, "" ); - - // store the new address in the current server storage list at - // the top, make sure we do not have more than allowed stored - // servers - vstrTempList[0] = strSelectedAddress; - int iTempListCnt = 1; - - for ( int iIdx = 0; iIdx < MAX_NUM_SERVER_ADDR_ITEMS; iIdx++ ) - { - // only add old server address if it is not the same as the - // selected one - if ( ( pClient->vstrIPAddress[iIdx].compare ( strSelectedAddress ) ) && - ( iTempListCnt < MAX_NUM_SERVER_ADDR_ITEMS ) ) - { - vstrTempList[iTempListCnt] = - pClient->vstrIPAddress[iIdx]; - - iTempListCnt++; - } - } - - // copy new generated list to client - pClient->vstrIPAddress = vstrTempList; - } - - // set address and check if address is valid - if ( pClient->SetServerAddr ( strSelectedAddress ) ) - { - bool bStartOk = true; - - // try to start client, if error occurred, do not go in - // running state but show error message - try - { - pClient->Start(); - } - - catch ( CGenErr generr ) - { - QMessageBox::critical ( - this, APP_NAME, generr.GetErrorText(), "Close", 0 ); - - bStartOk = false; - } - - if ( bStartOk ) - { - // change connect button text to "disconnect" - butConnect->setText ( CON_BUT_DISCONNECTTEXT ); - - // set server name in audio mixer group box title - if ( ConnectDlg.GetServerListItemWasChosen() ) - { - // in case a server in the server list was chosen, - // display the server name of the server list - MainMixerBoard->SetServerName ( - ConnectDlg.GetSelectedServerName() ); - } - else - { - // an item of the server address combo box was chosen, - // just show the address string as it was entered by the - // user - MainMixerBoard->SetServerName ( strSelectedAddress ); - } - - // start timer for level meter bar and ping time measurement - TimerSigMet.start ( LEVELMETER_UPDATE_TIME_MS ); - TimerPing.start ( PING_UPDATE_TIME_MS ); - } - } - else - { - // show the error as red light - ledConnection->SetLight ( MUL_COL_LED_RED ); - } - } - } - else - { - // only stop client if currently running, in case we received - // the stopped message, the client is already stopped but the - // connect/disconnect button and other GUI controls must be - // updated - if ( pClient->IsRunning() ) - { - pClient->Stop(); - } - - // change connect button text to "connect" - butConnect->setText ( CON_BUT_CONNECTTEXT ); - - // reset server name in audio mixer group box title - MainMixerBoard->SetServerName ( "" ); - - // stop timer for level meter bars and reset them - TimerSigMet.stop(); - lbrInputLevelL->setValue ( 0 ); - lbrInputLevelR->setValue ( 0 ); - - // stop ping time measurement timer - TimerPing.stop(); - - -// TODO is this still required??? -// immediately update status bar -OnTimerStatus(); - -// TODO this seems not to work, LEDs are still updated afterwards... -// reset LEDs -ledConnection->Reset(); -ledBuffers->Reset(); -ledDelay->Reset(); -ledChat->Reset(); - - - // clear mixer board (remove all faders) - MainMixerBoard->HideAll(); - } -} - -void CLlconClientDlg::UpdateDisplay() -{ - // update status LEDs - if ( pClient->IsRunning() ) - { - if ( pClient->IsConnected() ) - { - // chat LED - if ( bUnreadChatMessage ) - { - ledChat->SetLight ( MUL_COL_LED_GREEN ); - } - else - { - ledChat->Reset(); - } - - // connection LED - ledConnection->SetLight ( MUL_COL_LED_GREEN ); - } - else - { - // connection LED - ledConnection->SetLight ( MUL_COL_LED_RED ); - } - } -} - -void CLlconClientDlg::SetGUIDesign ( const EGUIDesign eNewDesign ) -{ - // apply GUI design to current window - switch ( eNewDesign ) - { - case GD_ORIGINAL: - backgroundFrame->setStyleSheet ( - "QFrame#backgroundFrame { border-image: url(:/png/fader/res/mixerboardbackground.png) 34px 30px 40px 40px;" - " border-top: 34px transparent;" - " border-bottom: 40px transparent;" - " border-left: 30px transparent;" - " border-right: 40px transparent;" - " padding: -5px;" - " margin: -5px, -5px, 0px, 0px; }" - "QLabel { color: rgb(148, 148, 148);" - " font: bold; }" - "QRadioButton { color: rgb(148, 148, 148);" - " font: bold; }" - "QGroupBox::title { color: rgb(148, 148, 148); }" ); - -#ifdef _WIN32 -// Workaround QT-Windows problem: This should not be necessary since in the -// background frame the style sheet for QRadioButton was already set. But it -// seems that it is only applied if the style was set to default and then back -// to GD_ORIGINAL. This seems to be a QT related issue... -rbtReverbSelL->setStyleSheet ( "color: rgb(148, 148, 148);" - "font: bold;" ); -rbtReverbSelR->setStyleSheet ( "color: rgb(148, 148, 148);" - "font: bold;" ); -#endif - - break; - - default: - // reset style sheet and set original paramters - backgroundFrame->setStyleSheet ( "" ); - -#ifdef _WIN32 -// Workaround QT-Windows problem: See above description -rbtReverbSelL->setStyleSheet ( "" ); -rbtReverbSelR->setStyleSheet ( "" ); -#endif - - break; - } - - // also apply GUI design to child GUI controls - MainMixerBoard->SetGUIDesign ( eNewDesign ); -} - -void CLlconClientDlg::customEvent ( QEvent* Event ) -{ - if ( Event->type() == QEvent::User + 11 ) - { - const int iMessType = ( (CLlconEvent*) Event )->iMessType; - const int iStatus = ( (CLlconEvent*) Event )->iStatus; - - switch ( iMessType ) - { - case MS_SOUND_IN: - case MS_SOUND_OUT: - case MS_JIT_BUF_PUT: - case MS_JIT_BUF_GET: - // buffer status -> if any buffer goes red, this LED will go red - ledBuffers->SetLight ( iStatus ); - break; - - case MS_RESET_ALL: - ledConnection->Reset(); - ledBuffers->Reset(); - ledDelay->Reset(); - ledChat->Reset(); - break; - } - - // update general settings dialog, too - ClientSettingsDlg.SetStatus ( iMessType, iStatus ); - } -} +/******************************************************************************\ + * Copyright (c) 2004-2011 + * + * Author(s): + * Volker Fischer + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +\******************************************************************************/ + +#include "llconclientdlg.h" + + +/* Implementation *************************************************************/ +CLlconClientDlg::CLlconClientDlg ( CClient* pNCliP, + CSettings* pNSetP, + const bool bNewConnectOnStartup, + const bool bNewDisalbeLEDs, + QWidget* parent, + Qt::WindowFlags f ) : + QDialog ( parent, f ), + pClient ( pNCliP ), + pSettings ( pNSetP ), + bUnreadChatMessage ( false ), + ClientSettingsDlg ( pNCliP, parent, Qt::Window ), + ChatDlg ( parent, Qt::Window ), + ConnectDlg ( parent, Qt::Dialog ) +{ + setupUi ( this ); + + + // Add help text to controls ----------------------------------------------- + // input level meter + QString strInpLevH = tr ( "Input Level Meter: The input level " + "indicators show the current input level of the two stereo channels " + "of the current selected audio input. The upper level display belongs " + "to the left channel and the lower level display to the right channel " + "of the audio input.
" + "Make sure not to clip the input signal to avoid distortions of the " + "audio signal." ); + + QString strInpLevHTT = tr ( "If the llcon software is connected and " + "you play your instrument/sing in the microphone, the LED level " + "meter should flicker. If this is not the case, you have " + "probably selected the wrong input channel (e.g. line in instead " + "of the microphone input) or set the input gain too low in the " + "(Windows) audio mixer.
For a proper usage of the llcon software, " + "you should not hear your singing/instrument in the loudspeaker or " + "your headphone when the llcon software is not connected. This can " + "be achieved by muting your input audio channel in the Playback " + "mixer (not the Recording mixer!)." ) + TOOLTIP_COM_END_TEXT; + + QString strInpLevHAccText = tr ( "Input level meter" ); + QString strInpLevHAccDescr = tr ( "Simulates an analog LED level meter." ); + + lblInputLEDMeter->setWhatsThis ( strInpLevH ); + lblLevelMeterLeft->setWhatsThis ( strInpLevH ); + lblLevelMeterRight->setWhatsThis ( strInpLevH ); + lbrInputLevelL->setWhatsThis ( strInpLevH ); + lbrInputLevelL->setAccessibleName ( strInpLevHAccText ); + lbrInputLevelL->setAccessibleDescription ( strInpLevHAccDescr ); + lbrInputLevelL->setToolTip ( strInpLevHTT ); + lbrInputLevelR->setWhatsThis ( strInpLevH ); + lbrInputLevelR->setAccessibleName ( strInpLevHAccText ); + lbrInputLevelR->setAccessibleDescription ( strInpLevHAccDescr ); + lbrInputLevelR->setToolTip ( strInpLevHTT ); + + // connect/disconnect button + butConnect->setWhatsThis ( tr ( "Connect / Disconnect Button:" + " Push this button to connect the server. A valid IP address has " + "to be specified before. If the client is connected, pressing this " + "button will disconnect the connection." ) ); + + butConnect->setAccessibleName ( + tr ( "Connect and disconnect toggle button" ) ); + + butConnect->setAccessibleDescription ( tr ( "Clicking on this " + "button changes the caption of the button from Connect to " + "Disconnect, i.e., it implements a toggle functionality for connecting " + "and disconnecting the llcon software." ) ); + + // fader tag + QString strFaderTag = tr ( "Fader Tag: The fader tag of the local " + "client is set in the fader tag edit box. This tag will appear " + "at your fader on the mixer board when you are connected to a llcon " + "server. This tag will also show up at each client which is connected " + "to the same server as the local client. If the fader tag is empty, " + "the IP address of the client is displayed instead." ); + + QString strFaderTagTT = tr ( "Set your name and/or instrument and/or " + "pseoudonym here so that the other musicians can identify you." ) + + TOOLTIP_COM_END_TEXT; + + lblServerTag->setWhatsThis ( strFaderTag ); + lblServerTag->setToolTip ( strFaderTagTT ); + edtFaderTag->setWhatsThis ( strFaderTag ); + edtFaderTag->setToolTip ( strFaderTagTT ); + + edtFaderTag->setAccessibleName ( tr ( "Fader tag edit box" ) ); + + // local audio input fader + QString strAudFader = tr ( "Local Audio Input Fader: With the " + "audio fader, the relative levels of the left and right local audio " + "channels can be changed. 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 indication. " ); + + lblAudioPan->setWhatsThis ( strAudFader ); + lblAudioPanValue->setWhatsThis ( strAudFader ); + sldAudioPan->setWhatsThis ( strAudFader ); + + sldAudioPan->setAccessibleName ( tr ( "Local audio input fader (left/right)" ) ); + + // reverberation level + QString strAudReverb = tr ( "Reverberation Level: A reverberation " + "effect can be applied to one local audio channel. The channel " + "selection and the reverberation level can be modified. If, e.g., " + "the microphone signal is fed into the right audio channel of the " + "sound card and a reverberation effect shall be applied, set the " + "channel selector to right and move the fader upwards until the " + "desired reverberation level is reached.
" + "The reverberation effect requires significant CPU so that it should " + "only be used on fast PCs. If the reverberation 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." ); + + lblAudioReverb->setWhatsThis ( strAudReverb ); + sldAudioReverb->setWhatsThis ( strAudReverb ); + + sldAudioReverb->setAccessibleName ( tr ( "Reverberation effect level setting" ) ); + + // reverberation channel selection + QString strRevChanSel = tr ( "Reverberation Channel Selection: " + "With these radio buttons the audio input channel on which the " + "reverberation effect is applied can be chosen. Either the left " + "or right input channel can be selected." ); + + rbtReverbSelL->setWhatsThis ( strRevChanSel ); + rbtReverbSelL->setAccessibleName ( tr ( "Left channel selection for reverberation" ) ); + rbtReverbSelR->setWhatsThis ( strRevChanSel ); + rbtReverbSelR->setAccessibleName ( tr ( "Right channel selection for reverberation" ) ); + + // connection LED + QString strLEDConnection = tr ( "Connection Status LED: " + "The connection status LED indicator shows the current connection " + "status. If the light is green, a successful connection to the " + "server is established. If the light blinks red right after " + "pressing the connect button, the server address is invalid. " + "If the light turns red and stays red, the connection to the " + "server is lost." ); + + lblConnection->setWhatsThis ( strLEDConnection ); + ledConnection->setWhatsThis ( strLEDConnection ); + + ledConnection->setAccessibleName ( tr ( "Connection status LED indicator" ) ); + + // delay LED + QString strLEDDelay = tr ( "Delay Status LED: " + "The delay status LED indicator shows the current audio delay " + "status. If the light is green, the delay is perfect for a jam " + "session. If the ligth is yellow, a session is still possible but " + "it may be harder to play. If the light is red, the delay is too " + "large for jamming." ); + + lblDelay->setWhatsThis ( strLEDDelay ); + ledDelay->setWhatsThis ( strLEDDelay ); + ledDelay->setToolTip ( tr ( "If this LED indicator turns red, " + "you will not have much fun using the llcon software." ) + + TOOLTIP_COM_END_TEXT ); + + ledDelay->setAccessibleName ( tr ( "Delay status LED indicator" ) ); + + // buffers LED + QString strLEDBuffers = tr ( "Buffers Status LED: " + "The buffers status LED indicator shows the current audio/streaming " + "status. If the light is green, there are no buffer overruns/underruns " + "and the audio stream is not interrupted. If the light is red, the " + "audio stream is interrupted caused by one of the following problems:" + "
    " + "
  • The network jitter buffer is not large enough for the current " + "network/audio interface jitter.
  • " + "
  • The sound card buffer delay (buffer size) is set to a too small " + "value.
  • " + "
  • The upload or download stream rate is too high for the current " + "available internet bandwidth.
  • " + "
  • The CPU of the client or server is at 100%.
  • " + "
" ); + + lblBuffers->setWhatsThis ( strLEDBuffers ); + ledBuffers->setWhatsThis ( strLEDBuffers ); + + ledBuffers->setAccessibleName ( tr ( "Buffers status LED indicator" ) ); + + // chat LED + QString strLEDChat = tr ( "Chat Status LED: " + "If the option Open Chat on New Message is not activated, this " + "status LED will turn green on a new received chat message." ); + + lblChat->setWhatsThis ( strLEDChat ); + ledChat->setWhatsThis ( strLEDChat ); + + ledBuffers->setAccessibleName ( tr ( "Chat status LED indicator" ) ); + + + // init GUI design + SetGUIDesign ( pClient->GetGUIDesign() ); + + // reset mixer board + MainMixerBoard->HideAll(); + + // init fader tag line edit + edtFaderTag->setText ( pClient->strName ); + + // init status label + OnTimerStatus(); + + // init connection button text + butConnect->setText ( CON_BUT_CONNECTTEXT ); + + // init input level meter bars + lbrInputLevelL->setValue ( 0 ); + lbrInputLevelR->setValue ( 0 ); + + // init status LEDs + ledConnection->SetUpdateTime ( 2 * LED_BAR_UPDATE_TIME_MS ); + ledChat->SetUpdateTime ( 2 * LED_BAR_UPDATE_TIME_MS ); + ledDelay->SetUpdateTime ( 2 * PING_UPDATE_TIME_MS ); + ledDelay->Reset(); + + + // init slider controls --- + // audio in fader + sldAudioPan->setRange ( AUD_FADER_IN_MIN, AUD_FADER_IN_MAX ); + sldAudioPan->setTickInterval ( AUD_FADER_IN_MAX / 5 ); + UpdateAudioFaderSlider(); + + // audio reverberation + sldAudioReverb->setRange ( 0, AUD_REVERB_MAX ); + const int iCurAudReverb = pClient->GetReverbLevel(); + sldAudioReverb->setValue ( iCurAudReverb ); + sldAudioReverb->setTickInterval ( AUD_REVERB_MAX / 5 ); + + // init reverb channel + UpdateRevSelection(); + + + // connect on startup --- + if ( bNewConnectOnStartup ) + { + // since the software starts up right now, the previous state was + // "not connected" so that a call to "OnConnectDisconBut()" will + // start the connection + OnConnectDisconBut(); + } + + + // disable controls on request --- + // disable LEDs in main window if requested + if ( bNewDisalbeLEDs ) + { + lbrInputLevelL->setEnabled ( false ); + lbrInputLevelR->setEnabled ( false ); + ledConnection->setEnabled ( false ); + ledBuffers->setEnabled ( false ); + ledDelay->setEnabled ( false ); + ledChat->setEnabled ( false ); + butConnect->setFocus(); + } + + + // Mac Workaround: + // If the connect button is the default button, on Mac it is highlighted + // by fading in and out a blue backgroud color. This operation consumes so + // much CPU that we get audio interruptions. + // Better solution: increase thread priority of worker thread (since the + // user can always highlight the button manually, too) -> TODO +#if defined ( __APPLE__ ) || defined ( __MACOSX ) + butConnect->setDefault ( false ); +#endif + + + // View menu -------------------------------------------------------------- + pViewMenu = new QMenu ( "&View", this ); + + pViewMenu->addAction ( tr ( "&Chat..." ), this, + SLOT ( OnOpenChatDialog() ) ); + + pViewMenu->addAction ( tr ( "&General Settings..." ), this, + SLOT ( OnOpenGeneralSettings() ) ); + + pViewMenu->addSeparator(); + + pViewMenu->addAction ( tr ( "E&xit" ), this, + SLOT ( close() ), QKeySequence ( Qt::CTRL + Qt::Key_Q ) ); + + + // Main menu bar ----------------------------------------------------------- + pMenu = new QMenuBar ( this ); + + pMenu->addMenu ( pViewMenu ); + pMenu->addMenu ( new CLlconHelpMenu ( this ) ); + + // Now tell the layout about the menu + layout()->setMenuBar ( pMenu ); + + + // Connections ------------------------------------------------------------- + // push buttons + QObject::connect ( butConnect, SIGNAL ( clicked() ), + this, SLOT ( OnConnectDisconBut() ) ); + + // check boxes + QObject::connect ( chbSettings, SIGNAL ( stateChanged ( int ) ), + this, SLOT ( OnSettingsStateChanged ( int ) ) ); + + QObject::connect ( chbChat, SIGNAL ( stateChanged ( int ) ), + this, SLOT ( OnChatStateChanged ( int ) ) ); + + // timers + QObject::connect ( &TimerSigMet, SIGNAL ( timeout() ), + this, SLOT ( OnTimerSigMet() ) ); + + QObject::connect ( &TimerStatus, SIGNAL ( timeout() ), + this, SLOT ( OnTimerStatus() ) ); + + QObject::connect ( &TimerPing, SIGNAL ( timeout() ), + this, SLOT ( OnTimerPing() ) ); + + // sliders + QObject::connect ( sldAudioPan, SIGNAL ( valueChanged ( int ) ), + this, SLOT ( OnAudioPanValueChanged ( int ) ) ); + + QObject::connect ( sldAudioReverb, SIGNAL ( valueChanged ( int ) ), + this, SLOT ( OnAudioReverbValueChanged ( int ) ) ); + + // radio buttons + QObject::connect ( rbtReverbSelL, SIGNAL ( clicked() ), + this, SLOT ( OnReverbSelLClicked() ) ); + + QObject::connect ( rbtReverbSelR, SIGNAL ( clicked() ), + this, SLOT ( OnReverbSelRClicked() ) ); + + // line edits + QObject::connect ( edtFaderTag, SIGNAL ( textChanged ( const QString& ) ), + this, SLOT ( OnFaderTagTextChanged ( const QString& ) ) ); + + // other + QObject::connect ( pClient, + SIGNAL ( ConClientListMesReceived ( CVector ) ), + this, SLOT ( OnConClientListMesReceived ( CVector ) ) ); + + QObject::connect ( pClient, + SIGNAL ( Disconnected() ), + this, SLOT ( OnDisconnected() ) ); + + QObject::connect ( pClient, + SIGNAL ( ChatTextReceived ( QString ) ), + this, SLOT ( OnChatTextReceived ( QString ) ) ); + + QObject::connect ( pClient, SIGNAL ( PingTimeReceived ( int ) ), + this, SLOT ( OnPingTimeResult ( int ) ) ); + + QObject::connect ( pClient, + SIGNAL ( CLServerListReceived ( CHostAddress, CVector ) ), + this, SLOT ( OnCLServerListReceived ( CHostAddress, CVector ) ) ); + + QObject::connect ( pClient, + SIGNAL ( CLPingTimeWithNumClientsReceived ( CHostAddress, int, int ) ), + this, SLOT ( OnCLPingTimeWithNumClientsReceived ( CHostAddress, int, int ) ) ); + + QObject::connect ( QCoreApplication::instance(), SIGNAL ( aboutToQuit() ), + this, SLOT ( OnAboutToQuit() ) ); + + QObject::connect ( &ClientSettingsDlg, SIGNAL ( GUIDesignChanged() ), + this, SLOT ( OnGUIDesignChanged() ) ); + + QObject::connect ( &ClientSettingsDlg, SIGNAL ( StereoCheckBoxChanged() ), + this, SLOT ( OnStereoCheckBoxChanged() ) ); + + QObject::connect ( MainMixerBoard, SIGNAL ( ChangeChanGain ( int, double ) ), + this, SLOT ( OnChangeChanGain ( int, double ) ) ); + + QObject::connect ( MainMixerBoard, SIGNAL ( NumClientsChanged ( int ) ), + this, SLOT ( OnNumClientsChanged ( int ) ) ); + + QObject::connect ( &ChatDlg, SIGNAL ( NewLocalInputText ( QString ) ), + this, SLOT ( OnNewLocalInputText ( QString ) ) ); + + QObject::connect ( &ConnectDlg, SIGNAL ( ReqServerListQuery ( CHostAddress ) ), + this, SLOT ( OnReqServerListQuery ( CHostAddress ) ) ); + + QObject::connect ( &ConnectDlg, SIGNAL ( CreateCLServerListPingMes ( CHostAddress ) ), + this, SLOT ( OnCreateCLServerListPingMes ( CHostAddress ) ) ); + + + // Timers ------------------------------------------------------------------ + // start timer for status bar + TimerStatus.start ( LED_BAR_UPDATE_TIME_MS ); +} + +void CLlconClientDlg::closeEvent ( QCloseEvent* Event ) +{ + // if settings/connect dialog or chat dialog is open, close it + ClientSettingsDlg.close(); + ConnectDlg.close(); + ChatDlg.close(); + + // if connected, terminate connection + if ( pClient->IsRunning() ) + { + pClient->Stop(); + } + + // store fader tag + pClient->strName = edtFaderTag->text(); + + // default implementation of this event handler routine + Event->accept(); +} + +void CLlconClientDlg::UpdateAudioFaderSlider() +{ + // update slider and label of audio fader + const int iCurAudInFader = pClient->GetAudioInFader(); + sldAudioPan->setValue ( iCurAudInFader ); + + // show in label the center position and what channel is + // attenuated + if ( iCurAudInFader == AUD_FADER_IN_MIDDLE ) + { + lblAudioPanValue->setText ( "Center" ); + } + else + { + if ( iCurAudInFader > AUD_FADER_IN_MIDDLE ) + { + // attenuation on right channel + lblAudioPanValue->setText ( "R -" + + QString().setNum ( iCurAudInFader - AUD_FADER_IN_MIDDLE ) ); + } + else + { + // attenuation on left channel + lblAudioPanValue->setText ( "L -" + + QString().setNum ( AUD_FADER_IN_MIDDLE - iCurAudInFader ) ); + } + } +} + +void CLlconClientDlg::UpdateRevSelection() +{ + if ( pClient->GetUseStereo() ) + { + // for stereo make channel selection invisible since + // reverberation effect is always applied to both channels + rbtReverbSelL->setVisible ( false ); + rbtReverbSelR->setVisible ( false ); + } + else + { + // make radio buttons visible + rbtReverbSelL->setVisible ( true ); + rbtReverbSelR->setVisible ( true ); + + // update value + if ( pClient->IsReverbOnLeftChan() ) + { + rbtReverbSelL->setChecked ( true ); + } + else + { + rbtReverbSelR->setChecked ( true ); + } + } +} + +void CLlconClientDlg::OnAudioPanValueChanged ( int value ) +{ + pClient->SetAudioInFader ( value ); + UpdateAudioFaderSlider(); +} + +void CLlconClientDlg::OnConnectDisconBut() +{ + // the connect/disconnect button implements a toggle functionality + // -> apply inverted running state + ConnectDisconnect ( !pClient->IsRunning() ); +} + +void CLlconClientDlg::OnChatTextReceived ( QString strChatText ) +{ + // init flag (will maybe overwritten later in this function) + bUnreadChatMessage = false; + + ChatDlg.AddChatText ( strChatText ); + + // if requested, open window + if ( pClient->GetOpenChatOnNewMessage() ) + { + ShowChatWindow(); + } + else + { + if ( !ChatDlg.isVisible() ) + { + bUnreadChatMessage = true; + } + } + + UpdateDisplay(); +} + +void CLlconClientDlg::OnDisconnected() +{ + // channel is now disconnected, clear mixer board (remove all faders) + MainMixerBoard->HideAll(); + + UpdateDisplay(); +} + +void CLlconClientDlg::OnConClientListMesReceived ( CVector vecChanInfo ) +{ + // update mixer board + MainMixerBoard->ApplyNewConClientList ( vecChanInfo ); +} + +void CLlconClientDlg::OnNumClientsChanged ( int iNewNumClients ) +{ + // update window title + SetMyWindowTitle ( iNewNumClients ); +} + +void CLlconClientDlg::SetMyWindowTitle ( const int iNumClients ) +{ + // show number of connected clients in window title (and therefore also in + // the task bar of the OS) + if ( iNumClients == 0 ) + { + // only application name + setWindowTitle ( APP_NAME ); + } + else + { + if ( iNumClients == 1 ) + { + setWindowTitle ( QString ( APP_NAME ) + " (1 user)" ); + } + else + { + setWindowTitle ( QString ( APP_NAME ) + + QString ( " (%1 users)" ).arg ( iNumClients ) ); + } + } +} + +void CLlconClientDlg::ShowGeneralSettings() +{ + // open general settings dialog + ClientSettingsDlg.show(); + + // make sure dialog is upfront and has focus + ClientSettingsDlg.raise(); + ClientSettingsDlg.activateWindow(); +} + +void CLlconClientDlg::ShowChatWindow() +{ + // open chat dialog + ChatDlg.show(); + + // make sure dialog is upfront and has focus + ChatDlg.raise(); + ChatDlg.activateWindow(); + + // chat dialog is opened, reset unread message flag + bUnreadChatMessage = false; + + UpdateDisplay(); +} + +void CLlconClientDlg::OnSettingsStateChanged ( int value ) +{ + if ( value == Qt::Checked ) + { + ShowGeneralSettings(); + } + else + { + ClientSettingsDlg.hide(); + } +} + +void CLlconClientDlg::OnChatStateChanged ( int value ) +{ + if ( value == Qt::Checked ) + { + ShowChatWindow(); + } + else + { + ChatDlg.hide(); + } +} + +void CLlconClientDlg::OnFaderTagTextChanged ( const QString& strNewName ) +{ + // check length + if ( strNewName.length() <= MAX_LEN_FADER_TAG ) + { + // refresh internal name parameter + pClient->strName = strNewName; + + // update name at server + pClient->SetRemoteName(); + } + else + { + // text is too long, update control with shortend text + edtFaderTag->setText ( strNewName.left ( MAX_LEN_FADER_TAG ) ); + } +} + +void CLlconClientDlg::OnTimerSigMet() +{ + // get current input levels + double dCurSigLevelL = pClient->MicLevelL(); + double dCurSigLevelR = pClient->MicLevelR(); + + // linear transformation of the input level range to the progress-bar + // range + dCurSigLevelL -= LOW_BOUND_SIG_METER; + dCurSigLevelL *= NUM_STEPS_INP_LEV_METER / + ( UPPER_BOUND_SIG_METER - LOW_BOUND_SIG_METER ); + + // lower bound the signal + if ( dCurSigLevelL < 0 ) + { + dCurSigLevelL = 0; + } + + dCurSigLevelR -= LOW_BOUND_SIG_METER; + dCurSigLevelR *= NUM_STEPS_INP_LEV_METER / + ( UPPER_BOUND_SIG_METER - LOW_BOUND_SIG_METER ); + + // lower bound the signal + if ( dCurSigLevelR < 0 ) + { + dCurSigLevelR = 0; + } + + // show current level + lbrInputLevelL->setValue ( (int) ceil ( dCurSigLevelL ) ); + lbrInputLevelR->setValue ( (int) ceil ( dCurSigLevelR ) ); +} + +void CLlconClientDlg::OnTimerPing() +{ + // send ping message to the server + pClient->CreateCLPingMes(); +} + +void CLlconClientDlg::OnPingTimeResult ( int iPingTime ) +{ + // calculate overall delay + const int iOverallDelayMs = pClient->EstimatedOverallDelay ( iPingTime ); + + // color definition: <= 40 ms green, <= 65 ms yellow, otherwise red + int iOverallDelayLEDColor; + if ( iOverallDelayMs <= 40 ) + { + iOverallDelayLEDColor = MUL_COL_LED_GREEN; + } + else + { + if ( iOverallDelayMs <= 65 ) + { + iOverallDelayLEDColor = MUL_COL_LED_YELLOW; + } + else + { + iOverallDelayLEDColor = MUL_COL_LED_RED; + } + } + + // only update delay information on settings dialog if it is visible to + // avoid CPU load on working thread if not necessary + if ( ClientSettingsDlg.isVisible() ) + { + // set ping time result to general settings dialog + ClientSettingsDlg.SetPingTimeResult ( iPingTime, + iOverallDelayMs, + iOverallDelayLEDColor ); + } + + // update delay LED on the main window + ledDelay->SetLight ( iOverallDelayLEDColor ); +} + +void CLlconClientDlg::OnCLPingTimeWithNumClientsReceived ( CHostAddress InetAddr, + int iPingTime, + int iNumClients ) +{ + // color definition: <= 25 ms green, <= 50 ms yellow, otherwise red + int iPingTimeLEDColor; + if ( iPingTime <= 25 ) + { + iPingTimeLEDColor = MUL_COL_LED_GREEN; + } + else + { + if ( iPingTime <= 50 ) + { + iPingTimeLEDColor = MUL_COL_LED_YELLOW; + } + else + { + iPingTimeLEDColor = MUL_COL_LED_RED; + } + } + + // update connection dialog server list + ConnectDlg.SetPingTimeAndNumClientsResult ( InetAddr, + iPingTime, + iPingTimeLEDColor, + iNumClients ); +} + +void CLlconClientDlg::ConnectDisconnect ( const bool bDoStart ) +{ + // start/stop client, set button text + if ( bDoStart ) + { + // get the central server address string + const QString strCurCentServAddr = + SELECT_SERVER_ADDRESS ( pClient->GetUseDefaultCentralServerAddress(), + pClient->GetServerListCentralServerAddress() ); + + // init the connect dialog and execute it (modal dialog) + ConnectDlg.Init ( strCurCentServAddr, pClient->vstrIPAddress ); + ConnectDlg.exec(); + + // check if state is OK (e.g., no Cancel was pressed) + if ( ConnectDlg.GetStateOK() ) + { + const QString strSelectedAddress = ConnectDlg.GetSelectedAddress(); + + // only store new host address in our data base if the address is + // not empty and it was not a server list item (only the addresses + // typed in manually are stored by definition) + if ( !strSelectedAddress.isEmpty() && + !ConnectDlg.GetServerListItemWasChosen() ) + { + CVector vstrTempList ( MAX_NUM_SERVER_ADDR_ITEMS, "" ); + + // store the new address in the current server storage list at + // the top, make sure we do not have more than allowed stored + // servers + vstrTempList[0] = strSelectedAddress; + int iTempListCnt = 1; + + for ( int iIdx = 0; iIdx < MAX_NUM_SERVER_ADDR_ITEMS; iIdx++ ) + { + // only add old server address if it is not the same as the + // selected one + if ( ( pClient->vstrIPAddress[iIdx].compare ( strSelectedAddress ) ) && + ( iTempListCnt < MAX_NUM_SERVER_ADDR_ITEMS ) ) + { + vstrTempList[iTempListCnt] = + pClient->vstrIPAddress[iIdx]; + + iTempListCnt++; + } + } + + // copy new generated list to client + pClient->vstrIPAddress = vstrTempList; + } + + // set address and check if address is valid + if ( pClient->SetServerAddr ( strSelectedAddress ) ) + { + bool bStartOk = true; + + // try to start client, if error occurred, do not go in + // running state but show error message + try + { + pClient->Start(); + } + + catch ( CGenErr generr ) + { + QMessageBox::critical ( + this, APP_NAME, generr.GetErrorText(), "Close", 0 ); + + bStartOk = false; + } + + if ( bStartOk ) + { + // change connect button text to "disconnect" + butConnect->setText ( CON_BUT_DISCONNECTTEXT ); + + // set server name in audio mixer group box title + if ( ConnectDlg.GetServerListItemWasChosen() ) + { + // in case a server in the server list was chosen, + // display the server name of the server list + MainMixerBoard->SetServerName ( + ConnectDlg.GetSelectedServerName() ); + } + else + { + // an item of the server address combo box was chosen, + // just show the address string as it was entered by the + // user + MainMixerBoard->SetServerName ( strSelectedAddress ); + } + + // start timer for level meter bar and ping time measurement + TimerSigMet.start ( LEVELMETER_UPDATE_TIME_MS ); + TimerPing.start ( PING_UPDATE_TIME_MS ); + } + } + else + { + // show the error as red light + ledConnection->SetLight ( MUL_COL_LED_RED ); + } + } + } + else + { + // only stop client if currently running, in case we received + // the stopped message, the client is already stopped but the + // connect/disconnect button and other GUI controls must be + // updated + if ( pClient->IsRunning() ) + { + pClient->Stop(); + } + + // change connect button text to "connect" + butConnect->setText ( CON_BUT_CONNECTTEXT ); + + // reset server name in audio mixer group box title + MainMixerBoard->SetServerName ( "" ); + + // stop timer for level meter bars and reset them + TimerSigMet.stop(); + lbrInputLevelL->setValue ( 0 ); + lbrInputLevelR->setValue ( 0 ); + + // stop ping time measurement timer + TimerPing.stop(); + + +// TODO is this still required??? +// immediately update status bar +OnTimerStatus(); + +// TODO this seems not to work, LEDs are still updated afterwards... +// reset LEDs +ledConnection->Reset(); +ledBuffers->Reset(); +ledDelay->Reset(); +ledChat->Reset(); + + + // clear mixer board (remove all faders) + MainMixerBoard->HideAll(); + } +} + +void CLlconClientDlg::UpdateDisplay() +{ + // update status LEDs + if ( pClient->IsRunning() ) + { + if ( pClient->IsConnected() ) + { + // chat LED + if ( bUnreadChatMessage ) + { + ledChat->SetLight ( MUL_COL_LED_GREEN ); + } + else + { + ledChat->Reset(); + } + + // connection LED + ledConnection->SetLight ( MUL_COL_LED_GREEN ); + } + else + { + // connection LED + ledConnection->SetLight ( MUL_COL_LED_RED ); + } + } +} + +void CLlconClientDlg::SetGUIDesign ( const EGUIDesign eNewDesign ) +{ + // apply GUI design to current window + switch ( eNewDesign ) + { + case GD_ORIGINAL: + backgroundFrame->setStyleSheet ( + "QFrame#backgroundFrame { border-image: url(:/png/fader/res/mixerboardbackground.png) 34px 30px 40px 40px;" + " border-top: 34px transparent;" + " border-bottom: 40px transparent;" + " border-left: 30px transparent;" + " border-right: 40px transparent;" + " padding: -5px;" + " margin: -5px, -5px, 0px, 0px; }" + "QLabel { color: rgb(148, 148, 148);" + " font: bold; }" + "QRadioButton { color: rgb(148, 148, 148);" + " font: bold; }" + "QGroupBox::title { color: rgb(148, 148, 148); }" + "QCheckBox::indicator { width: 38px;" + " height: 21px; }" + "QCheckBox::indicator:unchecked {" + " image: url(:/png/fader/res/ledbuttonnotpressed.png); }" + "QCheckBox::indicator:checked {" + " image: url(:/png/fader/res/ledbuttonpressed.png); }" + "QCheckBox { color: rgb(148, 148, 148);" + " font: bold; }" ); + +#ifdef _WIN32 +// Workaround QT-Windows problem: This should not be necessary since in the +// background frame the style sheet for QRadioButton was already set. But it +// seems that it is only applied if the style was set to default and then back +// to GD_ORIGINAL. This seems to be a QT related issue... +rbtReverbSelL->setStyleSheet ( "color: rgb(148, 148, 148);" + "font: bold;" ); +rbtReverbSelR->setStyleSheet ( "color: rgb(148, 148, 148);" + "font: bold;" ); +#endif + + break; + + default: + // reset style sheet and set original paramters + backgroundFrame->setStyleSheet ( "" ); + +#ifdef _WIN32 +// Workaround QT-Windows problem: See above description +rbtReverbSelL->setStyleSheet ( "" ); +rbtReverbSelR->setStyleSheet ( "" ); +#endif + + break; + } + + // also apply GUI design to child GUI controls + MainMixerBoard->SetGUIDesign ( eNewDesign ); +} + +void CLlconClientDlg::customEvent ( QEvent* Event ) +{ + if ( Event->type() == QEvent::User + 11 ) + { + const int iMessType = ( (CLlconEvent*) Event )->iMessType; + const int iStatus = ( (CLlconEvent*) Event )->iStatus; + + switch ( iMessType ) + { + case MS_SOUND_IN: + case MS_SOUND_OUT: + case MS_JIT_BUF_PUT: + case MS_JIT_BUF_GET: + // buffer status -> if any buffer goes red, this LED will go red + ledBuffers->SetLight ( iStatus ); + break; + + case MS_RESET_ALL: + ledConnection->Reset(); + ledBuffers->Reset(); + ledDelay->Reset(); + ledChat->Reset(); + break; + } + + // update general settings dialog, too + ClientSettingsDlg.SetStatus ( iMessType, iStatus ); + } +} diff --git a/src/llconclientdlg.h b/src/llconclientdlg.h index d8643179..6f460a1d 100755 --- a/src/llconclientdlg.h +++ b/src/llconclientdlg.h @@ -85,6 +85,7 @@ public: protected: void SetGUIDesign ( const EGUIDesign eNewDesign ); void SetMyWindowTitle ( const int iNumClients ); + void ShowGeneralSettings(); void ShowChatWindow(); void UpdateAudioFaderSlider(); void UpdateRevSelection(); @@ -124,10 +125,12 @@ public slots: void OnCLPingTimeWithNumClientsReceived ( CHostAddress InetAddr, int iPingTime, int iNumClients ); - void OnOpenGeneralSettings(); - void OnOpenChatDialog() - { ShowChatWindow(); } + void OnOpenGeneralSettings() { ShowGeneralSettings(); } + void OnOpenChatDialog() { ShowChatWindow(); } + + void OnSettingsStateChanged ( int value ); + void OnChatStateChanged ( int value ); void OnAudioPanValueChanged ( int value ); diff --git a/src/llconclientdlgbase.ui b/src/llconclientdlgbase.ui index ebb6e68c..f61a03ff 100755 --- a/src/llconclientdlgbase.ui +++ b/src/llconclientdlgbase.ui @@ -6,7 +6,7 @@ 0 0 345 - 338 + 382 @@ -426,6 +426,20 @@ + + + + Settings + + + + + + + Chat + + + @@ -752,6 +766,8 @@ butConnect edtFaderTag + chbChat + chbSettings sldAudioPan sldAudioReverb rbtReverbSelL diff --git a/src/util.h b/src/util.h index 76c63c42..497544f1 100755 --- a/src/util.h +++ b/src/util.h @@ -727,7 +727,7 @@ public: void Init ( const int iNewBlockLengthAtSystemSampleRate, const int iNewSystemSampleRateHz, const int iHistoryLengthTimeSec, - const int iNewNewValueBoundFactor = 4 ) + const int iNewNewValueBoundFactor = 10 ) { // store block size and new value bound factor iBlockLengthAtSystemSampleRate = iNewBlockLengthAtSystemSampleRate;