--- linuxsampler/trunk/src/engines/EngineChannelBase.h 2011/02/20 14:20:22 2165 +++ linuxsampler/trunk/src/engines/EngineChannelBase.h 2014/06/18 00:14:57 2645 @@ -4,7 +4,7 @@ * * * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * * Copyright (C) 2005-2008 Christian Schoenebeck * - * Copyright (C) 2009-2011 Christian Schoenebeck and Grigor Iliev * + * Copyright (C) 2009-2013 Christian Schoenebeck and Grigor Iliev * * * * 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 * @@ -39,6 +39,7 @@ bool bChangeInstrument; ///< Set to true by the loader when the channel should change instrument. I* pInstrument; ///< The new instrument. Also used by the loader to read the previously loaded instrument. RTList* pRegionsInUse; ///< List of dimension regions in use by the currently loaded instrument. Continuously updated by the audio thread. + InstrumentScript* pScript; ///< Instrument script to be executed for this instrument, or NULL if instrument does not have a script. }; template @@ -59,6 +60,10 @@ typedef typename RTList::Iterator RTListRegionIterator; typedef typename MidiKeyboardManager::MidiKey MidiKey; + virtual MidiKeyboardManagerBase* GetMidiKeyboardManager() OVERRIDE { + return this; + } + virtual void HandBack(I* Instrument) { ResourceManager* mgr = dynamic_cast*>(pEngine->GetInstrumentManager()); @@ -84,9 +89,11 @@ } virtual void DeleteRegionsInUse() { + RTList* previous = NULL; // prevent double free { InstrumentChangeCmd& cmd = InstrumentChangeCommand.GetConfigForUpdate(); if (cmd.pRegionsInUse) { + previous = cmd.pRegionsInUse; delete cmd.pRegionsInUse; cmd.pRegionsInUse = NULL; } @@ -95,7 +102,8 @@ { InstrumentChangeCmd& cmd = InstrumentChangeCommand.SwitchConfig(); if (cmd.pRegionsInUse) { - delete cmd.pRegionsInUse; + if (cmd.pRegionsInUse != previous) + delete cmd.pRegionsInUse; cmd.pRegionsInUse = NULL; } cmd.bChangeInstrument = false; @@ -120,7 +128,11 @@ if (pEngine->pAudioOutputDevice == pAudioOut) return; DisconnectAudioOutputDevice(); } - pEngine = AbstractEngine::AcquireEngine(this, pAudioOut); + AbstractEngine* newEngine = AbstractEngine::AcquireEngine(this, pAudioOut); + { + LockGuard lock(EngineMutex); + pEngine = newEngine; + } ResetInternal(); pEvents = new RTList(pEngine->pEventPool); @@ -175,6 +187,7 @@ ResetInternal(); DeleteRegionsInUse(); + UnloadScriptInUse(); InstrumentChangeCmd& cmd = InstrumentChangeCommand.GetConfigForUpdate(); if (cmd.pInstrument) { @@ -192,7 +205,10 @@ DeleteGroupEventLists(); AudioOutputDevice* oldAudioDevice = pEngine->pAudioOutputDevice; - pEngine = NULL; + { + LockGuard lock(EngineMutex); + pEngine = NULL; + } AbstractEngine::FreeEngine(this, oldAudioDevice); AudioDeviceChannelLeft = -1; AudioDeviceChannelRight = -1; @@ -214,10 +230,13 @@ pEvents->clear(); // empty MIDI key specific event lists ClearEventListsHandler handler; - ProcessActiveVoices(&handler); + this->ProcessActiveVoices(&handler); // empty exclusive group specific event lists - ClearGroupEventLists(); + // (pInstrument == 0 could mean that LoadInstrument is + // building new group event lists, so we must check + // for that) + if (pInstrument) ClearGroupEventLists(); } // implementation of abstract methods derived from interface class 'InstrumentConsumer' @@ -227,7 +246,7 @@ * we are currently using on this EngineChannel is going to be updated, * so we can stop playback before that happens. */ - virtual void ResourceToBeUpdated(I* pResource, void*& pUpdateArg) { + virtual void ResourceToBeUpdated(I* pResource, void*& pUpdateArg) OVERRIDE { dmsg(3,("EngineChannelBase: Received instrument update message.\n")); if (pEngine) pEngine->DisableAndLock(); ResetInternal(); @@ -238,7 +257,7 @@ * Will be called by the InstrumentResourceManager when the instrument * update process was completed, so we can continue with playback. */ - virtual void ResourceUpdated(I* pOldResource, I* pNewResource, void* pUpdateArg) { + virtual void ResourceUpdated(I* pOldResource, I* pNewResource, void* pUpdateArg) OVERRIDE { this->pInstrument = pNewResource; //TODO: there are couple of engine parameters we should update here as well if the instrument was updated (see LoadInstrument()) if (pEngine) pEngine->Enable(); bStatusChanged = true; // status of engine has changed, so set notify flag @@ -250,7 +269,7 @@ * * @param fProgress - current progress as value between 0.0 and 1.0 */ - virtual void OnResourceProgress(float fProgress) { + virtual void OnResourceProgress(float fProgress) OVERRIDE { this->InstrumentStat = int(fProgress * 100.0f); dmsg(7,("EngineChannelBase: progress %d%", InstrumentStat)); bStatusChanged = true; // status of engine has changed, so set notify flag @@ -258,7 +277,7 @@ void RenderActiveVoices(uint Samples) { RenderVoicesHandler handler(this, Samples); - ProcessActiveVoices(&handler); + this->ProcessActiveVoices(&handler); SetVoiceCount(handler.VoiceCount); SetDiskStreamCount(handler.StreamCount); @@ -270,7 +289,10 @@ template friend class EngineBase; protected: - EngineChannelBase() : InstrumentChangeCommandReader(InstrumentChangeCommand) { + EngineChannelBase() : + MidiKeyboardManager(this), + InstrumentChangeCommandReader(InstrumentChangeCommand) + { pInstrument = NULL; // reset the instrument change command struct (need to be done @@ -279,17 +301,37 @@ InstrumentChangeCmd& cmd = InstrumentChangeCommand.GetConfigForUpdate(); cmd.pRegionsInUse = NULL; cmd.pInstrument = NULL; + cmd.pScript = new InstrumentScript(this); cmd.bChangeInstrument = false; } { InstrumentChangeCmd& cmd = InstrumentChangeCommand.SwitchConfig(); cmd.pRegionsInUse = NULL; cmd.pInstrument = NULL; + cmd.pScript = new InstrumentScript(this); cmd.bChangeInstrument = false; } } - virtual ~EngineChannelBase() { } + virtual ~EngineChannelBase() { + InstrumentScript* previous = NULL; // prevent double free + { + InstrumentChangeCmd& cmd = InstrumentChangeCommand.GetConfigForUpdate(); + if (cmd.pScript) { + previous = cmd.pScript; + delete cmd.pScript; + cmd.pScript = NULL; + } + } + { + InstrumentChangeCmd& cmd = InstrumentChangeCommand.SwitchConfig(); + if (cmd.pScript) { + if (previous != cmd.pScript) + delete cmd.pScript; + cmd.pScript = NULL; + } + } + } typedef typename RTList::Iterator RTListVoiceIterator; @@ -316,6 +358,7 @@ if ((itVoice->DiskStreamRef).State != Stream::state_unused) StreamCount++; } } else { // voice reached end, is now inactive + itVoice->VoiceFreed(); pChannel->FreeVoice(itVoice); // remove voice from the list of active voices } } @@ -341,6 +384,35 @@ } /** + * Unload the currently used and loaded real-time instrument script. + * The source code of the script is retained, so that it can still + * be reloaded. + */ + void UnloadScriptInUse() { + { + InstrumentChangeCmd& cmd = InstrumentChangeCommand.GetConfigForUpdate(); + if (cmd.pScript) cmd.pScript->unload(); + } + { + InstrumentChangeCmd& cmd = InstrumentChangeCommand.SwitchConfig(); + if (cmd.pScript) cmd.pScript->unload(); + } + InstrumentChangeCommand.SwitchConfig(); // switch back to original one + } + + /** + * Load real-time instrument script and all its resources required + * for the upcoming instrument change. + * + * @param text - source code of script + */ + void LoadInstrumentScript(const String& text) { + InstrumentChangeCmd& cmd = InstrumentChangeCommand.GetConfigForUpdate(); + // load the new script + cmd.pScript->load(text); + } + + /** * Changes the instrument for an engine channel. * * @param pInstrument - new instrument