--- linuxsampler/trunk/src/engines/EngineChannelBase.h 2009/10/23 17:53:17 2012 +++ linuxsampler/trunk/src/engines/EngineChannelBase.h 2014/06/10 13:32:16 2612 @@ -3,8 +3,8 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * - * Copyright (C) 2005-2009 Christian Schoenebeck * - * Copyright (C) 2009 Grigor Iliev * + * Copyright (C) 2005-2008 Christian Schoenebeck * + * 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 @@ -68,11 +69,13 @@ virtual void ClearRegionsInUse() { { InstrumentChangeCmd& cmd = InstrumentChangeCommand.GetConfigForUpdate(); - if(cmd.pRegionsInUse != NULL) cmd.pRegionsInUse->clear(); + if (cmd.pRegionsInUse) cmd.pRegionsInUse->clear(); + cmd.bChangeInstrument = false; } { InstrumentChangeCmd& cmd = InstrumentChangeCommand.SwitchConfig(); - if(cmd.pRegionsInUse != NULL) cmd.pRegionsInUse->clear(); + if (cmd.pRegionsInUse) cmd.pRegionsInUse->clear(); + cmd.bChangeInstrument = false; } } @@ -82,19 +85,24 @@ } virtual void DeleteRegionsInUse() { + RTList* previous = NULL; // prevent double free { InstrumentChangeCmd& cmd = InstrumentChangeCommand.GetConfigForUpdate(); - if(cmd.pRegionsInUse != NULL) { + if (cmd.pRegionsInUse) { + previous = cmd.pRegionsInUse; delete cmd.pRegionsInUse; cmd.pRegionsInUse = NULL; } + cmd.bChangeInstrument = false; } { InstrumentChangeCmd& cmd = InstrumentChangeCommand.SwitchConfig(); - if(cmd.pRegionsInUse != NULL) { - delete cmd.pRegionsInUse; + if (cmd.pRegionsInUse) { + if (cmd.pRegionsInUse != previous) + delete cmd.pRegionsInUse; cmd.pRegionsInUse = NULL; } + cmd.bChangeInstrument = false; } } @@ -102,10 +110,12 @@ { InstrumentChangeCmd& cmd = InstrumentChangeCommand.GetConfigForUpdate(); cmd.pRegionsInUse = new RTList(pRegionPool[0]); + cmd.bChangeInstrument = false; } { InstrumentChangeCmd& cmd = InstrumentChangeCommand.SwitchConfig(); cmd.pRegionsInUse = new RTList(pRegionPool[1]); + cmd.bChangeInstrument = false; } } @@ -114,7 +124,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); @@ -169,6 +183,7 @@ ResetInternal(); DeleteRegionsInUse(); + UnloadScriptInUse(); InstrumentChangeCmd& cmd = InstrumentChangeCommand.GetConfigForUpdate(); if (cmd.pInstrument) { @@ -183,9 +198,13 @@ MidiKeyboardManager::DeleteActiveVoices(); MidiKeyboardManager::DeleteEvents(); + DeleteGroupEventLists(); AudioOutputDevice* oldAudioDevice = pEngine->pAudioOutputDevice; - pEngine = NULL; + { + LockGuard lock(EngineMutex); + pEngine = NULL; + } AbstractEngine::FreeEngine(this, oldAudioDevice); AudioDeviceChannelLeft = -1; AudioDeviceChannelRight = -1; @@ -207,7 +226,13 @@ pEvents->clear(); // empty MIDI key specific event lists ClearEventListsHandler handler; - ProcessActiveVoices(&handler); + this->ProcessActiveVoices(&handler); + + // empty exclusive group specific event lists + // (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' @@ -217,7 +242,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(); @@ -228,7 +253,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 @@ -240,7 +265,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 @@ -248,7 +273,7 @@ void RenderActiveVoices(uint Samples) { RenderVoicesHandler handler(this, Samples); - ProcessActiveVoices(&handler); + this->ProcessActiveVoices(&handler); SetVoiceCount(handler.VoiceCount); SetDiskStreamCount(handler.StreamCount); @@ -269,17 +294,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; @@ -306,6 +351,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 } } @@ -331,6 +377,34 @@ } /** + * 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) pScript->unload(); + } + { + InstrumentChangeCmd& cmd = InstrumentChangeCommand.SwitchConfig(); + if (cmd.pScript) pScript->unload(); + } + } + + /** + * 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