/[svn]/linuxsampler/trunk/src/engines/EngineChannelBase.h
ViewVC logotype

Annotation of /linuxsampler/trunk/src/engines/EngineChannelBase.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2659 - (hide annotations) (download) (as text)
Sun Jun 29 19:34:28 2014 UTC (9 years, 9 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 20149 byte(s)
* Fixed crash which happened if instrument without
  instrument script was loaded (fixes #227).
* Bumped version (1.0.0.svn55).

1 iliev 2012 /***************************************************************************
2     * *
3     * LinuxSampler - modular, streaming capable sampler *
4     * *
5     * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck *
6 persson 2072 * Copyright (C) 2005-2008 Christian Schoenebeck *
7 persson 2427 * Copyright (C) 2009-2013 Christian Schoenebeck and Grigor Iliev *
8 iliev 2012 * *
9     * This program is free software; you can redistribute it and/or modify *
10     * it under the terms of the GNU General Public License as published by *
11     * the Free Software Foundation; either version 2 of the License, or *
12     * (at your option) any later version. *
13     * *
14     * This program is distributed in the hope that it will be useful, *
15     * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17     * GNU General Public License for more details. *
18     * *
19     * You should have received a copy of the GNU General Public License *
20     * along with this program; if not, write to the Free Software *
21     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
22     * MA 02111-1307 USA *
23     ***************************************************************************/
24    
25     #ifndef __LS_ENGINECHANNELBASE_H__
26     #define __LS_ENGINECHANNELBASE_H__
27    
28     #include "AbstractEngineChannel.h"
29     #include "common/MidiKeyboardManager.h"
30     #include "common/Voice.h"
31     #include "../common/ResourceManager.h"
32    
33     namespace LinuxSampler {
34     /// Command used by the instrument loader thread to
35     /// request an instrument change on a channel.
36     template <class R /* Region */, class I /* Instrument */>
37     class InstrumentChangeCmd {
38     public:
39     bool bChangeInstrument; ///< Set to true by the loader when the channel should change instrument.
40     I* pInstrument; ///< The new instrument. Also used by the loader to read the previously loaded instrument.
41     RTList<R*>* pRegionsInUse; ///< List of dimension regions in use by the currently loaded instrument. Continuously updated by the audio thread.
42 schoenebeck 2659 InstrumentScript* pScript; ///< Instrument script to be executed for this instrument. This is never NULL, it is always a valid InstrumentScript pointer. Use InstrumentScript::bHasValidScript whether it reflects a valid instrument script to be executed.
43 iliev 2012 };
44    
45     template<class R>
46     class RegionPools {
47     public:
48     virtual Pool<R*>* GetRegionPool(int index) = 0;
49     };
50    
51     template<class V>
52     class VoicePool {
53     public:
54     virtual Pool<V>* GetVoicePool() = 0;
55     };
56    
57     template <class V /* Voice */, class R /* Region */, class I /* Instrument */>
58     class EngineChannelBase: public AbstractEngineChannel, public MidiKeyboardManager<V>, public ResourceConsumer<I> {
59     public:
60     typedef typename RTList<R*>::Iterator RTListRegionIterator;
61     typedef typename MidiKeyboardManager<V>::MidiKey MidiKey;
62    
63 schoenebeck 2613 virtual MidiKeyboardManagerBase* GetMidiKeyboardManager() OVERRIDE {
64     return this;
65     }
66    
67 iliev 2012 virtual void HandBack(I* Instrument) {
68     ResourceManager<InstrumentManager::instrument_id_t, I>* mgr =
69     dynamic_cast<ResourceManager<InstrumentManager::instrument_id_t, I>*>(pEngine->GetInstrumentManager());
70     mgr->HandBack(Instrument, this);
71     }
72    
73     virtual void ClearRegionsInUse() {
74     {
75     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
76 persson 2165 if (cmd.pRegionsInUse) cmd.pRegionsInUse->clear();
77     cmd.bChangeInstrument = false;
78 iliev 2012 }
79     {
80     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.SwitchConfig();
81 persson 2165 if (cmd.pRegionsInUse) cmd.pRegionsInUse->clear();
82     cmd.bChangeInstrument = false;
83 iliev 2012 }
84     }
85    
86     virtual void ResetRegionsInUse(Pool<R*>* pRegionPool[]) {
87     DeleteRegionsInUse();
88     AllocateRegionsInUse(pRegionPool);
89     }
90    
91     virtual void DeleteRegionsInUse() {
92 schoenebeck 2612 RTList<R*>* previous = NULL; // prevent double free
93 iliev 2012 {
94     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
95 persson 2165 if (cmd.pRegionsInUse) {
96 schoenebeck 2612 previous = cmd.pRegionsInUse;
97 iliev 2012 delete cmd.pRegionsInUse;
98     cmd.pRegionsInUse = NULL;
99     }
100 persson 2165 cmd.bChangeInstrument = false;
101 iliev 2012 }
102     {
103     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.SwitchConfig();
104 persson 2165 if (cmd.pRegionsInUse) {
105 schoenebeck 2612 if (cmd.pRegionsInUse != previous)
106     delete cmd.pRegionsInUse;
107 iliev 2012 cmd.pRegionsInUse = NULL;
108     }
109 persson 2165 cmd.bChangeInstrument = false;
110 iliev 2012 }
111     }
112    
113     virtual void AllocateRegionsInUse(Pool<R*>* pRegionPool[]) {
114     {
115     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
116     cmd.pRegionsInUse = new RTList<R*>(pRegionPool[0]);
117 persson 2165 cmd.bChangeInstrument = false;
118 iliev 2012 }
119     {
120     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.SwitchConfig();
121     cmd.pRegionsInUse = new RTList<R*>(pRegionPool[1]);
122 persson 2165 cmd.bChangeInstrument = false;
123 iliev 2012 }
124     }
125    
126     virtual void Connect(AudioOutputDevice* pAudioOut) {
127     if (pEngine) {
128     if (pEngine->pAudioOutputDevice == pAudioOut) return;
129     DisconnectAudioOutputDevice();
130     }
131 persson 2326 AbstractEngine* newEngine = AbstractEngine::AcquireEngine(this, pAudioOut);
132 persson 2427 {
133     LockGuard lock(EngineMutex);
134     pEngine = newEngine;
135     }
136 iliev 2012 ResetInternal();
137     pEvents = new RTList<Event>(pEngine->pEventPool);
138    
139     RegionPools<R>* pRegionPool = dynamic_cast<RegionPools<R>*>(pEngine);
140     // reset the instrument change command struct (need to be done
141     // twice, as it is double buffered)
142     {
143     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
144     cmd.pRegionsInUse = new RTList<R*>(pRegionPool->GetRegionPool(0));
145     cmd.pInstrument = 0;
146     cmd.bChangeInstrument = false;
147     }
148     {
149     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.SwitchConfig();
150     cmd.pRegionsInUse = new RTList<R*>(pRegionPool->GetRegionPool(1));
151     cmd.pInstrument = 0;
152     cmd.bChangeInstrument = false;
153     }
154    
155     if (pInstrument != NULL) {
156     pInstrument = NULL;
157     InstrumentStat = -1;
158     InstrumentIdx = -1;
159     InstrumentIdxName = "";
160     InstrumentFile = "";
161     bStatusChanged = true;
162     }
163    
164     VoicePool<V>* pVoicePool = dynamic_cast<VoicePool<V>*>(pEngine);
165     MidiKeyboardManager<V>::AllocateActiveVoices(pVoicePool->GetVoicePool());
166     MidiKeyboardManager<V>::AllocateEvents(pEngine->pEventPool);
167    
168     AudioDeviceChannelLeft = 0;
169     AudioDeviceChannelRight = 1;
170     if (fxSends.empty()) { // render directly into the AudioDevice's output buffers
171     pChannelLeft = pAudioOut->Channel(AudioDeviceChannelLeft);
172     pChannelRight = pAudioOut->Channel(AudioDeviceChannelRight);
173     } else { // use local buffers for rendering and copy later
174     // ensure the local buffers have the correct size
175     if (pChannelLeft) delete pChannelLeft;
176     if (pChannelRight) delete pChannelRight;
177     pChannelLeft = new AudioChannel(0, pAudioOut->MaxSamplesPerCycle());
178     pChannelRight = new AudioChannel(1, pAudioOut->MaxSamplesPerCycle());
179     }
180     if (pEngine->EngineDisabled.GetUnsafe()) pEngine->Enable();
181     MidiInputPort::AddSysexListener(pEngine);
182     }
183    
184     virtual void DisconnectAudioOutputDevice() {
185     if (pEngine) { // if clause to prevent disconnect loops
186    
187     ResetInternal();
188    
189     DeleteRegionsInUse();
190 schoenebeck 2612 UnloadScriptInUse();
191 iliev 2012
192     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
193     if (cmd.pInstrument) {
194     // release the currently loaded instrument
195     HandBack(cmd.pInstrument);
196     }
197    
198     if (pEvents) {
199     delete pEvents;
200     pEvents = NULL;
201     }
202    
203     MidiKeyboardManager<V>::DeleteActiveVoices();
204     MidiKeyboardManager<V>::DeleteEvents();
205 persson 2114 DeleteGroupEventLists();
206 iliev 2012
207     AudioOutputDevice* oldAudioDevice = pEngine->pAudioOutputDevice;
208 persson 2427 {
209     LockGuard lock(EngineMutex);
210     pEngine = NULL;
211     }
212 iliev 2012 AbstractEngine::FreeEngine(this, oldAudioDevice);
213     AudioDeviceChannelLeft = -1;
214     AudioDeviceChannelRight = -1;
215     if (!fxSends.empty()) { // free the local rendering buffers
216     if (pChannelLeft) delete pChannelLeft;
217     if (pChannelRight) delete pChannelRight;
218     }
219     pChannelLeft = NULL;
220     pChannelRight = NULL;
221     }
222     }
223    
224     class ClearEventListsHandler : public MidiKeyboardManager<V>::VoiceHandlerBase {
225     public:
226     virtual bool Process(MidiKey* pMidiKey) { pMidiKey->pEvents->clear(); return false; }
227     };
228    
229     void ClearEventLists() {
230     pEvents->clear();
231     // empty MIDI key specific event lists
232     ClearEventListsHandler handler;
233 persson 2335 this->ProcessActiveVoices(&handler);
234 persson 2114
235     // empty exclusive group specific event lists
236 persson 2352 // (pInstrument == 0 could mean that LoadInstrument is
237     // building new group event lists, so we must check
238     // for that)
239     if (pInstrument) ClearGroupEventLists();
240 iliev 2012 }
241    
242     // implementation of abstract methods derived from interface class 'InstrumentConsumer'
243    
244     /**
245     * Will be called by the InstrumentResourceManager when the instrument
246     * we are currently using on this EngineChannel is going to be updated,
247     * so we can stop playback before that happens.
248     */
249 schoenebeck 2434 virtual void ResourceToBeUpdated(I* pResource, void*& pUpdateArg) OVERRIDE {
250 iliev 2012 dmsg(3,("EngineChannelBase: Received instrument update message.\n"));
251     if (pEngine) pEngine->DisableAndLock();
252     ResetInternal();
253     this->pInstrument = NULL;
254     }
255    
256     /**
257     * Will be called by the InstrumentResourceManager when the instrument
258     * update process was completed, so we can continue with playback.
259     */
260 schoenebeck 2434 virtual void ResourceUpdated(I* pOldResource, I* pNewResource, void* pUpdateArg) OVERRIDE {
261 iliev 2012 this->pInstrument = pNewResource; //TODO: there are couple of engine parameters we should update here as well if the instrument was updated (see LoadInstrument())
262     if (pEngine) pEngine->Enable();
263     bStatusChanged = true; // status of engine has changed, so set notify flag
264     }
265    
266     /**
267     * Will be called by the InstrumentResourceManager on progress changes
268     * while loading or realoading an instrument for this EngineChannel.
269     *
270     * @param fProgress - current progress as value between 0.0 and 1.0
271     */
272 schoenebeck 2434 virtual void OnResourceProgress(float fProgress) OVERRIDE {
273 iliev 2012 this->InstrumentStat = int(fProgress * 100.0f);
274     dmsg(7,("EngineChannelBase: progress %d%", InstrumentStat));
275     bStatusChanged = true; // status of engine has changed, so set notify flag
276     }
277    
278     void RenderActiveVoices(uint Samples) {
279     RenderVoicesHandler handler(this, Samples);
280 persson 2335 this->ProcessActiveVoices(&handler);
281 iliev 2012
282     SetVoiceCount(handler.VoiceCount);
283     SetDiskStreamCount(handler.StreamCount);
284     }
285    
286     RTList<R*>* pRegionsInUse; ///< temporary pointer into the instrument change command, used by the audio thread
287     I* pInstrument;
288    
289     template<class TV, class TRR, class TR, class TD, class TIM, class TI> friend class EngineBase;
290    
291     protected:
292 schoenebeck 2645 EngineChannelBase() :
293     MidiKeyboardManager<V>(this),
294     InstrumentChangeCommandReader(InstrumentChangeCommand)
295     {
296 iliev 2012 pInstrument = NULL;
297    
298     // reset the instrument change command struct (need to be done
299     // twice, as it is double buffered)
300     {
301     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
302     cmd.pRegionsInUse = NULL;
303     cmd.pInstrument = NULL;
304 schoenebeck 2611 cmd.pScript = new InstrumentScript(this);
305 iliev 2012 cmd.bChangeInstrument = false;
306     }
307     {
308     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.SwitchConfig();
309     cmd.pRegionsInUse = NULL;
310     cmd.pInstrument = NULL;
311 schoenebeck 2611 cmd.pScript = new InstrumentScript(this);
312 iliev 2012 cmd.bChangeInstrument = false;
313     }
314     }
315    
316 schoenebeck 2611 virtual ~EngineChannelBase() {
317 schoenebeck 2612 InstrumentScript* previous = NULL; // prevent double free
318 schoenebeck 2611 {
319     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
320 schoenebeck 2612 if (cmd.pScript) {
321     previous = cmd.pScript;
322     delete cmd.pScript;
323     cmd.pScript = NULL;
324     }
325 schoenebeck 2611 }
326     {
327     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.SwitchConfig();
328 schoenebeck 2612 if (cmd.pScript) {
329     if (previous != cmd.pScript)
330     delete cmd.pScript;
331     cmd.pScript = NULL;
332     }
333 schoenebeck 2611 }
334     }
335 iliev 2012
336     typedef typename RTList<V>::Iterator RTListVoiceIterator;
337    
338     class RenderVoicesHandler : public MidiKeyboardManager<V>::VoiceHandlerBase {
339     public:
340     uint Samples;
341     uint VoiceCount;
342     uint StreamCount;
343     EngineChannelBase<V, R, I>* pChannel;
344    
345     RenderVoicesHandler(EngineChannelBase<V, R, I>* channel, uint samples) :
346     pChannel(channel), Samples(samples), VoiceCount(0), StreamCount(0) { }
347    
348     virtual void Process(RTListVoiceIterator& itVoice) {
349     // now render current voice
350     itVoice->Render(Samples);
351     if (itVoice->IsActive()) { // still active
352     if (!itVoice->Orphan) {
353     *(pChannel->pRegionsInUse->allocAppend()) = itVoice->GetRegion();
354     }
355     VoiceCount++;
356    
357     if (itVoice->PlaybackState == Voice::playback_state_disk) {
358     if ((itVoice->DiskStreamRef).State != Stream::state_unused) StreamCount++;
359     }
360     } else { // voice reached end, is now inactive
361 iliev 2244 itVoice->VoiceFreed();
362 iliev 2012 pChannel->FreeVoice(itVoice); // remove voice from the list of active voices
363     }
364     }
365     };
366    
367     typedef typename SynchronizedConfig<InstrumentChangeCmd<R, I> >::Reader SyncConfInstrChangeCmdReader;
368    
369     SynchronizedConfig<InstrumentChangeCmd<R, I> > InstrumentChangeCommand;
370     SyncConfInstrChangeCmdReader InstrumentChangeCommandReader;
371    
372     /** This method is not thread safe! */
373     virtual void ResetInternal() {
374     AbstractEngineChannel::ResetInternal();
375    
376     MidiKeyboardManager<V>::Reset();
377     }
378    
379     virtual void ResetControllers() {
380     AbstractEngineChannel::ResetControllers();
381    
382     MidiKeyboardManager<V>::SustainPedal = false;
383     MidiKeyboardManager<V>::SostenutoPedal = false;
384     }
385    
386     /**
387 schoenebeck 2612 * Unload the currently used and loaded real-time instrument script.
388     * The source code of the script is retained, so that it can still
389     * be reloaded.
390     */
391     void UnloadScriptInUse() {
392     {
393     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
394 schoenebeck 2618 if (cmd.pScript) cmd.pScript->unload();
395 schoenebeck 2612 }
396     {
397     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.SwitchConfig();
398 schoenebeck 2618 if (cmd.pScript) cmd.pScript->unload();
399 schoenebeck 2612 }
400 schoenebeck 2614 InstrumentChangeCommand.SwitchConfig(); // switch back to original one
401 schoenebeck 2612 }
402    
403     /**
404 schoenebeck 2611 * Load real-time instrument script and all its resources required
405     * for the upcoming instrument change.
406     *
407     * @param text - source code of script
408     */
409     void LoadInstrumentScript(const String& text) {
410     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
411     // load the new script
412     cmd.pScript->load(text);
413     }
414    
415     /**
416 iliev 2012 * Changes the instrument for an engine channel.
417     *
418     * @param pInstrument - new instrument
419     * @returns the resulting instrument change command after the
420     * command switch, containing the old instrument and
421     * the dimregions it is using
422     */
423     InstrumentChangeCmd<R, I>& ChangeInstrument(I* pInstrument) {
424     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
425     cmd.pInstrument = pInstrument;
426     cmd.bChangeInstrument = true;
427    
428     return InstrumentChangeCommand.SwitchConfig();
429     }
430    
431     virtual void ProcessKeySwitchChange(int key) = 0;
432     };
433    
434     } // namespace LinuxSampler
435    
436     #endif /* __LS_ENGINECHANNELBASE_H__ */

  ViewVC Help
Powered by ViewVC