/[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 2612 - (hide annotations) (download) (as text)
Tue Jun 10 13:32:16 2014 UTC (9 years, 10 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 19734 byte(s)
* Fixed crashes when exiting the sampler.
* Bumped version (1.0.0.svn47).

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 2611 InstrumentScript* pScript; ///< Instrument script to be executed for this instrument, or NULL if instrument does not have a script.
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     virtual void HandBack(I* Instrument) {
64     ResourceManager<InstrumentManager::instrument_id_t, I>* mgr =
65     dynamic_cast<ResourceManager<InstrumentManager::instrument_id_t, I>*>(pEngine->GetInstrumentManager());
66     mgr->HandBack(Instrument, this);
67     }
68    
69     virtual void ClearRegionsInUse() {
70     {
71     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
72 persson 2165 if (cmd.pRegionsInUse) cmd.pRegionsInUse->clear();
73     cmd.bChangeInstrument = false;
74 iliev 2012 }
75     {
76     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.SwitchConfig();
77 persson 2165 if (cmd.pRegionsInUse) cmd.pRegionsInUse->clear();
78     cmd.bChangeInstrument = false;
79 iliev 2012 }
80     }
81    
82     virtual void ResetRegionsInUse(Pool<R*>* pRegionPool[]) {
83     DeleteRegionsInUse();
84     AllocateRegionsInUse(pRegionPool);
85     }
86    
87     virtual void DeleteRegionsInUse() {
88 schoenebeck 2612 RTList<R*>* previous = NULL; // prevent double free
89 iliev 2012 {
90     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
91 persson 2165 if (cmd.pRegionsInUse) {
92 schoenebeck 2612 previous = cmd.pRegionsInUse;
93 iliev 2012 delete cmd.pRegionsInUse;
94     cmd.pRegionsInUse = NULL;
95     }
96 persson 2165 cmd.bChangeInstrument = false;
97 iliev 2012 }
98     {
99     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.SwitchConfig();
100 persson 2165 if (cmd.pRegionsInUse) {
101 schoenebeck 2612 if (cmd.pRegionsInUse != previous)
102     delete cmd.pRegionsInUse;
103 iliev 2012 cmd.pRegionsInUse = NULL;
104     }
105 persson 2165 cmd.bChangeInstrument = false;
106 iliev 2012 }
107     }
108    
109     virtual void AllocateRegionsInUse(Pool<R*>* pRegionPool[]) {
110     {
111     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
112     cmd.pRegionsInUse = new RTList<R*>(pRegionPool[0]);
113 persson 2165 cmd.bChangeInstrument = false;
114 iliev 2012 }
115     {
116     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.SwitchConfig();
117     cmd.pRegionsInUse = new RTList<R*>(pRegionPool[1]);
118 persson 2165 cmd.bChangeInstrument = false;
119 iliev 2012 }
120     }
121    
122     virtual void Connect(AudioOutputDevice* pAudioOut) {
123     if (pEngine) {
124     if (pEngine->pAudioOutputDevice == pAudioOut) return;
125     DisconnectAudioOutputDevice();
126     }
127 persson 2326 AbstractEngine* newEngine = AbstractEngine::AcquireEngine(this, pAudioOut);
128 persson 2427 {
129     LockGuard lock(EngineMutex);
130     pEngine = newEngine;
131     }
132 iliev 2012 ResetInternal();
133     pEvents = new RTList<Event>(pEngine->pEventPool);
134    
135     RegionPools<R>* pRegionPool = dynamic_cast<RegionPools<R>*>(pEngine);
136     // reset the instrument change command struct (need to be done
137     // twice, as it is double buffered)
138     {
139     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
140     cmd.pRegionsInUse = new RTList<R*>(pRegionPool->GetRegionPool(0));
141     cmd.pInstrument = 0;
142     cmd.bChangeInstrument = false;
143     }
144     {
145     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.SwitchConfig();
146     cmd.pRegionsInUse = new RTList<R*>(pRegionPool->GetRegionPool(1));
147     cmd.pInstrument = 0;
148     cmd.bChangeInstrument = false;
149     }
150    
151     if (pInstrument != NULL) {
152     pInstrument = NULL;
153     InstrumentStat = -1;
154     InstrumentIdx = -1;
155     InstrumentIdxName = "";
156     InstrumentFile = "";
157     bStatusChanged = true;
158     }
159    
160     VoicePool<V>* pVoicePool = dynamic_cast<VoicePool<V>*>(pEngine);
161     MidiKeyboardManager<V>::AllocateActiveVoices(pVoicePool->GetVoicePool());
162     MidiKeyboardManager<V>::AllocateEvents(pEngine->pEventPool);
163    
164     AudioDeviceChannelLeft = 0;
165     AudioDeviceChannelRight = 1;
166     if (fxSends.empty()) { // render directly into the AudioDevice's output buffers
167     pChannelLeft = pAudioOut->Channel(AudioDeviceChannelLeft);
168     pChannelRight = pAudioOut->Channel(AudioDeviceChannelRight);
169     } else { // use local buffers for rendering and copy later
170     // ensure the local buffers have the correct size
171     if (pChannelLeft) delete pChannelLeft;
172     if (pChannelRight) delete pChannelRight;
173     pChannelLeft = new AudioChannel(0, pAudioOut->MaxSamplesPerCycle());
174     pChannelRight = new AudioChannel(1, pAudioOut->MaxSamplesPerCycle());
175     }
176     if (pEngine->EngineDisabled.GetUnsafe()) pEngine->Enable();
177     MidiInputPort::AddSysexListener(pEngine);
178     }
179    
180     virtual void DisconnectAudioOutputDevice() {
181     if (pEngine) { // if clause to prevent disconnect loops
182    
183     ResetInternal();
184    
185     DeleteRegionsInUse();
186 schoenebeck 2612 UnloadScriptInUse();
187 iliev 2012
188     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
189     if (cmd.pInstrument) {
190     // release the currently loaded instrument
191     HandBack(cmd.pInstrument);
192     }
193    
194     if (pEvents) {
195     delete pEvents;
196     pEvents = NULL;
197     }
198    
199     MidiKeyboardManager<V>::DeleteActiveVoices();
200     MidiKeyboardManager<V>::DeleteEvents();
201 persson 2114 DeleteGroupEventLists();
202 iliev 2012
203     AudioOutputDevice* oldAudioDevice = pEngine->pAudioOutputDevice;
204 persson 2427 {
205     LockGuard lock(EngineMutex);
206     pEngine = NULL;
207     }
208 iliev 2012 AbstractEngine::FreeEngine(this, oldAudioDevice);
209     AudioDeviceChannelLeft = -1;
210     AudioDeviceChannelRight = -1;
211     if (!fxSends.empty()) { // free the local rendering buffers
212     if (pChannelLeft) delete pChannelLeft;
213     if (pChannelRight) delete pChannelRight;
214     }
215     pChannelLeft = NULL;
216     pChannelRight = NULL;
217     }
218     }
219    
220     class ClearEventListsHandler : public MidiKeyboardManager<V>::VoiceHandlerBase {
221     public:
222     virtual bool Process(MidiKey* pMidiKey) { pMidiKey->pEvents->clear(); return false; }
223     };
224    
225     void ClearEventLists() {
226     pEvents->clear();
227     // empty MIDI key specific event lists
228     ClearEventListsHandler handler;
229 persson 2335 this->ProcessActiveVoices(&handler);
230 persson 2114
231     // empty exclusive group specific event lists
232 persson 2352 // (pInstrument == 0 could mean that LoadInstrument is
233     // building new group event lists, so we must check
234     // for that)
235     if (pInstrument) ClearGroupEventLists();
236 iliev 2012 }
237    
238     // implementation of abstract methods derived from interface class 'InstrumentConsumer'
239    
240     /**
241     * Will be called by the InstrumentResourceManager when the instrument
242     * we are currently using on this EngineChannel is going to be updated,
243     * so we can stop playback before that happens.
244     */
245 schoenebeck 2434 virtual void ResourceToBeUpdated(I* pResource, void*& pUpdateArg) OVERRIDE {
246 iliev 2012 dmsg(3,("EngineChannelBase: Received instrument update message.\n"));
247     if (pEngine) pEngine->DisableAndLock();
248     ResetInternal();
249     this->pInstrument = NULL;
250     }
251    
252     /**
253     * Will be called by the InstrumentResourceManager when the instrument
254     * update process was completed, so we can continue with playback.
255     */
256 schoenebeck 2434 virtual void ResourceUpdated(I* pOldResource, I* pNewResource, void* pUpdateArg) OVERRIDE {
257 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())
258     if (pEngine) pEngine->Enable();
259     bStatusChanged = true; // status of engine has changed, so set notify flag
260     }
261    
262     /**
263     * Will be called by the InstrumentResourceManager on progress changes
264     * while loading or realoading an instrument for this EngineChannel.
265     *
266     * @param fProgress - current progress as value between 0.0 and 1.0
267     */
268 schoenebeck 2434 virtual void OnResourceProgress(float fProgress) OVERRIDE {
269 iliev 2012 this->InstrumentStat = int(fProgress * 100.0f);
270     dmsg(7,("EngineChannelBase: progress %d%", InstrumentStat));
271     bStatusChanged = true; // status of engine has changed, so set notify flag
272     }
273    
274     void RenderActiveVoices(uint Samples) {
275     RenderVoicesHandler handler(this, Samples);
276 persson 2335 this->ProcessActiveVoices(&handler);
277 iliev 2012
278     SetVoiceCount(handler.VoiceCount);
279     SetDiskStreamCount(handler.StreamCount);
280     }
281    
282     RTList<R*>* pRegionsInUse; ///< temporary pointer into the instrument change command, used by the audio thread
283     I* pInstrument;
284    
285     template<class TV, class TRR, class TR, class TD, class TIM, class TI> friend class EngineBase;
286    
287     protected:
288     EngineChannelBase() : InstrumentChangeCommandReader(InstrumentChangeCommand) {
289     pInstrument = NULL;
290    
291     // reset the instrument change command struct (need to be done
292     // twice, as it is double buffered)
293     {
294     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
295     cmd.pRegionsInUse = NULL;
296     cmd.pInstrument = NULL;
297 schoenebeck 2611 cmd.pScript = new InstrumentScript(this);
298 iliev 2012 cmd.bChangeInstrument = false;
299     }
300     {
301     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.SwitchConfig();
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    
309 schoenebeck 2611 virtual ~EngineChannelBase() {
310 schoenebeck 2612 InstrumentScript* previous = NULL; // prevent double free
311 schoenebeck 2611 {
312     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
313 schoenebeck 2612 if (cmd.pScript) {
314     previous = cmd.pScript;
315     delete cmd.pScript;
316     cmd.pScript = NULL;
317     }
318 schoenebeck 2611 }
319     {
320     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.SwitchConfig();
321 schoenebeck 2612 if (cmd.pScript) {
322     if (previous != cmd.pScript)
323     delete cmd.pScript;
324     cmd.pScript = NULL;
325     }
326 schoenebeck 2611 }
327     }
328 iliev 2012
329     typedef typename RTList<V>::Iterator RTListVoiceIterator;
330    
331     class RenderVoicesHandler : public MidiKeyboardManager<V>::VoiceHandlerBase {
332     public:
333     uint Samples;
334     uint VoiceCount;
335     uint StreamCount;
336     EngineChannelBase<V, R, I>* pChannel;
337    
338     RenderVoicesHandler(EngineChannelBase<V, R, I>* channel, uint samples) :
339     pChannel(channel), Samples(samples), VoiceCount(0), StreamCount(0) { }
340    
341     virtual void Process(RTListVoiceIterator& itVoice) {
342     // now render current voice
343     itVoice->Render(Samples);
344     if (itVoice->IsActive()) { // still active
345     if (!itVoice->Orphan) {
346     *(pChannel->pRegionsInUse->allocAppend()) = itVoice->GetRegion();
347     }
348     VoiceCount++;
349    
350     if (itVoice->PlaybackState == Voice::playback_state_disk) {
351     if ((itVoice->DiskStreamRef).State != Stream::state_unused) StreamCount++;
352     }
353     } else { // voice reached end, is now inactive
354 iliev 2244 itVoice->VoiceFreed();
355 iliev 2012 pChannel->FreeVoice(itVoice); // remove voice from the list of active voices
356     }
357     }
358     };
359    
360     typedef typename SynchronizedConfig<InstrumentChangeCmd<R, I> >::Reader SyncConfInstrChangeCmdReader;
361    
362     SynchronizedConfig<InstrumentChangeCmd<R, I> > InstrumentChangeCommand;
363     SyncConfInstrChangeCmdReader InstrumentChangeCommandReader;
364    
365     /** This method is not thread safe! */
366     virtual void ResetInternal() {
367     AbstractEngineChannel::ResetInternal();
368    
369     MidiKeyboardManager<V>::Reset();
370     }
371    
372     virtual void ResetControllers() {
373     AbstractEngineChannel::ResetControllers();
374    
375     MidiKeyboardManager<V>::SustainPedal = false;
376     MidiKeyboardManager<V>::SostenutoPedal = false;
377     }
378    
379     /**
380 schoenebeck 2612 * Unload the currently used and loaded real-time instrument script.
381     * The source code of the script is retained, so that it can still
382     * be reloaded.
383     */
384     void UnloadScriptInUse() {
385     {
386     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
387     if (cmd.pScript) pScript->unload();
388     }
389     {
390     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.SwitchConfig();
391     if (cmd.pScript) pScript->unload();
392     }
393     }
394    
395     /**
396 schoenebeck 2611 * Load real-time instrument script and all its resources required
397     * for the upcoming instrument change.
398     *
399     * @param text - source code of script
400     */
401     void LoadInstrumentScript(const String& text) {
402     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
403     // load the new script
404     cmd.pScript->load(text);
405     }
406    
407     /**
408 iliev 2012 * Changes the instrument for an engine channel.
409     *
410     * @param pInstrument - new instrument
411     * @returns the resulting instrument change command after the
412     * command switch, containing the old instrument and
413     * the dimregions it is using
414     */
415     InstrumentChangeCmd<R, I>& ChangeInstrument(I* pInstrument) {
416     InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
417     cmd.pInstrument = pInstrument;
418     cmd.bChangeInstrument = true;
419    
420     return InstrumentChangeCommand.SwitchConfig();
421     }
422    
423     virtual void ProcessKeySwitchChange(int key) = 0;
424     };
425    
426     } // namespace LinuxSampler
427    
428     #endif /* __LS_ENGINECHANNELBASE_H__ */

  ViewVC Help
Powered by ViewVC