|
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,140 @@ 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 |
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 ) |
262 | 243 | { |
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 | + } |
265 | 253 |
|
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 ) |
268 | 260 | { |
269 | | - iCtrlMIDIChannel = slMIDIParams[0].toUInt(); |
| 261 | + // [offset for fader] supplied (else use default) |
| 262 | + iMIDIOffsetFader = i; |
270 | 263 | } |
| 264 | + } |
271 | 265 |
|
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(); |
275 | 289 |
|
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 ) |
278 | 301 | { |
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; |
284 | 304 | } |
285 | 305 |
|
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 ) |
287 | 323 | { |
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; |
298 | 328 | } |
299 | 329 |
|
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 | + } |
301 | 333 |
|
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++ ) |
303 | 341 | { |
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 |
317 | 348 | { |
318 | | - if ( iOff >= MAX_NUM_CHANNELS ) |
319 | | - break; |
320 | | - if ( iFirst + iOff >= 128 ) |
321 | | - break; |
322 | 349 | aMidiCtls[iFirst + iOff] = { eTyp, iOff }; |
323 | 350 | } |
324 | 351 | } |
325 | 352 | } |
326 | 353 | } |
327 | 354 |
|
| 355 | +/******************************************************************************\ |
| 356 | +* MIDI handling * |
| 357 | +\******************************************************************************/ |
328 | 358 | void CSoundBase::ParseMIDIMessage ( const CVector<uint8_t>& vMIDIPaketBytes ) |
329 | 359 | { |
330 | 360 | if ( vMIDIPaketBytes.Size() > 0 ) |
@@ -357,22 +387,22 @@ void CSoundBase::ParseMIDIMessage ( const CVector<uint8_t>& vMIDIPaketBytes ) |
357 | 387 | // make sure packet is long enough |
358 | 388 | if ( vMIDIPaketBytes.Size() > 2 && vMIDIPaketBytes[1] <= uint8_t ( 127 ) && vMIDIPaketBytes[2] <= uint8_t ( 127 ) ) |
359 | 389 | { |
| 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. |
360 | 392 | const CMidiCtlEntry& cCtrl = aMidiCtls[vMIDIPaketBytes[1]]; |
361 | 393 | const int iValue = vMIDIPaketBytes[2]; |
362 | | - ; |
| 394 | + |
363 | 395 | switch ( cCtrl.eType ) |
364 | 396 | { |
365 | 397 | case Fader: |
366 | | - case OurFader: |
367 | 398 | { |
368 | 399 | // we are assuming that the controller number is the same |
369 | 400 | // as the audio fader index and the range is 0-127 |
370 | 401 | 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 | 402 |
|
373 | 403 | // consider offset for the faders |
374 | 404 |
|
375 | | - emit ControllerInFaderLevel ( iTheChannel, iFaderLevel ); |
| 405 | + emit ControllerInFaderLevel ( cCtrl.iChannel, iFaderLevel ); |
376 | 406 | } |
377 | 407 | break; |
378 | 408 | case Pan: |
|
0 commit comments