/[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 2611 - (hide annotations) (download) (as text)
Mon Jun 9 19:20:37 2014 UTC (9 years, 10 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 18769 byte(s)
* Fixed crash when loading an instrument script.
* Fixed "init" script handler only to be executed once:
  when the script was loaded.
* Fixed aftertouch script event which always had value zero
  and controller number was set to aftertouch value instead.
* gig Engine: Fixed handling of "smartmidi" dimension, which
  was recognized as "unknown" dimension.
* Fixed script function gig_set_dim_zone(): was accessing
  wrong event.
* ls_instr_script command line tool: is now not limited to
  core language scripts, but can now also parse sampler format
  dependent instrument scripts, with the respective specific
  built-in script variables and functions.
* ScriptVM: Fixed runtime behavior of "and" and "or" binary
  script expressions, which also evaluated the right hand side
  of the expression even if the left hand side already failed
  the overall expression semantic to become true.
* Bumped version (1.0.0.svn46).

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

  ViewVC Help
Powered by ViewVC