1 /*************************************************************************** 2 * * 3 * LinuxSampler - modular, streaming capable sampler * 4 * * 5 * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * 6 * Copyright (C) 2005 - 2020 Christian Schoenebeck * 7 * Copyright (C) 2009 - 2012 Grigor Iliev * 8 * Copyright (C) 2012 - 2016 Andreas Persson * 9 * * 10 * This program is free software; you can redistribute it and/or modify * 11 * it under the terms of the GNU General Public License as published by * 12 * the Free Software Foundation; either version 2 of the License, or * 13 * (at your option) any later version. * 14 * * 15 * This program is distributed in the hope that it will be useful, * 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 18 * GNU General Public License for more details. * 19 * * 20 * You should have received a copy of the GNU General Public License * 21 * along with this program; if not, write to the Free Software * 22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * 23 * MA 02111-1307 USA * 24 ***************************************************************************/ 25 26 #include "EngineChannel.h" 27 #include "Engine.h" 28 29 namespace LinuxSampler { namespace sfz { EngineChannel()30 EngineChannel::EngineChannel() { 31 for(int i = 0; i < 128; i++) PressedKeys[i] = false; 32 LastKey = LastKeySwitch = -1; 33 AddMidiKeyboardListener(this); 34 } 35 ~EngineChannel()36 EngineChannel::~EngineChannel() { 37 DisconnectAudioOutputDevice(); 38 RemoveMidiKeyboardListener(this); 39 // In case the channel was removed before the instrument was 40 // fully loaded, try to give back instrument again (see bug #113) 41 InstrumentChangeCmd< ::sfz::Region, ::sfz::Instrument>& cmd = ChangeInstrument(NULL); 42 if (cmd.pInstrument) { 43 Engine::instruments.HandBack(cmd.pInstrument, this); 44 } 45 /////// 46 } 47 GetEngineFormat()48 AbstractEngine::Format EngineChannel::GetEngineFormat() { return AbstractEngine::SFZ; } 49 50 /** This method is not thread safe! */ ResetInternal(bool bResetEngine)51 void EngineChannel::ResetInternal(bool bResetEngine) { 52 CurrentKeyDimension = 0; 53 EngineChannelBase<Voice, ::sfz::Region, ::sfz::Instrument>::ResetInternal(bResetEngine); 54 for(int i = 0; i < 128; i++) PressedKeys[i] = false; 55 } 56 57 /** 58 * Will be called by the MIDIIn Thread to signal that a program 59 * change should be performed. As a program change isn't 60 * real-time safe, the actual change is performed by the disk 61 * thread. 62 * 63 * @param Program - MIDI program change number 64 */ SendProgramChange(uint8_t Program)65 void EngineChannel::SendProgramChange(uint8_t Program) { 66 SetMidiProgram(Program); 67 Engine* engine = dynamic_cast<Engine*>(pEngine); 68 if(engine == NULL) return; 69 70 if(engine->GetDiskThread()) { 71 uint32_t merged = (GetMidiBankMsb() << 16) | (GetMidiBankLsb() << 8) | Program; 72 engine->GetDiskThread()->OrderProgramChange(merged, this); 73 } else { 74 // TODO: 75 } 76 } 77 78 /** 79 * Load an instrument from a .sfz file. PrepareLoadInstrument() has to 80 * be called first to provide the information which instrument to load. 81 * This method will then actually start to load the instrument and block 82 * the calling thread until loading was completed. 83 * 84 * @see PrepareLoadInstrument() 85 */ LoadInstrument()86 void EngineChannel::LoadInstrument() { 87 InstrumentResourceManager* pInstrumentManager = dynamic_cast<InstrumentResourceManager*>(pEngine->GetInstrumentManager()); 88 89 // make sure we don't trigger any new notes with an old 90 // instrument 91 InstrumentChangeCmd< ::sfz::Region, ::sfz::Instrument>& cmd = ChangeInstrument(0); 92 if (cmd.pInstrument) { 93 // give old instrument back to instrument manager, but 94 // keep the dimension regions and samples that are in use 95 pInstrumentManager->HandBackInstrument(cmd.pInstrument, this, cmd.pRegionsInUse); 96 } 97 if (cmd.pScript) { 98 // give old instrument script back to instrument resource manager 99 cmd.pScript->resetAll(); 100 } 101 cmd.pRegionsInUse->clear(); 102 103 // delete all key groups 104 DeleteGroupEventLists(); 105 106 // request sfz instrument from instrument manager 107 ::sfz::Instrument* newInstrument; 108 try { 109 InstrumentManager::instrument_id_t instrid; 110 instrid.FileName = InstrumentFile; 111 instrid.Index = InstrumentIdx; 112 113 newInstrument = pInstrumentManager->Borrow(instrid, this); 114 if (!newInstrument) { 115 throw InstrumentManagerException("resource was not created"); 116 } 117 118 // if requested by set_ccN opcode in sfz file, set initial CC values 119 for (std::map<uint8_t,uint8_t>::const_iterator itCC = newInstrument->initialCCValues.begin(); 120 itCC != newInstrument->initialCCValues.end(); ++itCC) 121 { 122 const uint8_t& cc = itCC->first; 123 uint8_t value = itCC->second; 124 if (cc >= CTRL_TABLE_SIZE) continue; 125 if ((cc < 128 || cc == CTRL_TABLE_IDX_AFTERTOUCH) && value > 127) value = 127; 126 ControllerTable[cc] = value; 127 } 128 129 if (newInstrument->scripts.size() > 1) { 130 std::cerr << "WARNING: Executing more than one real-time instrument script slot is not implemented yet!\n"; 131 } 132 ::sfz::Script* script = (!newInstrument->scripts.empty()) ? &newInstrument->scripts[0] : NULL; 133 if (script) { 134 String sourceCode = script->GetSourceCode(); 135 std::map<String,String> patchVars; //TODO: we need to invent some new sfz opcode(s) for 'patch' script variables 136 LoadInstrumentScript(sourceCode, patchVars); 137 } 138 } 139 catch (InstrumentManagerException e) { 140 InstrumentStat = -3; 141 StatusChanged(true); 142 String msg = "sfz::Engine error: Failed to load instrument, cause: " + e.Message(); 143 throw Exception(msg); 144 } 145 catch (::sfz::Exception e) { 146 InstrumentStat = -3; 147 StatusChanged(true); 148 String msg = "sfz::Engine error: Failed to load instrument, cause: " + e.Message(); 149 throw Exception(msg); 150 } 151 catch (::std::runtime_error e) { 152 InstrumentStat = -3; 153 StatusChanged(true); 154 String msg = "sfz::Engine error: Failed to load instrument, cause: "; 155 msg += e.what(); 156 throw Exception(msg); 157 } 158 catch (...) { 159 InstrumentStat = -4; 160 StatusChanged(true); 161 throw Exception("sfz::Engine error: Failed to load instrument, cause: Unknown exception while trying to parse sfz file."); 162 } 163 164 // rebuild ActiveKeyGroups map with key groups of current instrument 165 for (std::vector< ::sfz::Region*>::iterator itRegion = newInstrument->regions.begin() ; 166 itRegion != newInstrument->regions.end() ; ++itRegion) { 167 AddGroup((*itRegion)->group); 168 AddGroup((*itRegion)->off_by); 169 } 170 171 InstrumentIdxName = newInstrument->GetName(); 172 InstrumentStat = 100; 173 174 { 175 InstrumentChangeCmd< ::sfz::Region, ::sfz::Instrument>& cmd = 176 ChangeInstrument(newInstrument); 177 if (cmd.pScript) { 178 // give old instrument script back to instrument resource manager 179 cmd.pScript->resetAll(); 180 } 181 } 182 183 StatusChanged(true); 184 } 185 ProcessKeySwitchChange(int key)186 void EngineChannel::ProcessKeySwitchChange(int key) { } 187 PreProcessNoteOn(uint8_t key,uint8_t velocity)188 void EngineChannel::PreProcessNoteOn(uint8_t key, uint8_t velocity) { 189 if(pInstrument != NULL && pInstrument->HasKeySwitchBinding(key)) LastKeySwitch = key; 190 PressedKeys[key] = true; 191 } 192 PostProcessNoteOn(uint8_t key,uint8_t velocity)193 void EngineChannel::PostProcessNoteOn(uint8_t key, uint8_t velocity) { 194 LastKey = key; 195 } 196 PreProcessNoteOff(uint8_t key,uint8_t velocity)197 void EngineChannel::PreProcessNoteOff(uint8_t key, uint8_t velocity) { 198 PressedKeys[key] = false; 199 } 200 201 }} // namespace LinuxSampler::sfz 202