/[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 2879 - (hide annotations) (download) (as text)
Tue Apr 19 14:07:53 2016 UTC (8 years ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 23409 byte(s)
* All engines: Active voices are now internally grouped to "Note" objects,
  instead of being directly assigned to a keyboard key. This allows more
  fine graded processing of voices, which is i.e. required for certain
  instrument script features.
* Built-in script function "play_note()": Added support for passing
  special value -1 for "duration-us" argument, which will cause the
  triggered note to be released once the original note was released.
* Bumped version (2.0.0.svn3).

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

  ViewVC Help
Powered by ViewVC