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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2326 - (show annotations) (download)
Thu Mar 8 19:40:14 2012 UTC (12 years, 1 month ago) by persson
File size: 32730 byte(s)
* bugfix: instrument loading crashed for sfz and sf2 in Ardour (#176)
* more thread safety fixes for the instrument loading thread

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

  ViewVC Help
Powered by ViewVC