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

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005-2008 Christian Schoenebeck *
7 * Copyright (C) 2009-2012 Christian Schoenebeck and Grigor Iliev *
8 * Copyright (C) 2013-2014 Christian Schoenebeck and Andreas Persson *
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, write to the Free Software *
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
23 * MA 02111-1307 USA *
24 ***************************************************************************/
25
26 #include "AbstractEngineChannel.h"
27 #include "../common/global_private.h"
28 #include "../Sampler.h"
29
30 namespace LinuxSampler {
31
32 AbstractEngineChannel::AbstractEngineChannel() :
33 virtualMidiDevicesReader_AudioThread(virtualMidiDevices),
34 virtualMidiDevicesReader_MidiThread(virtualMidiDevices)
35 {
36 pEngine = NULL;
37 pEvents = NULL; // we allocate when we retrieve the right Engine object
38 pEventQueue = new RingBuffer<Event,false>(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0);
39 InstrumentIdx = -1;
40 InstrumentStat = -1;
41 pChannelLeft = NULL;
42 pChannelRight = NULL;
43 AudioDeviceChannelLeft = -1;
44 AudioDeviceChannelRight = -1;
45 midiChannel = midi_chan_all;
46 ResetControllers();
47 PortamentoMode = false;
48 PortamentoTime = CONFIG_PORTAMENTO_TIME_DEFAULT;
49 pScript = NULL;
50 }
51
52 AbstractEngineChannel::~AbstractEngineChannel() {
53 if (pScript) pScript->reset(); // unloads script (in case one is loaded)
54 delete pEventQueue;
55 DeleteGroupEventLists();
56 RemoveAllFxSends();
57 }
58
59 Engine* AbstractEngineChannel::GetEngine() {
60 return pEngine;
61 }
62
63 uint AbstractEngineChannel::Channels() {
64 return 2;
65 }
66
67 /**
68 * More or less a workaround to set the instrument name, index and load
69 * status variable to zero percent immediately, that is without blocking
70 * the calling thread. It might be used in future for other preparations
71 * as well though.
72 *
73 * @param FileName - file name of the instrument file
74 * @param Instrument - index of the instrument in the file
75 * @see LoadInstrument()
76 */
77 void AbstractEngineChannel::PrepareLoadInstrument(const char* FileName, uint Instrument) {
78 InstrumentFile = FileName;
79 InstrumentIdx = Instrument;
80 InstrumentStat = 0;
81 }
82
83 String AbstractEngineChannel::InstrumentFileName() {
84 return InstrumentFile;
85 }
86
87 String AbstractEngineChannel::InstrumentName() {
88 return InstrumentIdxName;
89 }
90
91 int AbstractEngineChannel::InstrumentIndex() {
92 return InstrumentIdx;
93 }
94
95 int AbstractEngineChannel::InstrumentStatus() {
96 return InstrumentStat;
97 }
98
99 String AbstractEngineChannel::EngineName() {
100 return AbstractEngine::GetFormatString(GetEngineFormat());
101 }
102
103 void AbstractEngineChannel::Reset() {
104 if (pEngine) pEngine->DisableAndLock();
105 ResetInternal();
106 ResetControllers();
107 if (pEngine) {
108 pEngine->Enable();
109 pEngine->Reset();
110 }
111 }
112
113 void AbstractEngineChannel::ResetControllers() {
114 Pitch = 0;
115 GlobalVolume = 1.0f;
116 MidiVolume = 1.0;
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 iLastPanRequest = iMidiPan;
186 }
187
188 AudioOutputDevice* AbstractEngineChannel::GetAudioOutputDevice() {
189 return (pEngine) ? pEngine->pAudioOutputDevice : NULL;
190 }
191
192 /**
193 * Gets thread safe access to the currently connected audio output
194 * device from other threads than the lscp thread.
195 */
196 AudioOutputDevice* AbstractEngineChannel::GetAudioOutputDeviceSafe() {
197 LockGuard lock(EngineMutex);
198 return GetAudioOutputDevice();
199 }
200
201 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) {
234 if (!pMidiPort) return;
235
236 Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back();
237
238 // check if connection already exists
239 for (int i = 0; i < connections->size(); ++i)
240 if ((*connections)[i] == pMidiPort)
241 return; // to avoid endless recursion
242
243 connections->add(pMidiPort);
244
245 // inform MIDI port about this new connection
246 pMidiPort->Connect(this, MidiChannel());
247 }
248
249 void AbstractEngineChannel::Disconnect(MidiInputPort* pMidiPort) {
250 if (!pMidiPort) return;
251
252 Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back();
253
254 for (int i = 0; i < connections->size(); ++i) {
255 if ((*connections)[i] == pMidiPort) {
256 connections->remove(i);
257 // inform MIDI port about this disconnection
258 pMidiPort->Disconnect(this);
259 return;
260 }
261 }
262 }
263
264 void AbstractEngineChannel::DisconnectAllMidiInputPorts() {
265 Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back();
266 ArrayList<MidiInputPort*> clonedList = *connections;
267 connections->clear();
268 for (int i = 0; i < clonedList.size(); ++i) clonedList[i]->Disconnect(this);
269 }
270
271 uint AbstractEngineChannel::GetMidiInputPortCount() {
272 Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back();
273 return connections->size();
274 }
275
276 MidiInputPort* AbstractEngineChannel::GetMidiInputPort(uint index) {
277 Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back();
278 return (index < connections->size()) ? (*connections)[index] : NULL;
279 }
280
281 // deprecated (just for API backward compatibility) - may be removed in future
282 void AbstractEngineChannel::Connect(MidiInputPort* pMidiPort, midi_chan_t MidiChannel) {
283 if (!pMidiPort) return;
284
285 Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back();
286
287 // check against endless recursion
288 if (connections->size() == 1 && (*connections)[0] == pMidiPort && this->midiChannel == MidiChannel)
289 return;
290
291 if (!isValidMidiChan(MidiChannel))
292 throw MidiInputException("Invalid MIDI channel (" + ToString(int(MidiChannel)) + ")");
293
294 this->midiChannel = MidiChannel;
295
296 // disconnect all currently connected MIDI ports
297 ArrayList<MidiInputPort*> clonedList = *connections;
298 connections->clear();
299 for (int i = 0; i < clonedList.size(); ++i)
300 clonedList[i]->Disconnect(this);
301
302 // connect the new port
303 connections->add(pMidiPort);
304 pMidiPort->Connect(this, MidiChannel);
305 }
306
307 // deprecated (just for API backward compatibility) - may be removed in future
308 void AbstractEngineChannel::DisconnectMidiInputPort() {
309 DisconnectAllMidiInputPorts();
310 }
311
312 // deprecated (just for API backward compatibility) - may be removed in future
313 MidiInputPort* AbstractEngineChannel::GetMidiInputPort() {
314 return GetMidiInputPort(0);
315 }
316
317 midi_chan_t AbstractEngineChannel::MidiChannel() {
318 return midiChannel;
319 }
320
321 void AbstractEngineChannel::SetMidiChannel(midi_chan_t MidiChannel) {
322 if (this->midiChannel == MidiChannel) return;
323 if (!isValidMidiChan(MidiChannel))
324 throw MidiInputException("Invalid MIDI channel (" + ToString(int(MidiChannel)) + ")");
325
326 this->midiChannel = MidiChannel;
327
328 Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back();
329 ArrayList<MidiInputPort*> clonedList = *connections;
330
331 DisconnectAllMidiInputPorts();
332
333 for (int i = 0; i < clonedList.size(); ++i) Connect(clonedList[i]);
334 }
335
336 void AbstractEngineChannel::Connect(VirtualMidiDevice* pDevice) {
337 // double buffer ... double work ...
338 {
339 ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.GetConfigForUpdate();
340 devices.add(pDevice);
341 }
342 {
343 ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.SwitchConfig();
344 devices.add(pDevice);
345 }
346 }
347
348 void AbstractEngineChannel::Disconnect(VirtualMidiDevice* pDevice) {
349 // double buffer ... double work ...
350 {
351 ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.GetConfigForUpdate();
352 devices.remove(pDevice);
353 }
354 {
355 ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.SwitchConfig();
356 devices.remove(pDevice);
357 }
358 }
359
360 /**
361 * Will be called by the MIDIIn Thread to let the audio thread trigger a new
362 * voice for the given key. This method is meant for real time rendering,
363 * that is an event will immediately be created with the current system
364 * time as time stamp.
365 *
366 * @param Key - MIDI key number of the triggered key
367 * @param Velocity - MIDI velocity value of the triggered key
368 */
369 void AbstractEngineChannel::SendNoteOn(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel) {
370 if (pEngine) {
371 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
372 LockGuard g;
373 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
374
375 Event event = pEngine->pEventGenerator->CreateEvent();
376 event.Type = Event::type_note_on;
377 event.Param.Note.Key = Key;
378 event.Param.Note.Velocity = Velocity;
379 event.Param.Note.Channel = MidiChannel;
380 memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes
381 event.pEngineChannel = this;
382 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
383 else dmsg(1,("EngineChannel: Input event queue full!"));
384 // inform connected virtual MIDI devices if any ...
385 // (e.g. virtual MIDI keyboard in instrument editor(s))
386 ArrayList<VirtualMidiDevice*>& devices =
387 const_cast<ArrayList<VirtualMidiDevice*>&>(
388 virtualMidiDevicesReader_MidiThread.Lock()
389 );
390 for (int i = 0; i < devices.size(); i++) {
391 devices[i]->SendNoteOnToDevice(Key, Velocity);
392 }
393 virtualMidiDevicesReader_MidiThread.Unlock();
394 }
395 }
396
397 /**
398 * Will be called by the MIDIIn Thread to let the audio thread trigger a new
399 * voice for the given key. This method is meant for offline rendering
400 * and / or for cases where the exact position of the event in the current
401 * audio fragment is already known.
402 *
403 * @param Key - MIDI key number of the triggered key
404 * @param Velocity - MIDI velocity value of the triggered key
405 * @param FragmentPos - sample point position in the current audio
406 * fragment to which this event belongs to
407 */
408 void AbstractEngineChannel::SendNoteOn(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel, int32_t FragmentPos) {
409 if (FragmentPos < 0) {
410 dmsg(1,("EngineChannel::SendNoteOn(): negative FragmentPos! Seems MIDI driver is buggy!"));
411 }
412 else if (pEngine) {
413 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
414 LockGuard g;
415 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
416
417 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
418 event.Type = Event::type_note_on;
419 event.Param.Note.Key = Key;
420 event.Param.Note.Velocity = Velocity;
421 event.Param.Note.Channel = MidiChannel;
422 memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes
423 event.pEngineChannel = this;
424 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
425 else dmsg(1,("EngineChannel: Input event queue full!"));
426 // inform connected virtual MIDI devices if any ...
427 // (e.g. virtual MIDI keyboard in instrument editor(s))
428 ArrayList<VirtualMidiDevice*>& devices =
429 const_cast<ArrayList<VirtualMidiDevice*>&>(
430 virtualMidiDevicesReader_MidiThread.Lock()
431 );
432 for (int i = 0; i < devices.size(); i++) {
433 devices[i]->SendNoteOnToDevice(Key, Velocity);
434 }
435 virtualMidiDevicesReader_MidiThread.Unlock();
436 }
437 }
438
439 /**
440 * Will be called by the MIDIIn Thread to signal the audio thread to release
441 * voice(s) on the given key. This method is meant for real time rendering,
442 * that is an event will immediately be created with the current system
443 * time as time stamp.
444 *
445 * @param Key - MIDI key number of the released key
446 * @param Velocity - MIDI release velocity value of the released key
447 */
448 void AbstractEngineChannel::SendNoteOff(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel) {
449 if (pEngine) {
450 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
451 LockGuard g;
452 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
453
454 Event event = pEngine->pEventGenerator->CreateEvent();
455 event.Type = Event::type_note_off;
456 event.Param.Note.Key = Key;
457 event.Param.Note.Velocity = Velocity;
458 event.Param.Note.Channel = MidiChannel;
459 memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes
460 event.pEngineChannel = this;
461 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
462 else dmsg(1,("EngineChannel: Input event queue full!"));
463 // inform connected virtual MIDI devices if any ...
464 // (e.g. virtual MIDI keyboard in instrument editor(s))
465 ArrayList<VirtualMidiDevice*>& devices =
466 const_cast<ArrayList<VirtualMidiDevice*>&>(
467 virtualMidiDevicesReader_MidiThread.Lock()
468 );
469 for (int i = 0; i < devices.size(); i++) {
470 devices[i]->SendNoteOffToDevice(Key, Velocity);
471 }
472 virtualMidiDevicesReader_MidiThread.Unlock();
473 }
474 }
475
476 /**
477 * Will be called by the MIDIIn Thread to signal the audio thread to release
478 * voice(s) on the given key. This method is meant for offline rendering
479 * and / or for cases where the exact position of the event in the current
480 * audio fragment is already known.
481 *
482 * @param Key - MIDI key number of the released key
483 * @param Velocity - MIDI release velocity value of the released key
484 * @param FragmentPos - sample point position in the current audio
485 * fragment to which this event belongs to
486 */
487 void AbstractEngineChannel::SendNoteOff(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel, int32_t FragmentPos) {
488 if (FragmentPos < 0) {
489 dmsg(1,("EngineChannel::SendNoteOff(): negative FragmentPos! Seems MIDI driver is buggy!"));
490 }
491 else if (pEngine) {
492 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
493 LockGuard g;
494 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
495
496 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
497 event.Type = Event::type_note_off;
498 event.Param.Note.Key = Key;
499 event.Param.Note.Velocity = Velocity;
500 event.Param.Note.Channel = MidiChannel;
501 memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes
502 event.pEngineChannel = this;
503 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
504 else dmsg(1,("EngineChannel: Input event queue full!"));
505 // inform connected virtual MIDI devices if any ...
506 // (e.g. virtual MIDI keyboard in instrument editor(s))
507 ArrayList<VirtualMidiDevice*>& devices =
508 const_cast<ArrayList<VirtualMidiDevice*>&>(
509 virtualMidiDevicesReader_MidiThread.Lock()
510 );
511 for (int i = 0; i < devices.size(); i++) {
512 devices[i]->SendNoteOffToDevice(Key, Velocity);
513 }
514 virtualMidiDevicesReader_MidiThread.Unlock();
515 }
516 }
517
518 /**
519 * Will be called by the MIDIIn Thread to signal the audio thread to change
520 * the pitch value for all voices. This method is meant for real time
521 * rendering, that is an event will immediately be created with the
522 * current system time as time stamp.
523 *
524 * @param Pitch - MIDI pitch value (-8192 ... +8191)
525 */
526 void AbstractEngineChannel::SendPitchbend(int Pitch, uint8_t MidiChannel) {
527 if (pEngine) {
528 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
529 LockGuard g;
530 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
531
532 Event event = pEngine->pEventGenerator->CreateEvent();
533 event.Type = Event::type_pitchbend;
534 event.Param.Pitch.Pitch = Pitch;
535 event.Param.Pitch.Channel = MidiChannel;
536 memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes
537 event.pEngineChannel = this;
538 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
539 else dmsg(1,("EngineChannel: Input event queue full!"));
540 }
541 }
542
543 /**
544 * Will be called by the MIDIIn Thread to signal the audio thread to change
545 * the pitch value for all voices. This method is meant for offline
546 * rendering and / or for cases where the exact position of the event in
547 * the current audio fragment is already known.
548 *
549 * @param Pitch - MIDI pitch value (-8192 ... +8191)
550 * @param FragmentPos - sample point position in the current audio
551 * fragment to which this event belongs to
552 */
553 void AbstractEngineChannel::SendPitchbend(int Pitch, uint8_t MidiChannel, int32_t FragmentPos) {
554 if (FragmentPos < 0) {
555 dmsg(1,("AbstractEngineChannel::SendPitchBend(): negative FragmentPos! Seems MIDI driver is buggy!"));
556 }
557 else if (pEngine) {
558 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
559 LockGuard g;
560 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
561
562 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
563 event.Type = Event::type_pitchbend;
564 event.Param.Pitch.Pitch = Pitch;
565 event.Param.Pitch.Channel = MidiChannel;
566 memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes
567 event.pEngineChannel = this;
568 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
569 else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
570 }
571 }
572
573 /**
574 * Will be called by the MIDIIn Thread to signal the audio thread that a
575 * continuous controller value has changed. This method is meant for real
576 * time rendering, that is an event will immediately be created with the
577 * current system time as time stamp.
578 *
579 * @param Controller - MIDI controller number of the occured control change
580 * @param Value - value of the control change
581 */
582 void AbstractEngineChannel::SendControlChange(uint8_t Controller, uint8_t Value, uint8_t MidiChannel) {
583 if (pEngine) {
584 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
585 LockGuard g;
586 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
587
588 Event event = pEngine->pEventGenerator->CreateEvent();
589 event.Type = Event::type_control_change;
590 event.Param.CC.Controller = Controller;
591 event.Param.CC.Value = Value;
592 event.Param.CC.Channel = MidiChannel;
593 memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes
594 event.pEngineChannel = this;
595 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
596 else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
597 }
598 }
599
600 /**
601 * Will be called by the MIDIIn Thread to signal the audio thread that a
602 * continuous controller value has changed. This method is meant for
603 * offline rendering and / or for cases where the exact position of the
604 * event in the current audio fragment is already known.
605 *
606 * @param Controller - MIDI controller number of the occured control change
607 * @param Value - value of the control change
608 * @param FragmentPos - sample point position in the current audio
609 * fragment to which this event belongs to
610 */
611 void AbstractEngineChannel::SendControlChange(uint8_t Controller, uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) {
612 if (FragmentPos < 0) {
613 dmsg(1,("AbstractEngineChannel::SendControlChange(): negative FragmentPos! Seems MIDI driver is buggy!"));
614 }
615 else if (pEngine) {
616 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
617 LockGuard g;
618 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
619
620 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
621 event.Type = Event::type_control_change;
622 event.Param.CC.Controller = Controller;
623 event.Param.CC.Value = Value;
624 event.Param.CC.Channel = MidiChannel;
625 memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes
626 event.pEngineChannel = this;
627 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
628 else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
629 }
630 }
631
632 void AbstractEngineChannel::SendChannelPressure(uint8_t Value, uint8_t MidiChannel) {
633 if (pEngine) {
634 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
635 LockGuard g;
636 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
637
638 Event event = pEngine->pEventGenerator->CreateEvent();
639 event.Type = Event::type_channel_pressure;
640 event.Param.ChannelPressure.Controller = CTRL_TABLE_IDX_AFTERTOUCH; // required for instrument scripts
641 event.Param.ChannelPressure.Value = Value;
642 event.Param.ChannelPressure.Channel = MidiChannel;
643 memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes
644 event.pEngineChannel = this;
645 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
646 else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
647 }
648 }
649
650 void AbstractEngineChannel::SendChannelPressure(uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) {
651 if (pEngine) {
652 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
653 LockGuard g;
654 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
655
656 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
657 event.Type = Event::type_channel_pressure;
658 event.Param.ChannelPressure.Controller = CTRL_TABLE_IDX_AFTERTOUCH; // required for instrument scripts
659 event.Param.ChannelPressure.Value = Value;
660 event.Param.ChannelPressure.Channel = MidiChannel;
661 memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes
662 event.pEngineChannel = this;
663 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
664 else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
665 }
666 }
667
668 void AbstractEngineChannel::SendPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint8_t MidiChannel) {
669 if (pEngine) {
670 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
671 LockGuard g;
672 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
673
674 Event event = pEngine->pEventGenerator->CreateEvent();
675 event.Type = Event::type_note_pressure;
676 event.Param.NotePressure.Key = Key;
677 event.Param.NotePressure.Value = Value;
678 event.Param.NotePressure.Channel = MidiChannel;
679 memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes
680 event.pEngineChannel = this;
681 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
682 else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
683 }
684 }
685
686 void AbstractEngineChannel::SendPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) {
687 if (pEngine) {
688 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
689 LockGuard g;
690 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
691
692 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
693 event.Type = Event::type_note_pressure;
694 event.Param.NotePressure.Key = Key;
695 event.Param.NotePressure.Value = Value;
696 event.Param.NotePressure.Channel = MidiChannel;
697 memset(&event.Format, 0, sizeof(event.Format)); // init format speific stuff with zeroes
698 event.pEngineChannel = this;
699 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
700 else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
701 }
702 }
703
704 /**
705 * Copy all events from the engine channel's input event queue buffer to
706 * the internal event list. This will be done at the beginning of each
707 * audio cycle (that is each RenderAudio() call) to distinguish all
708 * events which have to be processed in the current audio cycle. Each
709 * EngineChannel has it's own input event queue for the common channel
710 * specific events (like NoteOn, NoteOff and ControlChange events).
711 * Beside that, the engine also has a input event queue for global
712 * events (usually SysEx messages).
713 *
714 * @param Samples - number of sample points to be processed in the
715 * current audio cycle
716 */
717 void AbstractEngineChannel::ImportEvents(uint Samples) {
718 // import events from pure software MIDI "devices"
719 // (e.g. virtual keyboard in instrument editor)
720 {
721 const uint8_t channel = MidiChannel() == midi_chan_all ? 0 : MidiChannel();
722 const int FragmentPos = 0; // randomly chosen, we don't care about jitter for virtual MIDI devices
723 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
724 VirtualMidiDevice::event_t devEvent; // the event format we get from the virtual MIDI device
725 // as we're going to (carefully) write some status to the
726 // synchronized struct, we cast away the const
727 ArrayList<VirtualMidiDevice*>& devices =
728 const_cast<ArrayList<VirtualMidiDevice*>&>(virtualMidiDevicesReader_AudioThread.Lock());
729 // iterate through all virtual MIDI devices
730 for (int i = 0; i < devices.size(); i++) {
731 VirtualMidiDevice* pDev = devices[i];
732 // I think we can simply flush the whole FIFO(s), the user shouldn't be so fast ;-)
733 while (pDev->GetMidiEventFromDevice(devEvent)) {
734 switch (devEvent.Type) {
735 case VirtualMidiDevice::EVENT_TYPE_NOTEON:
736 event.Type = Event::type_note_on;
737 event.Param.Note.Key = devEvent.Arg1;
738 event.Param.Note.Velocity = devEvent.Arg2;
739 event.Param.Note.Channel = channel;
740 break;
741 case VirtualMidiDevice::EVENT_TYPE_NOTEOFF:
742 event.Type = Event::type_note_off;
743 event.Param.Note.Key = devEvent.Arg1;
744 event.Param.Note.Velocity = devEvent.Arg2;
745 event.Param.Note.Channel = channel;
746 break;
747 case VirtualMidiDevice::EVENT_TYPE_CC:
748 switch (devEvent.Arg1) {
749 case 0: // bank select MSB ...
750 SetMidiBankMsb(devEvent.Arg2);
751 continue; // don't push this event into FIFO
752 case 32: // bank select LSB ...
753 SetMidiBankLsb(devEvent.Arg2);
754 continue; // don't push this event into FIFO
755 default: // regular MIDI CC ...
756 event.Type = Event::type_control_change;
757 event.Param.CC.Controller = devEvent.Arg1;
758 event.Param.CC.Value = devEvent.Arg2;
759 event.Param.CC.Channel = channel;
760 }
761 break;
762 case VirtualMidiDevice::EVENT_TYPE_PITCHBEND:
763 event.Type = Event::type_pitchbend;
764 event.Param.Pitch.Pitch = int(devEvent.Arg2 << 7 | devEvent.Arg1) - 8192;
765 event.Param.Pitch.Channel = channel;
766 break;
767 case VirtualMidiDevice::EVENT_TYPE_PROGRAM:
768 SendProgramChange(devEvent.Arg1);
769 continue; // don't push this event into FIFO
770 default:
771 std::cerr << "AbstractEngineChannel::ImportEvents() ERROR: unknown event type ("
772 << devEvent.Type << "). This is a bug!";
773 continue;
774 }
775 event.pEngineChannel = this;
776 // copy event to internal event list
777 if (pEvents->poolIsEmpty()) {
778 dmsg(1,("Event pool emtpy!\n"));
779 goto exitVirtualDevicesLoop;
780 }
781 *pEvents->allocAppend() = event;
782 }
783 }
784 }
785 exitVirtualDevicesLoop:
786 virtualMidiDevicesReader_AudioThread.Unlock();
787
788 // import events from the regular MIDI devices
789 RingBuffer<Event,false>::NonVolatileReader eventQueueReader = pEventQueue->get_non_volatile_reader();
790 Event* pEvent;
791 while (true) {
792 // get next event from input event queue
793 if (!(pEvent = eventQueueReader.pop())) break;
794 // if younger event reached, ignore that and all subsequent ones for now
795 if (pEvent->FragmentPos() >= Samples) {
796 eventQueueReader--;
797 dmsg(2,("Younger Event, pos=%d ,Samples=%d!\n",pEvent->FragmentPos(),Samples));
798 pEvent->ResetFragmentPos();
799 break;
800 }
801 // copy event to internal event list
802 if (pEvents->poolIsEmpty()) {
803 dmsg(1,("Event pool emtpy!\n"));
804 break;
805 }
806 *pEvents->allocAppend() = *pEvent;
807 }
808 eventQueueReader.free(); // free all copied events from input queue
809 }
810
811 /**
812 * Called by real-time instrument script functions to schedule a new event
813 * somewhere in future.
814 *
815 * @returns unique event ID of scheduled new event
816 */
817 int AbstractEngineChannel::ScheduleEvent(const Event* pEvent, int delay) { //TODO: delay not implemented yet
818 // since delay is not implemented yet, we simply add the new event
819 // to the event list of the current audio fragmet cycle for now
820 RTList<Event>::Iterator itEvent = pEvents->allocAppend();
821 if (itEvent) *itEvent = *pEvent; // copy event
822 return pEvents->getID(itEvent);
823 }
824
825 /**
826 * Called by real-time instrument script functions to ignore the event
827 * reflected by given event ID. The event will be freed immediately to its
828 * pool and cannot be dereferenced by its old ID anymore. Even if its
829 * allocated back from the Pool later on, it will have a different ID.
830 */
831 void AbstractEngineChannel::IgnoreEvent(int id) {
832 RTList<Event>::Iterator it = pEvents->fromID(id);
833 if (it) pEvents->free(it);
834 }
835
836 FxSend* AbstractEngineChannel::AddFxSend(uint8_t MidiCtrl, String Name) throw (Exception) {
837 if (pEngine) pEngine->DisableAndLock();
838 FxSend* pFxSend = new FxSend(this, MidiCtrl, Name);
839 if (fxSends.empty()) {
840 if (pEngine && pEngine->pAudioOutputDevice) {
841 AudioOutputDevice* pDevice = pEngine->pAudioOutputDevice;
842 // create local render buffers
843 pChannelLeft = new AudioChannel(0, pDevice->MaxSamplesPerCycle());
844 pChannelRight = new AudioChannel(1, pDevice->MaxSamplesPerCycle());
845 } else {
846 // postpone local render buffer creation until audio device is assigned
847 pChannelLeft = NULL;
848 pChannelRight = NULL;
849 }
850 }
851 fxSends.push_back(pFxSend);
852 if (pEngine) pEngine->Enable();
853 fireFxSendCountChanged(GetSamplerChannel()->Index(), GetFxSendCount());
854
855 return pFxSend;
856 }
857
858 FxSend* AbstractEngineChannel::GetFxSend(uint FxSendIndex) {
859 return (FxSendIndex < fxSends.size()) ? fxSends[FxSendIndex] : NULL;
860 }
861
862 uint AbstractEngineChannel::GetFxSendCount() {
863 return fxSends.size();
864 }
865
866 void AbstractEngineChannel::RemoveFxSend(FxSend* pFxSend) {
867 if (pEngine) pEngine->DisableAndLock();
868 for (
869 std::vector<FxSend*>::iterator iter = fxSends.begin();
870 iter != fxSends.end(); iter++
871 ) {
872 if (*iter == pFxSend) {
873 delete pFxSend;
874 fxSends.erase(iter);
875 if (fxSends.empty()) {
876 // destroy local render buffers
877 if (pChannelLeft) delete pChannelLeft;
878 if (pChannelRight) delete pChannelRight;
879 // fallback to render directly into AudioOutputDevice's buffers
880 if (pEngine && pEngine->pAudioOutputDevice) {
881 pChannelLeft = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelLeft);
882 pChannelRight = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelRight);
883 } else { // we update the pointers later
884 pChannelLeft = NULL;
885 pChannelRight = NULL;
886 }
887 }
888 break;
889 }
890 }
891 if (pEngine) pEngine->Enable();
892 fireFxSendCountChanged(GetSamplerChannel()->Index(), GetFxSendCount());
893 }
894
895 void AbstractEngineChannel::RemoveAllFxSends() {
896 if (pEngine) pEngine->DisableAndLock();
897 if (!fxSends.empty()) { // free local render buffers
898 if (pChannelLeft) {
899 delete pChannelLeft;
900 if (pEngine && pEngine->pAudioOutputDevice) {
901 // fallback to render directly to the AudioOutputDevice's buffer
902 pChannelLeft = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelLeft);
903 } else pChannelLeft = NULL;
904 }
905 if (pChannelRight) {
906 delete pChannelRight;
907 if (pEngine && pEngine->pAudioOutputDevice) {
908 // fallback to render directly to the AudioOutputDevice's buffer
909 pChannelRight = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelRight);
910 } else pChannelRight = NULL;
911 }
912 }
913 for (int i = 0; i < fxSends.size(); i++) delete fxSends[i];
914 fxSends.clear();
915 if (pEngine) pEngine->Enable();
916 }
917
918 /**
919 * Add a group number to the set of key groups. Should be called
920 * when an instrument is loaded to make sure there are event lists
921 * for all key groups.
922 */
923 void AbstractEngineChannel::AddGroup(uint group) {
924 if (group) {
925 std::pair<ActiveKeyGroupMap::iterator, bool> p =
926 ActiveKeyGroups.insert(ActiveKeyGroupMap::value_type(group, 0));
927 if (p.second) {
928 // If the engine channel is pending deletion (see bug
929 // #113), pEngine will be null, so we can't use
930 // pEngine->pEventPool here. Instead we're using a
931 // specialized RTList that allows specifying the pool
932 // later.
933 (*p.first).second = new LazyList<Event>;
934 }
935 }
936 }
937
938 /**
939 * Handle key group (a.k.a. exclusive group) conflicts.
940 */
941 void AbstractEngineChannel::HandleKeyGroupConflicts(uint KeyGroup, Pool<Event>::Iterator& itNoteOnEvent) {
942 dmsg(4,("HandelKeyGroupConflicts KeyGroup=%d\n", KeyGroup));
943 if (KeyGroup) {
944 // send a release event to all active voices in the group
945 RTList<Event>::Iterator itEvent = ActiveKeyGroups[KeyGroup]->allocAppend(pEngine->pEventPool);
946 *itEvent = *itNoteOnEvent;
947 }
948 }
949
950 /**
951 * Empty the lists of group events. Should be called from the
952 * audio thread, after all voices have been rendered.
953 */
954 void AbstractEngineChannel::ClearGroupEventLists() {
955 for (ActiveKeyGroupMap::iterator iter = ActiveKeyGroups.begin();
956 iter != ActiveKeyGroups.end(); iter++) {
957 if (iter->second) {
958 iter->second->clear();
959 } else {
960 dmsg(1,("EngineChannel: group event list was NULL"));
961 }
962 }
963 }
964
965 /**
966 * Remove all lists with group events.
967 */
968 void AbstractEngineChannel::DeleteGroupEventLists() {
969 for (ActiveKeyGroupMap::iterator iter = ActiveKeyGroups.begin();
970 iter != ActiveKeyGroups.end(); iter++) {
971 delete iter->second;
972 }
973 ActiveKeyGroups.clear();
974 }
975
976 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC