|
24 | 24 |
|
25 | 25 | #include "soundbase.h" |
26 | 26 |
|
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 | | - |
39 | 27 | /* Implementation *************************************************************/ |
40 | 28 | CSoundBase::CSoundBase ( const QString& strNewSystemDriverTechniqueName, |
41 | 29 | void ( *fpNewProcessCallback ) ( CVector<int16_t>& psData, void* pParg ), |
@@ -233,98 +221,176 @@ QVector<QString> CSoundBase::LoadAndInitializeFirstValidDriver ( const bool bOpe |
233 | 221 | } |
234 | 222 |
|
235 | 223 | /******************************************************************************\ |
236 | | -* MIDI handling * |
| 224 | +* Command Line Handling * |
237 | 225 | \******************************************************************************/ |
238 | 226 | void CSoundBase::ParseCommandLineArgument ( const QString& strMIDISetup ) |
239 | 227 | { |
| 228 | + if ( strMIDISetup.isEmpty() ) |
| 229 | + return; |
| 230 | + |
240 | 231 | int iMIDIOffsetFader = 70; // Behringer X-TOUCH: offset of 0x46 |
241 | 232 |
|
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 |
| 233 | + // default to the legacy kind of specifying |
| 234 | + // the fader controller offset without an |
| 235 | + // indication of the count of controllers |
| 236 | + bool bSimple = true; |
| 237 | + |
| 238 | + // Parse the --ctrlmidich string. There are two formats. |
| 239 | + // |
| 240 | + // The legacy definition has just one or two numbers that only |
| 241 | + // provides a definition for the controller offset of the fader |
245 | 242 | // controllers (default 70 for the sake of Behringer X-Touch) |
246 | | - // [MIDI channel];[offset for level] |
| 243 | + // [MIDI channel];[offset for first fader] |
247 | 244 | // |
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 |
| 245 | + // The more verbose new form is a sequence of offsets and counts for various |
| 246 | + // controllers: |
| 247 | + // [MIDI channel];[control letter][offset]*[count](;...) |
| 248 | + // Currently, the following control letters are defined: |
| 249 | + // Fader - 'f' |
| 250 | + // Pan - 'p' |
| 251 | + // Solo - 's' |
| 252 | + // Mute - 'm' |
| 253 | + // 'offset' is the base MIDI CC number for the control. |
| 254 | + // 'count' is the number of CC values for the control (Jamulus channels). |
255 | 255 | // |
| 256 | + // Additionally, 'o' has a single offset and controls Mute Myself. |
| 257 | + // |
| 258 | + // In addition, 'z' reserves the first CC number for a control |
| 259 | + // to mean "my Jamulus channel". (For example |
| 260 | + // 1;f0*9;z |
| 261 | + // would mean fader CC0 controlled "my" channel, with CC1 to CC8 for normal channels 1 to 8. |
| 262 | + // |
| 263 | + // An example for a Korg nanoKONTROL2 with 8 fader controllers |
| 264 | + // starting at offset 0 and 8 pan controllers starting at offset 16 |
| 265 | + // would be |
256 | 266 | // [MIDI channel];f0*8;p16*8 |
257 | 267 | // |
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() ) |
262 | | - { |
263 | | - // split the different parameter strings |
264 | | - const QStringList slMIDIParams = strMIDISetup.split ( ";" ); |
| 268 | + // However, at the current point of time only 'f' and 'p' |
| 269 | + // controllers are actually implemented. |
| 270 | + |
| 271 | + // split the different parameter strings |
| 272 | + const QStringList slMIDIParams = strMIDISetup.split ( ";" ); |
265 | 273 |
|
266 | | - // [MIDI channel] |
267 | | - if ( slMIDIParams.count() >= 1 ) |
| 274 | + int iMIDIParamsStart = 0; |
| 275 | + if ( slMIDIParams.count() >= 1 ) |
| 276 | + { |
| 277 | + bool bChOK = false; |
| 278 | + int i = slMIDIParams[0].toUInt ( &bChOK ); |
| 279 | + if ( bChOK ) |
268 | 280 | { |
269 | | - iCtrlMIDIChannel = slMIDIParams[0].toUInt(); |
| 281 | + // [MIDI channel] supplied |
| 282 | + iCtrlMIDIChannel = i; |
| 283 | + iMIDIParamsStart = 1; |
270 | 284 | } |
| 285 | + else |
| 286 | + { |
| 287 | + // Something else, use default channel and try new-style parsing |
| 288 | + bSimple = false; |
| 289 | + } |
| 290 | + } |
271 | 291 |
|
272 | | - bool bSimple = true; // Indicates the legacy kind of specifying |
273 | | - // the fader controller offset without an |
274 | | - // indication of the count of controllers |
| 292 | + if ( bSimple && slMIDIParams.count() >= 2 ) |
| 293 | + { |
| 294 | + // if there is a second parameter that can be parsed as a number, |
| 295 | + // we have the legacy specification of controllers. |
| 296 | + int i = slMIDIParams[1].toUInt ( &bSimple ); |
| 297 | + if ( bSimple ) |
| 298 | + { |
| 299 | + // [offset for fader] supplied (else use default) |
| 300 | + iMIDIOffsetFader = i; |
| 301 | + } |
| 302 | + } |
275 | 303 |
|
276 | | - // [offset for level] |
277 | | - if ( slMIDIParams.count() >= 2 ) |
| 304 | + if ( bSimple ) |
| 305 | + { |
| 306 | + // For the legacy specification, we consider every controller |
| 307 | + // up to the maximum number of channels (or the maximum |
| 308 | + // controller number) a fader. |
| 309 | + for ( int i = 0; i + iMIDIOffsetFader <= 127 && i < MAX_NUM_CHANNELS; i++ ) |
278 | 310 | { |
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; |
| 311 | + // add a list entry for the CMidiCtlEntry |
| 312 | + aMidiCtls[i + iMIDIOffsetFader] = { EMidiCtlType::Fader, i }; |
284 | 313 | } |
| 314 | + return; |
| 315 | + } |
285 | 316 |
|
286 | | - if ( bSimple ) |
| 317 | + // We have named controllers |
| 318 | + // Validate and see whether "MyChannel" option is present |
| 319 | + |
| 320 | + bool hasMyChannel = false; |
| 321 | + QStringList slValid; |
| 322 | + |
| 323 | + // if the first param was not a number, use "any channel" and see if it is a valid param |
| 324 | + for ( int i = iMIDIParamsStart; i < slMIDIParams.count(); i++ ) |
| 325 | + { |
| 326 | + QString sParm = slMIDIParams[i].trimmed(); |
| 327 | + |
| 328 | + // skip empty entries silently |
| 329 | + if ( sParm.isEmpty() ) |
| 330 | + continue; |
| 331 | + |
| 332 | + // skip unknown entries silently |
| 333 | + int iCtrl = sMidiCtl.indexOf ( sParm[0] ); |
| 334 | + if ( iCtrl < 0 ) |
| 335 | + continue; |
| 336 | + |
| 337 | + // once seen, just remember this |
| 338 | + if ( static_cast<EMidiCtlType> ( iCtrl ) == EMidiCtlType::MyChannel ) |
287 | 339 | { |
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; |
| 340 | + hasMyChannel = true; |
| 341 | + continue; |
298 | 342 | } |
299 | 343 |
|
300 | | - // We have named controllers |
| 344 | + const QStringList slP = sParm.mid ( 1 ).split ( '*' ); |
| 345 | + |
| 346 | + // skip invalid entries silently |
| 347 | + if ( slP.count() > 2 ) |
| 348 | + continue; |
301 | 349 |
|
302 | | - for ( int i = 1; i < slMIDIParams.count(); i++ ) |
| 350 | + bool bIsUInt = false; |
| 351 | + |
| 352 | + // skip invalid entries silently |
| 353 | + unsigned int u = slP[0].toUInt ( &bIsUInt ); |
| 354 | + if ( !bIsUInt ) |
| 355 | + continue; |
| 356 | + int iFirst = u; |
| 357 | + |
| 358 | + // silently default incoherent count to 1 |
| 359 | + int iNum = 1; |
| 360 | + if ( slP.count() == 2 ) |
303 | 361 | { |
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++ ) |
| 362 | + bIsUInt = false; |
| 363 | + unsigned int u = slP[0].toUInt ( &bIsUInt ); |
| 364 | + if ( bIsUInt ) |
| 365 | + iNum = u; |
| 366 | + } |
| 367 | + slValid.append ( QString ( "%1*%2*%3" ).arg ( iCtrl ).arg ( iFirst ).arg ( iNum ) ); |
| 368 | + } |
| 369 | + |
| 370 | + foreach ( QString sParm, slValid ) |
| 371 | + { |
| 372 | + const QStringList slP = sParm.split ( '*' ); |
| 373 | + const EMidiCtlType eTyp = static_cast<EMidiCtlType> ( slP[0].toInt() ); |
| 374 | + const int iFirst = slP[0].toUInt(); |
| 375 | + const int iNum = slP[1].toUInt(); |
| 376 | + for ( int iOff = 0; iOff < iNum && iOff + iFirst <= 127 && iOff < MAX_NUM_CHANNELS; iOff++ ) |
| 377 | + { |
| 378 | + // For MyChannel option, first offset is "MyChannel", then the rest are 0 to iNum-1 channels |
| 379 | + if ( hasMyChannel ) |
| 380 | + { |
| 381 | + aMidiCtls[iFirst + iOff] = { eTyp, iOff == 0 ? INVALID_INDEX : iOff - 1 }; |
| 382 | + } |
| 383 | + else |
317 | 384 | { |
318 | | - if ( iOff >= MAX_NUM_CHANNELS ) |
319 | | - break; |
320 | | - if ( iFirst + iOff >= 128 ) |
321 | | - break; |
322 | 385 | aMidiCtls[iFirst + iOff] = { eTyp, iOff }; |
323 | 386 | } |
324 | 387 | } |
325 | 388 | } |
326 | 389 | } |
327 | 390 |
|
| 391 | +/******************************************************************************\ |
| 392 | +* MIDI handling * |
| 393 | +\******************************************************************************/ |
328 | 394 | void CSoundBase::ParseMIDIMessage ( const CVector<uint8_t>& vMIDIPaketBytes ) |
329 | 395 | { |
330 | 396 | if ( vMIDIPaketBytes.Size() > 0 ) |
@@ -357,22 +423,22 @@ void CSoundBase::ParseMIDIMessage ( const CVector<uint8_t>& vMIDIPaketBytes ) |
357 | 423 | // make sure packet is long enough |
358 | 424 | if ( vMIDIPaketBytes.Size() > 2 && vMIDIPaketBytes[1] <= uint8_t ( 127 ) && vMIDIPaketBytes[2] <= uint8_t ( 127 ) ) |
359 | 425 | { |
| 426 | + // Where "MyChannel" is in effect, cCtrl.iChannel will be INVALID_INDEX |
| 427 | + // for the first CC number in the range for a cCtrl.eType and then zero upwards. |
360 | 428 | const CMidiCtlEntry& cCtrl = aMidiCtls[vMIDIPaketBytes[1]]; |
361 | 429 | const int iValue = vMIDIPaketBytes[2]; |
362 | | - ; |
| 430 | + |
363 | 431 | switch ( cCtrl.eType ) |
364 | 432 | { |
365 | 433 | case Fader: |
366 | | - case OurFader: |
367 | 434 | { |
368 | 435 | // we are assuming that the controller number is the same |
369 | 436 | // as the audio fader index and the range is 0-127 |
370 | 437 | 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; |
372 | 438 |
|
373 | 439 | // consider offset for the faders |
374 | 440 |
|
375 | | - emit ControllerInFaderLevel ( iTheChannel, iFaderLevel ); |
| 441 | + emit ControllerInFaderLevel ( cCtrl.iChannel, iFaderLevel ); |
376 | 442 | } |
377 | 443 | break; |
378 | 444 | case Pan: |
|
0 commit comments