/[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 2382 - (hide annotations) (download)
Sun Dec 2 16:30:42 2012 UTC (11 years, 3 months ago) by persson
File size: 32540 byte(s)
* all engines: add pan CC value to instrument pan parameter before
  applying panning, instead of using two separate pan functions in
  series (#182)
* sfz parser: allow -200 to 200 for pan_oncc opcode (#182)
* gig engine: handle special case when pan parameter in gig file has
  max or min value
* CoreMIDI: fixed memory deallocation error

1 iliev 2012 /***************************************************************************
2     * *
3     * LinuxSampler - modular, streaming capable sampler *
4     * *
5     * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck *
6 persson 2114 * Copyright (C) 2005-2008 Christian Schoenebeck *
7 persson 2317 * Copyright (C) 2009-2012 Christian Schoenebeck and Grigor Iliev *
8 iliev 2012 * *
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 persson 2114 delete pEventQueue;
53     DeleteGroupEventLists();
54 iliev 2012 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     iLastPanRequest = 64;
116     GlobalTranspose = 0;
117     // set all MIDI controller values to zero
118     memset(ControllerTable, 0x00, 129);
119     // reset all FX Send levels
120     for (
121     std::vector<FxSend*>::iterator iter = fxSends.begin();
122     iter != fxSends.end(); iter++
123     ) {
124     (*iter)->Reset();
125     }
126     }
127    
128     /**
129     * This method is not thread safe!
130     */
131     void AbstractEngineChannel::ResetInternal() {
132     CurrentKeyDimension = 0;
133     PortamentoPos = -1.0f; // no portamento active yet
134    
135     // delete all input events
136     pEventQueue->init();
137    
138     if (pEngine) pEngine->ResetInternal();
139    
140     // status of engine channel has changed, so set notify flag
141     bStatusChanged = true;
142     }
143    
144     /**
145     * Implementation of virtual method from abstract EngineChannel interface.
146     * This method will periodically be polled (e.g. by the LSCP server) to
147     * check if some engine channel parameter has changed since the last
148     * StatusChanged() call.
149     *
150     * This method can also be used to mark the engine channel as changed
151     * from outside, e.g. by a MIDI input device. The optional argument
152     * \a nNewStatus can be used for this.
153     *
154     * TODO: This "poll method" is just a lazy solution and might be
155     * replaced in future.
156     * @param bNewStatus - (optional, default: false) sets the new status flag
157     * @returns true if engine channel status has changed since last
158     * StatusChanged() call
159     */
160     bool AbstractEngineChannel::StatusChanged(bool bNewStatus) {
161     bool b = bStatusChanged;
162     bStatusChanged = bNewStatus;
163     return b;
164     }
165    
166     float AbstractEngineChannel::Volume() {
167     return GlobalVolume;
168     }
169    
170     void AbstractEngineChannel::Volume(float f) {
171     GlobalVolume = f;
172     bStatusChanged = true; // status of engine channel has changed, so set notify flag
173     }
174    
175     float AbstractEngineChannel::Pan() {
176     return float(iLastPanRequest - 64) / 64.0f;
177     }
178    
179     void AbstractEngineChannel::Pan(float f) {
180     int iMidiPan = int(f * 64.0f) + 64;
181     if (iMidiPan > 127) iMidiPan = 127;
182     else if (iMidiPan < 0) iMidiPan = 0;
183     iLastPanRequest = iMidiPan;
184     }
185    
186     AudioOutputDevice* AbstractEngineChannel::GetAudioOutputDevice() {
187     return (pEngine) ? pEngine->pAudioOutputDevice : NULL;
188     }
189    
190 persson 2326 /**
191     * Gets thread safe access to the currently connected audio output
192     * device from other threads than the lscp thread.
193     */
194     AudioOutputDevice* AbstractEngineChannel::GetAudioOutputDeviceSafe() {
195     EngineMutex.Lock();
196     AudioOutputDevice* res = GetAudioOutputDevice();
197     EngineMutex.Unlock();
198     return res;
199     }
200    
201 iliev 2012 void AbstractEngineChannel::SetOutputChannel(uint EngineAudioChannel, uint AudioDeviceChannel) {
202     if (!pEngine || !pEngine->pAudioOutputDevice) throw AudioOutputException("No audio output device connected yet.");
203    
204     AudioChannel* pChannel = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannel);
205     if (!pChannel) throw AudioOutputException("Invalid audio output device channel " + ToString(AudioDeviceChannel));
206     switch (EngineAudioChannel) {
207     case 0: // left output channel
208     if (fxSends.empty()) pChannelLeft = pChannel;
209     AudioDeviceChannelLeft = AudioDeviceChannel;
210     break;
211     case 1: // right output channel
212     if (fxSends.empty()) pChannelRight = pChannel;
213     AudioDeviceChannelRight = AudioDeviceChannel;
214     break;
215     default:
216     throw AudioOutputException("Invalid engine audio channel " + ToString(EngineAudioChannel));
217     }
218    
219     bStatusChanged = true;
220     }
221    
222     int AbstractEngineChannel::OutputChannel(uint EngineAudioChannel) {
223     switch (EngineAudioChannel) {
224     case 0: // left channel
225     return AudioDeviceChannelLeft;
226     case 1: // right channel
227     return AudioDeviceChannelRight;
228     default:
229     throw AudioOutputException("Invalid engine audio channel " + ToString(EngineAudioChannel));
230     }
231     }
232    
233     void AbstractEngineChannel::Connect(MidiInputPort* pMidiPort, midi_chan_t MidiChannel) {
234     if (!pMidiPort || pMidiPort == this->pMidiInputPort) return;
235     DisconnectMidiInputPort();
236     this->pMidiInputPort = pMidiPort;
237     this->midiChannel = MidiChannel;
238     pMidiPort->Connect(this, MidiChannel);
239     }
240    
241     void AbstractEngineChannel::DisconnectMidiInputPort() {
242     MidiInputPort* pOldPort = this->pMidiInputPort;
243     this->pMidiInputPort = NULL;
244     if (pOldPort) pOldPort->Disconnect(this);
245     }
246    
247     MidiInputPort* AbstractEngineChannel::GetMidiInputPort() {
248     return pMidiInputPort;
249     }
250    
251     midi_chan_t AbstractEngineChannel::MidiChannel() {
252     return midiChannel;
253     }
254    
255     void AbstractEngineChannel::Connect(VirtualMidiDevice* pDevice) {
256     // double buffer ... double work ...
257     {
258     ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.GetConfigForUpdate();
259     devices.add(pDevice);
260     }
261     {
262     ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.SwitchConfig();
263     devices.add(pDevice);
264     }
265     }
266    
267     void AbstractEngineChannel::Disconnect(VirtualMidiDevice* pDevice) {
268     // double buffer ... double work ...
269     {
270     ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.GetConfigForUpdate();
271     devices.remove(pDevice);
272     }
273     {
274     ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.SwitchConfig();
275     devices.remove(pDevice);
276     }
277     }
278    
279     /**
280     * Will be called by the MIDIIn Thread to let the audio thread trigger a new
281     * voice for the given key. This method is meant for real time rendering,
282     * that is an event will immediately be created with the current system
283     * time as time stamp.
284     *
285     * @param Key - MIDI key number of the triggered key
286     * @param Velocity - MIDI velocity value of the triggered key
287     */
288 persson 2317 void AbstractEngineChannel::SendNoteOn(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel) {
289 iliev 2012 if (pEngine) {
290     Event event = pEngine->pEventGenerator->CreateEvent();
291     event.Type = Event::type_note_on;
292     event.Param.Note.Key = Key;
293     event.Param.Note.Velocity = Velocity;
294 persson 2317 event.Param.Note.Channel = MidiChannel;
295 iliev 2012 event.pEngineChannel = this;
296     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
297     else dmsg(1,("EngineChannel: Input event queue full!"));
298     // inform connected virtual MIDI devices if any ...
299     // (e.g. virtual MIDI keyboard in instrument editor(s))
300     ArrayList<VirtualMidiDevice*>& devices =
301     const_cast<ArrayList<VirtualMidiDevice*>&>(
302     virtualMidiDevicesReader_MidiThread.Lock()
303     );
304     for (int i = 0; i < devices.size(); i++) {
305     devices[i]->SendNoteOnToDevice(Key, Velocity);
306     }
307     virtualMidiDevicesReader_MidiThread.Unlock();
308     }
309     }
310    
311     /**
312     * Will be called by the MIDIIn Thread to let the audio thread trigger a new
313     * voice for the given key. This method is meant for offline rendering
314     * and / or for cases where the exact position of the event in the current
315     * audio fragment is already known.
316     *
317     * @param Key - MIDI key number of the triggered key
318     * @param Velocity - MIDI velocity value of the triggered key
319     * @param FragmentPos - sample point position in the current audio
320     * fragment to which this event belongs to
321     */
322 persson 2317 void AbstractEngineChannel::SendNoteOn(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel, int32_t FragmentPos) {
323 iliev 2012 if (FragmentPos < 0) {
324     dmsg(1,("EngineChannel::SendNoteOn(): negative FragmentPos! Seems MIDI driver is buggy!"));
325     }
326     else if (pEngine) {
327     Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
328     event.Type = Event::type_note_on;
329     event.Param.Note.Key = Key;
330     event.Param.Note.Velocity = Velocity;
331 persson 2317 event.Param.Note.Channel = MidiChannel;
332 iliev 2012 event.pEngineChannel = this;
333     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
334     else dmsg(1,("EngineChannel: Input event queue full!"));
335     // inform connected virtual MIDI devices if any ...
336     // (e.g. virtual MIDI keyboard in instrument editor(s))
337     ArrayList<VirtualMidiDevice*>& devices =
338     const_cast<ArrayList<VirtualMidiDevice*>&>(
339     virtualMidiDevicesReader_MidiThread.Lock()
340     );
341     for (int i = 0; i < devices.size(); i++) {
342     devices[i]->SendNoteOnToDevice(Key, Velocity);
343     }
344     virtualMidiDevicesReader_MidiThread.Unlock();
345     }
346     }
347    
348     /**
349     * Will be called by the MIDIIn Thread to signal the audio thread to release
350     * voice(s) on the given key. This method is meant for real time rendering,
351     * that is an event will immediately be created with the current system
352     * time as time stamp.
353     *
354     * @param Key - MIDI key number of the released key
355     * @param Velocity - MIDI release velocity value of the released key
356     */
357 persson 2317 void AbstractEngineChannel::SendNoteOff(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel) {
358 iliev 2012 if (pEngine) {
359     Event event = pEngine->pEventGenerator->CreateEvent();
360     event.Type = Event::type_note_off;
361     event.Param.Note.Key = Key;
362     event.Param.Note.Velocity = Velocity;
363 persson 2317 event.Param.Note.Channel = MidiChannel;
364 iliev 2012 event.pEngineChannel = this;
365     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
366     else dmsg(1,("EngineChannel: Input event queue full!"));
367     // inform connected virtual MIDI devices if any ...
368     // (e.g. virtual MIDI keyboard in instrument editor(s))
369     ArrayList<VirtualMidiDevice*>& devices =
370     const_cast<ArrayList<VirtualMidiDevice*>&>(
371     virtualMidiDevicesReader_MidiThread.Lock()
372     );
373     for (int i = 0; i < devices.size(); i++) {
374     devices[i]->SendNoteOffToDevice(Key, Velocity);
375     }
376     virtualMidiDevicesReader_MidiThread.Unlock();
377     }
378     }
379    
380     /**
381     * Will be called by the MIDIIn Thread to signal the audio thread to release
382     * voice(s) on the given key. This method is meant for offline rendering
383     * and / or for cases where the exact position of the event in the current
384     * audio fragment is already known.
385     *
386     * @param Key - MIDI key number of the released key
387     * @param Velocity - MIDI release velocity value of the released key
388     * @param FragmentPos - sample point position in the current audio
389     * fragment to which this event belongs to
390     */
391 persson 2317 void AbstractEngineChannel::SendNoteOff(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel, int32_t FragmentPos) {
392 iliev 2012 if (FragmentPos < 0) {
393     dmsg(1,("EngineChannel::SendNoteOff(): negative FragmentPos! Seems MIDI driver is buggy!"));
394     }
395     else if (pEngine) {
396     Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
397     event.Type = Event::type_note_off;
398     event.Param.Note.Key = Key;
399     event.Param.Note.Velocity = Velocity;
400 persson 2317 event.Param.Note.Channel = MidiChannel;
401 iliev 2012 event.pEngineChannel = this;
402     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
403     else dmsg(1,("EngineChannel: Input event queue full!"));
404     // inform connected virtual MIDI devices if any ...
405     // (e.g. virtual MIDI keyboard in instrument editor(s))
406     ArrayList<VirtualMidiDevice*>& devices =
407     const_cast<ArrayList<VirtualMidiDevice*>&>(
408     virtualMidiDevicesReader_MidiThread.Lock()
409     );
410     for (int i = 0; i < devices.size(); i++) {
411     devices[i]->SendNoteOffToDevice(Key, Velocity);
412     }
413     virtualMidiDevicesReader_MidiThread.Unlock();
414     }
415     }
416    
417     /**
418     * Will be called by the MIDIIn Thread to signal the audio thread to change
419     * the pitch value for all voices. This method is meant for real time
420     * rendering, that is an event will immediately be created with the
421     * current system time as time stamp.
422     *
423     * @param Pitch - MIDI pitch value (-8192 ... +8191)
424     */
425 persson 2317 void AbstractEngineChannel::SendPitchbend(int Pitch, uint8_t MidiChannel) {
426 iliev 2012 if (pEngine) {
427     Event event = pEngine->pEventGenerator->CreateEvent();
428     event.Type = Event::type_pitchbend;
429     event.Param.Pitch.Pitch = Pitch;
430 persson 2317 event.Param.Pitch.Channel = MidiChannel;
431 iliev 2012 event.pEngineChannel = this;
432     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
433     else dmsg(1,("EngineChannel: Input event queue full!"));
434     }
435     }
436    
437     /**
438     * Will be called by the MIDIIn Thread to signal the audio thread to change
439     * the pitch value for all voices. This method is meant for offline
440     * rendering and / or for cases where the exact position of the event in
441     * the current audio fragment is already known.
442     *
443     * @param Pitch - MIDI pitch value (-8192 ... +8191)
444     * @param FragmentPos - sample point position in the current audio
445     * fragment to which this event belongs to
446     */
447 persson 2317 void AbstractEngineChannel::SendPitchbend(int Pitch, uint8_t MidiChannel, int32_t FragmentPos) {
448 iliev 2012 if (FragmentPos < 0) {
449     dmsg(1,("AbstractEngineChannel::SendPitchBend(): negative FragmentPos! Seems MIDI driver is buggy!"));
450     }
451     else if (pEngine) {
452     Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
453     event.Type = Event::type_pitchbend;
454     event.Param.Pitch.Pitch = Pitch;
455 persson 2317 event.Param.Pitch.Channel = MidiChannel;
456 iliev 2012 event.pEngineChannel = this;
457     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
458     else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
459     }
460     }
461    
462     /**
463     * Will be called by the MIDIIn Thread to signal the audio thread that a
464     * continuous controller value has changed. This method is meant for real
465     * time rendering, that is an event will immediately be created with the
466     * current system time as time stamp.
467     *
468     * @param Controller - MIDI controller number of the occured control change
469     * @param Value - value of the control change
470     */
471 persson 2317 void AbstractEngineChannel::SendControlChange(uint8_t Controller, uint8_t Value, uint8_t MidiChannel) {
472 iliev 2012 if (pEngine) {
473     Event event = pEngine->pEventGenerator->CreateEvent();
474     event.Type = Event::type_control_change;
475     event.Param.CC.Controller = Controller;
476     event.Param.CC.Value = Value;
477 persson 2317 event.Param.CC.Channel = MidiChannel;
478 iliev 2012 event.pEngineChannel = this;
479     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
480     else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
481     }
482     }
483    
484     /**
485     * Will be called by the MIDIIn Thread to signal the audio thread that a
486     * continuous controller value has changed. This method is meant for
487     * offline rendering and / or for cases where the exact position of the
488     * event in the current audio fragment is already known.
489     *
490     * @param Controller - MIDI controller number of the occured control change
491     * @param Value - value of the control change
492     * @param FragmentPos - sample point position in the current audio
493     * fragment to which this event belongs to
494     */
495 persson 2317 void AbstractEngineChannel::SendControlChange(uint8_t Controller, uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) {
496 iliev 2012 if (FragmentPos < 0) {
497     dmsg(1,("AbstractEngineChannel::SendControlChange(): negative FragmentPos! Seems MIDI driver is buggy!"));
498     }
499     else if (pEngine) {
500     Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
501     event.Type = Event::type_control_change;
502     event.Param.CC.Controller = Controller;
503     event.Param.CC.Value = Value;
504 persson 2317 event.Param.CC.Channel = MidiChannel;
505 iliev 2012 event.pEngineChannel = this;
506     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
507     else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
508     }
509     }
510    
511     /**
512     * Copy all events from the engine channel's input event queue buffer to
513     * the internal event list. This will be done at the beginning of each
514     * audio cycle (that is each RenderAudio() call) to distinguish all
515     * events which have to be processed in the current audio cycle. Each
516     * EngineChannel has it's own input event queue for the common channel
517     * specific events (like NoteOn, NoteOff and ControlChange events).
518     * Beside that, the engine also has a input event queue for global
519     * events (usually SysEx messages).
520     *
521     * @param Samples - number of sample points to be processed in the
522     * current audio cycle
523     */
524     void AbstractEngineChannel::ImportEvents(uint Samples) {
525     // import events from pure software MIDI "devices"
526     // (e.g. virtual keyboard in instrument editor)
527     {
528 persson 2317 const uint8_t channel = MidiChannel() == midi_chan_all ? 0 : MidiChannel();
529 iliev 2012 const int FragmentPos = 0; // randomly chosen, we don't care about jitter for virtual MIDI devices
530     Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
531     VirtualMidiDevice::event_t devEvent; // the event format we get from the virtual MIDI device
532     // as we're going to (carefully) write some status to the
533     // synchronized struct, we cast away the const
534     ArrayList<VirtualMidiDevice*>& devices =
535     const_cast<ArrayList<VirtualMidiDevice*>&>(virtualMidiDevicesReader_AudioThread.Lock());
536     // iterate through all virtual MIDI devices
537     for (int i = 0; i < devices.size(); i++) {
538     VirtualMidiDevice* pDev = devices[i];
539     // I think we can simply flush the whole FIFO(s), the user shouldn't be so fast ;-)
540     while (pDev->GetMidiEventFromDevice(devEvent)) {
541 schoenebeck 2025 switch (devEvent.Type) {
542     case VirtualMidiDevice::EVENT_TYPE_NOTEON:
543     event.Type = Event::type_note_on;
544     event.Param.Note.Key = devEvent.Arg1;
545     event.Param.Note.Velocity = devEvent.Arg2;
546 persson 2317 event.Param.Note.Channel = channel;
547 schoenebeck 2025 break;
548     case VirtualMidiDevice::EVENT_TYPE_NOTEOFF:
549     event.Type = Event::type_note_off;
550     event.Param.Note.Key = devEvent.Arg1;
551     event.Param.Note.Velocity = devEvent.Arg2;
552 persson 2317 event.Param.Note.Channel = channel;
553 schoenebeck 2025 break;
554     case VirtualMidiDevice::EVENT_TYPE_CC:
555     event.Type = Event::type_control_change;
556     event.Param.CC.Controller = devEvent.Arg1;
557     event.Param.CC.Value = devEvent.Arg2;
558 persson 2317 event.Param.CC.Channel = channel;
559 schoenebeck 2025 break;
560     default:
561     std::cerr << "AbstractEngineChannel::ImportEvents() ERROR: unknown event type ("
562     << devEvent.Type << "). This is a bug!";
563     continue;
564     }
565     event.pEngineChannel = this;
566 iliev 2012 // copy event to internal event list
567     if (pEvents->poolIsEmpty()) {
568     dmsg(1,("Event pool emtpy!\n"));
569     goto exitVirtualDevicesLoop;
570     }
571     *pEvents->allocAppend() = event;
572     }
573     }
574     }
575     exitVirtualDevicesLoop:
576     virtualMidiDevicesReader_AudioThread.Unlock();
577    
578     // import events from the regular MIDI devices
579     RingBuffer<Event,false>::NonVolatileReader eventQueueReader = pEventQueue->get_non_volatile_reader();
580     Event* pEvent;
581     while (true) {
582     // get next event from input event queue
583     if (!(pEvent = eventQueueReader.pop())) break;
584     // if younger event reached, ignore that and all subsequent ones for now
585     if (pEvent->FragmentPos() >= Samples) {
586     eventQueueReader--;
587     dmsg(2,("Younger Event, pos=%d ,Samples=%d!\n",pEvent->FragmentPos(),Samples));
588     pEvent->ResetFragmentPos();
589     break;
590     }
591     // copy event to internal event list
592     if (pEvents->poolIsEmpty()) {
593     dmsg(1,("Event pool emtpy!\n"));
594     break;
595     }
596     *pEvents->allocAppend() = *pEvent;
597     }
598     eventQueueReader.free(); // free all copied events from input queue
599     }
600    
601     FxSend* AbstractEngineChannel::AddFxSend(uint8_t MidiCtrl, String Name) throw (Exception) {
602     if (pEngine) pEngine->DisableAndLock();
603     FxSend* pFxSend = new FxSend(this, MidiCtrl, Name);
604     if (fxSends.empty()) {
605     if (pEngine && pEngine->pAudioOutputDevice) {
606     AudioOutputDevice* pDevice = pEngine->pAudioOutputDevice;
607     // create local render buffers
608     pChannelLeft = new AudioChannel(0, pDevice->MaxSamplesPerCycle());
609     pChannelRight = new AudioChannel(1, pDevice->MaxSamplesPerCycle());
610     } else {
611     // postpone local render buffer creation until audio device is assigned
612     pChannelLeft = NULL;
613     pChannelRight = NULL;
614     }
615     }
616     fxSends.push_back(pFxSend);
617     if (pEngine) pEngine->Enable();
618     fireFxSendCountChanged(GetSamplerChannel()->Index(), GetFxSendCount());
619    
620     return pFxSend;
621     }
622    
623     FxSend* AbstractEngineChannel::GetFxSend(uint FxSendIndex) {
624     return (FxSendIndex < fxSends.size()) ? fxSends[FxSendIndex] : NULL;
625     }
626    
627     uint AbstractEngineChannel::GetFxSendCount() {
628     return fxSends.size();
629     }
630    
631     void AbstractEngineChannel::RemoveFxSend(FxSend* pFxSend) {
632     if (pEngine) pEngine->DisableAndLock();
633     for (
634     std::vector<FxSend*>::iterator iter = fxSends.begin();
635     iter != fxSends.end(); iter++
636     ) {
637     if (*iter == pFxSend) {
638     delete pFxSend;
639     fxSends.erase(iter);
640     if (fxSends.empty()) {
641     // destroy local render buffers
642     if (pChannelLeft) delete pChannelLeft;
643     if (pChannelRight) delete pChannelRight;
644     // fallback to render directly into AudioOutputDevice's buffers
645     if (pEngine && pEngine->pAudioOutputDevice) {
646     pChannelLeft = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelLeft);
647     pChannelRight = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelRight);
648     } else { // we update the pointers later
649     pChannelLeft = NULL;
650     pChannelRight = NULL;
651     }
652     }
653     break;
654     }
655     }
656     if (pEngine) pEngine->Enable();
657     fireFxSendCountChanged(GetSamplerChannel()->Index(), GetFxSendCount());
658     }
659    
660     void AbstractEngineChannel::RemoveAllFxSends() {
661     if (pEngine) pEngine->DisableAndLock();
662     if (!fxSends.empty()) { // free local render buffers
663     if (pChannelLeft) {
664     delete pChannelLeft;
665     if (pEngine && pEngine->pAudioOutputDevice) {
666     // fallback to render directly to the AudioOutputDevice's buffer
667     pChannelLeft = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelLeft);
668     } else pChannelLeft = NULL;
669     }
670     if (pChannelRight) {
671     delete pChannelRight;
672     if (pEngine && pEngine->pAudioOutputDevice) {
673     // fallback to render directly to the AudioOutputDevice's buffer
674     pChannelRight = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelRight);
675     } else pChannelRight = NULL;
676     }
677     }
678     for (int i = 0; i < fxSends.size(); i++) delete fxSends[i];
679     fxSends.clear();
680     if (pEngine) pEngine->Enable();
681     }
682    
683 persson 2114 /**
684     * Add a group number to the set of key groups. Should be called
685     * when an instrument is loaded to make sure there are event lists
686     * for all key groups.
687     */
688     void AbstractEngineChannel::AddGroup(uint group) {
689     if (group) {
690 persson 2127 std::pair<ActiveKeyGroupMap::iterator, bool> p =
691     ActiveKeyGroups.insert(ActiveKeyGroupMap::value_type(group, 0));
692 persson 2114 if (p.second) {
693 persson 2127 // If the engine channel is pending deletion (see bug
694     // #113), pEngine will be null, so we can't use
695     // pEngine->pEventPool here. Instead we're using a
696     // specialized RTList that allows specifying the pool
697     // later.
698     (*p.first).second = new LazyList<Event>;
699 persson 2114 }
700     }
701     }
702    
703     /**
704     * Handle key group (a.k.a. exclusive group) conflicts.
705     */
706     void AbstractEngineChannel::HandleKeyGroupConflicts(uint KeyGroup, Pool<Event>::Iterator& itNoteOnEvent) {
707     dmsg(4,("HandelKeyGroupConflicts KeyGroup=%d\n", KeyGroup));
708     if (KeyGroup) {
709     // send a release event to all active voices in the group
710 persson 2127 RTList<Event>::Iterator itEvent = ActiveKeyGroups[KeyGroup]->allocAppend(pEngine->pEventPool);
711 persson 2114 *itEvent = *itNoteOnEvent;
712     }
713     }
714    
715     /**
716     * Empty the lists of group events. Should be called from the
717     * audio thread, after all voices have been rendered.
718     */
719     void AbstractEngineChannel::ClearGroupEventLists() {
720 persson 2127 for (ActiveKeyGroupMap::iterator iter = ActiveKeyGroups.begin();
721 persson 2114 iter != ActiveKeyGroups.end(); iter++) {
722     if (iter->second) {
723     iter->second->clear();
724     } else {
725     dmsg(1,("EngineChannel: group event list was NULL"));
726     }
727     }
728     }
729    
730     /**
731     * Remove all lists with group events.
732     */
733     void AbstractEngineChannel::DeleteGroupEventLists() {
734 persson 2127 for (ActiveKeyGroupMap::iterator iter = ActiveKeyGroups.begin();
735 persson 2114 iter != ActiveKeyGroups.end(); iter++) {
736     delete iter->second;
737     }
738     ActiveKeyGroups.clear();
739     }
740    
741 iliev 2012 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC