3 |
* LinuxSampler - modular, streaming capable sampler * |
* LinuxSampler - modular, streaming capable sampler * |
4 |
* * |
* * |
5 |
* Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * |
* Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * |
6 |
* Copyright (C) 2005 - 2012 Christian Schoenebeck * |
* Copyright (C) 2005 - 2013 Christian Schoenebeck * |
7 |
* * |
* * |
8 |
* This program is free software; you can redistribute it and/or modify * |
* This program is free software; you can redistribute it and/or modify * |
9 |
* it under the terms of the GNU General Public License as published by * |
* it under the terms of the GNU General Public License as published by * |
77 |
MidiInputPort::MidiInputPort(MidiInputDevice* pDevice, int portNumber) |
MidiInputPort::MidiInputPort(MidiInputDevice* pDevice, int portNumber) |
78 |
: MidiChannelMapReader(MidiChannelMap), |
: MidiChannelMapReader(MidiChannelMap), |
79 |
SysexListenersReader(SysexListeners), |
SysexListenersReader(SysexListeners), |
80 |
virtualMidiDevicesReader(virtualMidiDevices) { |
virtualMidiDevicesReader(virtualMidiDevices), |
81 |
|
noteOnVelocityFilterReader(noteOnVelocityFilter) |
82 |
|
{ |
83 |
this->pDevice = pDevice; |
this->pDevice = pDevice; |
84 |
this->portNumber = portNumber; |
this->portNumber = portNumber; |
85 |
|
runningStatusBuf[0] = 0; |
86 |
Parameters["NAME"] = new ParameterName(this); |
Parameters["NAME"] = new ParameterName(this); |
87 |
} |
} |
88 |
|
|
100 |
|
|
101 |
void MidiInputPort::DispatchNoteOn(uint8_t Key, uint8_t Velocity, uint MidiChannel) { |
void MidiInputPort::DispatchNoteOn(uint8_t Key, uint8_t Velocity, uint MidiChannel) { |
102 |
if (Key > 127 || Velocity > 127 || MidiChannel > 16) return; |
if (Key > 127 || Velocity > 127 || MidiChannel > 16) return; |
103 |
|
|
104 |
|
// apply velocity filter (if any) |
105 |
|
const std::vector<uint8_t>& velocityFilter = noteOnVelocityFilterReader.Lock(); |
106 |
|
if (!velocityFilter.empty()) Velocity = velocityFilter[Velocity]; |
107 |
|
noteOnVelocityFilterReader.Unlock(); |
108 |
|
|
109 |
const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock(); |
const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock(); |
110 |
// dispatch event for engines listening to the same MIDI channel |
// dispatch event for engines listening to the same MIDI channel |
111 |
{ |
{ |
131 |
|
|
132 |
void MidiInputPort::DispatchNoteOn(uint8_t Key, uint8_t Velocity, uint MidiChannel, int32_t FragmentPos) { |
void MidiInputPort::DispatchNoteOn(uint8_t Key, uint8_t Velocity, uint MidiChannel, int32_t FragmentPos) { |
133 |
if (Key > 127 || Velocity > 127 || MidiChannel > 16) return; |
if (Key > 127 || Velocity > 127 || MidiChannel > 16) return; |
134 |
|
|
135 |
|
// apply velocity filter (if any) |
136 |
|
const std::vector<uint8_t>& velocityFilter = noteOnVelocityFilterReader.Lock(); |
137 |
|
if (!velocityFilter.empty()) Velocity = velocityFilter[Velocity]; |
138 |
|
noteOnVelocityFilterReader.Unlock(); |
139 |
|
|
140 |
const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock(); |
const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock(); |
141 |
// dispatch event for engines listening to the same MIDI channel |
// dispatch event for engines listening to the same MIDI channel |
142 |
{ |
{ |
378 |
} |
} |
379 |
MidiChannelMapReader.Unlock(); |
MidiChannelMapReader.Unlock(); |
380 |
} |
} |
381 |
|
|
382 |
|
/** |
383 |
|
* Handles the so called MIDI "running status" mode, which allows devices |
384 |
|
* to reduce bandwidth (data reduction). |
385 |
|
* |
386 |
|
* If the passed in MIDI data is regular MIDI data, this method will simply |
387 |
|
* return the original data pointer and just stores the status byte for |
388 |
|
* potential "running status" event eventually coming next. |
389 |
|
* |
390 |
|
* If the passed in MIDI data however seems to be in "running status" mode, |
391 |
|
* this method will return another buffer, which allows the MIDI parser |
392 |
|
* to handle the MIDI data as usually with "normal" MIDI data. |
393 |
|
*/ |
394 |
|
uint8_t* MidiInputPort::handleRunningStatus(uint8_t* pData) { |
395 |
|
if ((pData[0] & 0x80) || !runningStatusBuf[0]) { |
396 |
|
// store status byte for eventual "running status" in next event |
397 |
|
if (pData[0] & 0x80) { |
398 |
|
if (pData[0] < 0xf0) { |
399 |
|
// "running status" is only allowed for channel messages |
400 |
|
runningStatusBuf[0] = pData[0]; |
401 |
|
} else if (pData[0] < 0xf8) { |
402 |
|
// "system common" messages (0xf0..0xf7) shall reset any running |
403 |
|
// status, however "realtime" messages (0xf8..0xff) shall be |
404 |
|
// ignored here |
405 |
|
runningStatusBuf[0] = 0; |
406 |
|
} |
407 |
|
} |
408 |
|
// it's either a regular status byte, or some invalid "running status" |
409 |
|
return pData; |
410 |
|
} else { // "running status" mode ... |
411 |
|
const uint8_t type = runningStatusBuf[0] & 0xf0; |
412 |
|
const int size = (type == 0xc0 || type == 0xd0) ? 1 : 2; // only program change & channel pressure have 1 data bytes |
413 |
|
memcpy(&runningStatusBuf[1], pData, size); |
414 |
|
return runningStatusBuf; |
415 |
|
} |
416 |
|
} |
417 |
|
|
418 |
void MidiInputPort::DispatchRaw(uint8_t* pData) { |
void MidiInputPort::DispatchRaw(uint8_t* pData) { |
419 |
|
pData = handleRunningStatus(pData); |
420 |
|
|
421 |
uint8_t channel = pData[0] & 0x0f; |
uint8_t channel = pData[0] & 0x0f; |
422 |
switch (pData[0] & 0xf0) { |
switch (pData[0] & 0xf0) { |
423 |
case 0x80: |
case 0x80: |
451 |
} |
} |
452 |
|
|
453 |
void MidiInputPort::DispatchRaw(uint8_t* pData, int32_t FragmentPos) { |
void MidiInputPort::DispatchRaw(uint8_t* pData, int32_t FragmentPos) { |
454 |
|
pData = handleRunningStatus(pData); |
455 |
|
|
456 |
uint8_t channel = pData[0] & 0x0f; |
uint8_t channel = pData[0] & 0x0f; |
457 |
switch (pData[0] & 0xf0) { |
switch (pData[0] & 0xf0) { |
458 |
case 0x80: |
case 0x80: |
484 |
break; |
break; |
485 |
} |
} |
486 |
} |
} |
487 |
|
|
488 |
|
void MidiInputPort::SetNoteOnVelocityFilter(const std::vector<uint8_t>& filter) { |
489 |
|
if (filter.size() != 128 && filter.size() != 0) |
490 |
|
throw MidiInputException("Note on velocity filter must be either of size 128 or 0"); |
491 |
|
|
492 |
|
// check the value range of the filter |
493 |
|
if (!filter.empty()) |
494 |
|
for (int i = 0; i < 128; i++) |
495 |
|
if (filter[i] > 127) |
496 |
|
throw MidiInputException("Invalid note on velocity filter, values must be in range 0 .. 127"); |
497 |
|
|
498 |
|
// apply new filter ... |
499 |
|
LockGuard lock(noteOnVelocityFilterMutex); |
500 |
|
// double buffer ... double work ... |
501 |
|
{ |
502 |
|
std::vector<uint8_t>& config = |
503 |
|
noteOnVelocityFilter.GetConfigForUpdate(); |
504 |
|
config = filter; |
505 |
|
} |
506 |
|
{ |
507 |
|
std::vector<uint8_t>& config = |
508 |
|
noteOnVelocityFilter.SwitchConfig(); |
509 |
|
config = filter; |
510 |
|
} |
511 |
|
} |
512 |
|
|
513 |
void MidiInputPort::Connect(EngineChannel* pEngineChannel, midi_chan_t MidiChannel) { |
void MidiInputPort::Connect(EngineChannel* pEngineChannel, midi_chan_t MidiChannel) { |
514 |
if (MidiChannel < 0 || MidiChannel > 16) |
if (MidiChannel < 0 || MidiChannel > 16) |
515 |
throw MidiInputException("MIDI channel index out of bounds"); |
throw MidiInputException("MIDI channel index out of bounds"); |
516 |
|
|
517 |
// first check if desired connection is already established |
// first check if desired connection is already established |
518 |
MidiChannelMapMutex.Lock(); |
{ |
519 |
MidiChannelMap_t& midiChannelMap = MidiChannelMap.GetConfigForUpdate(); |
LockGuard lock(MidiChannelMapMutex); |
520 |
bool bAlreadyDone = midiChannelMap[MidiChannel].count(pEngineChannel); |
MidiChannelMap_t& midiChannelMap = MidiChannelMap.GetConfigForUpdate(); |
521 |
MidiChannelMapMutex.Unlock(); |
if (midiChannelMap[MidiChannel].count(pEngineChannel)) return; |
522 |
if (bAlreadyDone) return; |
} |
523 |
|
|
524 |
// remove all other connections of that engine channel (if any) |
// remove all other connections of that engine channel (if any) |
525 |
Disconnect(pEngineChannel); |
Disconnect(pEngineChannel); |
526 |
|
|
527 |
// register engine channel on the desired MIDI channel |
// register engine channel on the desired MIDI channel |
528 |
MidiChannelMapMutex.Lock(); |
{ |
529 |
MidiChannelMap.GetConfigForUpdate()[MidiChannel].insert(pEngineChannel); |
LockGuard lock(MidiChannelMapMutex); |
530 |
MidiChannelMap.SwitchConfig()[MidiChannel].insert(pEngineChannel); |
MidiChannelMap.GetConfigForUpdate()[MidiChannel].insert(pEngineChannel); |
531 |
MidiChannelMapMutex.Unlock(); |
MidiChannelMap.SwitchConfig()[MidiChannel].insert(pEngineChannel); |
532 |
|
} |
533 |
|
|
534 |
// inform engine channel about this connection |
// inform engine channel about this connection |
535 |
pEngineChannel->Connect(this, MidiChannel); |
pEngineChannel->Connect(this, MidiChannel); |
544 |
bool bChannelFound = false; |
bool bChannelFound = false; |
545 |
|
|
546 |
// unregister engine channel from all MIDI channels |
// unregister engine channel from all MIDI channels |
|
MidiChannelMapMutex.Lock(); |
|
547 |
try { |
try { |
548 |
|
LockGuard lock(MidiChannelMapMutex); |
549 |
{ |
{ |
550 |
MidiChannelMap_t& midiChannelMap = MidiChannelMap.GetConfigForUpdate(); |
MidiChannelMap_t& midiChannelMap = MidiChannelMap.GetConfigForUpdate(); |
551 |
for (int i = 0; i <= 16; i++) { |
for (int i = 0; i <= 16; i++) { |
563 |
} |
} |
564 |
} |
} |
565 |
catch(...) { /* NOOP */ } |
catch(...) { /* NOOP */ } |
|
MidiChannelMapMutex.Unlock(); |
|
566 |
|
|
567 |
// inform engine channel about the disconnection (if there is one) |
// inform engine channel about the disconnection (if there is one) |
568 |
if (bChannelFound) pEngineChannel->DisconnectMidiInputPort(); |
if (bChannelFound) pEngineChannel->DisconnectMidiInputPort(); |
585 |
} |
} |
586 |
|
|
587 |
void MidiInputPort::Connect(VirtualMidiDevice* pDevice) { |
void MidiInputPort::Connect(VirtualMidiDevice* pDevice) { |
588 |
virtualMidiDevicesMutex.Lock(); |
LockGuard lock(virtualMidiDevicesMutex); |
589 |
// double buffer ... double work ... |
// double buffer ... double work ... |
590 |
{ |
{ |
591 |
std::vector<VirtualMidiDevice*>& devices = |
std::vector<VirtualMidiDevice*>& devices = |
597 |
virtualMidiDevices.SwitchConfig(); |
virtualMidiDevices.SwitchConfig(); |
598 |
devices.push_back(pDevice); |
devices.push_back(pDevice); |
599 |
} |
} |
|
virtualMidiDevicesMutex.Unlock(); |
|
600 |
} |
} |
601 |
|
|
602 |
void MidiInputPort::Disconnect(VirtualMidiDevice* pDevice) { |
void MidiInputPort::Disconnect(VirtualMidiDevice* pDevice) { |
603 |
virtualMidiDevicesMutex.Lock(); |
LockGuard lock(virtualMidiDevicesMutex); |
604 |
// double buffer ... double work ... |
// double buffer ... double work ... |
605 |
{ |
{ |
606 |
std::vector<VirtualMidiDevice*>& devices = |
std::vector<VirtualMidiDevice*>& devices = |
612 |
virtualMidiDevices.SwitchConfig(); |
virtualMidiDevices.SwitchConfig(); |
613 |
devices.erase(std::find(devices.begin(), devices.end(), pDevice)); |
devices.erase(std::find(devices.begin(), devices.end(), pDevice)); |
614 |
} |
} |
615 |
virtualMidiDevicesMutex.Unlock(); |
} |
616 |
|
|
617 |
|
int MidiInputPort::expectedEventSize(unsigned char byte) { |
618 |
|
if (!(byte & 0x80) && runningStatusBuf[0]) |
619 |
|
byte = runningStatusBuf[0]; // "running status" mode |
620 |
|
|
621 |
|
if (byte < 0x80) return -1; // not a valid status byte |
622 |
|
if (byte < 0xC0) return 3; // note on/off, note pressure, control change |
623 |
|
if (byte < 0xE0) return 2; // program change, channel pressure |
624 |
|
if (byte < 0xF0) return 3; // pitch wheel |
625 |
|
if (byte == 0xF0) return -1; // sysex message (variable size) |
626 |
|
if (byte == 0xF1) return 2; // time code per quarter frame |
627 |
|
if (byte == 0xF2) return 3; // sys. common song position pointer |
628 |
|
if (byte == 0xF3) return 2; // sys. common song select |
629 |
|
if (byte == 0xF4) return -1; // sys. common undefined / reserved |
630 |
|
if (byte == 0xF5) return -1; // sys. common undefined / reserved |
631 |
|
return 1; // tune request, end of SysEx, system real-time events |
632 |
} |
} |
633 |
|
|
634 |
} // namespace LinuxSampler |
} // namespace LinuxSampler |