/[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 2837 - (show annotations) (download) (as text)
Sun Aug 23 06:14:00 2015 UTC (8 years, 7 months ago) by persson
File MIME type: text/x-c++hdr
File size: 20150 byte(s)
* fixed printf type errors (mostly in debug messages)


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-2015 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. This is never NULL, it is always a valid InstrumentScript pointer. Use InstrumentScript::bHasValidScript whether it reflects a valid instrument script to be executed.
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() :
293 MidiKeyboardManager<V>(this),
294 InstrumentChangeCommandReader(InstrumentChangeCommand)
295 {
296 pInstrument = NULL;
297
298 // reset the instrument change command struct (need to be done
299 // twice, as it is double buffered)
300 {
301 InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
302 cmd.pRegionsInUse = NULL;
303 cmd.pInstrument = NULL;
304 cmd.pScript = new InstrumentScript(this);
305 cmd.bChangeInstrument = false;
306 }
307 {
308 InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.SwitchConfig();
309 cmd.pRegionsInUse = NULL;
310 cmd.pInstrument = NULL;
311 cmd.pScript = new InstrumentScript(this);
312 cmd.bChangeInstrument = false;
313 }
314 }
315
316 virtual ~EngineChannelBase() {
317 InstrumentScript* previous = NULL; // prevent double free
318 {
319 InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
320 if (cmd.pScript) {
321 previous = cmd.pScript;
322 delete cmd.pScript;
323 cmd.pScript = NULL;
324 }
325 }
326 {
327 InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.SwitchConfig();
328 if (cmd.pScript) {
329 if (previous != cmd.pScript)
330 delete cmd.pScript;
331 cmd.pScript = NULL;
332 }
333 }
334 }
335
336 typedef typename RTList<V>::Iterator RTListVoiceIterator;
337
338 class RenderVoicesHandler : public MidiKeyboardManager<V>::VoiceHandlerBase {
339 public:
340 uint Samples;
341 uint VoiceCount;
342 uint StreamCount;
343 EngineChannelBase<V, R, I>* pChannel;
344
345 RenderVoicesHandler(EngineChannelBase<V, R, I>* channel, uint samples) :
346 pChannel(channel), Samples(samples), VoiceCount(0), StreamCount(0) { }
347
348 virtual void Process(RTListVoiceIterator& itVoice) {
349 // now render current voice
350 itVoice->Render(Samples);
351 if (itVoice->IsActive()) { // still active
352 if (!itVoice->Orphan) {
353 *(pChannel->pRegionsInUse->allocAppend()) = itVoice->GetRegion();
354 }
355 VoiceCount++;
356
357 if (itVoice->PlaybackState == Voice::playback_state_disk) {
358 if ((itVoice->DiskStreamRef).State != Stream::state_unused) StreamCount++;
359 }
360 } else { // voice reached end, is now inactive
361 itVoice->VoiceFreed();
362 pChannel->FreeVoice(itVoice); // remove voice from the list of active voices
363 }
364 }
365 };
366
367 typedef typename SynchronizedConfig<InstrumentChangeCmd<R, I> >::Reader SyncConfInstrChangeCmdReader;
368
369 SynchronizedConfig<InstrumentChangeCmd<R, I> > InstrumentChangeCommand;
370 SyncConfInstrChangeCmdReader InstrumentChangeCommandReader;
371
372 /** This method is not thread safe! */
373 virtual void ResetInternal() {
374 AbstractEngineChannel::ResetInternal();
375
376 MidiKeyboardManager<V>::Reset();
377 }
378
379 virtual void ResetControllers() {
380 AbstractEngineChannel::ResetControllers();
381
382 MidiKeyboardManager<V>::SustainPedal = false;
383 MidiKeyboardManager<V>::SostenutoPedal = false;
384 }
385
386 /**
387 * Unload the currently used and loaded real-time instrument script.
388 * The source code of the script is retained, so that it can still
389 * be reloaded.
390 */
391 void UnloadScriptInUse() {
392 {
393 InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
394 if (cmd.pScript) cmd.pScript->unload();
395 }
396 {
397 InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.SwitchConfig();
398 if (cmd.pScript) cmd.pScript->unload();
399 }
400 InstrumentChangeCommand.SwitchConfig(); // switch back to original one
401 }
402
403 /**
404 * Load real-time instrument script and all its resources required
405 * for the upcoming instrument change.
406 *
407 * @param text - source code of script
408 */
409 void LoadInstrumentScript(const String& text) {
410 InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
411 // load the new script
412 cmd.pScript->load(text);
413 }
414
415 /**
416 * Changes the instrument for an engine channel.
417 *
418 * @param pInstrument - new instrument
419 * @returns the resulting instrument change command after the
420 * command switch, containing the old instrument and
421 * the dimregions it is using
422 */
423 InstrumentChangeCmd<R, I>& ChangeInstrument(I* pInstrument) {
424 InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
425 cmd.pInstrument = pInstrument;
426 cmd.bChangeInstrument = true;
427
428 return InstrumentChangeCommand.SwitchConfig();
429 }
430
431 virtual void ProcessKeySwitchChange(int key) = 0;
432 };
433
434 } // namespace LinuxSampler
435
436 #endif /* __LS_ENGINECHANNELBASE_H__ */

  ViewVC Help
Powered by ViewVC