/[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 2612 - (show annotations) (download) (as text)
Tue Jun 10 13:32:16 2014 UTC (9 years, 10 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 19734 byte(s)
* Fixed crashes when exiting the sampler.
* Bumped version (1.0.0.svn47).

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

  ViewVC Help
Powered by ViewVC