/[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 2434 - (hide annotations) (download) (as text)
Thu Mar 7 19:23:24 2013 UTC (11 years ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 17359 byte(s)
* Started to spread new C++ keyword "override" over the code base
  (keyword introduced with C++11 standard).

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

  ViewVC Help
Powered by ViewVC