--- linuxsampler/trunk/src/drivers/midi/MidiInputPort.cpp 2005/02/26 02:01:14 411 +++ linuxsampler/trunk/src/drivers/midi/MidiInputPort.cpp 2008/02/16 01:09:33 1695 @@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005 Christian Schoenebeck * + * Copyright (C) 2005 - 2008 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -23,6 +23,14 @@ #include "MidiInputPort.h" +#include "../../common/global_private.h" +#include "MidiInstrumentMapper.h" +#include "../../Sampler.h" +#include "../../engines/EngineFactory.h" +#include "VirtualMidiDevice.h" + +#include + namespace LinuxSampler { // *************** ParameterName *************** @@ -48,7 +56,7 @@ return std::vector(); } - void MidiInputPort::ParameterName::OnSetValue(String s) throw (LinuxSamplerException) { + void MidiInputPort::ParameterName::OnSetValue(String s) throw (Exception) { return; /* FIXME: Nothing to do here */ } @@ -60,13 +68,16 @@ MidiInputPort::~MidiInputPort() { std::map::iterator iter = Parameters.begin(); while (iter != Parameters.end()) { - Parameters.erase(iter); delete iter->second; iter++; } + Parameters.clear(); } - MidiInputPort::MidiInputPort(MidiInputDevice* pDevice, int portNumber) { + MidiInputPort::MidiInputPort(MidiInputDevice* pDevice, int portNumber) + : MidiChannelMapReader(MidiChannelMap), + SysexListenersReader(SysexListeners), + virtualMidiDevicesReader(virtualMidiDevices) { this->pDevice = pDevice; this->portNumber = portNumber; Parameters["NAME"] = new ParameterName(this); @@ -85,100 +96,411 @@ } void MidiInputPort::DispatchNoteOn(uint8_t Key, uint8_t Velocity, uint MidiChannel) { + if (Key > 127 || Velocity > 127 || MidiChannel > 16) return; + const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock(); // dispatch event for engines listening to the same MIDI channel { - std::set::iterator engineiter = MidiChannelMap[MidiChannel].begin(); - std::set::iterator end = MidiChannelMap[MidiChannel].end(); + std::set::iterator engineiter = midiChannelMap[MidiChannel].begin(); + std::set::iterator end = midiChannelMap[MidiChannel].end(); for (; engineiter != end; engineiter++) (*engineiter)->SendNoteOn(Key, Velocity); } // dispatch event for engines listening to ALL MIDI channels { - std::set::iterator engineiter = MidiChannelMap[midi_chan_all].begin(); - std::set::iterator end = MidiChannelMap[midi_chan_all].end(); + std::set::iterator engineiter = midiChannelMap[midi_chan_all].begin(); + std::set::iterator end = midiChannelMap[midi_chan_all].end(); for (; engineiter != end; engineiter++) (*engineiter)->SendNoteOn(Key, Velocity); } + MidiChannelMapReader.Unlock(); + + // dispatch event to all low priority MIDI listeners + const std::vector& listeners = + virtualMidiDevicesReader.Lock(); + for (int i = 0; i < listeners.size(); ++i) + listeners[i]->SendNoteOnToDevice(Key, Velocity); + virtualMidiDevicesReader.Unlock(); + } + + void MidiInputPort::DispatchNoteOn(uint8_t Key, uint8_t Velocity, uint MidiChannel, int32_t FragmentPos) { + if (Key > 127 || Velocity > 127 || MidiChannel > 16) return; + const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock(); + // dispatch event for engines listening to the same MIDI channel + { + std::set::iterator engineiter = midiChannelMap[MidiChannel].begin(); + std::set::iterator end = midiChannelMap[MidiChannel].end(); + for (; engineiter != end; engineiter++) (*engineiter)->SendNoteOn(Key, Velocity, FragmentPos); + } + // dispatch event for engines listening to ALL MIDI channels + { + std::set::iterator engineiter = midiChannelMap[midi_chan_all].begin(); + std::set::iterator end = midiChannelMap[midi_chan_all].end(); + for (; engineiter != end; engineiter++) (*engineiter)->SendNoteOn(Key, Velocity, FragmentPos); + } + MidiChannelMapReader.Unlock(); + + // dispatch event to all low priority MIDI listeners + const std::vector& listeners = + virtualMidiDevicesReader.Lock(); + for (int i = 0; i < listeners.size(); ++i) + listeners[i]->SendNoteOnToDevice(Key, Velocity); + virtualMidiDevicesReader.Unlock(); } void MidiInputPort::DispatchNoteOff(uint8_t Key, uint8_t Velocity, uint MidiChannel) { + if (Key > 127 || Velocity > 127 || MidiChannel > 16) return; + const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock(); // dispatch event for engines listening to the same MIDI channel { - std::set::iterator engineiter = MidiChannelMap[MidiChannel].begin(); - std::set::iterator end = MidiChannelMap[MidiChannel].end(); + std::set::iterator engineiter = midiChannelMap[MidiChannel].begin(); + std::set::iterator end = midiChannelMap[MidiChannel].end(); for (; engineiter != end; engineiter++) (*engineiter)->SendNoteOff(Key, Velocity); } // dispatch event for engines listening to ALL MIDI channels { - std::set::iterator engineiter = MidiChannelMap[midi_chan_all].begin(); - std::set::iterator end = MidiChannelMap[midi_chan_all].end(); + std::set::iterator engineiter = midiChannelMap[midi_chan_all].begin(); + std::set::iterator end = midiChannelMap[midi_chan_all].end(); for (; engineiter != end; engineiter++) (*engineiter)->SendNoteOff(Key, Velocity); } + MidiChannelMapReader.Unlock(); + + // dispatch event to all low priority MIDI listeners + const std::vector& listeners = + virtualMidiDevicesReader.Lock(); + for (int i = 0; i < listeners.size(); ++i) + listeners[i]->SendNoteOffToDevice(Key, Velocity); + virtualMidiDevicesReader.Unlock(); + } + + void MidiInputPort::DispatchNoteOff(uint8_t Key, uint8_t Velocity, uint MidiChannel, int32_t FragmentPos) { + if (Key > 127 || Velocity > 127 || MidiChannel > 16) return; + const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock(); + // dispatch event for engines listening to the same MIDI channel + { + std::set::iterator engineiter = midiChannelMap[MidiChannel].begin(); + std::set::iterator end = midiChannelMap[MidiChannel].end(); + for (; engineiter != end; engineiter++) (*engineiter)->SendNoteOff(Key, Velocity, FragmentPos); + } + // dispatch event for engines listening to ALL MIDI channels + { + std::set::iterator engineiter = midiChannelMap[midi_chan_all].begin(); + std::set::iterator end = midiChannelMap[midi_chan_all].end(); + for (; engineiter != end; engineiter++) (*engineiter)->SendNoteOff(Key, Velocity, FragmentPos); + } + MidiChannelMapReader.Unlock(); + + // dispatch event to all low priority MIDI listeners + const std::vector& listeners = + virtualMidiDevicesReader.Lock(); + for (int i = 0; i < listeners.size(); ++i) + listeners[i]->SendNoteOffToDevice(Key, Velocity); + virtualMidiDevicesReader.Unlock(); } void MidiInputPort::DispatchPitchbend(int Pitch, uint MidiChannel) { + if (Pitch < -8192 || Pitch > 8191 || MidiChannel > 16) return; + const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock(); // dispatch event for engines listening to the same MIDI channel { - std::set::iterator engineiter = MidiChannelMap[MidiChannel].begin(); - std::set::iterator end = MidiChannelMap[MidiChannel].end(); + std::set::iterator engineiter = midiChannelMap[MidiChannel].begin(); + std::set::iterator end = midiChannelMap[MidiChannel].end(); for (; engineiter != end; engineiter++) (*engineiter)->SendPitchbend(Pitch); } // dispatch event for engines listening to ALL MIDI channels { - std::set::iterator engineiter = MidiChannelMap[midi_chan_all].begin(); - std::set::iterator end = MidiChannelMap[midi_chan_all].end(); + std::set::iterator engineiter = midiChannelMap[midi_chan_all].begin(); + std::set::iterator end = midiChannelMap[midi_chan_all].end(); for (; engineiter != end; engineiter++) (*engineiter)->SendPitchbend(Pitch); } + MidiChannelMapReader.Unlock(); + } + + void MidiInputPort::DispatchPitchbend(int Pitch, uint MidiChannel, int32_t FragmentPos) { + if (Pitch < -8192 || Pitch > 8191 || MidiChannel > 16) return; + const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock(); + // dispatch event for engines listening to the same MIDI channel + { + std::set::iterator engineiter = midiChannelMap[MidiChannel].begin(); + std::set::iterator end = midiChannelMap[MidiChannel].end(); + for (; engineiter != end; engineiter++) (*engineiter)->SendPitchbend(Pitch, FragmentPos); + } + // dispatch event for engines listening to ALL MIDI channels + { + std::set::iterator engineiter = midiChannelMap[midi_chan_all].begin(); + std::set::iterator end = midiChannelMap[midi_chan_all].end(); + for (; engineiter != end; engineiter++) (*engineiter)->SendPitchbend(Pitch, FragmentPos); + } + MidiChannelMapReader.Unlock(); } void MidiInputPort::DispatchControlChange(uint8_t Controller, uint8_t Value, uint MidiChannel) { + if (Controller > 128 || Value > 127 || MidiChannel > 16) return; + const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock(); // dispatch event for engines listening to the same MIDI channel { - std::set::iterator engineiter = MidiChannelMap[MidiChannel].begin(); - std::set::iterator end = MidiChannelMap[MidiChannel].end(); + std::set::iterator engineiter = midiChannelMap[MidiChannel].begin(); + std::set::iterator end = midiChannelMap[MidiChannel].end(); for (; engineiter != end; engineiter++) (*engineiter)->SendControlChange(Controller, Value); } // dispatch event for engines listening to ALL MIDI channels { - std::set::iterator engineiter = MidiChannelMap[midi_chan_all].begin(); - std::set::iterator end = MidiChannelMap[midi_chan_all].end(); + std::set::iterator engineiter = midiChannelMap[midi_chan_all].begin(); + std::set::iterator end = midiChannelMap[midi_chan_all].end(); for (; engineiter != end; engineiter++) (*engineiter)->SendControlChange(Controller, Value); } + MidiChannelMapReader.Unlock(); + } + + void MidiInputPort::DispatchControlChange(uint8_t Controller, uint8_t Value, uint MidiChannel, int32_t FragmentPos) { + if (Controller > 128 || Value > 127 || MidiChannel > 16) return; + const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock(); + // dispatch event for engines listening to the same MIDI channel + { + std::set::iterator engineiter = midiChannelMap[MidiChannel].begin(); + std::set::iterator end = midiChannelMap[MidiChannel].end(); + for (; engineiter != end; engineiter++) (*engineiter)->SendControlChange(Controller, Value, FragmentPos); + } + // dispatch event for engines listening to ALL MIDI channels + { + std::set::iterator engineiter = midiChannelMap[midi_chan_all].begin(); + std::set::iterator end = midiChannelMap[midi_chan_all].end(); + for (; engineiter != end; engineiter++) (*engineiter)->SendControlChange(Controller, Value, FragmentPos); + } + MidiChannelMapReader.Unlock(); } void MidiInputPort::DispatchSysex(void* pData, uint Size) { + const std::set allEngines = SysexListenersReader.Lock(); + // dispatch event to all engine instances + std::set::iterator engineiter = allEngines.begin(); + std::set::iterator end = allEngines.end(); + for (; engineiter != end; engineiter++) (*engineiter)->SendSysex(pData, Size); + SysexListenersReader.Unlock(); + } + + void MidiInputPort::DispatchProgramChange(uint8_t Program, uint MidiChannel) { + dmsg(1,("Received MIDI program change (prog=%d,ch=%d)\n",Program,MidiChannel)); + if (Program > 127 || MidiChannel > 16) return; + if (!pDevice || !pDevice->pSampler) { + std::cerr << "MidiInputPort: ERROR, no sampler instance to handle program change." + << "This is a bug, please report it!\n" << std::flush; + return; + } + std::vector maps = MidiInstrumentMapper::Maps(); + if (maps.empty()) return; + + const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock(); // dispatch event for engines listening to the same MIDI channel { - for (uint MidiChannel = 0; MidiChannel <= 16; MidiChannel++) { - std::set::iterator engineiter = MidiChannelMap[MidiChannel].begin(); - std::set::iterator end = MidiChannelMap[MidiChannel].end(); - for (; engineiter != end; engineiter++) { - Engine* pEngine = (*engineiter)->GetEngine(); - if (pEngine) pEngine->SendSysex(pData, Size); + std::set::iterator engineiter = midiChannelMap[MidiChannel].begin(); + std::set::iterator end = midiChannelMap[MidiChannel].end(); + for (; engineiter != end; engineiter++) { + (*engineiter)->SetMidiProgram(Program); + if ((*engineiter)->UsesNoMidiInstrumentMap()) continue; + if (MidiInstrumentMapper::GetMapCount() == 0) continue; + // retrieve the MIDI instrument map this engine channel is assigned to + int iMapID = ((*engineiter)->UsesDefaultMidiInstrumentMap()) + ? MidiInstrumentMapper::GetDefaultMap() /*default*/ : (*engineiter)->GetMidiInstrumentMap(); + // is there an entry for this MIDI bank&prog pair in that map? + midi_prog_index_t midiIndex; + midiIndex.midi_bank_msb = (*engineiter)->GetMidiBankMsb(); + midiIndex.midi_bank_lsb = (*engineiter)->GetMidiBankLsb(); + midiIndex.midi_prog = (*engineiter)->GetMidiProgram(); + optional mapping = + MidiInstrumentMapper::GetEntry(iMapID, midiIndex); + if (mapping) { // if mapping exists ... + InstrumentManager::instrument_id_t id; + id.FileName = mapping->InstrumentFile; + id.Index = mapping->InstrumentIndex; + //TODO: we should switch the engine type here + InstrumentManager::LoadInstrumentInBackground(id, *engineiter); + (*engineiter)->Volume(mapping->Volume); } } } // dispatch event for engines listening to ALL MIDI channels { - for (uint MidiChannel = 0; MidiChannel <= 16; MidiChannel++) { - std::set::iterator engineiter = MidiChannelMap[midi_chan_all].begin(); - std::set::iterator end = MidiChannelMap[midi_chan_all].end(); - for (; engineiter != end; engineiter++) { - Engine* pEngine = (*engineiter)->GetEngine(); - if (pEngine) pEngine->SendSysex(pData, Size); + std::set::iterator engineiter = midiChannelMap[midi_chan_all].begin(); + std::set::iterator end = midiChannelMap[midi_chan_all].end(); + for (; engineiter != end; engineiter++) { + (*engineiter)->SetMidiProgram(Program); + if ((*engineiter)->UsesNoMidiInstrumentMap()) continue; + if (MidiInstrumentMapper::GetMapCount() == 0) continue; + // retrieve the MIDI instrument map this engine channel is assigned to + int iMapID = ((*engineiter)->UsesDefaultMidiInstrumentMap()) + ? MidiInstrumentMapper::GetDefaultMap() /*default*/ : (*engineiter)->GetMidiInstrumentMap(); + // is there an entry for this MIDI bank&prog pair in that map? + midi_prog_index_t midiIndex; + midiIndex.midi_bank_msb = (*engineiter)->GetMidiBankMsb(); + midiIndex.midi_bank_lsb = (*engineiter)->GetMidiBankLsb(); + midiIndex.midi_prog = (*engineiter)->GetMidiProgram(); + optional mapping = + MidiInstrumentMapper::GetEntry(iMapID, midiIndex); + if (mapping) { // if mapping exists ... + InstrumentManager::instrument_id_t id; + id.FileName = mapping->InstrumentFile; + id.Index = mapping->InstrumentIndex; + //TODO: we should switch the engine type here + InstrumentManager::LoadInstrumentInBackground(id, *engineiter); + (*engineiter)->Volume(mapping->Volume); } } } + MidiChannelMapReader.Unlock(); + } + + void MidiInputPort::DispatchBankSelectMsb(uint8_t BankMSB, uint MidiChannel) { + if (BankMSB > 127 || MidiChannel > 16) return; + if (!pDevice || !pDevice->pSampler) { + std::cerr << "MidiInputPort: ERROR, no sampler instance to handle bank select MSB." + << "This is a bug, please report it!\n" << std::flush; + return; + } + const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock(); + // dispatch event for engines listening to the same MIDI channel + { + std::set::iterator engineiter = midiChannelMap[MidiChannel].begin(); + std::set::iterator end = midiChannelMap[MidiChannel].end(); + // according to the MIDI specs, a bank select should not alter the patch + for (; engineiter != end; engineiter++) (*engineiter)->SetMidiBankMsb(BankMSB); + } + // dispatch event for engines listening to ALL MIDI channels + { + std::set::iterator engineiter = midiChannelMap[midi_chan_all].begin(); + std::set::iterator end = midiChannelMap[midi_chan_all].end(); + // according to the MIDI specs, a bank select should not alter the patch + for (; engineiter != end; engineiter++) (*engineiter)->SetMidiBankMsb(BankMSB); + } + MidiChannelMapReader.Unlock(); + } + + void MidiInputPort::DispatchBankSelectLsb(uint8_t BankLSB, uint MidiChannel) { + if (BankLSB > 127 || MidiChannel > 16) return; + if (!pDevice || !pDevice->pSampler) { + std::cerr << "MidiInputPort: ERROR, no sampler instance to handle bank select LSB." + << "This is a bug, please report it!\n" << std::flush; + return; + } + const MidiChannelMap_t& midiChannelMap = MidiChannelMapReader.Lock(); + // dispatch event for engines listening to the same MIDI channel + { + std::set::iterator engineiter = midiChannelMap[MidiChannel].begin(); + std::set::iterator end = midiChannelMap[MidiChannel].end(); + // according to the MIDI specs, a bank select should not alter the patch + for (; engineiter != end; engineiter++) (*engineiter)->SetMidiBankLsb(BankLSB); + } + // dispatch event for engines listening to ALL MIDI channels + { + std::set::iterator engineiter = midiChannelMap[midi_chan_all].begin(); + std::set::iterator end = midiChannelMap[midi_chan_all].end(); + // according to the MIDI specs, a bank select should not alter the patch + for (; engineiter != end; engineiter++) (*engineiter)->SetMidiBankLsb(BankLSB); + } + MidiChannelMapReader.Unlock(); } void MidiInputPort::Connect(EngineChannel* pEngineChannel, midi_chan_t MidiChannel) { if (MidiChannel < 0 || MidiChannel > 16) throw MidiInputException("MIDI channel index out of bounds"); + + // first check if desired connection is already established + MidiChannelMapMutex.Lock(); + MidiChannelMap_t& midiChannelMap = MidiChannelMap.GetConfigForUpdate(); + bool bAlreadyDone = midiChannelMap[MidiChannel].count(pEngineChannel); + MidiChannelMapMutex.Unlock(); + if (bAlreadyDone) return; + + // remove all other connections of that engine channel (if any) Disconnect(pEngineChannel); - MidiChannelMap[MidiChannel].insert(pEngineChannel); + + // register engine channel on the desired MIDI channel + MidiChannelMapMutex.Lock(); + MidiChannelMap.GetConfigForUpdate()[MidiChannel].insert(pEngineChannel); + MidiChannelMap.SwitchConfig()[MidiChannel].insert(pEngineChannel); + MidiChannelMapMutex.Unlock(); + + // inform engine channel about this connection + pEngineChannel->Connect(this, MidiChannel); + + // mark engine channel as changed + pEngineChannel->StatusChanged(true); } void MidiInputPort::Disconnect(EngineChannel* pEngineChannel) { - try { for (int i = 0; i <= 16; i++) MidiChannelMap[i].erase(pEngineChannel); } + if (!pEngineChannel) return; + + bool bChannelFound = false; + + // unregister engine channel from all MIDI channels + MidiChannelMapMutex.Lock(); + try { + { + MidiChannelMap_t& midiChannelMap = MidiChannelMap.GetConfigForUpdate(); + for (int i = 0; i <= 16; i++) { + bChannelFound |= midiChannelMap[i].count(pEngineChannel); + midiChannelMap[i].erase(pEngineChannel); + } + } + // do the same update again, after switching to the other config + { + MidiChannelMap_t& midiChannelMap = MidiChannelMap.SwitchConfig(); + for (int i = 0; i <= 16; i++) { + bChannelFound |= midiChannelMap[i].count(pEngineChannel); + midiChannelMap[i].erase(pEngineChannel); + } + } + } catch(...) { /* NOOP */ } + MidiChannelMapMutex.Unlock(); + + // inform engine channel about the disconnection (if there is one) + if (bChannelFound) pEngineChannel->DisconnectMidiInputPort(); + + // mark engine channel as changed + pEngineChannel->StatusChanged(true); + } + + SynchronizedConfig > MidiInputPort::SysexListeners; + + void MidiInputPort::AddSysexListener(Engine* engine) { + std::pair::iterator, bool> p = SysexListeners.GetConfigForUpdate().insert(engine); + if (p.second) SysexListeners.SwitchConfig().insert(engine); + } + + bool MidiInputPort::RemoveSysexListener(Engine* engine) { + int count = SysexListeners.GetConfigForUpdate().erase(engine); + if (count) SysexListeners.SwitchConfig().erase(engine); + return count; + } + + void MidiInputPort::Connect(VirtualMidiDevice* pDevice) { + virtualMidiDevicesMutex.Lock(); + // double buffer ... double work ... + { + std::vector& devices = + virtualMidiDevices.GetConfigForUpdate(); + devices.push_back(pDevice); + } + { + std::vector& devices = + virtualMidiDevices.SwitchConfig(); + devices.push_back(pDevice); + } + virtualMidiDevicesMutex.Unlock(); + } + + void MidiInputPort::Disconnect(VirtualMidiDevice* pDevice) { + virtualMidiDevicesMutex.Lock(); + // double buffer ... double work ... + { + std::vector& devices = + virtualMidiDevices.GetConfigForUpdate(); + devices.erase(std::find(devices.begin(), devices.end(), pDevice)); + } + { + std::vector& devices = + virtualMidiDevices.SwitchConfig(); + devices.erase(std::find(devices.begin(), devices.end(), pDevice)); + } + virtualMidiDevicesMutex.Unlock(); } } // namespace LinuxSampler