/[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 2317 - (show annotations) (download)
Sun Feb 19 12:13:19 2012 UTC (12 years, 2 months ago) by persson
File size: 32371 byte(s)
* sfz engine bugfix: looping was disabled if loop_start was set to 0
* sfz engine: allow regions with end=-1 to turn off other regions
  using the group and off_by opcodes (#168)
* sfz engine: made end=0 play the whole sample
* sfz engine: fixed support for lochan and hichan opcodes (#155)
* bumped version to 1.0.0.svn17

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

  ViewVC Help
Powered by ViewVC