--- linuxsampler/trunk/src/drivers/midi/MidiInputPort.cpp 2006/02/26 13:00:08 840 +++ linuxsampler/trunk/src/drivers/midi/MidiInputPort.cpp 2007/03/29 09:40:45 1135 @@ -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, 2006 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,7 @@ #include "MidiInputPort.h" +#include "MidiInstrumentMapper.h" #include "../../Sampler.h" #include "../../engines/EngineFactory.h" @@ -51,7 +52,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 */ } @@ -69,11 +70,12 @@ Parameters.clear(); } - MidiInputPort::MidiInputPort(MidiInputDevice* pDevice, int portNumber) { + MidiInputPort::MidiInputPort(MidiInputDevice* pDevice, int portNumber) + : MidiChannelMapReader(MidiChannelMap), + SysexListenersReader(SysexListeners) { this->pDevice = pDevice; this->portNumber = portNumber; Parameters["NAME"] = new ParameterName(this); - pPreviousProgramChangeEngineChannel = NULL; } MidiInputDevice* MidiInputPort::GetDevice() { @@ -89,7 +91,8 @@ } void MidiInputPort::DispatchNoteOn(uint8_t Key, uint8_t Velocity, uint MidiChannel) { - const MidiChannelMap_t& midiChannelMap = MidiChannelMap.Lock(); + 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(); @@ -102,11 +105,30 @@ std::set::iterator end = midiChannelMap[midi_chan_all].end(); for (; engineiter != end; engineiter++) (*engineiter)->SendNoteOn(Key, Velocity); } - MidiChannelMap.Unlock(); + MidiChannelMapReader.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(); } void MidiInputPort::DispatchNoteOff(uint8_t Key, uint8_t Velocity, uint MidiChannel) { - const MidiChannelMap_t& midiChannelMap = MidiChannelMap.Lock(); + 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(); @@ -119,11 +141,30 @@ std::set::iterator end = midiChannelMap[midi_chan_all].end(); for (; engineiter != end; engineiter++) (*engineiter)->SendNoteOff(Key, Velocity); } - MidiChannelMap.Unlock(); + MidiChannelMapReader.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(); } void MidiInputPort::DispatchPitchbend(int Pitch, uint MidiChannel) { - const MidiChannelMap_t& midiChannelMap = MidiChannelMap.Lock(); + 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(); @@ -136,11 +177,30 @@ std::set::iterator end = midiChannelMap[midi_chan_all].end(); for (; engineiter != end; engineiter++) (*engineiter)->SendPitchbend(Pitch); } - MidiChannelMap.Unlock(); + 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) { - const MidiChannelMap_t& midiChannelMap = MidiChannelMap.Lock(); + 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(); @@ -153,40 +213,155 @@ std::set::iterator end = midiChannelMap[midi_chan_all].end(); for (; engineiter != end; engineiter++) (*engineiter)->SendControlChange(Controller, Value); } - MidiChannelMap.Unlock(); + 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 = EngineFactory::EngineInstances().begin(); - std::set::iterator end = EngineFactory::EngineInstances().end(); + 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; - Sampler* pSampler = (Sampler*) pDevice->pSampler; - SamplerChannel* pSamplerChannel = pSampler->GetSamplerChannel(Program); - if (!pSamplerChannel) return; - - EngineChannel* pEngineChannel = pSamplerChannel->GetEngineChannel(); - if (!pEngineChannel) 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)->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 + { + 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(); + } - // disconnect from the engine channel which was connected by the last PC event - if (pPreviousProgramChangeEngineChannel) - Disconnect(pPreviousProgramChangeEngineChannel); + 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(); + } - // now connect to the new engine channel and remember it - try { - Connect(pEngineChannel, (midi_chan_t) MidiChannel); - pPreviousProgramChangeEngineChannel = pEngineChannel; + 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); } - catch (...) { /* NOOP */ } + MidiChannelMapReader.Unlock(); } void MidiInputPort::Connect(EngineChannel* pEngineChannel, midi_chan_t MidiChannel) { @@ -250,4 +425,17 @@ 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; + } + } // namespace LinuxSampler