1 /******************************************************************************\
2  * Copyright (c) 2004-2020
3  *
4  * Author(s):
5  *  Volker Fischer
6  *
7  ******************************************************************************
8  *
9  * This program is free software; you can redistribute it and/or modify it under
10  * the terms of the GNU General Public License as published by the Free Software
11  * Foundation; either version 2 of the License, or (at your option) any later
12  * version.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU General Public License along with
20  * this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
22  *
23 \******************************************************************************/
24 
25 #include "audiomixerboard.h"
26 
27 /******************************************************************************\
28 * CChanneFader                                                                 *
29 \******************************************************************************/
CChannelFader(QWidget * pNW)30 CChannelFader::CChannelFader ( QWidget* pNW ) :
31     eDesign ( GD_STANDARD ),
32     BitmapMutedIcon ( QString::fromUtf8 ( ":/png/fader/res/mutediconorange.png" ) )
33 {
34     // create new GUI control objects and store pointers to them (note that
35     // QWidget takes the ownership of the pMainGrid so that this only has
36     // to be created locally in this constructor)
37     pFrame = new QFrame ( pNW );
38 
39     pLevelsBox       = new QWidget ( pFrame );
40     plbrChannelLevel = new CLevelMeter ( pLevelsBox );
41     pFader           = new QSlider ( Qt::Vertical, pLevelsBox );
42     pPan             = new QDial ( pLevelsBox );
43     pPanLabel        = new QLabel ( tr ( "Pan" ), pLevelsBox );
44     pInfoLabel       = new QLabel ( "", pLevelsBox );
45 
46     pMuteSoloBox = new QWidget ( pFrame );
47     pcbMute      = new QCheckBox ( tr ( "Mute" ), pMuteSoloBox );
48     pcbSolo      = new QCheckBox ( tr ( "Solo" ), pMuteSoloBox );
49     pcbGroup     = new QCheckBox ( "", pMuteSoloBox );
50 
51     pLabelInstBox   = new QGroupBox ( pFrame );
52     plblLabel       = new QLabel ( "", pFrame );
53     plblInstrument  = new QLabel ( pFrame );
54     plblCountryFlag = new QLabel ( pFrame );
55 
56     QVBoxLayout* pMainGrid     = new QVBoxLayout ( pFrame );
57     QHBoxLayout* pLevelsGrid   = new QHBoxLayout ( pLevelsBox );
58     QVBoxLayout* pMuteSoloGrid = new QVBoxLayout ( pMuteSoloBox );
59     pLabelGrid                 = new QHBoxLayout ( pLabelInstBox );
60     pLabelPictGrid             = new QVBoxLayout();
61     QVBoxLayout* pPanGrid      = new QVBoxLayout();
62     QHBoxLayout* pPanInfoGrid  = new QHBoxLayout();
63 
64     // define the popup menu for the group checkbox
65     pGroupPopupMenu = new QMenu ( "", pcbGroup );
66     pGroupPopupMenu->addAction ( tr ( "&No grouping" ), this, [=] { OnGroupMenuGrp ( INVALID_INDEX ); } );
67     for ( int iGrp = 0; iGrp < MAX_NUM_FADER_GROUPS; iGrp++ )
68     {
69         pGroupPopupMenu->addAction ( tr ( "Assign to group" ) + ( QString ( " &%1" ).arg ( iGrp + 1 ) ), this, [=] { OnGroupMenuGrp ( iGrp ); } );
70     }
71 #if ( MAX_NUM_FADER_GROUPS != 8 )
72 #    error "MAX_NUM_FADER_GROUPS must be set to 8, see implementation in CChannelFader()"
73 #endif
74 
75     // setup channel level
76     plbrChannelLevel->setContentsMargins ( 0, 3, 2, 3 );
77 
78     // setup slider
79     pFader->setPageStep ( 1 );
80     pFader->setRange ( 0, AUD_MIX_FADER_MAX );
81     pFader->setTickInterval ( AUD_MIX_FADER_MAX / 9 );
82 
83     // setup panning control and info label
84     pPan->setRange ( 0, AUD_MIX_PAN_MAX );
85     pPan->setValue ( AUD_MIX_PAN_MAX / 2 );
86     pPan->setNotchesVisible ( true );
87     pInfoLabel->setMinimumHeight ( 14 ); // prevents jitter when muting/unmuting (#811)
88     pInfoLabel->setAlignment ( Qt::AlignTop );
89     pPanInfoGrid->addWidget ( pPanLabel, 0, Qt::AlignLeft | Qt::AlignTop );
90     pPanInfoGrid->addWidget ( pInfoLabel, 0, Qt::AlignHCenter | Qt::AlignTop );
91     pPanGrid->addLayout ( pPanInfoGrid );
92     pPanGrid->addWidget ( pPan, 0, Qt::AlignHCenter );
93 
94     // setup fader tag label (black bold text which is centered)
95     plblLabel->setTextFormat ( Qt::PlainText );
96     plblLabel->setAlignment ( Qt::AlignHCenter | Qt::AlignVCenter );
97 
98     // set margins of the layouts to zero to get maximum space for the controls
99     pMainGrid->setContentsMargins ( 0, 0, 0, 0 );
100 
101     pPanGrid->setContentsMargins ( 0, 0, 0, 0 );
102     pPanGrid->setSpacing ( 0 ); // only minimal space
103 
104     pLevelsGrid->setContentsMargins ( 0, 0, 0, 0 );
105     pLevelsGrid->setSpacing ( 0 ); // only minimal space
106 
107     pMuteSoloGrid->setContentsMargins ( 0, 0, 0, 0 );
108     pMuteSoloGrid->setSpacing ( 0 ); // only minimal space
109 
110     pLabelGrid->setContentsMargins ( 0, 0, 0, 0 );
111     pLabelGrid->setSpacing ( 2 ); // only minimal space between picture and text
112 
113     // add user controls to the grids
114     pLabelPictGrid->addWidget ( plblCountryFlag, 0, Qt::AlignHCenter );
115     pLabelPictGrid->addWidget ( plblInstrument, 0, Qt::AlignHCenter );
116 
117     pLabelGrid->addLayout ( pLabelPictGrid );
118     pLabelGrid->addWidget ( plblLabel, 0, Qt::AlignVCenter ); // note: just initial add, may be changed later
119 
120     pLevelsGrid->addWidget ( plbrChannelLevel, 0, Qt::AlignRight );
121     pLevelsGrid->addWidget ( pFader, 0, Qt::AlignLeft );
122 
123     pMuteSoloGrid->addWidget ( pcbGroup, 0, Qt::AlignLeft );
124     pMuteSoloGrid->addWidget ( pcbMute, 0, Qt::AlignLeft );
125     pMuteSoloGrid->addWidget ( pcbSolo, 0, Qt::AlignLeft );
126 
127     pMainGrid->addLayout ( pPanGrid );
128     pMainGrid->addWidget ( pLevelsBox, 0, Qt::AlignHCenter );
129     pMainGrid->addWidget ( pMuteSoloBox, 0, Qt::AlignHCenter );
130     pMainGrid->addWidget ( pLabelInstBox );
131 
132     // reset current fader
133     strGroupBaseText  = "Grp";         // this will most probably overwritten by SetGUIDesign()
134     iInstrPicMaxWidth = INVALID_INDEX; // this will most probably overwritten by SetGUIDesign()
135     Reset();
136 
137     // add help text to controls
138     plbrChannelLevel->setWhatsThis ( "<b>" + tr ( "Channel Level" ) + ":</b> " +
139                                      tr ( "Displays the pre-fader audio level of this channel.  All clients connected to the "
140                                           "server will be assigned an audio level, the same value for every client." ) );
141     plbrChannelLevel->setAccessibleName ( tr ( "Input level of the current audio "
142                                                "channel at the server" ) );
143 
144     pFader->setWhatsThis ( "<b>" + tr ( "Mixer Fader" ) + ":</b> " +
145                            tr ( "Adjusts the audio level of this channel. All clients connected to the server "
146                                 "will be assigned an audio fader, displayed at each client, to adjust the local mix." ) );
147     pFader->setAccessibleName ( tr ( "Local mix level setting of the current audio "
148                                      "channel at the server" ) );
149 
150     pInfoLabel->setWhatsThis ( "<b>" + tr ( "Status Indicator" ) + ":</b> " +
151                                tr ( "Shows a status indication about the client which is assigned to this channel. "
152                                     "Supported indicators are:" ) +
153                                "<ul><li>" + tr ( "Speaker with cancellation stroke: Indicates that another client has muted you." ) + "</li></ul>" );
154     pInfoLabel->setAccessibleName ( tr ( "Status indicator label" ) );
155 
156     pPan->setWhatsThis ( "<b>" + tr ( "Panning" ) + ":</b> " +
157                          tr ( "Sets the pan from Left to Right of the channel. "
158                               "Works only in stereo or preferably mono in/stereo out mode." ) );
159     pPan->setAccessibleName ( tr ( "Local panning position of the current audio channel at the server" ) );
160 
161     pcbMute->setWhatsThis ( "<b>" + tr ( "Mute" ) + ":</b> " + tr ( "With the Mute checkbox, the audio channel can be muted." ) );
162     pcbMute->setAccessibleName ( tr ( "Mute button" ) );
163 
164     pcbSolo->setWhatsThis ( "<b>" + tr ( "Solo" ) + ":</b> " +
165                             tr ( "With the Solo checkbox, the "
166                                  "audio channel can be set to solo which means that all other channels "
167                                  "except the soloed channel are muted. It is possible to set more than "
168                                  "one channel to solo." ) );
169     pcbSolo->setAccessibleName ( tr ( "Solo button" ) );
170 
171     pcbGroup->setWhatsThis ( "<b>" + tr ( "Group" ) + ":</b> " +
172                              tr ( "With the Grp checkbox, a "
173                                   "group of audio channels can be defined. All channel faders in a group are moved "
174                                   "in proportional synchronization if any one of the group faders are moved." ) );
175     pcbGroup->setAccessibleName ( tr ( "Group button" ) );
176 
177     QString strFaderText = "<b>" + tr ( "Fader Tag" ) + ":</b> " +
178                            tr ( "The fader tag "
179                                 "identifies the connected client. The tag name, a picture of your "
180                                 "instrument and the flag of your country can be set in the main window." );
181 
182     plblInstrument->setWhatsThis ( strFaderText );
183     plblInstrument->setAccessibleName ( tr ( "Mixer channel instrument picture" ) );
184     plblLabel->setWhatsThis ( strFaderText );
185     plblLabel->setAccessibleName ( tr ( "Mixer channel label (fader tag)" ) );
186     plblCountryFlag->setWhatsThis ( strFaderText );
187     plblCountryFlag->setAccessibleName ( tr ( "Mixer channel country flag" ) );
188 
189     // Connections -------------------------------------------------------------
190     QObject::connect ( pFader, &QSlider::valueChanged, this, &CChannelFader::OnLevelValueChanged );
191 
192     QObject::connect ( pPan, &QDial::valueChanged, this, &CChannelFader::OnPanValueChanged );
193 
194     QObject::connect ( pcbMute, &QCheckBox::stateChanged, this, &CChannelFader::OnMuteStateChanged );
195 
196     QObject::connect ( pcbSolo, &QCheckBox::stateChanged, this, &CChannelFader::soloStateChanged );
197 
198     QObject::connect ( pcbGroup, &QCheckBox::stateChanged, this, &CChannelFader::OnGroupStateChanged );
199 }
200 
SetGUIDesign(const EGUIDesign eNewDesign)201 void CChannelFader::SetGUIDesign ( const EGUIDesign eNewDesign )
202 {
203     eDesign = eNewDesign;
204 
205     switch ( eNewDesign )
206     {
207     case GD_ORIGINAL:
208         pFader->setStyleSheet ( "QSlider { width:         45px;"
209                                 "          border-image:  url(:/png/fader/res/faderbackground.png) repeat;"
210                                 "          border-top:    10px transparent;"
211                                 "          border-bottom: 10px transparent;"
212                                 "          border-left:   20px transparent;"
213                                 "          border-right:  -25px transparent; }"
214                                 "QSlider::groove { image:          url(:/png/fader/res/transparent1x1.png);"
215                                 "                  padding-left:   -34px;"
216                                 "                  padding-top:    -10px;"
217                                 "                  padding-bottom: -15px; }"
218                                 "QSlider::handle { image: url(:/png/fader/res/faderhandle.png); }" );
219 
220         pLabelGrid->addWidget ( plblLabel, 0, Qt::AlignVCenter ); // label next to icons
221         pLabelInstBox->setMinimumHeight ( 52 );                   // maximum height of the instrument+flag pictures
222         pFader->setMinimumHeight ( 120 ); // if this value is too small, the fader might not be movable with the mouse for fancy skin (#292)
223         pPan->setFixedSize ( 50, 50 );
224         pPanLabel->setText ( tr ( "PAN" ) );
225         pcbMute->setText ( tr ( "MUTE" ) );
226         pcbSolo->setText ( tr ( "SOLO" ) );
227         strGroupBaseText = tr ( "GRP" );
228         plbrChannelLevel->SetLevelMeterType ( CLevelMeter::MT_LED );
229         iInstrPicMaxWidth = INVALID_INDEX; // no instrument picture scaling
230         break;
231 
232     case GD_SLIMFADER:
233         pLabelPictGrid->addWidget ( plblLabel, 0, Qt::AlignHCenter ); // label below icons
234         pLabelInstBox->setMinimumHeight ( 130 );                      // maximum height of the instrument+flag+label
235         pFader->setMinimumHeight ( 85 );
236         pPan->setFixedSize ( 28, 28 );
237         pFader->setTickPosition ( QSlider::NoTicks );
238         pFader->setStyleSheet ( "" );
239         pPanLabel->setText ( tr ( "Pan" ) );
240         pcbMute->setText ( tr ( "M" ) );
241         pcbSolo->setText ( tr ( "S" ) );
242         strGroupBaseText = tr ( "G" );
243         plbrChannelLevel->SetLevelMeterType ( CLevelMeter::MT_SLIM_BAR );
244         iInstrPicMaxWidth = 18; // scale instrument picture to avoid enlarging the width by the picture
245         break;
246 
247     default:
248         // reset style sheet and set original parameters
249         pFader->setTickPosition ( QSlider::TicksBothSides );
250         pFader->setStyleSheet ( "" );
251         pLabelGrid->addWidget ( plblLabel, 0, Qt::AlignVCenter ); // label next to icons
252         pLabelInstBox->setMinimumHeight ( 52 );                   // maximum height of the instrument+flag pictures
253         pFader->setMinimumHeight ( 85 );
254         pPan->setFixedSize ( 50, 50 );
255         pPanLabel->setText ( tr ( "Pan" ) );
256         pcbMute->setText ( tr ( "Mute" ) );
257         pcbSolo->setText ( tr ( "Solo" ) );
258         strGroupBaseText = tr ( "Grp" );
259         plbrChannelLevel->SetLevelMeterType ( CLevelMeter::MT_BAR );
260         iInstrPicMaxWidth = INVALID_INDEX; // no instrument picture scaling
261         break;
262     }
263 
264     // we need to update since we changed the checkbox text
265     UpdateGroupIDDependencies();
266 
267     // the instrument picture might need scaling after a style change
268     SetChannelInfos ( cReceivedChanInfo );
269 }
270 
SetDisplayChannelLevel(const bool eNDCL)271 void CChannelFader::SetDisplayChannelLevel ( const bool eNDCL ) { plbrChannelLevel->setHidden ( !eNDCL ); }
272 
GetDisplayChannelLevel()273 bool CChannelFader::GetDisplayChannelLevel() { return !plbrChannelLevel->isHidden(); }
274 
SetDisplayPans(const bool eNDP)275 void CChannelFader::SetDisplayPans ( const bool eNDP )
276 {
277     pPanLabel->setHidden ( !eNDP );
278     pPan->setHidden ( !eNDP );
279 }
280 
SetupFaderTag(const ESkillLevel eSkillLevel)281 void CChannelFader::SetupFaderTag ( const ESkillLevel eSkillLevel )
282 {
283     // Should never happen here
284     if ( iGroupID >= MAX_NUM_FADER_GROUPS )
285     {
286         SetGroupID ( INVALID_INDEX );
287     }
288 
289     // the group ID defines the border color and style
290     QString strBorderColor = "black";
291     QString strBorderStyle = "solid";
292 
293     if ( iGroupID != INVALID_INDEX )
294     {
295         switch ( iGroupID % 4 )
296         {
297         case 0:
298             strBorderColor = "#C43AC5";
299             break;
300 
301         case 1:
302             strBorderColor = "#2B93D4";
303             break;
304 
305         case 2:
306             strBorderColor = "#3BC53A";
307             break;
308 
309         case 3:
310             strBorderColor = "#D46C2B";
311             break;
312 
313         default:
314             break;
315         }
316 
317         switch ( iGroupID / 4 )
318         {
319         case 0:
320             strBorderStyle = "solid";
321             break;
322 
323         case 1:
324             strBorderStyle = "dashed";
325             break;
326 
327         case 2:
328             strBorderStyle = "dotted";
329             break;
330 
331         case 3:
332             strBorderStyle = "double";
333             break;
334 
335         default:
336             break;
337         }
338     }
339 
340     // setup group box for label/instrument picture: set a thick black border
341     // with nice round edges
342     QString strStile = "QGroupBox { border:        2px " + strBorderStyle + " " + strBorderColor +
343                        ";"
344                        "            border-radius: 4px;"
345                        "            padding:       3px;";
346 
347     // the background color depends on the skill level
348     switch ( eSkillLevel )
349     {
350     case SL_BEGINNER:
351         strStile +=
352             QString ( "background-color: rgb(%1, %2, %3); }" ).arg ( RGBCOL_R_SL_BEGINNER ).arg ( RGBCOL_G_SL_BEGINNER ).arg ( RGBCOL_B_SL_BEGINNER );
353         break;
354 
355     case SL_INTERMEDIATE:
356         strStile += QString ( "background-color: rgb(%1, %2, %3); }" )
357                         .arg ( RGBCOL_R_SL_INTERMEDIATE )
358                         .arg ( RGBCOL_G_SL_INTERMEDIATE )
359                         .arg ( RGBCOL_B_SL_INTERMEDIATE );
360         break;
361 
362     case SL_PROFESSIONAL:
363         strStile += QString ( "background-color: rgb(%1, %2, %3); }" )
364                         .arg ( RGBCOL_R_SL_SL_PROFESSIONAL )
365                         .arg ( RGBCOL_G_SL_SL_PROFESSIONAL )
366                         .arg ( RGBCOL_B_SL_SL_PROFESSIONAL );
367         break;
368 
369     default:
370         strStile +=
371             QString ( "background-color: rgb(%1, %2, %3); }" ).arg ( RGBCOL_R_SL_NOT_SET ).arg ( RGBCOL_G_SL_NOT_SET ).arg ( RGBCOL_B_SL_NOT_SET );
372         break;
373     }
374 
375     pLabelInstBox->setStyleSheet ( strStile );
376 }
377 
Reset()378 void CChannelFader::Reset()
379 {
380     // it is important to reset the group index first (#611)
381     iGroupID = INVALID_INDEX;
382 
383     // general initializations
384     SetRemoteFaderIsMute ( false );
385 
386     // init gain and pan value -> maximum value as definition according to server
387     pFader->setValue ( AUD_MIX_FADER_MAX );
388     dPreviousFaderLevel = AUD_MIX_FADER_MAX;
389     pPan->setValue ( AUD_MIX_PAN_MAX / 2 );
390 
391     // reset mute/solo/group check boxes and level meter
392     pcbMute->setChecked ( false );
393     pcbSolo->setChecked ( false );
394     plbrChannelLevel->SetValue ( 0 );
395     plbrChannelLevel->ClipReset();
396 
397     // clear instrument picture, country flag, tool tips and label text
398     plblLabel->setText ( "" );
399     plblLabel->setToolTip ( "" );
400     plblInstrument->setVisible ( false );
401     plblInstrument->setToolTip ( "" );
402     plblCountryFlag->setVisible ( false );
403     plblCountryFlag->setToolTip ( "" );
404     cReceivedChanInfo = CChannelInfo();
405     SetupFaderTag ( SL_NOT_SET );
406 
407     // set a defined tool tip time out
408     const int iToolTipDurMs = 30000;
409     plblLabel->setToolTipDuration ( iToolTipDurMs );
410     plblInstrument->setToolTipDuration ( iToolTipDurMs );
411     plblCountryFlag->setToolTipDuration ( iToolTipDurMs );
412 
413     bOtherChannelIsSolo  = false;
414     bIsMyOwnFader        = false;
415     bIsMutedAtServer     = false;
416     iRunningNewClientCnt = 0;
417 
418     UpdateGroupIDDependencies();
419 }
420 
SetFaderLevel(const double dLevel,const bool bIsGroupUpdate)421 void CChannelFader::SetFaderLevel ( const double dLevel, const bool bIsGroupUpdate )
422 {
423     // first make a range check
424     if ( dLevel >= 0 )
425     {
426         // we set the new fader level in the GUI (slider control) and also tell the
427         // server about the change (block the signal of the fader since we want to
428         // call SendFaderLevelToServer with a special additional parameter)
429         pFader->blockSignals ( true );
430         pFader->setValue ( std::min ( AUD_MIX_FADER_MAX, MathUtils::round ( dLevel ) ) );
431         pFader->blockSignals ( false );
432 
433         SendFaderLevelToServer ( std::min ( static_cast<double> ( AUD_MIX_FADER_MAX ), dLevel ), bIsGroupUpdate );
434 
435         if ( dLevel > AUD_MIX_FADER_MAX )
436         {
437             // If the level is above the maximum, we have to store it for the purpose
438             // of group fader movement. If you move a fader which has lower volume than
439             // this one and this clips at max, we want to retain the ratio between this
440             // fader and the others in the group.
441             dPreviousFaderLevel = dLevel;
442         }
443     }
444 }
445 
SetPanValue(const int iPan)446 void CChannelFader::SetPanValue ( const int iPan )
447 {
448     // first make a range check
449     if ( ( iPan >= 0 ) && ( iPan <= AUD_MIX_PAN_MAX ) )
450     {
451         // we set the new fader level in the GUI (slider control) which then
452         // emits to signal to tell the server about the change (implicitly)
453         pPan->setValue ( iPan );
454         pPan->setAccessibleName ( QString::number ( iPan ) );
455     }
456 }
457 
SetFaderIsSolo(const bool bIsSolo)458 void CChannelFader::SetFaderIsSolo ( const bool bIsSolo )
459 {
460     // changing the state automatically emits the signal, too
461     pcbSolo->setChecked ( bIsSolo );
462 }
463 
SetFaderIsMute(const bool bIsMute)464 void CChannelFader::SetFaderIsMute ( const bool bIsMute )
465 {
466     // changing the state automatically emits the signal, too
467     pcbMute->setChecked ( bIsMute );
468 }
469 
SetRemoteFaderIsMute(const bool bIsMute)470 void CChannelFader::SetRemoteFaderIsMute ( const bool bIsMute )
471 {
472     if ( bIsMute )
473     {
474         // show muted icon orange
475         pInfoLabel->setPixmap ( BitmapMutedIcon );
476     }
477     else
478     {
479         pInfoLabel->setPixmap ( QPixmap() );
480     }
481 }
482 
SendFaderLevelToServer(const double dLevel,const bool bIsGroupUpdate)483 void CChannelFader::SendFaderLevelToServer ( const double dLevel, const bool bIsGroupUpdate )
484 {
485     // if mute flag is set or other channel is on solo, do not apply the new
486     // fader value to the server (exception: we are on solo, in that case we
487     // ignore the "other channel is on solo" flag)
488     const bool bSuppressServerUpdate = !( ( pcbMute->checkState() == Qt::Unchecked ) && ( !bOtherChannelIsSolo || IsSolo() ) );
489 
490     // emit signal for new fader gain value
491     emit gainValueChanged ( MathUtils::CalcFaderGain ( static_cast<float> ( dLevel ) ),
492                             bIsMyOwnFader,
493                             bIsGroupUpdate,
494                             bSuppressServerUpdate,
495                             dLevel / dPreviousFaderLevel );
496 
497     // update previous fader level since the level has changed, avoid to use
498     // the zero value not to have division by zero and also to retain the ratio
499     // after the fader is moved up again from the zero position
500     if ( dLevel > 0 )
501     {
502         dPreviousFaderLevel = dLevel;
503     }
504 }
505 
SendPanValueToServer(const int iPan)506 void CChannelFader::SendPanValueToServer ( const int iPan ) { emit panValueChanged ( static_cast<float> ( iPan ) / AUD_MIX_PAN_MAX ); }
507 
OnPanValueChanged(int value)508 void CChannelFader::OnPanValueChanged ( int value )
509 {
510     // on shift-click the pan shall reset to 0 L/R (#707)
511     if ( QGuiApplication::keyboardModifiers() == Qt::ShiftModifier )
512     {
513         // correct the value to the center position
514         value = AUD_MIX_PAN_MAX / 2;
515 
516         // set the GUI control in the center position while deactivating
517         // the signals to avoid an infinite loop
518         pPan->blockSignals ( true );
519         pPan->setValue ( value );
520         pPan->blockSignals ( false );
521     }
522 
523     pPan->setAccessibleName ( QString::number ( value ) );
524     SendPanValueToServer ( value );
525 }
526 
OnMuteStateChanged(int value)527 void CChannelFader::OnMuteStateChanged ( int value )
528 {
529     // call muting function
530     SetMute ( static_cast<Qt::CheckState> ( value ) == Qt::Checked );
531 }
532 
SetGroupID(const int iNGroupID)533 void CChannelFader::SetGroupID ( const int iNGroupID )
534 {
535     iGroupID = iNGroupID;
536     UpdateGroupIDDependencies();
537 }
538 
UpdateGroupIDDependencies()539 void CChannelFader::UpdateGroupIDDependencies()
540 {
541     // update the group checkbox according the current group ID setting
542     pcbGroup->blockSignals ( true ); // make sure no signals are fired
543     if ( iGroupID == INVALID_INDEX )
544     {
545         pcbGroup->setCheckState ( Qt::Unchecked );
546     }
547     else
548     {
549         pcbGroup->setCheckState ( Qt::Checked );
550     }
551     pcbGroup->blockSignals ( false );
552 
553     // update group checkbox text
554     if ( iGroupID != INVALID_INDEX )
555     {
556         pcbGroup->setText ( strGroupBaseText + QString::number ( iGroupID + 1 ) );
557     }
558     else
559     {
560         pcbGroup->setText ( strGroupBaseText );
561     }
562 
563     // if the group is disable for this fader, reset the previous fader level
564     if ( iGroupID == INVALID_INDEX )
565     {
566         // for the special case that the fader is all the way down, use a small
567         // value instead
568         if ( GetFaderLevel() > 0 )
569         {
570             dPreviousFaderLevel = GetFaderLevel();
571         }
572         else
573         {
574             dPreviousFaderLevel = 1; // small value
575         }
576     }
577 
578     // the fader tag border color is set according to the selected group
579     SetupFaderTag ( cReceivedChanInfo.eSkillLevel );
580 }
581 
OnGroupStateChanged(int)582 void CChannelFader::OnGroupStateChanged ( int )
583 {
584     // we want a popup menu shown if the user presses the group checkbox but
585     // we want to make sure that the checkbox state represents the current group
586     // setting and not the current click state since the user might not click
587     // on the menu but at one other place and then the popup menu disappears but
588     // the checkobx state would be on an invalid state
589     UpdateGroupIDDependencies();
590     pGroupPopupMenu->popup ( QCursor::pos() );
591 }
592 
SetMute(const bool bState)593 void CChannelFader::SetMute ( const bool bState )
594 {
595     if ( bState )
596     {
597         if ( !bIsMutedAtServer )
598         {
599             // mute channel -> send gain of 0
600             emit gainValueChanged ( 0, bIsMyOwnFader, false, false, -1 ); // set level ratio to in invalid value
601             bIsMutedAtServer = true;
602         }
603     }
604     else
605     {
606         // only unmute if we are not solot but an other channel is on solo
607         if ( ( !bOtherChannelIsSolo || IsSolo() ) && bIsMutedAtServer )
608         {
609             // mute was unchecked, get current fader value and apply
610             emit gainValueChanged ( MathUtils::CalcFaderGain ( GetFaderLevel() ),
611                                     bIsMyOwnFader,
612                                     false,
613                                     false,
614                                     -1 ); // set level ratio to in invalid value
615             bIsMutedAtServer = false;
616         }
617     }
618 }
619 
UpdateSoloState(const bool bNewOtherSoloState)620 void CChannelFader::UpdateSoloState ( const bool bNewOtherSoloState )
621 {
622     // store state (must be done before the SetMute() call!)
623     bOtherChannelIsSolo = bNewOtherSoloState;
624 
625     // mute overwrites solo -> if mute is active, do not change anything
626     if ( !pcbMute->isChecked() )
627     {
628         // mute channel if we are not solo but another channel is solo
629         SetMute ( bOtherChannelIsSolo && !IsSolo() );
630     }
631 }
632 
SetChannelLevel(const uint16_t iLevel)633 void CChannelFader::SetChannelLevel ( const uint16_t iLevel ) { plbrChannelLevel->SetValue ( iLevel ); }
634 
SetChannelInfos(const CChannelInfo & cChanInfo)635 void CChannelFader::SetChannelInfos ( const CChannelInfo& cChanInfo )
636 {
637     // store received channel info
638     cReceivedChanInfo = cChanInfo;
639 
640     // init properties for the tool tip
641     int              iTTInstrument = CInstPictures::GetNotUsedInstrument();
642     QLocale::Country eTTCountry    = QLocale::AnyCountry;
643 
644     // Label text --------------------------------------------------------------
645 
646     QString strModText = cChanInfo.strName;
647 
648     // apply break position and font size depending on the selected design
649     if ( eDesign == GD_SLIMFADER )
650     {
651         // in slim mode use a non-bold font (smaller width font)
652         plblLabel->setStyleSheet ( "QLabel { color: black; }" );
653 
654         // break at every 4th character
655         for ( int iInsPos = 4; iInsPos <= strModText.size() - 1; iInsPos += 4 + 1 )
656         {
657             strModText.insert ( iInsPos, "\n" );
658         }
659     }
660     else
661     {
662         // in normal mode use bold font
663         plblLabel->setStyleSheet ( "QLabel { color: black; font: bold; }" );
664 
665         // break text at predefined position
666         const int iBreakPos = MAX_LEN_FADER_TAG / 2;
667 
668         if ( strModText.length() > iBreakPos )
669         {
670             strModText.insert ( iBreakPos, QString ( "\n" ) );
671         }
672     }
673 
674     plblLabel->setText ( strModText );
675 
676     // Instrument picture ------------------------------------------------------
677     // get the resource reference string for this instrument
678     const QString strCurResourceRef = CInstPictures::GetResourceReference ( cChanInfo.iInstrument );
679 
680     // first check if instrument picture is used or not and if it is valid
681     if ( CInstPictures::IsNotUsedInstrument ( cChanInfo.iInstrument ) || strCurResourceRef.isEmpty() )
682     {
683         // disable instrument picture
684         plblInstrument->setVisible ( false );
685     }
686     else
687     {
688         // set correct picture
689         QPixmap pixInstr ( strCurResourceRef );
690 
691         if ( ( iInstrPicMaxWidth != INVALID_INDEX ) && ( pixInstr.width() > iInstrPicMaxWidth ) )
692         {
693             // scale instrument picture on request (scale to the width with correct aspect ratio)
694             plblInstrument->setPixmap ( pixInstr.scaledToWidth ( iInstrPicMaxWidth, Qt::SmoothTransformation ) );
695         }
696         else
697         {
698             plblInstrument->setPixmap ( pixInstr );
699         }
700         iTTInstrument = cChanInfo.iInstrument;
701 
702         // enable instrument picture
703         plblInstrument->setVisible ( true );
704     }
705 
706     // Country flag icon -------------------------------------------------------
707     if ( cChanInfo.eCountry != QLocale::AnyCountry )
708     {
709         // try to load the country flag icon
710         QPixmap CountryFlagPixmap ( CLocale::GetCountryFlagIconsResourceReference ( cChanInfo.eCountry ) );
711 
712         // first check if resource reference was valid
713         if ( CountryFlagPixmap.isNull() )
714         {
715             // disable country flag
716             plblCountryFlag->setVisible ( false );
717         }
718         else
719         {
720             // set correct picture
721             plblCountryFlag->setPixmap ( CountryFlagPixmap );
722             eTTCountry = cChanInfo.eCountry;
723 
724             // enable country flag
725             plblCountryFlag->setVisible ( true );
726         }
727     }
728     else
729     {
730         // disable country flag
731         plblCountryFlag->setVisible ( false );
732     }
733 
734     // Skill level background color --------------------------------------------
735     SetupFaderTag ( cChanInfo.eSkillLevel );
736 
737     // Tool tip ----------------------------------------------------------------
738     // complete musician profile in the tool tip
739     QString strToolTip              = "";
740     QString strAliasAccessible      = "";
741     QString strInstrumentAccessible = "";
742     QString strLocationAccessible   = "";
743 
744     // alias/name
745     if ( !cChanInfo.strName.isEmpty() )
746     {
747         strToolTip += "<h4>" + tr ( "Alias/Name" ) + "</h4>" + cChanInfo.strName;
748         strAliasAccessible += cChanInfo.strName;
749     }
750 
751     // instrument
752     if ( !CInstPictures::IsNotUsedInstrument ( iTTInstrument ) )
753     {
754         strToolTip += "<h4>" + tr ( "Instrument" ) + "</h4>" + CInstPictures::GetName ( iTTInstrument );
755 
756         strInstrumentAccessible += CInstPictures::GetName ( iTTInstrument );
757     }
758 
759     // location
760     if ( ( eTTCountry != QLocale::AnyCountry ) || ( !cChanInfo.strCity.isEmpty() ) )
761     {
762         strToolTip += "<h4>" + tr ( "Location" ) + "</h4>";
763 
764         if ( !cChanInfo.strCity.isEmpty() )
765         {
766             strToolTip += cChanInfo.strCity;
767             strLocationAccessible += cChanInfo.strCity;
768 
769             if ( eTTCountry != QLocale::AnyCountry )
770             {
771                 strToolTip += ", ";
772                 strLocationAccessible += ", ";
773             }
774         }
775 
776         if ( eTTCountry != QLocale::AnyCountry )
777         {
778             strToolTip += QLocale::countryToString ( eTTCountry );
779             strLocationAccessible += QLocale::countryToString ( eTTCountry );
780         }
781     }
782 
783     // skill level
784     QString strSkillLevel;
785 
786     switch ( cChanInfo.eSkillLevel )
787     {
788     case SL_BEGINNER:
789         strSkillLevel = tr ( "Beginner" );
790         strToolTip += "<h4>" + tr ( "Skill Level" ) + "</h4>" + strSkillLevel;
791         strInstrumentAccessible += ", " + strSkillLevel;
792         break;
793 
794     case SL_INTERMEDIATE:
795         strSkillLevel = tr ( "Intermediate" );
796         strToolTip += "<h4>" + tr ( "Skill Level" ) + "</h4>" + strSkillLevel;
797         strInstrumentAccessible += ", " + strSkillLevel;
798         break;
799 
800     case SL_PROFESSIONAL:
801         strSkillLevel = tr ( "Expert" );
802         strToolTip += "<h4>" + tr ( "Skill Level" ) + "</h4>" + strSkillLevel;
803         strInstrumentAccessible += ", " + strSkillLevel;
804         break;
805 
806     case SL_NOT_SET:
807         // skill level not set, do not add this entry
808         break;
809     }
810 
811     // if no information is given, leave the tool tip empty, otherwise add header
812     if ( !strToolTip.isEmpty() )
813     {
814         strToolTip.prepend ( "<h3>" + tr ( "Musician Profile" ) + "</h3>" );
815     }
816 
817     plblCountryFlag->setToolTip ( strToolTip );
818     plblCountryFlag->setAccessibleDescription ( strLocationAccessible );
819     plblInstrument->setToolTip ( strToolTip );
820     plblInstrument->setAccessibleDescription ( strInstrumentAccessible );
821     plblLabel->setToolTip ( strToolTip );
822     plblLabel->setAccessibleName ( strAliasAccessible );
823     plblLabel->setAccessibleDescription ( tr ( "Alias" ) );
824 }
825 
826 /******************************************************************************\
827 * CAudioMixerBoard                                                             *
828 \******************************************************************************/
CAudioMixerBoard(QWidget * parent)829 CAudioMixerBoard::CAudioMixerBoard ( QWidget* parent ) :
830     QGroupBox ( parent ),
831     pSettings ( nullptr ),
832     bDisplayPans ( false ),
833     bIsPanSupported ( false ),
834     bNoFaderVisible ( true ),
835     iMyChannelID ( INVALID_INDEX ),
836     iRunningNewClientCnt ( 0 ),
837     iNumMixerPanelRows ( 1 ), // pSettings->iNumMixerPanelRows is not yet available
838     strServerName ( "" ),
839     eRecorderState ( RS_UNDEFINED ),
840     eChSortType ( ST_NO_SORT )
841 {
842     // add group box and hboxlayout
843     QHBoxLayout* pGroupBoxLayout = new QHBoxLayout ( this );
844     QWidget*     pMixerWidget    = new QWidget(); // will be added to the scroll area which is then the parent
845     pScrollArea                  = new CMixerBoardScrollArea ( this );
846     pMainLayout                  = new QGridLayout ( pMixerWidget );
847 
848     setAccessibleName ( "Personal Mix at the Server groupbox" );
849     setWhatsThis ( "<b>" + tr ( "Personal Mix at the Server" ) + ":</b> " +
850                    tr ( "When connected to a server, the controls here allow you to set your "
851                         "local mix without affecting what others hear from you. The title shows "
852                         "the server name and, when known, whether it is actively recording." ) );
853 
854     // set title text (default: no server given)
855     SetServerName ( "" );
856 
857     // create all mixer controls and make them invisible
858     vecpChanFader.Init ( MAX_NUM_CHANNELS );
859 
860     vecAvgLevels.Init ( MAX_NUM_CHANNELS, 0.0f );
861 
862     for ( int i = 0; i < MAX_NUM_CHANNELS; i++ )
863     {
864         vecpChanFader[i] = new CChannelFader ( this );
865         vecpChanFader[i]->Hide();
866     }
867 
868     // insert horizontal spacer (at position MAX_NUM_CHANNELS+1 which is index MAX_NUM_CHANNELS)
869     pMainLayout->addItem ( new QSpacerItem ( 0, 0, QSizePolicy::Expanding ), 0, MAX_NUM_CHANNELS );
870 
871     // set margins of the layout to zero to get maximum space for the controls
872     pGroupBoxLayout->setContentsMargins ( 0, 0, 0, 1 ); // note: to avoid problems at the bottom, use a small margin for that
873 
874     // add the group box to the scroll area
875     pScrollArea->setMinimumWidth ( 200 ); // at least two faders shall be visible
876     pScrollArea->setWidget ( pMixerWidget );
877     pScrollArea->setWidgetResizable ( true ); // make sure it fills the entire scroll area
878     pScrollArea->setFrameShape ( QFrame::NoFrame );
879     pGroupBoxLayout->addWidget ( pScrollArea );
880 
881     // Connections -------------------------------------------------------------
882     connectFaderSignalsToMixerBoardSlots<MAX_NUM_CHANNELS>();
883 }
884 
~CAudioMixerBoard()885 CAudioMixerBoard::~CAudioMixerBoard()
886 {
887     for ( int i = 0; i < MAX_NUM_CHANNELS; i++ )
888     {
889         delete vecpChanFader[i];
890     }
891 }
892 
893 template<unsigned int slotId>
connectFaderSignalsToMixerBoardSlots()894 inline void CAudioMixerBoard::connectFaderSignalsToMixerBoardSlots()
895 {
896     int iCurChanID = slotId - 1;
897 
898     void ( CAudioMixerBoard::*pGainValueChanged ) ( float, bool, bool, bool, double ) = &CAudioMixerBoardSlots<slotId>::OnChGainValueChanged;
899 
900     void ( CAudioMixerBoard::*pPanValueChanged ) ( float ) = &CAudioMixerBoardSlots<slotId>::OnChPanValueChanged;
901 
902     QObject::connect ( vecpChanFader[iCurChanID], &CChannelFader::soloStateChanged, this, &CAudioMixerBoard::UpdateSoloStates );
903 
904     QObject::connect ( vecpChanFader[iCurChanID], &CChannelFader::gainValueChanged, this, pGainValueChanged );
905 
906     QObject::connect ( vecpChanFader[iCurChanID], &CChannelFader::panValueChanged, this, pPanValueChanged );
907 
908     connectFaderSignalsToMixerBoardSlots<slotId - 1>();
909 }
910 
911 template<>
connectFaderSignalsToMixerBoardSlots()912 inline void CAudioMixerBoard::connectFaderSignalsToMixerBoardSlots<0>()
913 {}
914 
SetServerName(const QString & strNewServerName)915 void CAudioMixerBoard::SetServerName ( const QString& strNewServerName )
916 {
917     // store the current server name
918     strServerName = strNewServerName;
919 
920     if ( strServerName.isEmpty() )
921     {
922         // no connection or connection was reset: show default title
923         setTitle ( tr ( "Server" ) );
924     }
925     else
926     {
927         // Do not set the server name directly but first show a label which indicates
928         // that we are trying to connect the server. First if a connected client
929         // list was received, the connection was successful and the title is updated
930         // with the correct server name. Make sure to choose a "try to connect" title
931         // which is most striking (we use filled blocks and upper case letters).
932         setTitle ( u8"\u2588\u2588\u2588\u2588\u2588  " + tr ( "T R Y I N G   T O   C O N N E C T" ) + u8"  \u2588\u2588\u2588\u2588\u2588" );
933     }
934 }
935 
SetGUIDesign(const EGUIDesign eNewDesign)936 void CAudioMixerBoard::SetGUIDesign ( const EGUIDesign eNewDesign )
937 {
938     // move the channels tighter together in slim fader mode
939     if ( eNewDesign == GD_SLIMFADER )
940     {
941         pMainLayout->setSpacing ( 2 );
942     }
943     else
944     {
945         pMainLayout->setSpacing ( 6 ); // Qt default spacing value
946     }
947 
948     // apply GUI design to child GUI controls
949     for ( int i = 0; i < MAX_NUM_CHANNELS; i++ )
950     {
951         vecpChanFader[i]->SetGUIDesign ( eNewDesign );
952     }
953 }
954 
SetDisplayPans(const bool eNDP)955 void CAudioMixerBoard::SetDisplayPans ( const bool eNDP )
956 {
957     bDisplayPans = eNDP;
958 
959     for ( int i = 0; i < MAX_NUM_CHANNELS; i++ )
960     {
961         vecpChanFader[i]->SetDisplayPans ( eNDP && bIsPanSupported );
962     }
963 }
964 
SetPanIsSupported()965 void CAudioMixerBoard::SetPanIsSupported()
966 {
967     bIsPanSupported = true;
968     SetDisplayPans ( bDisplayPans );
969 }
970 
HideAll()971 void CAudioMixerBoard::HideAll()
972 {
973     // before hiding the faders, store their settings
974     StoreAllFaderSettings();
975 
976     // make all controls invisible
977     for ( int i = 0; i < MAX_NUM_CHANNELS; i++ )
978     {
979         vecpChanFader[i]->SetChannelLevel ( 0 );
980         vecpChanFader[i]->SetDisplayChannelLevel ( false );
981         vecpChanFader[i]->SetDisplayPans ( false );
982         vecpChanFader[i]->Hide();
983     }
984 
985     // initialize flags and other parameters
986     bIsPanSupported      = false;
987     bNoFaderVisible      = true;
988     eRecorderState       = RS_UNDEFINED;
989     iMyChannelID         = INVALID_INDEX;
990     iRunningNewClientCnt = 0; // reset running counter on new server connection
991 
992     // use original order of channel (by server ID)
993     ChangeFaderOrder ( ST_NO_SORT );
994 
995     // Reset recording indication styleSheet
996     setStyleSheet ( "" );
997 
998     // emit status of connected clients
999     emit NumClientsChanged ( 0 ); // -> no clients connected
1000 }
1001 
SetNumMixerPanelRows(const int iNNumMixerPanelRows)1002 void CAudioMixerBoard::SetNumMixerPanelRows ( const int iNNumMixerPanelRows )
1003 {
1004     // store new value and immediately initiate the sorting
1005     iNumMixerPanelRows = iNNumMixerPanelRows;
1006     ChangeFaderOrder ( eChSortType );
1007 }
1008 
SetFaderSorting(const EChSortType eNChSortType)1009 void CAudioMixerBoard::SetFaderSorting ( const EChSortType eNChSortType )
1010 {
1011     // store new sort type and update the fader order
1012     eChSortType = eNChSortType;
1013     ChangeFaderOrder ( eNChSortType );
1014 }
1015 
ChangeFaderOrder(const EChSortType eChSortType)1016 void CAudioMixerBoard::ChangeFaderOrder ( const EChSortType eChSortType )
1017 {
1018     QMutexLocker locker ( &Mutex );
1019 
1020     // create a pair list of lower strings and fader ID for each channel
1021     QList<QPair<QString, int>> PairList;
1022     int                        iNumVisibleFaders = 0;
1023 
1024     for ( int i = 0; i < MAX_NUM_CHANNELS; i++ )
1025     {
1026         if ( eChSortType == ST_BY_NAME )
1027         {
1028             PairList << QPair<QString, int> ( vecpChanFader[i]->GetReceivedName().toLower(), i );
1029         }
1030         else if ( eChSortType == ST_BY_CITY )
1031         {
1032             PairList << QPair<QString, int> ( vecpChanFader[i]->GetReceivedCity().toLower(), i );
1033         }
1034         else if ( eChSortType == ST_BY_INSTRUMENT )
1035         {
1036             // sort first "by instrument" and second "by name" by adding the name after the instrument
1037             PairList << QPair<QString, int> ( CInstPictures::GetName ( vecpChanFader[i]->GetReceivedInstrument() ) +
1038                                                   vecpChanFader[i]->GetReceivedName().toLower(),
1039                                               i );
1040         }
1041         else if ( eChSortType == ST_BY_GROUPID )
1042         {
1043             if ( vecpChanFader[i]->GetGroupID() == INVALID_INDEX )
1044             {
1045                 // put channels without a group at the end
1046                 PairList << QPair<QString, int> ( "z", i ); // group IDs are numbers, use letter to put it at the end
1047             }
1048             else
1049             {
1050                 PairList << QPair<QString, int> ( QString::number ( vecpChanFader[i]->GetGroupID() ), i );
1051             }
1052         }
1053         else // ST_NO_SORT
1054         {
1055             // per definition for no sort: faders are sorted in the order they appeared (note that we
1056             // pad to a total of 11 characters with zeros to make sure the sorting is done correctly)
1057             PairList << QPair<QString, int> ( QString ( "%1" ).arg ( vecpChanFader[i]->GetRunningNewClientCnt(), 11, 10, QLatin1Char ( '0' ) ), i );
1058         }
1059 
1060         // count the number of visible faders
1061         if ( vecpChanFader[i]->IsVisible() )
1062         {
1063             iNumVisibleFaders++;
1064         }
1065     }
1066 
1067     // sort the channels according to the first of the pair
1068     std::stable_sort ( PairList.begin(), PairList.end() );
1069 
1070     // we want to distribute iNumVisibleFaders across the first row, then the next, etc
1071     // up to iNumMixerPanelRows.  So row wants to start at 0 until we get to some number,
1072     // then increase, where "some number" means we get no more than iNumMixerPanelRows.
1073     const int iNumFadersFirstRow = ( iNumVisibleFaders + iNumMixerPanelRows - 1 ) / iNumMixerPanelRows;
1074 
1075     // add channels to the layout in the new order, note that it is not required to remove
1076     // the widget from the layout first but it is moved to the new position automatically
1077     int iVisibleFaderCnt = 0;
1078 
1079     for ( int i = 0; i < MAX_NUM_CHANNELS; i++ )
1080     {
1081         const int iCurFaderID = PairList[i].second;
1082 
1083         if ( vecpChanFader[iCurFaderID]->IsVisible() )
1084         {
1085             // channels are added row-first, up to iNumFadersFirstRow, then onto
1086             // the next row.
1087             pMainLayout->addWidget ( vecpChanFader[iCurFaderID]->GetMainWidget(),
1088                                      iVisibleFaderCnt / iNumFadersFirstRow,
1089                                      iVisibleFaderCnt % iNumFadersFirstRow );
1090 
1091             iVisibleFaderCnt++;
1092         }
1093     }
1094 }
1095 
UpdateTitle()1096 void CAudioMixerBoard::UpdateTitle()
1097 {
1098     QString strTitlePrefix = "";
1099 
1100     if ( eRecorderState == RS_RECORDING )
1101     {
1102         strTitlePrefix = "[" + tr ( "RECORDING ACTIVE" ) + "] ";
1103     }
1104 
1105     // replace & signs with && (See Qt documentation for QLabel)
1106     // if strServerName includes an "&" sign, this is interpreted as keyboard shortcut (#1886)
1107     // it might be possible to find a more elegant solution here?
1108 
1109     QString strEscServerName = strServerName;
1110     strEscServerName.replace ( "&", "&&" );
1111 
1112     setTitle ( strTitlePrefix + tr ( "Personal Mix at: " ) + strEscServerName );
1113     setAccessibleName ( title() );
1114 }
1115 
SetRecorderState(const ERecorderState newRecorderState)1116 void CAudioMixerBoard::SetRecorderState ( const ERecorderState newRecorderState )
1117 {
1118     // store the new recorder state and update the title
1119     eRecorderState = newRecorderState;
1120     UpdateTitle();
1121 }
1122 
ApplyNewConClientList(CVector<CChannelInfo> & vecChanInfo)1123 void CAudioMixerBoard::ApplyNewConClientList ( CVector<CChannelInfo>& vecChanInfo )
1124 {
1125     // get number of connected clients
1126     const int iNumConnectedClients = vecChanInfo.Size();
1127 
1128     Mutex.lock();
1129     {
1130         // we want to set the server name only if the very first faders appear
1131         // in the audio mixer board to show a "try to connect" before
1132         if ( bNoFaderVisible )
1133         {
1134             UpdateTitle();
1135         }
1136 
1137         // search for channels with are already present and preserve their gain
1138         // setting, for all other channels reset gain
1139         for ( int i = 0; i < MAX_NUM_CHANNELS; i++ )
1140         {
1141             bool bFaderIsUsed = false;
1142 
1143             for ( int j = 0; j < iNumConnectedClients; j++ )
1144             {
1145                 // check if current fader is used
1146                 if ( vecChanInfo[j].iChanID == i )
1147                 {
1148                     // check if fader was already in use -> preserve gain value
1149                     if ( !vecpChanFader[i]->IsVisible() )
1150                     {
1151                         // the fader was not in use, reset everything for new client
1152                         vecpChanFader[i]->Reset();
1153                         vecAvgLevels[i] = 0.0f;
1154 
1155                         // check if this is my own fader and set fader property
1156                         if ( i == iMyChannelID )
1157                         {
1158                             vecpChanFader[i]->SetIsMyOwnFader();
1159                         }
1160 
1161                         // set and increment the running new client counter needed for sorting (per definition:
1162                         // a fader for a new client shall always be inserted at the right-hand-side if no other
1163                         // sorting type is selected (i.e. "no sorting" is active) (#673)
1164                         vecpChanFader[i]->SetRunningNewClientCnt ( iRunningNewClientCnt++ );
1165 
1166                         // show fader
1167                         vecpChanFader[i]->Show();
1168 
1169                         // Set the default initial fader level. Check first that
1170                         // this is not the initialization (i.e. previously there
1171                         // were no faders visible) to avoid that our own level is
1172                         // adjusted. If we have received our own channel ID, then
1173                         // we can adjust the level even if no fader was visible.
1174                         // The fader level of 100 % is the default in the
1175                         // server, in that case we do not have to do anything here.
1176                         if ( ( !bNoFaderVisible || ( ( iMyChannelID != INVALID_INDEX ) && ( iMyChannelID != i ) ) ) &&
1177                              ( pSettings->iNewClientFaderLevel != 100 ) )
1178                         {
1179                             // the value is in percent -> convert range
1180                             vecpChanFader[i]->SetFaderLevel ( pSettings->iNewClientFaderLevel / 100.0 * AUD_MIX_FADER_MAX );
1181                         }
1182                     }
1183 
1184                     // restore gain (if new name is different from the current one)
1185                     if ( vecpChanFader[i]->GetReceivedName().compare ( vecChanInfo[j].strName ) )
1186                     {
1187                         // the text has actually changed, search in the list of
1188                         // stored settings if we have a matching entry
1189                         int  iStoredFaderLevel;
1190                         int  iStoredPanValue;
1191                         bool bStoredFaderIsSolo;
1192                         bool bStoredFaderIsMute;
1193                         int  iGroupID;
1194 
1195                         if ( GetStoredFaderSettings ( vecChanInfo[j].strName,
1196                                                       iStoredFaderLevel,
1197                                                       iStoredPanValue,
1198                                                       bStoredFaderIsSolo,
1199                                                       bStoredFaderIsMute,
1200                                                       iGroupID ) )
1201                         {
1202                             vecpChanFader[i]->SetFaderLevel ( iStoredFaderLevel, true ); // suppress group update
1203                             vecpChanFader[i]->SetPanValue ( iStoredPanValue );
1204                             vecpChanFader[i]->SetFaderIsSolo ( bStoredFaderIsSolo );
1205                             vecpChanFader[i]->SetFaderIsMute ( bStoredFaderIsMute );
1206                             vecpChanFader[i]->SetGroupID ( iGroupID ); // Must be the last to be set in the fader!
1207                         }
1208                     }
1209 
1210                     // set the channel infos
1211                     vecpChanFader[i]->SetChannelInfos ( vecChanInfo[j] );
1212 
1213                     bFaderIsUsed = true;
1214                 }
1215             }
1216 
1217             // if current fader is not used, hide it
1218             if ( !bFaderIsUsed )
1219             {
1220                 // before hiding the fader, store its level (if some conditions are fulfilled)
1221                 StoreFaderSettings ( vecpChanFader[i] );
1222 
1223                 vecpChanFader[i]->Hide();
1224             }
1225         }
1226 
1227         // update the solo states since if any channel was on solo and a new client
1228         // has just connected, the new channel must be muted
1229         UpdateSoloStates();
1230 
1231         // update flag for "all faders are invisible"
1232         bNoFaderVisible = ( iNumConnectedClients == 0 );
1233     }
1234     Mutex.unlock(); // release mutex
1235 
1236     // sort the channels according to the selected sorting type
1237     ChangeFaderOrder ( eChSortType );
1238 
1239     // emit status of connected clients
1240     emit NumClientsChanged ( iNumConnectedClients );
1241 }
1242 
SetFaderLevel(const int iChannelIdx,const int iValue)1243 void CAudioMixerBoard::SetFaderLevel ( const int iChannelIdx, const int iValue )
1244 {
1245     // only apply new fader level if channel index is valid and the fader is visible
1246     if ( ( iChannelIdx >= 0 ) && ( iChannelIdx < MAX_NUM_CHANNELS ) )
1247     {
1248         if ( vecpChanFader[iChannelIdx]->IsVisible() )
1249         {
1250             vecpChanFader[iChannelIdx]->SetFaderLevel ( iValue );
1251         }
1252     }
1253 }
1254 
SetPanValue(const int iChannelIdx,const int iValue)1255 void CAudioMixerBoard::SetPanValue ( const int iChannelIdx, const int iValue )
1256 {
1257     // only apply new pan value if channel index is valid and the panner is visible
1258     if ( ( iChannelIdx >= 0 ) && ( iChannelIdx < MAX_NUM_CHANNELS ) && bDisplayPans )
1259     {
1260         if ( vecpChanFader[iChannelIdx]->IsVisible() )
1261         {
1262             vecpChanFader[iChannelIdx]->SetPanValue ( iValue );
1263         }
1264     }
1265 }
1266 
SetFaderIsSolo(const int iChannelIdx,const bool bIsSolo)1267 void CAudioMixerBoard::SetFaderIsSolo ( const int iChannelIdx, const bool bIsSolo )
1268 {
1269     // only apply solo if channel index is valid and the fader is visible
1270     if ( ( iChannelIdx >= 0 ) && ( iChannelIdx < MAX_NUM_CHANNELS ) )
1271 
1272     {
1273         if ( vecpChanFader[iChannelIdx]->IsVisible() )
1274         {
1275             vecpChanFader[iChannelIdx]->SetFaderIsSolo ( bIsSolo );
1276         }
1277     }
1278 }
1279 
SetFaderIsMute(const int iChannelIdx,const bool bIsMute)1280 void CAudioMixerBoard::SetFaderIsMute ( const int iChannelIdx, const bool bIsMute )
1281 {
1282     // only apply mute if channel index is valid and the fader is visible
1283     if ( ( iChannelIdx >= 0 ) && ( iChannelIdx < MAX_NUM_CHANNELS ) )
1284     {
1285         if ( vecpChanFader[iChannelIdx]->IsVisible() )
1286         {
1287             vecpChanFader[iChannelIdx]->SetFaderIsMute ( bIsMute );
1288         }
1289     }
1290 }
1291 
SetAllFaderLevelsToNewClientLevel()1292 void CAudioMixerBoard::SetAllFaderLevelsToNewClientLevel()
1293 {
1294     QMutexLocker locker ( &Mutex );
1295 
1296     for ( int i = 0; i < MAX_NUM_CHANNELS; i++ )
1297     {
1298         // only apply to visible faders and not to my own channel fader
1299         if ( vecpChanFader[i]->IsVisible() && ( i != iMyChannelID ) )
1300         {
1301             // the value is in percent -> convert range, also use the group
1302             // update flag to make sure the group values are all set to the
1303             // same fader level now
1304             vecpChanFader[i]->SetFaderLevel ( pSettings->iNewClientFaderLevel / 100.0 * AUD_MIX_FADER_MAX, true );
1305         }
1306     }
1307 }
1308 
AutoAdjustAllFaderLevels()1309 void CAudioMixerBoard::AutoAdjustAllFaderLevels()
1310 {
1311     QMutexLocker locker ( &Mutex );
1312 
1313     // initialize variables used for statistics
1314     float vecMaxLevel[MAX_NUM_FADER_GROUPS + 1];
1315     int   vecChannelsPerGroup[MAX_NUM_FADER_GROUPS + 1];
1316     for ( int i = 0; i < MAX_NUM_FADER_GROUPS + 1; ++i )
1317     {
1318         vecMaxLevel[i]         = LOW_BOUND_SIG_METER;
1319         vecChannelsPerGroup[i] = 0;
1320     }
1321     CVector<CVector<float>> levels;
1322     levels.resize ( MAX_NUM_FADER_GROUPS + 1 );
1323 
1324     // compute min/max level per group and number of channels per group
1325     for ( int i = 0; i < MAX_NUM_CHANNELS; ++i )
1326     {
1327         // only apply to visible faders (and not to my own channel fader)
1328         if ( vecpChanFader[i]->IsVisible() && ( i != iMyChannelID ) )
1329         {
1330             // map averaged meter output level to decibels
1331             // (invert CStereoSignalLevelMeter::CalcLogResultForMeter)
1332             float leveldB = vecAvgLevels[i] * ( UPPER_BOUND_SIG_METER - LOW_BOUND_SIG_METER ) / NUM_STEPS_LED_BAR + LOW_BOUND_SIG_METER;
1333 
1334             int group = vecpChanFader[i]->GetGroupID();
1335             if ( group == INVALID_INDEX )
1336             {
1337                 group = MAX_NUM_FADER_GROUPS;
1338             }
1339 
1340             if ( leveldB >= AUTO_FADER_NOISE_THRESHOLD_DB )
1341             {
1342                 vecMaxLevel[group] = fmax ( vecMaxLevel[group], leveldB );
1343                 levels[group].Add ( leveldB );
1344             }
1345             ++vecChannelsPerGroup[group];
1346         }
1347     }
1348 
1349     // sort levels for later median computation
1350     for ( int i = 0; i < MAX_NUM_FADER_GROUPS + 1; ++i )
1351     {
1352         std::sort ( levels[i].begin(), levels[i].end() );
1353     }
1354 
1355     // compute the number of active groups (at least one channel)
1356     int cntActiveGroups = 0;
1357     for ( int i = 0; i < MAX_NUM_FADER_GROUPS; ++i )
1358     {
1359         cntActiveGroups += vecChannelsPerGroup[i] > 0;
1360     }
1361 
1362     // only my channel is active, nothing to do
1363     if ( cntActiveGroups == 0 && vecChannelsPerGroup[MAX_NUM_FADER_GROUPS] == 0 )
1364     {
1365         return;
1366     }
1367 
1368     // compute target level for each group
1369     // (prevent clipping when each group contributes at maximum level)
1370     float targetLevelPerGroup = -20.0f * log10 ( std::max ( cntActiveGroups, 1 ) );
1371 
1372     // compute target levels for the channels of each group individually
1373     float vecTargetChannelLevel[MAX_NUM_FADER_GROUPS + 1];
1374     float levelOffset = 0.0f;
1375     float minFader    = 0.0f;
1376     for ( int i = 0; i < MAX_NUM_FADER_GROUPS + 1; ++i )
1377     {
1378         // compute the target level for each channel in the current group
1379         // (prevent clipping when each channel in this group contributes at
1380         // the maximum level)
1381         vecTargetChannelLevel[i] = vecChannelsPerGroup[i] > 0 ? targetLevelPerGroup - 20.0f * log10 ( vecChannelsPerGroup[i] ) : 0.0f;
1382 
1383         // get median level
1384         int cntChannels = levels[i].Size();
1385         if ( cntChannels == 0 )
1386         {
1387             continue;
1388         }
1389         float refLevel = levels[i][cntChannels / 2];
1390 
1391         // since we can only attenuate channels but not amplify, we have to
1392         // check that the reference channel can be brought to the target
1393         // level
1394         if ( refLevel < vecTargetChannelLevel[i] )
1395         {
1396             // otherwise, we adjust the level offset in such a way that
1397             // the level can be reached
1398             levelOffset = fmin ( levelOffset, refLevel - vecTargetChannelLevel[i] );
1399 
1400             // compute the minimum necessary fader setting
1401             minFader = fmin ( minFader, -vecMaxLevel[i] + vecTargetChannelLevel[i] + levelOffset );
1402         }
1403     }
1404 
1405     // take minimum fader value into account
1406     // very weak channels would actually require strong channels to be
1407     // attenuated to a large amount; however, the attenuation is limited by
1408     // the faders
1409     if ( minFader < -AUD_MIX_FADER_RANGE_DB )
1410     {
1411         levelOffset += -AUD_MIX_FADER_RANGE_DB - minFader;
1412     }
1413 
1414     // adjust all levels
1415     for ( int i = 0; i < MAX_NUM_CHANNELS; ++i )
1416     {
1417         // only apply to visible faders (and not to my own channel fader)
1418         if ( vecpChanFader[i]->IsVisible() && ( i != iMyChannelID ) )
1419         {
1420             // map averaged meter output level to decibels
1421             // (invert CStereoSignalLevelMeter::CalcLogResultForMeter)
1422             float leveldB = vecAvgLevels[i] * ( UPPER_BOUND_SIG_METER - LOW_BOUND_SIG_METER ) / NUM_STEPS_LED_BAR + LOW_BOUND_SIG_METER;
1423 
1424             int group = vecpChanFader[i]->GetGroupID();
1425             if ( group == INVALID_INDEX )
1426             {
1427                 if ( cntActiveGroups > 0 )
1428                 {
1429                     // do not adjust the channels without group in group mode
1430                     continue;
1431                 }
1432                 else
1433                 {
1434                     group = MAX_NUM_FADER_GROUPS;
1435                 }
1436             }
1437 
1438             // do not adjust channels with almost zero level to full level since
1439             // the channel might simply be silent at the moment
1440             if ( leveldB >= AUTO_FADER_NOISE_THRESHOLD_DB )
1441             {
1442                 // compute new level
1443                 float newdBLevel = -leveldB + vecTargetChannelLevel[group] + levelOffset;
1444 
1445                 // map range from decibels to fader level
1446                 // (this inverts MathUtils::CalcFaderGain)
1447                 float newFaderLevel = ( newdBLevel / AUD_MIX_FADER_RANGE_DB + 1.0f ) * AUD_MIX_FADER_MAX;
1448 
1449                 // limit fader
1450                 newFaderLevel = fmin ( fmax ( newFaderLevel, 0.0f ), float ( AUD_MIX_FADER_MAX ) );
1451 
1452                 // set fader level
1453                 vecpChanFader[i]->SetFaderLevel ( newFaderLevel, true );
1454             }
1455         }
1456     }
1457 }
1458 
StoreAllFaderSettings()1459 void CAudioMixerBoard::StoreAllFaderSettings()
1460 {
1461     QMutexLocker locker ( &Mutex );
1462 
1463     for ( int i = 0; i < MAX_NUM_CHANNELS; i++ )
1464     {
1465         StoreFaderSettings ( vecpChanFader[i] );
1466     }
1467 }
1468 
LoadAllFaderSettings()1469 void CAudioMixerBoard::LoadAllFaderSettings()
1470 {
1471     QMutexLocker locker ( &Mutex );
1472 
1473     int  iStoredFaderLevel;
1474     int  iStoredPanValue;
1475     bool bStoredFaderIsSolo;
1476     bool bStoredFaderIsMute;
1477     int  iGroupID;
1478 
1479     for ( int i = 0; i < MAX_NUM_CHANNELS; i++ )
1480     {
1481         if ( GetStoredFaderSettings ( vecpChanFader[i]->GetReceivedName(),
1482                                       iStoredFaderLevel,
1483                                       iStoredPanValue,
1484                                       bStoredFaderIsSolo,
1485                                       bStoredFaderIsMute,
1486                                       iGroupID ) )
1487         {
1488             vecpChanFader[i]->SetFaderLevel ( iStoredFaderLevel, true ); // suppress group update
1489             vecpChanFader[i]->SetPanValue ( iStoredPanValue );
1490             vecpChanFader[i]->SetFaderIsSolo ( bStoredFaderIsSolo );
1491             vecpChanFader[i]->SetFaderIsMute ( bStoredFaderIsMute );
1492             vecpChanFader[i]->SetGroupID ( iGroupID ); // Must be the last to be set in the fader!
1493         }
1494     }
1495 }
1496 
SetRemoteFaderIsMute(const int iChannelIdx,const bool bIsMute)1497 void CAudioMixerBoard::SetRemoteFaderIsMute ( const int iChannelIdx, const bool bIsMute )
1498 {
1499     // only apply remote mute state if channel index is valid and the fader is visible
1500     if ( ( iChannelIdx >= 0 ) && ( iChannelIdx < MAX_NUM_CHANNELS ) )
1501     {
1502         if ( vecpChanFader[iChannelIdx]->IsVisible() )
1503         {
1504             vecpChanFader[iChannelIdx]->SetRemoteFaderIsMute ( bIsMute );
1505         }
1506     }
1507 }
1508 
UpdateSoloStates()1509 void CAudioMixerBoard::UpdateSoloStates()
1510 {
1511     // first check if any channel has a solo state active
1512     bool bAnyChannelIsSolo = false;
1513 
1514     for ( int i = 0; i < MAX_NUM_CHANNELS; i++ )
1515     {
1516         // check if fader is in use and has solo state active
1517         if ( vecpChanFader[i]->IsVisible() && vecpChanFader[i]->IsSolo() )
1518         {
1519             bAnyChannelIsSolo = true;
1520             continue;
1521         }
1522     }
1523 
1524     // now update the solo state of all active faders
1525     for ( int i = 0; i < MAX_NUM_CHANNELS; i++ )
1526     {
1527         if ( vecpChanFader[i]->IsVisible() )
1528         {
1529             vecpChanFader[i]->UpdateSoloState ( bAnyChannelIsSolo );
1530         }
1531     }
1532 }
1533 
UpdateGainValue(const int iChannelIdx,const float fValue,const bool bIsMyOwnFader,const bool bIsGroupUpdate,const bool bSuppressServerUpdate,const double dLevelRatio)1534 void CAudioMixerBoard::UpdateGainValue ( const int    iChannelIdx,
1535                                          const float  fValue,
1536                                          const bool   bIsMyOwnFader,
1537                                          const bool   bIsGroupUpdate,
1538                                          const bool   bSuppressServerUpdate,
1539                                          const double dLevelRatio )
1540 {
1541     // update current gain
1542     if ( !bSuppressServerUpdate )
1543     {
1544         emit ChangeChanGain ( iChannelIdx, fValue, bIsMyOwnFader );
1545     }
1546 
1547     // if this fader is selected, all other in the group must be updated as
1548     // well (note that we do not have to update if this is already a group update
1549     // to avoid an infinite loop)
1550     if ( ( vecpChanFader[iChannelIdx]->GetGroupID() != INVALID_INDEX ) && !bIsGroupUpdate )
1551     {
1552         for ( int i = 0; i < MAX_NUM_CHANNELS; i++ )
1553         {
1554             // update rest of faders selected
1555             if ( vecpChanFader[i]->IsVisible() && ( vecpChanFader[i]->GetGroupID() == vecpChanFader[iChannelIdx]->GetGroupID() ) &&
1556                  ( i != iChannelIdx ) && ( dLevelRatio >= 0 ) )
1557             {
1558                 // synchronize faders with moving fader level (it is important
1559                 // to set the group flag to avoid infinite looping)
1560                 vecpChanFader[i]->SetFaderLevel ( vecpChanFader[i]->GetPreviousFaderLevel() * dLevelRatio, true );
1561             }
1562         }
1563     }
1564 }
1565 
UpdatePanValue(const int iChannelIdx,const float fValue)1566 void CAudioMixerBoard::UpdatePanValue ( const int iChannelIdx, const float fValue ) { emit ChangeChanPan ( iChannelIdx, fValue ); }
1567 
StoreFaderSettings(CChannelFader * pChanFader)1568 void CAudioMixerBoard::StoreFaderSettings ( CChannelFader* pChanFader )
1569 {
1570     // if the fader was visible and the name is not empty, we store the old gain
1571     if ( pChanFader->IsVisible() && !pChanFader->GetReceivedName().isEmpty() )
1572     {
1573         CVector<int> viOldStoredFaderLevels ( pSettings->vecStoredFaderLevels );
1574         CVector<int> viOldStoredPanValues ( pSettings->vecStoredPanValues );
1575         CVector<int> vbOldStoredFaderIsSolo ( pSettings->vecStoredFaderIsSolo );
1576         CVector<int> vbOldStoredFaderIsMute ( pSettings->vecStoredFaderIsMute );
1577         CVector<int> vbOldStoredFaderGroupID ( pSettings->vecStoredFaderGroupID );
1578 
1579         // put new value on the top of the list
1580         const int iOldIdx = pSettings->vecStoredFaderTags.StringFiFoWithCompare ( pChanFader->GetReceivedName() );
1581 
1582         // current fader level and solo state is at the top of the list
1583         pSettings->vecStoredFaderLevels[0]  = pChanFader->GetFaderLevel();
1584         pSettings->vecStoredPanValues[0]    = pChanFader->GetPanValue();
1585         pSettings->vecStoredFaderIsSolo[0]  = pChanFader->IsSolo();
1586         pSettings->vecStoredFaderIsMute[0]  = pChanFader->IsMute();
1587         pSettings->vecStoredFaderGroupID[0] = pChanFader->GetGroupID();
1588         int iTempListCnt                    = 1; // current fader is on top, other faders index start at 1
1589 
1590         for ( int iIdx = 0; iIdx < MAX_NUM_STORED_FADER_SETTINGS; iIdx++ )
1591         {
1592             // first check if we still have space in our data storage
1593             if ( iTempListCnt < MAX_NUM_STORED_FADER_SETTINGS )
1594             {
1595                 // check for the old index of the current entry (this has to be
1596                 // skipped), note that per definition: the old index is an illegal
1597                 // index in case the entry was not present in the vector before
1598                 if ( iIdx != iOldIdx )
1599                 {
1600                     pSettings->vecStoredFaderLevels[iTempListCnt]  = viOldStoredFaderLevels[iIdx];
1601                     pSettings->vecStoredPanValues[iTempListCnt]    = viOldStoredPanValues[iIdx];
1602                     pSettings->vecStoredFaderIsSolo[iTempListCnt]  = vbOldStoredFaderIsSolo[iIdx];
1603                     pSettings->vecStoredFaderIsMute[iTempListCnt]  = vbOldStoredFaderIsMute[iIdx];
1604                     pSettings->vecStoredFaderGroupID[iTempListCnt] = vbOldStoredFaderGroupID[iIdx];
1605 
1606                     iTempListCnt++;
1607                 }
1608             }
1609         }
1610     }
1611 }
1612 
GetStoredFaderSettings(const QString & strName,int & iStoredFaderLevel,int & iStoredPanValue,bool & bStoredFaderIsSolo,bool & bStoredFaderIsMute,int & iGroupID)1613 bool CAudioMixerBoard::GetStoredFaderSettings ( const QString& strName,
1614                                                 int&           iStoredFaderLevel,
1615                                                 int&           iStoredPanValue,
1616                                                 bool&          bStoredFaderIsSolo,
1617                                                 bool&          bStoredFaderIsMute,
1618                                                 int&           iGroupID )
1619 {
1620     // only do the check if the name string is not empty
1621     if ( !strName.isEmpty() )
1622     {
1623         for ( int iIdx = 0; iIdx < MAX_NUM_STORED_FADER_SETTINGS; iIdx++ )
1624         {
1625             // check if fader text is already known in the list
1626             if ( !pSettings->vecStoredFaderTags[iIdx].compare ( strName ) )
1627             {
1628                 // copy stored settings values
1629                 iStoredFaderLevel  = pSettings->vecStoredFaderLevels[iIdx];
1630                 iStoredPanValue    = pSettings->vecStoredPanValues[iIdx];
1631                 bStoredFaderIsSolo = pSettings->vecStoredFaderIsSolo[iIdx] != 0;
1632                 bStoredFaderIsMute = pSettings->vecStoredFaderIsMute[iIdx] != 0;
1633                 iGroupID           = pSettings->vecStoredFaderGroupID[iIdx];
1634 
1635                 // values found and copied, return OK
1636                 return true;
1637             }
1638         }
1639     }
1640 
1641     // return "not OK" since we did not find matching fader settings
1642     return false;
1643 }
1644 
SetChannelLevels(const CVector<uint16_t> & vecChannelLevel)1645 void CAudioMixerBoard::SetChannelLevels ( const CVector<uint16_t>& vecChannelLevel )
1646 {
1647     const int iNumChannelLevels = vecChannelLevel.Size();
1648     int       i                 = 0;
1649 
1650     for ( int iChId = 0; iChId < MAX_NUM_CHANNELS; iChId++ )
1651     {
1652         if ( vecpChanFader[iChId]->IsVisible() && ( i < iNumChannelLevels ) )
1653         {
1654             // compute exponential moving average
1655             vecAvgLevels[iChId] = ( 1.0f - AUTO_FADER_ADJUST_ALPHA ) * vecAvgLevels[iChId] + AUTO_FADER_ADJUST_ALPHA * vecChannelLevel[i];
1656 
1657             vecpChanFader[iChId]->SetChannelLevel ( vecChannelLevel[i++] );
1658 
1659             // show level only if we successfully received levels from the
1660             // server (if server does not support levels, do not show levels)
1661             if ( !vecpChanFader[iChId]->GetDisplayChannelLevel() )
1662             {
1663                 vecpChanFader[iChId]->SetDisplayChannelLevel ( true );
1664             }
1665         }
1666     }
1667 }
1668 
MuteMyChannel()1669 void CAudioMixerBoard::MuteMyChannel() { SetFaderIsMute ( iMyChannelID, true ); }
1670