/[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 2027 - (hide annotations) (download) (as text)
Tue Nov 3 19:27:42 2009 UTC (14 years, 5 months ago) by iliev
File MIME type: text/x-c++hdr
File size: 17707 byte(s)
* sfz engine: support for exclusive groups
* sf2 engine: support for exclusive groups
* sf2 engine: manage presets only
* sf2 engine: preset regions are now taken into account

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

  ViewVC Help
Powered by ViewVC