/[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 2244 - (show annotations) (download) (as text)
Thu Aug 18 11:32:33 2011 UTC (12 years, 7 months ago) by iliev
File MIME type: text/x-c++hdr
File size: 16890 byte(s)
* sfz engine: use common pool of CC objects to minimize RAM usage

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

  ViewVC Help
Powered by ViewVC