/[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 2335 - (show annotations) (download) (as text)
Sat Mar 17 06:19:01 2012 UTC (8 years, 7 months ago) by persson
File MIME type: text/x-c++hdr
File size: 17113 byte(s)
* fixed compilation with gcc 4.7

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 ClearGroupEventLists();
226 }
227
228 // implementation of abstract methods derived from interface class 'InstrumentConsumer'
229
230 /**
231 * Will be called by the InstrumentResourceManager when the instrument
232 * we are currently using on this EngineChannel is going to be updated,
233 * so we can stop playback before that happens.
234 */
235 virtual void ResourceToBeUpdated(I* pResource, void*& pUpdateArg) {
236 dmsg(3,("EngineChannelBase: Received instrument update message.\n"));
237 if (pEngine) pEngine->DisableAndLock();
238 ResetInternal();
239 this->pInstrument = NULL;
240 }
241
242 /**
243 * Will be called by the InstrumentResourceManager when the instrument
244 * update process was completed, so we can continue with playback.
245 */
246 virtual void ResourceUpdated(I* pOldResource, I* pNewResource, void* pUpdateArg) {
247 this->pInstrument = pNewResource; //TODO: there are couple of engine parameters we should update here as well if the instrument was updated (see LoadInstrument())
248 if (pEngine) pEngine->Enable();
249 bStatusChanged = true; // status of engine has changed, so set notify flag
250 }
251
252 /**
253 * Will be called by the InstrumentResourceManager on progress changes
254 * while loading or realoading an instrument for this EngineChannel.
255 *
256 * @param fProgress - current progress as value between 0.0 and 1.0
257 */
258 virtual void OnResourceProgress(float fProgress) {
259 this->InstrumentStat = int(fProgress * 100.0f);
260 dmsg(7,("EngineChannelBase: progress %d%", InstrumentStat));
261 bStatusChanged = true; // status of engine has changed, so set notify flag
262 }
263
264 void RenderActiveVoices(uint Samples) {
265 RenderVoicesHandler handler(this, Samples);
266 this->ProcessActiveVoices(&handler);
267
268 SetVoiceCount(handler.VoiceCount);
269 SetDiskStreamCount(handler.StreamCount);
270 }
271
272 RTList<R*>* pRegionsInUse; ///< temporary pointer into the instrument change command, used by the audio thread
273 I* pInstrument;
274
275 template<class TV, class TRR, class TR, class TD, class TIM, class TI> friend class EngineBase;
276
277 protected:
278 EngineChannelBase() : InstrumentChangeCommandReader(InstrumentChangeCommand) {
279 pInstrument = NULL;
280
281 // reset the instrument change command struct (need to be done
282 // twice, as it is double buffered)
283 {
284 InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
285 cmd.pRegionsInUse = NULL;
286 cmd.pInstrument = NULL;
287 cmd.bChangeInstrument = false;
288 }
289 {
290 InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.SwitchConfig();
291 cmd.pRegionsInUse = NULL;
292 cmd.pInstrument = NULL;
293 cmd.bChangeInstrument = false;
294 }
295 }
296
297 virtual ~EngineChannelBase() { }
298
299 typedef typename RTList<V>::Iterator RTListVoiceIterator;
300
301 class RenderVoicesHandler : public MidiKeyboardManager<V>::VoiceHandlerBase {
302 public:
303 uint Samples;
304 uint VoiceCount;
305 uint StreamCount;
306 EngineChannelBase<V, R, I>* pChannel;
307
308 RenderVoicesHandler(EngineChannelBase<V, R, I>* channel, uint samples) :
309 pChannel(channel), Samples(samples), VoiceCount(0), StreamCount(0) { }
310
311 virtual void Process(RTListVoiceIterator& itVoice) {
312 // now render current voice
313 itVoice->Render(Samples);
314 if (itVoice->IsActive()) { // still active
315 if (!itVoice->Orphan) {
316 *(pChannel->pRegionsInUse->allocAppend()) = itVoice->GetRegion();
317 }
318 VoiceCount++;
319
320 if (itVoice->PlaybackState == Voice::playback_state_disk) {
321 if ((itVoice->DiskStreamRef).State != Stream::state_unused) StreamCount++;
322 }
323 } else { // voice reached end, is now inactive
324 itVoice->VoiceFreed();
325 pChannel->FreeVoice(itVoice); // remove voice from the list of active voices
326 }
327 }
328 };
329
330 typedef typename SynchronizedConfig<InstrumentChangeCmd<R, I> >::Reader SyncConfInstrChangeCmdReader;
331
332 SynchronizedConfig<InstrumentChangeCmd<R, I> > InstrumentChangeCommand;
333 SyncConfInstrChangeCmdReader InstrumentChangeCommandReader;
334
335 /** This method is not thread safe! */
336 virtual void ResetInternal() {
337 AbstractEngineChannel::ResetInternal();
338
339 MidiKeyboardManager<V>::Reset();
340 }
341
342 virtual void ResetControllers() {
343 AbstractEngineChannel::ResetControllers();
344
345 MidiKeyboardManager<V>::SustainPedal = false;
346 MidiKeyboardManager<V>::SostenutoPedal = false;
347 }
348
349 /**
350 * Changes the instrument for an engine channel.
351 *
352 * @param pInstrument - new instrument
353 * @returns the resulting instrument change command after the
354 * command switch, containing the old instrument and
355 * the dimregions it is using
356 */
357 InstrumentChangeCmd<R, I>& ChangeInstrument(I* pInstrument) {
358 InstrumentChangeCmd<R, I>& cmd = InstrumentChangeCommand.GetConfigForUpdate();
359 cmd.pInstrument = pInstrument;
360 cmd.bChangeInstrument = true;
361
362 return InstrumentChangeCommand.SwitchConfig();
363 }
364
365 virtual void ProcessKeySwitchChange(int key) = 0;
366 };
367
368 } // namespace LinuxSampler
369
370 #endif /* __LS_ENGINECHANNELBASE_H__ */

  ViewVC Help
Powered by ViewVC