/[svn]/linuxsampler/trunk/src/engines/AbstractEngineChannel.cpp
ViewVC logotype

Annotation of /linuxsampler/trunk/src/engines/AbstractEngineChannel.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2012 - (hide annotations) (download)
Fri Oct 23 17:53:17 2009 UTC (14 years, 5 months ago) by iliev
File size: 28389 byte(s)
* Refactoring: moved the independent code from
  the Gigasampler format engine to base classes
* SFZ format engine: experimental code (not usable yet)
* SoundFont format engine: experimental code (not usable yet)
* Fixed crash which may occur when MIDI key + transpose is out of range

1 iliev 2012 /***************************************************************************
2     * *
3     * LinuxSampler - modular, streaming capable sampler *
4     * *
5     * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck *
6     * Copyright (C) 2005-2009 Christian Schoenebeck *
7     * Copyright (C) 2009 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     #include "AbstractEngineChannel.h"
26     #include "../common/global_private.h"
27     #include "../Sampler.h"
28    
29     namespace LinuxSampler {
30    
31     AbstractEngineChannel::AbstractEngineChannel() :
32     virtualMidiDevicesReader_AudioThread(virtualMidiDevices),
33     virtualMidiDevicesReader_MidiThread(virtualMidiDevices)
34     {
35     pEngine = NULL;
36     pEvents = NULL; // we allocate when we retrieve the right Engine object
37     pEventQueue = new RingBuffer<Event,false>(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0);
38     InstrumentIdx = -1;
39     InstrumentStat = -1;
40     pChannelLeft = NULL;
41     pChannelRight = NULL;
42     AudioDeviceChannelLeft = -1;
43     AudioDeviceChannelRight = -1;
44     pMidiInputPort = NULL;
45     midiChannel = midi_chan_all;
46     ResetControllers();
47     PortamentoMode = false;
48     PortamentoTime = CONFIG_PORTAMENTO_TIME_DEFAULT;
49     }
50    
51     AbstractEngineChannel::~AbstractEngineChannel() {
52    
53     if (pEventQueue) delete pEventQueue;
54     RemoveAllFxSends();
55     }
56    
57     Engine* AbstractEngineChannel::GetEngine() {
58     return pEngine;
59     }
60    
61     uint AbstractEngineChannel::Channels() {
62     return 2;
63     }
64    
65     /**
66     * More or less a workaround to set the instrument name, index and load
67     * status variable to zero percent immediately, that is without blocking
68     * the calling thread. It might be used in future for other preparations
69     * as well though.
70     *
71     * @param FileName - file name of the instrument file
72     * @param Instrument - index of the instrument in the file
73     * @see LoadInstrument()
74     */
75     void AbstractEngineChannel::PrepareLoadInstrument(const char* FileName, uint Instrument) {
76     InstrumentFile = FileName;
77     InstrumentIdx = Instrument;
78     InstrumentStat = 0;
79     }
80    
81     String AbstractEngineChannel::InstrumentFileName() {
82     return InstrumentFile;
83     }
84    
85     String AbstractEngineChannel::InstrumentName() {
86     return InstrumentIdxName;
87     }
88    
89     int AbstractEngineChannel::InstrumentIndex() {
90     return InstrumentIdx;
91     }
92    
93     int AbstractEngineChannel::InstrumentStatus() {
94     return InstrumentStat;
95     }
96    
97     String AbstractEngineChannel::EngineName() {
98     return AbstractEngine::GetFormatString(GetEngineFormat());
99     }
100    
101     void AbstractEngineChannel::Reset() {
102     if (pEngine) pEngine->DisableAndLock();
103     ResetInternal();
104     ResetControllers();
105     if (pEngine) {
106     pEngine->Enable();
107     pEngine->Reset();
108     }
109     }
110    
111     void AbstractEngineChannel::ResetControllers() {
112     Pitch = 0;
113     GlobalVolume = 1.0f;
114     MidiVolume = 1.0;
115     GlobalPanLeft = 1.0f;
116     GlobalPanRight = 1.0f;
117     iLastPanRequest = 64;
118     GlobalTranspose = 0;
119     // set all MIDI controller values to zero
120     memset(ControllerTable, 0x00, 129);
121     // reset all FX Send levels
122     for (
123     std::vector<FxSend*>::iterator iter = fxSends.begin();
124     iter != fxSends.end(); iter++
125     ) {
126     (*iter)->Reset();
127     }
128     }
129    
130     /**
131     * This method is not thread safe!
132     */
133     void AbstractEngineChannel::ResetInternal() {
134     CurrentKeyDimension = 0;
135     PortamentoPos = -1.0f; // no portamento active yet
136    
137     // delete all input events
138     pEventQueue->init();
139    
140     if (pEngine) pEngine->ResetInternal();
141    
142     // status of engine channel has changed, so set notify flag
143     bStatusChanged = true;
144     }
145    
146     /**
147     * Implementation of virtual method from abstract EngineChannel interface.
148     * This method will periodically be polled (e.g. by the LSCP server) to
149     * check if some engine channel parameter has changed since the last
150     * StatusChanged() call.
151     *
152     * This method can also be used to mark the engine channel as changed
153     * from outside, e.g. by a MIDI input device. The optional argument
154     * \a nNewStatus can be used for this.
155     *
156     * TODO: This "poll method" is just a lazy solution and might be
157     * replaced in future.
158     * @param bNewStatus - (optional, default: false) sets the new status flag
159     * @returns true if engine channel status has changed since last
160     * StatusChanged() call
161     */
162     bool AbstractEngineChannel::StatusChanged(bool bNewStatus) {
163     bool b = bStatusChanged;
164     bStatusChanged = bNewStatus;
165     return b;
166     }
167    
168     float AbstractEngineChannel::Volume() {
169     return GlobalVolume;
170     }
171    
172     void AbstractEngineChannel::Volume(float f) {
173     GlobalVolume = f;
174     bStatusChanged = true; // status of engine channel has changed, so set notify flag
175     }
176    
177     float AbstractEngineChannel::Pan() {
178     return float(iLastPanRequest - 64) / 64.0f;
179     }
180    
181     void AbstractEngineChannel::Pan(float f) {
182     int iMidiPan = int(f * 64.0f) + 64;
183     if (iMidiPan > 127) iMidiPan = 127;
184     else if (iMidiPan < 0) iMidiPan = 0;
185     GlobalPanLeft = AbstractEngine::PanCurve[128 - iMidiPan];
186     GlobalPanRight = AbstractEngine::PanCurve[iMidiPan];
187     iLastPanRequest = iMidiPan;
188     }
189    
190     AudioOutputDevice* AbstractEngineChannel::GetAudioOutputDevice() {
191     return (pEngine) ? pEngine->pAudioOutputDevice : NULL;
192     }
193    
194     void AbstractEngineChannel::SetOutputChannel(uint EngineAudioChannel, uint AudioDeviceChannel) {
195     if (!pEngine || !pEngine->pAudioOutputDevice) throw AudioOutputException("No audio output device connected yet.");
196    
197     AudioChannel* pChannel = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannel);
198     if (!pChannel) throw AudioOutputException("Invalid audio output device channel " + ToString(AudioDeviceChannel));
199     switch (EngineAudioChannel) {
200     case 0: // left output channel
201     if (fxSends.empty()) pChannelLeft = pChannel;
202     AudioDeviceChannelLeft = AudioDeviceChannel;
203     break;
204     case 1: // right output channel
205     if (fxSends.empty()) pChannelRight = pChannel;
206     AudioDeviceChannelRight = AudioDeviceChannel;
207     break;
208     default:
209     throw AudioOutputException("Invalid engine audio channel " + ToString(EngineAudioChannel));
210     }
211    
212     bStatusChanged = true;
213     }
214    
215     int AbstractEngineChannel::OutputChannel(uint EngineAudioChannel) {
216     switch (EngineAudioChannel) {
217     case 0: // left channel
218     return AudioDeviceChannelLeft;
219     case 1: // right channel
220     return AudioDeviceChannelRight;
221     default:
222     throw AudioOutputException("Invalid engine audio channel " + ToString(EngineAudioChannel));
223     }
224     }
225    
226     void AbstractEngineChannel::Connect(MidiInputPort* pMidiPort, midi_chan_t MidiChannel) {
227     if (!pMidiPort || pMidiPort == this->pMidiInputPort) return;
228     DisconnectMidiInputPort();
229     this->pMidiInputPort = pMidiPort;
230     this->midiChannel = MidiChannel;
231     pMidiPort->Connect(this, MidiChannel);
232     }
233    
234     void AbstractEngineChannel::DisconnectMidiInputPort() {
235     MidiInputPort* pOldPort = this->pMidiInputPort;
236     this->pMidiInputPort = NULL;
237     if (pOldPort) pOldPort->Disconnect(this);
238     }
239    
240     MidiInputPort* AbstractEngineChannel::GetMidiInputPort() {
241     return pMidiInputPort;
242     }
243    
244     midi_chan_t AbstractEngineChannel::MidiChannel() {
245     return midiChannel;
246     }
247    
248     void AbstractEngineChannel::Connect(VirtualMidiDevice* pDevice) {
249     // double buffer ... double work ...
250     {
251     ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.GetConfigForUpdate();
252     devices.add(pDevice);
253     }
254     {
255     ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.SwitchConfig();
256     devices.add(pDevice);
257     }
258     }
259    
260     void AbstractEngineChannel::Disconnect(VirtualMidiDevice* pDevice) {
261     // double buffer ... double work ...
262     {
263     ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.GetConfigForUpdate();
264     devices.remove(pDevice);
265     }
266     {
267     ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.SwitchConfig();
268     devices.remove(pDevice);
269     }
270     }
271    
272     /**
273     * Will be called by the MIDIIn Thread to let the audio thread trigger a new
274     * voice for the given key. This method is meant for real time rendering,
275     * that is an event will immediately be created with the current system
276     * time as time stamp.
277     *
278     * @param Key - MIDI key number of the triggered key
279     * @param Velocity - MIDI velocity value of the triggered key
280     */
281     void AbstractEngineChannel::SendNoteOn(uint8_t Key, uint8_t Velocity) {
282     if (pEngine) {
283     Event event = pEngine->pEventGenerator->CreateEvent();
284     event.Type = Event::type_note_on;
285     event.Param.Note.Key = Key;
286     event.Param.Note.Velocity = Velocity;
287     event.pEngineChannel = this;
288     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
289     else dmsg(1,("EngineChannel: Input event queue full!"));
290     // inform connected virtual MIDI devices if any ...
291     // (e.g. virtual MIDI keyboard in instrument editor(s))
292     ArrayList<VirtualMidiDevice*>& devices =
293     const_cast<ArrayList<VirtualMidiDevice*>&>(
294     virtualMidiDevicesReader_MidiThread.Lock()
295     );
296     for (int i = 0; i < devices.size(); i++) {
297     devices[i]->SendNoteOnToDevice(Key, Velocity);
298     }
299     virtualMidiDevicesReader_MidiThread.Unlock();
300     }
301     }
302    
303     /**
304     * Will be called by the MIDIIn Thread to let the audio thread trigger a new
305     * voice for the given key. This method is meant for offline rendering
306     * and / or for cases where the exact position of the event in the current
307     * audio fragment is already known.
308     *
309     * @param Key - MIDI key number of the triggered key
310     * @param Velocity - MIDI velocity value of the triggered key
311     * @param FragmentPos - sample point position in the current audio
312     * fragment to which this event belongs to
313     */
314     void AbstractEngineChannel::SendNoteOn(uint8_t Key, uint8_t Velocity, int32_t FragmentPos) {
315     if (FragmentPos < 0) {
316     dmsg(1,("EngineChannel::SendNoteOn(): negative FragmentPos! Seems MIDI driver is buggy!"));
317     }
318     else if (pEngine) {
319     Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
320     event.Type = Event::type_note_on;
321     event.Param.Note.Key = Key;
322     event.Param.Note.Velocity = Velocity;
323     event.pEngineChannel = this;
324     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
325     else dmsg(1,("EngineChannel: Input event queue full!"));
326     // inform connected virtual MIDI devices if any ...
327     // (e.g. virtual MIDI keyboard in instrument editor(s))
328     ArrayList<VirtualMidiDevice*>& devices =
329     const_cast<ArrayList<VirtualMidiDevice*>&>(
330     virtualMidiDevicesReader_MidiThread.Lock()
331     );
332     for (int i = 0; i < devices.size(); i++) {
333     devices[i]->SendNoteOnToDevice(Key, Velocity);
334     }
335     virtualMidiDevicesReader_MidiThread.Unlock();
336     }
337     }
338    
339     /**
340     * Will be called by the MIDIIn Thread to signal the audio thread to release
341     * voice(s) on the given key. This method is meant for real time rendering,
342     * that is an event will immediately be created with the current system
343     * time as time stamp.
344     *
345     * @param Key - MIDI key number of the released key
346     * @param Velocity - MIDI release velocity value of the released key
347     */
348     void AbstractEngineChannel::SendNoteOff(uint8_t Key, uint8_t Velocity) {
349     if (pEngine) {
350     Event event = pEngine->pEventGenerator->CreateEvent();
351     event.Type = Event::type_note_off;
352     event.Param.Note.Key = Key;
353     event.Param.Note.Velocity = Velocity;
354     event.pEngineChannel = this;
355     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
356     else dmsg(1,("EngineChannel: Input event queue full!"));
357     // inform connected virtual MIDI devices if any ...
358     // (e.g. virtual MIDI keyboard in instrument editor(s))
359     ArrayList<VirtualMidiDevice*>& devices =
360     const_cast<ArrayList<VirtualMidiDevice*>&>(
361     virtualMidiDevicesReader_MidiThread.Lock()
362     );
363     for (int i = 0; i < devices.size(); i++) {
364     devices[i]->SendNoteOffToDevice(Key, Velocity);
365     }
366     virtualMidiDevicesReader_MidiThread.Unlock();
367     }
368     }
369    
370     /**
371     * Will be called by the MIDIIn Thread to signal the audio thread to release
372     * voice(s) on the given key. This method is meant for offline rendering
373     * and / or for cases where the exact position of the event in the current
374     * audio fragment is already known.
375     *
376     * @param Key - MIDI key number of the released key
377     * @param Velocity - MIDI release velocity value of the released key
378     * @param FragmentPos - sample point position in the current audio
379     * fragment to which this event belongs to
380     */
381     void AbstractEngineChannel::SendNoteOff(uint8_t Key, uint8_t Velocity, int32_t FragmentPos) {
382     if (FragmentPos < 0) {
383     dmsg(1,("EngineChannel::SendNoteOff(): negative FragmentPos! Seems MIDI driver is buggy!"));
384     }
385     else if (pEngine) {
386     Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
387     event.Type = Event::type_note_off;
388     event.Param.Note.Key = Key;
389     event.Param.Note.Velocity = Velocity;
390     event.pEngineChannel = this;
391     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
392     else dmsg(1,("EngineChannel: Input event queue full!"));
393     // inform connected virtual MIDI devices if any ...
394     // (e.g. virtual MIDI keyboard in instrument editor(s))
395     ArrayList<VirtualMidiDevice*>& devices =
396     const_cast<ArrayList<VirtualMidiDevice*>&>(
397     virtualMidiDevicesReader_MidiThread.Lock()
398     );
399     for (int i = 0; i < devices.size(); i++) {
400     devices[i]->SendNoteOffToDevice(Key, Velocity);
401     }
402     virtualMidiDevicesReader_MidiThread.Unlock();
403     }
404     }
405    
406     /**
407     * Will be called by the MIDIIn Thread to signal the audio thread to change
408     * the pitch value for all voices. This method is meant for real time
409     * rendering, that is an event will immediately be created with the
410     * current system time as time stamp.
411     *
412     * @param Pitch - MIDI pitch value (-8192 ... +8191)
413     */
414     void AbstractEngineChannel::SendPitchbend(int Pitch) {
415     if (pEngine) {
416     Event event = pEngine->pEventGenerator->CreateEvent();
417     event.Type = Event::type_pitchbend;
418     event.Param.Pitch.Pitch = Pitch;
419     event.pEngineChannel = this;
420     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
421     else dmsg(1,("EngineChannel: Input event queue full!"));
422     }
423     }
424    
425     /**
426     * Will be called by the MIDIIn Thread to signal the audio thread to change
427     * the pitch value for all voices. This method is meant for offline
428     * rendering and / or for cases where the exact position of the event in
429     * the current audio fragment is already known.
430     *
431     * @param Pitch - MIDI pitch value (-8192 ... +8191)
432     * @param FragmentPos - sample point position in the current audio
433     * fragment to which this event belongs to
434     */
435     void AbstractEngineChannel::SendPitchbend(int Pitch, int32_t FragmentPos) {
436     if (FragmentPos < 0) {
437     dmsg(1,("AbstractEngineChannel::SendPitchBend(): negative FragmentPos! Seems MIDI driver is buggy!"));
438     }
439     else if (pEngine) {
440     Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
441     event.Type = Event::type_pitchbend;
442     event.Param.Pitch.Pitch = Pitch;
443     event.pEngineChannel = this;
444     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
445     else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
446     }
447     }
448    
449     /**
450     * Will be called by the MIDIIn Thread to signal the audio thread that a
451     * continuous controller value has changed. This method is meant for real
452     * time rendering, that is an event will immediately be created with the
453     * current system time as time stamp.
454     *
455     * @param Controller - MIDI controller number of the occured control change
456     * @param Value - value of the control change
457     */
458     void AbstractEngineChannel::SendControlChange(uint8_t Controller, uint8_t Value) {
459     if (pEngine) {
460     Event event = pEngine->pEventGenerator->CreateEvent();
461     event.Type = Event::type_control_change;
462     event.Param.CC.Controller = Controller;
463     event.Param.CC.Value = Value;
464     event.pEngineChannel = this;
465     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
466     else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
467     }
468     }
469    
470     /**
471     * Will be called by the MIDIIn Thread to signal the audio thread that a
472     * continuous controller value has changed. This method is meant for
473     * offline rendering and / or for cases where the exact position of the
474     * event in the current audio fragment is already known.
475     *
476     * @param Controller - MIDI controller number of the occured control change
477     * @param Value - value of the control change
478     * @param FragmentPos - sample point position in the current audio
479     * fragment to which this event belongs to
480     */
481     void AbstractEngineChannel::SendControlChange(uint8_t Controller, uint8_t Value, int32_t FragmentPos) {
482     if (FragmentPos < 0) {
483     dmsg(1,("AbstractEngineChannel::SendControlChange(): negative FragmentPos! Seems MIDI driver is buggy!"));
484     }
485     else if (pEngine) {
486     Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
487     event.Type = Event::type_control_change;
488     event.Param.CC.Controller = Controller;
489     event.Param.CC.Value = Value;
490     event.pEngineChannel = this;
491     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
492     else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
493     }
494     }
495    
496     /**
497     * Copy all events from the engine channel's input event queue buffer to
498     * the internal event list. This will be done at the beginning of each
499     * audio cycle (that is each RenderAudio() call) to distinguish all
500     * events which have to be processed in the current audio cycle. Each
501     * EngineChannel has it's own input event queue for the common channel
502     * specific events (like NoteOn, NoteOff and ControlChange events).
503     * Beside that, the engine also has a input event queue for global
504     * events (usually SysEx messages).
505     *
506     * @param Samples - number of sample points to be processed in the
507     * current audio cycle
508     */
509     void AbstractEngineChannel::ImportEvents(uint Samples) {
510     // import events from pure software MIDI "devices"
511     // (e.g. virtual keyboard in instrument editor)
512     {
513     const int FragmentPos = 0; // randomly chosen, we don't care about jitter for virtual MIDI devices
514     Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
515     VirtualMidiDevice::event_t devEvent; // the event format we get from the virtual MIDI device
516     // as we're going to (carefully) write some status to the
517     // synchronized struct, we cast away the const
518     ArrayList<VirtualMidiDevice*>& devices =
519     const_cast<ArrayList<VirtualMidiDevice*>&>(virtualMidiDevicesReader_AudioThread.Lock());
520     // iterate through all virtual MIDI devices
521     for (int i = 0; i < devices.size(); i++) {
522     VirtualMidiDevice* pDev = devices[i];
523     // I think we can simply flush the whole FIFO(s), the user shouldn't be so fast ;-)
524     while (pDev->GetMidiEventFromDevice(devEvent)) {
525     event.Type =
526     (devEvent.Type == VirtualMidiDevice::EVENT_TYPE_NOTEON) ?
527     Event::type_note_on : Event::type_note_off;
528     event.Param.Note.Key = devEvent.Key;
529     event.Param.Note.Velocity = devEvent.Velocity;
530     event.pEngineChannel = this;
531     // copy event to internal event list
532     if (pEvents->poolIsEmpty()) {
533     dmsg(1,("Event pool emtpy!\n"));
534     goto exitVirtualDevicesLoop;
535     }
536     *pEvents->allocAppend() = event;
537     }
538     }
539     }
540     exitVirtualDevicesLoop:
541     virtualMidiDevicesReader_AudioThread.Unlock();
542    
543     // import events from the regular MIDI devices
544     RingBuffer<Event,false>::NonVolatileReader eventQueueReader = pEventQueue->get_non_volatile_reader();
545     Event* pEvent;
546     while (true) {
547     // get next event from input event queue
548     if (!(pEvent = eventQueueReader.pop())) break;
549     // if younger event reached, ignore that and all subsequent ones for now
550     if (pEvent->FragmentPos() >= Samples) {
551     eventQueueReader--;
552     dmsg(2,("Younger Event, pos=%d ,Samples=%d!\n",pEvent->FragmentPos(),Samples));
553     pEvent->ResetFragmentPos();
554     break;
555     }
556     // copy event to internal event list
557     if (pEvents->poolIsEmpty()) {
558     dmsg(1,("Event pool emtpy!\n"));
559     break;
560     }
561     *pEvents->allocAppend() = *pEvent;
562     }
563     eventQueueReader.free(); // free all copied events from input queue
564     }
565    
566     FxSend* AbstractEngineChannel::AddFxSend(uint8_t MidiCtrl, String Name) throw (Exception) {
567     if (pEngine) pEngine->DisableAndLock();
568     FxSend* pFxSend = new FxSend(this, MidiCtrl, Name);
569     if (fxSends.empty()) {
570     if (pEngine && pEngine->pAudioOutputDevice) {
571     AudioOutputDevice* pDevice = pEngine->pAudioOutputDevice;
572     // create local render buffers
573     pChannelLeft = new AudioChannel(0, pDevice->MaxSamplesPerCycle());
574     pChannelRight = new AudioChannel(1, pDevice->MaxSamplesPerCycle());
575     } else {
576     // postpone local render buffer creation until audio device is assigned
577     pChannelLeft = NULL;
578     pChannelRight = NULL;
579     }
580     }
581     fxSends.push_back(pFxSend);
582     if (pEngine) pEngine->Enable();
583     fireFxSendCountChanged(GetSamplerChannel()->Index(), GetFxSendCount());
584    
585     return pFxSend;
586     }
587    
588     FxSend* AbstractEngineChannel::GetFxSend(uint FxSendIndex) {
589     return (FxSendIndex < fxSends.size()) ? fxSends[FxSendIndex] : NULL;
590     }
591    
592     uint AbstractEngineChannel::GetFxSendCount() {
593     return fxSends.size();
594     }
595    
596     void AbstractEngineChannel::RemoveFxSend(FxSend* pFxSend) {
597     if (pEngine) pEngine->DisableAndLock();
598     for (
599     std::vector<FxSend*>::iterator iter = fxSends.begin();
600     iter != fxSends.end(); iter++
601     ) {
602     if (*iter == pFxSend) {
603     delete pFxSend;
604     fxSends.erase(iter);
605     if (fxSends.empty()) {
606     // destroy local render buffers
607     if (pChannelLeft) delete pChannelLeft;
608     if (pChannelRight) delete pChannelRight;
609     // fallback to render directly into AudioOutputDevice's buffers
610     if (pEngine && pEngine->pAudioOutputDevice) {
611     pChannelLeft = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelLeft);
612     pChannelRight = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelRight);
613     } else { // we update the pointers later
614     pChannelLeft = NULL;
615     pChannelRight = NULL;
616     }
617     }
618     break;
619     }
620     }
621     if (pEngine) pEngine->Enable();
622     fireFxSendCountChanged(GetSamplerChannel()->Index(), GetFxSendCount());
623     }
624    
625     void AbstractEngineChannel::RemoveAllFxSends() {
626     if (pEngine) pEngine->DisableAndLock();
627     if (!fxSends.empty()) { // free local render buffers
628     if (pChannelLeft) {
629     delete pChannelLeft;
630     if (pEngine && pEngine->pAudioOutputDevice) {
631     // fallback to render directly to the AudioOutputDevice's buffer
632     pChannelLeft = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelLeft);
633     } else pChannelLeft = NULL;
634     }
635     if (pChannelRight) {
636     delete pChannelRight;
637     if (pEngine && pEngine->pAudioOutputDevice) {
638     // fallback to render directly to the AudioOutputDevice's buffer
639     pChannelRight = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelRight);
640     } else pChannelRight = NULL;
641     }
642     }
643     for (int i = 0; i < fxSends.size(); i++) delete fxSends[i];
644     fxSends.clear();
645     if (pEngine) pEngine->Enable();
646     }
647    
648     } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC