/[svn]/linuxsampler/trunk/src/engines/EngineChannelBase.h
ViewVC logotype

Contents of /linuxsampler/trunk/src/engines/EngineChannelBase.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2618 - (show annotations) (download) (as text)
Wed Jun 11 11:39:44 2014 UTC (9 years, 10 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 19954 byte(s)
* Fixed execution of "init" instrument script handler.
* Fixed further crashes.
* Bumped version (1.0.0.svn50).

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

  ViewVC Help
Powered by ViewVC