/[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 2427 - (show annotations) (download) (as text)
Sat Mar 2 07:03:04 2013 UTC (11 years, 1 month ago) by persson
File MIME type: text/x-c++hdr
File size: 17332 byte(s)
* code refactoring: added a lock guard class for exception safe mutex
  handling and used it everywhere appropriate

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

  ViewVC Help
Powered by ViewVC