/[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 2611 - (show annotations) (download) (as text)
Mon Jun 9 19:20:37 2014 UTC (9 years, 9 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 18769 byte(s)
* Fixed crash when loading an instrument script.
* Fixed "init" script handler only to be executed once:
  when the script was loaded.
* Fixed aftertouch script event which always had value zero
  and controller number was set to aftertouch value instead.
* gig Engine: Fixed handling of "smartmidi" dimension, which
  was recognized as "unknown" dimension.
* Fixed script function gig_set_dim_zone(): was accessing
  wrong event.
* ls_instr_script command line tool: is now not limited to
  core language scripts, but can now also parse sampler format
  dependent instrument scripts, with the respective specific
  built-in script variables and functions.
* ScriptVM: Fixed runtime behavior of "and" and "or" binary
  script expressions, which also evaluated the right hand side
  of the expression even if the left hand side already failed
  the overall expression semantic to become true.
* Bumped version (1.0.0.svn46).

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

  ViewVC Help
Powered by ViewVC