/[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 2114 - (show annotations) (download) (as text)
Tue Aug 10 12:05:19 2010 UTC (13 years, 7 months ago) by persson
File MIME type: text/x-c++hdr
File size: 16561 byte(s)
* sfz engine: improved support for exclusive groups (group, off_by and
  off_mode)
* minor valgrind fixes

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

  ViewVC Help
Powered by ViewVC