Skip to content

Commit 3f0983e

Browse files
committed
Make "My Channel" work for all controls
1 parent b1eda2e commit 3f0983e

File tree

2 files changed

+126
-84
lines changed

2 files changed

+126
-84
lines changed

src/sound/soundbase.cpp

Lines changed: 112 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,140 @@ 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
{
228+
if ( strMIDISetup.isEmpty() )
229+
return;
230+
240231
int iMIDIOffsetFader = 70; // Behringer X-TOUCH: offset of 0x46
241232

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() )
233+
// Parse the --ctrlmidich string. There are two formats.
234+
// Default to the legacy kind of specifying the fader controller offset
235+
// without an indication of the count of controllers.
236+
bool bSimple = true;
237+
238+
// split the different parameter strings
239+
const QStringList slMIDIParams = strMIDISetup.split ( ";" );
240+
241+
int iMIDIParamsStart = 0;
242+
if ( slMIDIParams.count() >= 1 )
262243
{
263-
// split the different parameter strings
264-
const QStringList slMIDIParams = strMIDISetup.split ( ";" );
244+
bool bChOK = false;
245+
int i = slMIDIParams[0].toUInt ( &bChOK );
246+
if ( bChOK )
247+
{
248+
// [MIDI channel] supplied (else use default)
249+
iCtrlMIDIChannel = i;
250+
iMIDIParamsStart = 1;
251+
}
252+
}
265253

266-
// [MIDI channel]
267-
if ( slMIDIParams.count() >= 1 )
254+
if ( slMIDIParams.count() >= 2 )
255+
{
256+
// if there is a second parameter that can be parsed as a number,
257+
// we have the legacy specification of controllers.
258+
int i = slMIDIParams[1].toUInt ( &bSimple );
259+
if ( bSimple )
268260
{
269-
iCtrlMIDIChannel = slMIDIParams[0].toUInt();
261+
// [offset for fader] supplied (else use default)
262+
iMIDIOffsetFader = i;
270263
}
264+
}
271265

272-
bool bSimple = true; // Indicates the legacy kind of specifying
273-
// the fader controller offset without an
274-
// indication of the count of controllers
266+
if ( bSimple )
267+
{
268+
// For the legacy specification, we consider every controller
269+
// up to the maximum number of channels (or the maximum
270+
// controller number) a fader.
271+
for ( int i = 0; i + iMIDIOffsetFader <= 127 && i < MAX_NUM_CHANNELS; i++ )
272+
{
273+
// add a list entry for the CMidiCtlEntry
274+
aMidiCtls[i + iMIDIOffsetFader] = { EMidiCtlType::Fader, i };
275+
}
276+
return;
277+
}
278+
279+
// We have named controllers
280+
// Validate and see whether "MyChannel" option is present
281+
282+
bool bMyChannel = false;
283+
QStringList slValid; // keep track of valid entries to make later processing simple
284+
285+
// if the first param was not a number, use "any channel" and see if it is a valid param
286+
for ( int i = iMIDIParamsStart; i < slMIDIParams.count(); i++ )
287+
{
288+
QString sParm = slMIDIParams[i].trimmed();
275289

276-
// [offset for level]
277-
if ( slMIDIParams.count() >= 2 )
290+
// skip empty entries silently
291+
if ( sParm.isEmpty() )
292+
continue;
293+
294+
// skip unknown entries silently
295+
int iCtrl = sMidiCtl.indexOf ( sParm[0] );
296+
if ( iCtrl < 0 )
297+
continue;
298+
299+
// once seen, just remember this
300+
if ( static_cast<EMidiCtlType> ( iCtrl ) == EMidiCtlType::MyChannel )
278301
{
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;
302+
bMyChannel = true;
303+
continue;
284304
}
285305

286-
if ( bSimple )
306+
const QStringList slP = sParm.mid ( 1 ).split ( '*' );
307+
308+
// skip invalid entries silently
309+
if ( slP.count() > 2 )
310+
continue;
311+
312+
bool bIsUInt = false;
313+
314+
// skip invalid entries silently
315+
unsigned int u = slP[0].toUInt ( &bIsUInt );
316+
if ( !bIsUInt )
317+
continue;
318+
int iFirst = u;
319+
320+
// silently default incoherent count to 1
321+
int iNum = 1;
322+
if ( slP.count() == 2 )
287323
{
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++ )
292-
{
293-
if ( i + iMIDIOffsetFader > 127 )
294-
break;
295-
aMidiCtls[i + iMIDIOffsetFader] = { EMidiCtlType::Fader, i };
296-
}
297-
return;
324+
bIsUInt = false;
325+
unsigned int u = slP[0].toUInt ( &bIsUInt );
326+
if ( bIsUInt )
327+
iNum = u;
298328
}
299329

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

302-
for ( int i = 1; i < slMIDIParams.count(); i++ )
334+
foreach ( QString sParm, slValid )
335+
{
336+
const QStringList slP = sParm.split ( '*' );
337+
const EMidiCtlType eTyp = static_cast<EMidiCtlType> ( slP[0].toInt() );
338+
const int iFirst = slP[0].toUInt();
339+
const int iNum = slP[1].toUInt();
340+
for ( int iOff = 0; iOff < iNum && iOff + iFirst <= 127 && iOff < MAX_NUM_CHANNELS; iOff++ )
303341
{
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++ )
342+
// For MyChannel option, first offset is "MyChannel", then the rest are 0 to iNum-1 channels
343+
if ( bMyChannel )
344+
{
345+
aMidiCtls[iFirst + iOff] = { eTyp, iOff == 0 ? INVALID_INDEX : iOff - 1 };
346+
}
347+
else
317348
{
318-
if ( iOff >= MAX_NUM_CHANNELS )
319-
break;
320-
if ( iFirst + iOff >= 128 )
321-
break;
322349
aMidiCtls[iFirst + iOff] = { eTyp, iOff };
323350
}
324351
}
325352
}
326353
}
327354

355+
/******************************************************************************\
356+
* MIDI handling *
357+
\******************************************************************************/
328358
void CSoundBase::ParseMIDIMessage ( const CVector<uint8_t>& vMIDIPaketBytes )
329359
{
330360
if ( vMIDIPaketBytes.Size() > 0 )
@@ -357,22 +387,22 @@ void CSoundBase::ParseMIDIMessage ( const CVector<uint8_t>& vMIDIPaketBytes )
357387
// make sure packet is long enough
358388
if ( vMIDIPaketBytes.Size() > 2 && vMIDIPaketBytes[1] <= uint8_t ( 127 ) && vMIDIPaketBytes[2] <= uint8_t ( 127 ) )
359389
{
390+
// Where "MyChannel" is in effect, cCtrl.iChannel will be INVALID_INDEX
391+
// for the first CC number in the range for a cCtrl.eType and then zero upwards.
360392
const CMidiCtlEntry& cCtrl = aMidiCtls[vMIDIPaketBytes[1]];
361393
const int iValue = vMIDIPaketBytes[2];
362-
;
394+
363395
switch ( cCtrl.eType )
364396
{
365397
case Fader:
366-
case OurFader:
367398
{
368399
// we are assuming that the controller number is the same
369400
// as the audio fader index and the range is 0-127
370401
const int iFaderLevel = static_cast<int> ( static_cast<double> ( iValue ) / 127 * AUD_MIX_FADER_MAX );
371-
const int iTheChannel = cCtrl.eType == OurFader ? INVALID_INDEX : cCtrl.iChannel;
372402

373403
// consider offset for the faders
374404

375-
emit ControllerInFaderLevel ( iTheChannel, iFaderLevel );
405+
emit ControllerInFaderLevel ( cCtrl.iChannel, iFaderLevel );
376406
}
377407
break;
378408
case Pan:

src/sound/soundbase.h

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

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

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

0 commit comments

Comments
 (0)