Skip to content

Commit 42ce439

Browse files
committed
Make "My Channel" work for all controls
1 parent ee960df commit 42ce439

File tree

4 files changed

+170
-100
lines changed

4 files changed

+170
-100
lines changed

src/audiomixerboard.cpp

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,53 +1343,56 @@ void CAudioMixerBoard::ApplyNewConClientList ( CVector<CChannelInfo>& vecChanInf
13431343

13441344
void CAudioMixerBoard::SetFaderLevel ( const int iChannelIdx, const int iValue )
13451345
{
1346-
// If iChannelIdx is I_MY_CHANNEL and our own channel ID is a valid index
1347-
// then we adjust our own fader level
1348-
const int iTheChannelIdx = ( iChannelIdx == I_MY_CHANNEL ) ? iMyChannelID : iChannelIdx;
1346+
const int iRealChannelIdx = getRealChannelIdx ( iChannelIdx );
13491347

13501348
// only apply new fader level if channel index is valid and the fader is visible
1351-
if ( ( iTheChannelIdx >= 0 ) && ( iTheChannelIdx < MAX_NUM_CHANNELS ) )
1349+
if ( ( iRealChannelIdx >= 0 ) && ( iRealChannelIdx < MAX_NUM_CHANNELS ) )
13521350
{
1353-
if ( vecpChanFader[static_cast<size_t> ( iTheChannelIdx )]->IsVisible() )
1351+
if ( vecpChanFader[static_cast<size_t> ( iRealChannelIdx )]->IsVisible() )
13541352
{
1355-
vecpChanFader[static_cast<size_t> ( iTheChannelIdx )]->SetFaderLevel ( iValue );
1353+
vecpChanFader[static_cast<size_t> ( iRealChannelIdx )]->SetFaderLevel ( iValue );
13561354
}
13571355
}
13581356
}
13591357

13601358
void CAudioMixerBoard::SetPanValue ( const int iChannelIdx, const int iValue )
13611359
{
1360+
const int iRealChannelIdx = getRealChannelIdx ( iChannelIdx );
1361+
13621362
// only apply new pan value if channel index is valid and the panner is visible
1363-
if ( ( iChannelIdx >= 0 ) && ( iChannelIdx < MAX_NUM_CHANNELS ) && bDisplayPans )
1363+
if ( ( iRealChannelIdx >= 0 ) && ( iRealChannelIdx < MAX_NUM_CHANNELS ) && bDisplayPans )
13641364
{
1365-
if ( vecpChanFader[static_cast<size_t> ( iChannelIdx )]->IsVisible() )
1365+
if ( vecpChanFader[static_cast<size_t> ( iRealChannelIdx )]->IsVisible() )
13661366
{
1367-
vecpChanFader[static_cast<size_t> ( iChannelIdx )]->SetPanValue ( iValue );
1367+
vecpChanFader[static_cast<size_t> ( iRealChannelIdx )]->SetPanValue ( iValue );
13681368
}
13691369
}
13701370
}
13711371

13721372
void CAudioMixerBoard::SetFaderIsSolo ( const int iChannelIdx, const bool bIsSolo )
13731373
{
1374-
// only apply solo if channel index is valid and the fader is visible
1375-
if ( ( iChannelIdx >= 0 ) && ( iChannelIdx < MAX_NUM_CHANNELS ) )
1374+
const int iRealChannelIdx = getRealChannelIdx ( iChannelIdx );
13761375

1376+
// only apply solo if channel index is valid and the fader is visible
1377+
if ( ( iRealChannelIdx >= 0 ) && ( iRealChannelIdx < MAX_NUM_CHANNELS ) )
13771378
{
1378-
if ( vecpChanFader[static_cast<size_t> ( iChannelIdx )]->IsVisible() )
1379+
if ( vecpChanFader[static_cast<size_t> ( iRealChannelIdx )]->IsVisible() )
13791380
{
1380-
vecpChanFader[static_cast<size_t> ( iChannelIdx )]->SetFaderIsSolo ( bIsSolo );
1381+
vecpChanFader[static_cast<size_t> ( iRealChannelIdx )]->SetFaderIsSolo ( bIsSolo );
13811382
}
13821383
}
13831384
}
13841385

13851386
void CAudioMixerBoard::SetFaderIsMute ( const int iChannelIdx, const bool bIsMute )
13861387
{
1388+
const int iRealChannelIdx = getRealChannelIdx ( iChannelIdx );
1389+
13871390
// only apply mute if channel index is valid and the fader is visible
1388-
if ( ( iChannelIdx >= 0 ) && ( iChannelIdx < MAX_NUM_CHANNELS ) )
1391+
if ( ( iRealChannelIdx >= 0 ) && ( iRealChannelIdx < MAX_NUM_CHANNELS ) )
13891392
{
1390-
if ( vecpChanFader[static_cast<size_t> ( iChannelIdx )]->IsVisible() )
1393+
if ( vecpChanFader[static_cast<size_t> ( iRealChannelIdx )]->IsVisible() )
13911394
{
1392-
vecpChanFader[static_cast<size_t> ( iChannelIdx )]->SetFaderIsMute ( bIsMute );
1395+
vecpChanFader[static_cast<size_t> ( iRealChannelIdx )]->SetFaderIsMute ( bIsMute );
13931396
}
13941397
}
13951398
}

src/audiomixerboard.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,15 @@ class CAudioMixerBoard : public QGroupBox, public CAudioMixerBoardSlots<MAX_NUM_
288288
template<unsigned int slotId>
289289
inline void connectFaderSignalsToMixerBoardSlots();
290290

291+
// When handling MIDI controllers for adjusting Jamulus channel controls,
292+
// each channel is assigned by the server. As a user's Jamulus channel
293+
// will vary on each server connection, to enable the assignment of MIDI
294+
// controllers to the user's own Jamulus channel, the constant I_MY_CHANNEL
295+
// is passed into the methods used to handle the requests. This method then
296+
// maps I_MY_CHANNEL to the current value of the user's Jamulus channel,
297+
// held in iMyChannel.
298+
inline int getRealChannelIdx ( const int iChannelIdx ) const { return iChannelIdx == I_MY_CHANNEL ? iMyChannelID : iChannelIdx; }
299+
291300
signals:
292301
void ChangeChanGain ( int iId, float fGain, bool bIsMyOwnFader );
293302
void ChangeChanPan ( int iId, float fPan );

src/sound/soundbase.cpp

Lines changed: 128 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,6 @@
2424

2525
#include "soundbase.h"
2626

27-
// This is used as a lookup table for parsing option letters, mapping
28-
// a single character to an EMidiCtlType
29-
char const sMidiCtlChar[] = {
30-
// Has to follow order of EMidiCtlType
31-
/* [EMidiCtlType::Fader] = */ 'f',
32-
/* [EMidiCtlType::Pan] = */ 'p',
33-
/* [EMidiCtlType::Solo] = */ 's',
34-
/* [EMidiCtlType::Mute] = */ 'm',
35-
/* [EMidiCtlType::MuteMyself] = */ 'o',
36-
/* [EMidiCtlType::OurFader] = */ 'z', // Proposed addition: a new enum value for "our fader"
37-
/* [EMidiCtlType::None] = */ '\0' };
38-
3927
/* Implementation *************************************************************/
4028
CSoundBase::CSoundBase ( const QString& strNewSystemDriverTechniqueName,
4129
void ( *fpNewProcessCallback ) ( CVector<int16_t>& psData, void* pParg ),
@@ -233,98 +221,156 @@ QVector<QString> CSoundBase::LoadAndInitializeFirstValidDriver ( const bool bOpe
233221
}
234222

235223
/******************************************************************************\
236-
* MIDI handling *
224+
* Command Line Handling *
237225
\******************************************************************************/
238226
void CSoundBase::ParseCommandLineArgument ( const QString& strMIDISetup )
239227
{
240-
int iMIDIOffsetFader = 70; // Behringer X-TOUCH: offset of 0x46
241-
242-
// parse the server info string according to definition: there is
243-
// the legacy definition with just one or two numbers that only
244-
// provides a definition for the controller offset of the level
245-
// controllers (default 70 for the sake of Behringer X-Touch)
246-
// [MIDI channel];[offset for level]
247-
//
248-
// The more verbose new form is a sequence of offsets for various
249-
// controllers: at the current point, 'f', 'p', 's', and 'm' are
250-
// parsed for fader, pan, solo, mute controllers respectively.
251-
// However, at the current point of time only 'f' and 'p'
252-
// controllers are actually implemented. The syntax for a Korg
253-
// nanoKONTROL2 with 8 fader controllers starting at offset 0 and
254-
// 8 pan controllers starting at offset 16 would be
255-
//
256-
// [MIDI channel];f0*8;p16*8
257-
//
258-
// Namely a sequence of letters indicating the kind of controller,
259-
// followed by the offset of the first such controller, followed
260-
// by * and a count for number of controllers (if more than 1)
261-
if ( !strMIDISetup.isEmpty() )
228+
if ( strMIDISetup.isEmpty() )
262229
{
263-
// split the different parameter strings
264-
const QStringList slMIDIParams = strMIDISetup.split ( ";" );
230+
// should be caught in main.cpp
231+
return;
232+
}
233+
234+
// Parse the --ctrlmidich string. There are two formats.
235+
// Default to the legacy kind of specifying the fader controller offset
236+
// without an indication of the count of controllers.
237+
bool bSimple = true;
265238

266-
// [MIDI channel]
267-
if ( slMIDIParams.count() >= 1 )
239+
// split the different parameter strings
240+
const QStringList slMIDIParams = strMIDISetup.split ( ";" );
241+
int iNumParams = slMIDIParams.count();
242+
243+
if ( iNumParams >= 1 )
244+
{
245+
bool bChOK = false;
246+
int i = slMIDIParams[0].toUInt ( &bChOK );
247+
if ( bChOK )
268248
{
269-
iCtrlMIDIChannel = slMIDIParams[0].toUInt();
249+
// [MIDI channel] supplied (else use default)
250+
iCtrlMIDIChannel = i;
270251
}
252+
else
253+
{
254+
// iCtrlMIDIChannel == INVALID_MIDI_CH, so no point continuing
255+
return;
256+
}
257+
}
271258

272-
bool bSimple = true; // Indicates the legacy kind of specifying
273-
// the fader controller offset without an
274-
// indication of the count of controllers
259+
// Use Behringer X-TOUCH as default offset of 0x46
260+
int iMIDIOffsetFader = 70;
261+
if ( iNumParams >= 2 )
262+
{
263+
// if there is a second parameter that can be parsed as a number,
264+
// we have the legacy specification of controllers.
265+
int i = slMIDIParams[1].toUInt ( &bSimple );
266+
if ( bSimple )
267+
{
268+
// [offset for fader] supplied (else use default)
269+
iMIDIOffsetFader = i;
270+
}
271+
}
275272

276-
// [offset for level]
277-
if ( slMIDIParams.count() >= 2 )
273+
if ( bSimple )
274+
{
275+
// For the legacy specification, we consider every controller
276+
// up to the maximum number of channels (or the maximum
277+
// controller number) a fader.
278+
for ( int i = 0; i + iMIDIOffsetFader <= 127 && i < MAX_NUM_CHANNELS; i++ )
278279
{
279-
int i = slMIDIParams[1].toUInt ( &bSimple );
280-
// if the second parameter can be parsed as a number, we
281-
// have the legacy specification of controllers.
282-
if ( bSimple )
283-
iMIDIOffsetFader = i;
280+
// add a list entry for the CMidiCtlEntry
281+
aMidiCtls[i + iMIDIOffsetFader] = { EMidiCtlType::Fader, i };
284282
}
283+
return;
284+
}
285285

286-
if ( bSimple )
286+
// We have named controllers
287+
// Validate and see whether "MyChannel" option is present
288+
289+
bool bMyChannel = false;
290+
QStringList slValid; // keep track of valid entries to make later processing simple
291+
292+
for ( int i = 0; i < iNumParams; i++ )
293+
{
294+
QString sParm = slMIDIParams[i].trimmed();
295+
296+
if ( sParm.isEmpty() )
297+
{
298+
// skip empty entries silently
299+
continue;
300+
}
301+
302+
int iCtrl = sMidiCtl.indexOf ( sParm[0] );
303+
if ( iCtrl < 0 )
304+
{
305+
// skip unknown entries silently
306+
continue;
307+
}
308+
309+
if ( static_cast<EMidiCtlType> ( iCtrl ) == EMidiCtlType::MyChannel )
287310
{
288-
// For the legacy specification, we consider every controller
289-
// up to the maximum number of channels (or the maximum
290-
// controller number) a fader.
291-
for ( int i = 0; i < MAX_NUM_CHANNELS; i++ )
311+
// once seen, just remember this
312+
bMyChannel = true;
313+
continue;
314+
}
315+
316+
const QStringList slP = sParm.mid ( 1 ).split ( '*' );
317+
318+
if ( slP.count() > 2 )
319+
{
320+
// skip invalid entries silently
321+
continue;
322+
}
323+
324+
bool bIsUInt = false;
325+
326+
unsigned int u = slP[0].toUInt ( &bIsUInt );
327+
if ( !bIsUInt )
328+
{
329+
// skip invalid entries silently
330+
continue;
331+
}
332+
int iFirst = u;
333+
334+
// silently default incoherent count to 1
335+
int iNum = 1;
336+
if ( static_cast<EMidiCtlType> ( iCtrl ) != EMidiCtlType::MuteMyself && slP.count() == 2 )
337+
{
338+
bIsUInt = false;
339+
unsigned int u = slP[1].toUInt ( &bIsUInt );
340+
if ( bIsUInt )
292341
{
293-
if ( i + iMIDIOffsetFader > 127 )
294-
break;
295-
aMidiCtls[i + iMIDIOffsetFader] = { EMidiCtlType::Fader, i };
342+
iNum = u;
296343
}
297-
return;
298344
}
299345

300-
// We have named controllers
346+
// store the valid entry in a more splittable format
347+
slValid.append ( QString ( "%1*%2*%3" ).arg ( iCtrl ).arg ( iFirst ).arg ( iNum ) );
348+
}
301349

302-
for ( int i = 1; i < slMIDIParams.count(); i++ )
350+
foreach ( QString sParm, slValid )
351+
{
352+
const QStringList slP = sParm.split ( '*' );
353+
const EMidiCtlType eTyp = static_cast<EMidiCtlType> ( slP[0].toInt() );
354+
const int iFirst = slP[1].toUInt();
355+
const int iNum = slP[2].toUInt();
356+
for ( int iOff = 0; iOff < iNum && iOff + iFirst <= 127 && iOff < MAX_NUM_CHANNELS; iOff++ )
303357
{
304-
QString sParm = slMIDIParams[i].trimmed();
305-
if ( sParm.isEmpty() )
306-
continue;
307-
308-
int iCtrl = QString ( sMidiCtlChar ).indexOf ( sParm[0] );
309-
if ( iCtrl < 0 )
310-
continue;
311-
EMidiCtlType eTyp = static_cast<EMidiCtlType> ( iCtrl );
312-
313-
const QStringList slP = sParm.mid ( 1 ).split ( '*' );
314-
int iFirst = slP[0].toUInt();
315-
int iNum = ( slP.count() > 1 ) ? slP[1].toUInt() : 1;
316-
for ( int iOff = 0; iOff < iNum; iOff++ )
358+
// For MyChannel option, first offset is "MyChannel", then the rest are 0 to iNum-1 channels
359+
if ( bMyChannel )
360+
{
361+
aMidiCtls[iFirst + iOff] = { eTyp, iOff == 0 ? I_MY_CHANNEL : iOff - 1 };
362+
}
363+
else
317364
{
318-
if ( iOff >= MAX_NUM_CHANNELS )
319-
break;
320-
if ( iFirst + iOff >= 128 )
321-
break;
322365
aMidiCtls[iFirst + iOff] = { eTyp, iOff };
323366
}
324367
}
325368
}
326369
}
327370

371+
/******************************************************************************\
372+
* MIDI handling *
373+
\******************************************************************************/
328374
void CSoundBase::ParseMIDIMessage ( const CVector<uint8_t>& vMIDIPaketBytes )
329375
{
330376
if ( vMIDIPaketBytes.Size() > 0 )
@@ -357,22 +403,22 @@ void CSoundBase::ParseMIDIMessage ( const CVector<uint8_t>& vMIDIPaketBytes )
357403
// make sure packet is long enough
358404
if ( vMIDIPaketBytes.Size() > 2 && vMIDIPaketBytes[1] <= uint8_t ( 127 ) && vMIDIPaketBytes[2] <= uint8_t ( 127 ) )
359405
{
406+
// Where "MyChannel" is in effect, cCtrl.iChannel will be I_MY_CHANNEL
407+
// for the first CC number in the range for a cCtrl.eType and then zero upwards.
360408
const CMidiCtlEntry& cCtrl = aMidiCtls[vMIDIPaketBytes[1]];
361409
const int iValue = vMIDIPaketBytes[2];
362-
;
410+
363411
switch ( cCtrl.eType )
364412
{
365413
case Fader:
366-
case OurFader:
367414
{
368415
// we are assuming that the controller number is the same
369416
// as the audio fader index and the range is 0-127
370417
const int iFaderLevel = static_cast<int> ( static_cast<double> ( iValue ) / 127 * AUD_MIX_FADER_MAX );
371-
const int iTheChannel = cCtrl.eType == OurFader ? I_MY_CHANNEL : cCtrl.iChannel;
372418

373419
// consider offset for the faders
374420

375-
emit ControllerInFaderLevel ( iTheChannel, iFaderLevel );
421+
emit ControllerInFaderLevel ( cCtrl.iChannel, iFaderLevel );
376422
}
377423
break;
378424
case Pan:

src/sound/soundbase.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ enum EMidiCtlType
5050
Solo,
5151
Mute,
5252
MuteMyself,
53-
OurFader, // Proposed addition: a MidiCtrlType for our own fader level
53+
MyChannel,
5454
None
5555
};
5656

@@ -160,7 +160,19 @@ class CSoundBase : public QThread
160160
QMutex MutexAudioProcessCallback;
161161
QMutex MutexDevProperties;
162162

163-
QString strSystemDriverTechniqueName;
163+
QString strSystemDriverTechniqueName;
164+
165+
// This is used as a lookup table for parsing option letters, mapping
166+
// a single character to an EMidiCtlType. Has to follow order of EMidiCtlType.
167+
const QString sMidiCtl = QString ( "f" // [EMidiCtlType::Fader]
168+
"p" // [EMidiCtlType::Pan]
169+
"s" // [EMidiCtlType::Solo]
170+
"m" // [EMidiCtlType::Mute]
171+
"o" // [EMidiCtlType::MuteMyself]
172+
"z" // [EMidiCtlType::MyChannel]
173+
"\0" // [EMidiCtlType::None]
174+
);
175+
164176
int iCtrlMIDIChannel;
165177
QVector<CMidiCtlEntry> aMidiCtls;
166178

0 commit comments

Comments
 (0)