/[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 2352 - (show annotations) (download) (as text)
Sun Jun 24 16:11:31 2012 UTC (11 years, 9 months ago) by persson
File MIME type: text/x-c++hdr
File size: 17298 byte(s)
* fixed crash when a channel received a program change while playing a
  note in a key group

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

  ViewVC Help
Powered by ViewVC