/[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 2594 - (show annotations) (download)
Thu Jun 5 00:16:25 2014 UTC (9 years, 10 months ago) by schoenebeck
File size: 45829 byte(s)
* ScriptVM (WIP): started to integrate real-time instrument script
  support into the sampler engine implementations. The code is
  shared among all sampler engines, however currently only the gig
  file format supports storing instrument scripts (as LinuxSampler
  extension to the original GigaStudio 4 file format).
* gig engine: Added support for loading instrument scripts from .gig
  files.
* ScriptVM (WIP): Implemented built-in script variables %CC, $CC_NUM,
  $EVENT_NOTE, $EVENT_VELOCITY, $VCC_MONO_AT, $VCC_PITCH_BEND.
* ScriptVM (WIP): Implemented execution of script event handler "init".
* ScriptVM (WIP): Implemented execution of script event handler
  "controller".
* Bumped version (1.0.0.svn42).

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 pScriptEvents = NULL;
50 }
51
52 AbstractEngineChannel::~AbstractEngineChannel() {
53 unloadCurrentInstrumentScript();
54 if (pScriptEvents) delete pScriptEvents;
55 delete pEventQueue;
56 DeleteGroupEventLists();
57 RemoveAllFxSends();
58 }
59
60 Engine* AbstractEngineChannel::GetEngine() {
61 return pEngine;
62 }
63
64 uint AbstractEngineChannel::Channels() {
65 return 2;
66 }
67
68 /**
69 * More or less a workaround to set the instrument name, index and load
70 * status variable to zero percent immediately, that is without blocking
71 * the calling thread. It might be used in future for other preparations
72 * as well though.
73 *
74 * @param FileName - file name of the instrument file
75 * @param Instrument - index of the instrument in the file
76 * @see LoadInstrument()
77 */
78 void AbstractEngineChannel::PrepareLoadInstrument(const char* FileName, uint Instrument) {
79 InstrumentFile = FileName;
80 InstrumentIdx = Instrument;
81 InstrumentStat = 0;
82 }
83
84 String AbstractEngineChannel::InstrumentFileName() {
85 return InstrumentFile;
86 }
87
88 String AbstractEngineChannel::InstrumentName() {
89 return InstrumentIdxName;
90 }
91
92 int AbstractEngineChannel::InstrumentIndex() {
93 return InstrumentIdx;
94 }
95
96 int AbstractEngineChannel::InstrumentStatus() {
97 return InstrumentStat;
98 }
99
100 String AbstractEngineChannel::EngineName() {
101 return AbstractEngine::GetFormatString(GetEngineFormat());
102 }
103
104 void AbstractEngineChannel::Reset() {
105 if (pEngine) pEngine->DisableAndLock();
106 ResetInternal();
107 ResetControllers();
108 if (pEngine) {
109 pEngine->Enable();
110 pEngine->Reset();
111 }
112 }
113
114 void AbstractEngineChannel::ResetControllers() {
115 Pitch = 0;
116 GlobalVolume = 1.0f;
117 MidiVolume = 1.0;
118 iLastPanRequest = 64;
119 GlobalTranspose = 0;
120 // set all MIDI controller values to zero
121 memset(ControllerTable, 0x00, 129);
122 // reset all FX Send levels
123 for (
124 std::vector<FxSend*>::iterator iter = fxSends.begin();
125 iter != fxSends.end(); iter++
126 ) {
127 (*iter)->Reset();
128 }
129 }
130
131 /**
132 * This method is not thread safe!
133 */
134 void AbstractEngineChannel::ResetInternal() {
135 CurrentKeyDimension = 0;
136 PortamentoPos = -1.0f; // no portamento active yet
137
138 // delete all input events
139 pEventQueue->init();
140
141 if (pEngine) pEngine->ResetInternal();
142
143 // status of engine channel has changed, so set notify flag
144 bStatusChanged = true;
145 }
146
147 /**
148 * Loads the real-time instrument script given by @a text on this engine
149 * channel. A resource manager is used to allocate and share equivalent
150 * scripts on multiple engine channels.
151 *
152 * @param text - source code of script
153 */
154 void AbstractEngineChannel::loadInstrumentScript(const String& text) {
155 dmsg(1,("Loading real-time instrument script ... "));
156
157 // hand back old script reference and VM execution contexts
158 // (if not done already)
159 unloadCurrentInstrumentScript();
160
161 // get new script reference
162 script.parserContext = pEngine->scripts.Borrow(text, this);
163 if (!script.parserContext->errors().empty()) {
164 std::vector<ParserIssue> errors = script.parserContext->errors();
165 std::cerr << "[ScriptVM] Could not load instrument script, there were "
166 << errors.size() << " parser errors:\n";
167 for (int i = 0; i < errors.size(); ++i)
168 errors[i].dump();
169 return; // stop here if there were any parser errors
170 }
171
172 script.handlerInit = script.parserContext->eventHandlerByName("init");
173 script.handlerNote = script.parserContext->eventHandlerByName("note");
174 script.handlerController = script.parserContext->eventHandlerByName("controller");
175 script.bHasValidScript =
176 script.handlerInit || script.handlerNote || script.handlerController;
177
178 // amount of script handlers each script event has to execute
179 int handlerExecCount = 0;
180 if (script.handlerInit) handlerExecCount++;
181 if (script.handlerNote || script.handlerController) handlerExecCount++;
182
183 // create script event pool (if it doesn't exist already)
184 if (!pScriptEvents)
185 pScriptEvents = new Pool<ScriptEvent>(CONFIG_MAX_EVENTS_PER_FRAGMENT);
186
187 // create new VM execution contexts for new script
188 while (!pScriptEvents->poolIsEmpty()) {
189 RTList<ScriptEvent>::Iterator it = pScriptEvents->allocAppend();
190 it->execCtx = pEngine->pScriptVM->createExecContext(
191 script.parserContext
192 );
193 it->handlers = new VMEventHandler*[handlerExecCount+1];
194 }
195 pScriptEvents->clear();
196
197 dmsg(1,("Done\n"));
198 }
199
200 /**
201 * Unloads the currently used real-time instrument script on this sampler
202 * channel. A resource manager is used to share equivalent scripts among
203 * multiple sampler channels, and to deallocate the parsed script once not
204 * used on any engine channel anymore.
205 */
206 void AbstractEngineChannel::unloadCurrentInstrumentScript() {
207 if (script.parserContext)
208 dmsg(1,("Unloading current instrument script."));
209
210 // free allocated VM execution contexts
211 if (pScriptEvents) {
212 pScriptEvents->clear();
213 while (!pScriptEvents->poolIsEmpty()) {
214 RTList<ScriptEvent>::Iterator it = pScriptEvents->allocAppend();
215 if (it->execCtx) {
216 // free VM execution context object
217 delete it->execCtx;
218 it->execCtx = NULL;
219 // free C array of handler pointers
220 delete [] it->handlers;
221 }
222 }
223 pScriptEvents->clear();
224 }
225 // hand back VM representation of script
226 if (script.parserContext) {
227 pEngine->scripts.HandBack(script.parserContext, this);
228 script.parserContext = NULL;
229 script.handlerInit = NULL;
230 script.handlerNote = NULL;
231 script.handlerController = NULL;
232 }
233 script.bHasValidScript = false;
234 }
235
236 /**
237 * Implementation of virtual method from abstract EngineChannel interface.
238 * This method will periodically be polled (e.g. by the LSCP server) to
239 * check if some engine channel parameter has changed since the last
240 * StatusChanged() call.
241 *
242 * This method can also be used to mark the engine channel as changed
243 * from outside, e.g. by a MIDI input device. The optional argument
244 * \a nNewStatus can be used for this.
245 *
246 * TODO: This "poll method" is just a lazy solution and might be
247 * replaced in future.
248 * @param bNewStatus - (optional, default: false) sets the new status flag
249 * @returns true if engine channel status has changed since last
250 * StatusChanged() call
251 */
252 bool AbstractEngineChannel::StatusChanged(bool bNewStatus) {
253 bool b = bStatusChanged;
254 bStatusChanged = bNewStatus;
255 return b;
256 }
257
258 float AbstractEngineChannel::Volume() {
259 return GlobalVolume;
260 }
261
262 void AbstractEngineChannel::Volume(float f) {
263 GlobalVolume = f;
264 bStatusChanged = true; // status of engine channel has changed, so set notify flag
265 }
266
267 float AbstractEngineChannel::Pan() {
268 return float(iLastPanRequest - 64) / 64.0f;
269 }
270
271 void AbstractEngineChannel::Pan(float f) {
272 int iMidiPan = int(f * 64.0f) + 64;
273 if (iMidiPan > 127) iMidiPan = 127;
274 else if (iMidiPan < 0) iMidiPan = 0;
275 iLastPanRequest = iMidiPan;
276 }
277
278 AudioOutputDevice* AbstractEngineChannel::GetAudioOutputDevice() {
279 return (pEngine) ? pEngine->pAudioOutputDevice : NULL;
280 }
281
282 /**
283 * Gets thread safe access to the currently connected audio output
284 * device from other threads than the lscp thread.
285 */
286 AudioOutputDevice* AbstractEngineChannel::GetAudioOutputDeviceSafe() {
287 LockGuard lock(EngineMutex);
288 return GetAudioOutputDevice();
289 }
290
291 void AbstractEngineChannel::SetOutputChannel(uint EngineAudioChannel, uint AudioDeviceChannel) {
292 if (!pEngine || !pEngine->pAudioOutputDevice) throw AudioOutputException("No audio output device connected yet.");
293
294 AudioChannel* pChannel = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannel);
295 if (!pChannel) throw AudioOutputException("Invalid audio output device channel " + ToString(AudioDeviceChannel));
296 switch (EngineAudioChannel) {
297 case 0: // left output channel
298 if (fxSends.empty()) pChannelLeft = pChannel;
299 AudioDeviceChannelLeft = AudioDeviceChannel;
300 break;
301 case 1: // right output channel
302 if (fxSends.empty()) pChannelRight = pChannel;
303 AudioDeviceChannelRight = AudioDeviceChannel;
304 break;
305 default:
306 throw AudioOutputException("Invalid engine audio channel " + ToString(EngineAudioChannel));
307 }
308
309 bStatusChanged = true;
310 }
311
312 int AbstractEngineChannel::OutputChannel(uint EngineAudioChannel) {
313 switch (EngineAudioChannel) {
314 case 0: // left channel
315 return AudioDeviceChannelLeft;
316 case 1: // right channel
317 return AudioDeviceChannelRight;
318 default:
319 throw AudioOutputException("Invalid engine audio channel " + ToString(EngineAudioChannel));
320 }
321 }
322
323 void AbstractEngineChannel::Connect(MidiInputPort* pMidiPort) {
324 if (!pMidiPort) return;
325
326 Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back();
327
328 // check if connection already exists
329 for (int i = 0; i < connections->size(); ++i)
330 if ((*connections)[i] == pMidiPort)
331 return; // to avoid endless recursion
332
333 connections->add(pMidiPort);
334
335 // inform MIDI port about this new connection
336 pMidiPort->Connect(this, MidiChannel());
337 }
338
339 void AbstractEngineChannel::Disconnect(MidiInputPort* pMidiPort) {
340 if (!pMidiPort) return;
341
342 Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back();
343
344 for (int i = 0; i < connections->size(); ++i) {
345 if ((*connections)[i] == pMidiPort) {
346 connections->remove(i);
347 // inform MIDI port about this disconnection
348 pMidiPort->Disconnect(this);
349 return;
350 }
351 }
352 }
353
354 void AbstractEngineChannel::DisconnectAllMidiInputPorts() {
355 Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back();
356 ArrayList<MidiInputPort*> clonedList = *connections;
357 connections->clear();
358 for (int i = 0; i < clonedList.size(); ++i) clonedList[i]->Disconnect(this);
359 }
360
361 uint AbstractEngineChannel::GetMidiInputPortCount() {
362 Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back();
363 return connections->size();
364 }
365
366 MidiInputPort* AbstractEngineChannel::GetMidiInputPort(uint index) {
367 Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back();
368 return (index < connections->size()) ? (*connections)[index] : NULL;
369 }
370
371 // deprecated (just for API backward compatibility) - may be removed in future
372 void AbstractEngineChannel::Connect(MidiInputPort* pMidiPort, midi_chan_t MidiChannel) {
373 if (!pMidiPort) return;
374
375 Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back();
376
377 // check against endless recursion
378 if (connections->size() == 1 && (*connections)[0] == pMidiPort && this->midiChannel == MidiChannel)
379 return;
380
381 if (!isValidMidiChan(MidiChannel))
382 throw MidiInputException("Invalid MIDI channel (" + ToString(int(MidiChannel)) + ")");
383
384 this->midiChannel = MidiChannel;
385
386 // disconnect all currently connected MIDI ports
387 ArrayList<MidiInputPort*> clonedList = *connections;
388 connections->clear();
389 for (int i = 0; i < clonedList.size(); ++i)
390 clonedList[i]->Disconnect(this);
391
392 // connect the new port
393 connections->add(pMidiPort);
394 pMidiPort->Connect(this, MidiChannel);
395 }
396
397 // deprecated (just for API backward compatibility) - may be removed in future
398 void AbstractEngineChannel::DisconnectMidiInputPort() {
399 DisconnectAllMidiInputPorts();
400 }
401
402 // deprecated (just for API backward compatibility) - may be removed in future
403 MidiInputPort* AbstractEngineChannel::GetMidiInputPort() {
404 return GetMidiInputPort(0);
405 }
406
407 midi_chan_t AbstractEngineChannel::MidiChannel() {
408 return midiChannel;
409 }
410
411 void AbstractEngineChannel::SetMidiChannel(midi_chan_t MidiChannel) {
412 if (this->midiChannel == MidiChannel) return;
413 if (!isValidMidiChan(MidiChannel))
414 throw MidiInputException("Invalid MIDI channel (" + ToString(int(MidiChannel)) + ")");
415
416 this->midiChannel = MidiChannel;
417
418 Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back();
419 ArrayList<MidiInputPort*> clonedList = *connections;
420
421 DisconnectAllMidiInputPorts();
422
423 for (int i = 0; i < clonedList.size(); ++i) Connect(clonedList[i]);
424 }
425
426 void AbstractEngineChannel::Connect(VirtualMidiDevice* pDevice) {
427 // double buffer ... double work ...
428 {
429 ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.GetConfigForUpdate();
430 devices.add(pDevice);
431 }
432 {
433 ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.SwitchConfig();
434 devices.add(pDevice);
435 }
436 }
437
438 void AbstractEngineChannel::Disconnect(VirtualMidiDevice* pDevice) {
439 // double buffer ... double work ...
440 {
441 ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.GetConfigForUpdate();
442 devices.remove(pDevice);
443 }
444 {
445 ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.SwitchConfig();
446 devices.remove(pDevice);
447 }
448 }
449
450 /**
451 * Will be called by the MIDIIn Thread to let the audio thread trigger a new
452 * voice for the given key. This method is meant for real time rendering,
453 * that is an event will immediately be created with the current system
454 * time as time stamp.
455 *
456 * @param Key - MIDI key number of the triggered key
457 * @param Velocity - MIDI velocity value of the triggered key
458 */
459 void AbstractEngineChannel::SendNoteOn(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel) {
460 if (pEngine) {
461 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
462 LockGuard g;
463 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
464
465 Event event = pEngine->pEventGenerator->CreateEvent();
466 event.Type = Event::type_note_on;
467 event.Param.Note.Key = Key;
468 event.Param.Note.Velocity = Velocity;
469 event.Param.Note.Channel = MidiChannel;
470 event.pEngineChannel = this;
471 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
472 else dmsg(1,("EngineChannel: Input event queue full!"));
473 // inform connected virtual MIDI devices if any ...
474 // (e.g. virtual MIDI keyboard in instrument editor(s))
475 ArrayList<VirtualMidiDevice*>& devices =
476 const_cast<ArrayList<VirtualMidiDevice*>&>(
477 virtualMidiDevicesReader_MidiThread.Lock()
478 );
479 for (int i = 0; i < devices.size(); i++) {
480 devices[i]->SendNoteOnToDevice(Key, Velocity);
481 }
482 virtualMidiDevicesReader_MidiThread.Unlock();
483 }
484 }
485
486 /**
487 * Will be called by the MIDIIn Thread to let the audio thread trigger a new
488 * voice for the given key. This method is meant for offline rendering
489 * and / or for cases where the exact position of the event in the current
490 * audio fragment is already known.
491 *
492 * @param Key - MIDI key number of the triggered key
493 * @param Velocity - MIDI velocity value of the triggered key
494 * @param FragmentPos - sample point position in the current audio
495 * fragment to which this event belongs to
496 */
497 void AbstractEngineChannel::SendNoteOn(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel, int32_t FragmentPos) {
498 if (FragmentPos < 0) {
499 dmsg(1,("EngineChannel::SendNoteOn(): negative FragmentPos! Seems MIDI driver is buggy!"));
500 }
501 else if (pEngine) {
502 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
503 LockGuard g;
504 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
505
506 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
507 event.Type = Event::type_note_on;
508 event.Param.Note.Key = Key;
509 event.Param.Note.Velocity = Velocity;
510 event.Param.Note.Channel = MidiChannel;
511 event.pEngineChannel = this;
512 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
513 else dmsg(1,("EngineChannel: Input event queue full!"));
514 // inform connected virtual MIDI devices if any ...
515 // (e.g. virtual MIDI keyboard in instrument editor(s))
516 ArrayList<VirtualMidiDevice*>& devices =
517 const_cast<ArrayList<VirtualMidiDevice*>&>(
518 virtualMidiDevicesReader_MidiThread.Lock()
519 );
520 for (int i = 0; i < devices.size(); i++) {
521 devices[i]->SendNoteOnToDevice(Key, Velocity);
522 }
523 virtualMidiDevicesReader_MidiThread.Unlock();
524 }
525 }
526
527 /**
528 * Will be called by the MIDIIn Thread to signal the audio thread to release
529 * voice(s) on the given key. This method is meant for real time rendering,
530 * that is an event will immediately be created with the current system
531 * time as time stamp.
532 *
533 * @param Key - MIDI key number of the released key
534 * @param Velocity - MIDI release velocity value of the released key
535 */
536 void AbstractEngineChannel::SendNoteOff(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel) {
537 if (pEngine) {
538 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
539 LockGuard g;
540 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
541
542 Event event = pEngine->pEventGenerator->CreateEvent();
543 event.Type = Event::type_note_off;
544 event.Param.Note.Key = Key;
545 event.Param.Note.Velocity = Velocity;
546 event.Param.Note.Channel = MidiChannel;
547 event.pEngineChannel = this;
548 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
549 else dmsg(1,("EngineChannel: Input event queue full!"));
550 // inform connected virtual MIDI devices if any ...
551 // (e.g. virtual MIDI keyboard in instrument editor(s))
552 ArrayList<VirtualMidiDevice*>& devices =
553 const_cast<ArrayList<VirtualMidiDevice*>&>(
554 virtualMidiDevicesReader_MidiThread.Lock()
555 );
556 for (int i = 0; i < devices.size(); i++) {
557 devices[i]->SendNoteOffToDevice(Key, Velocity);
558 }
559 virtualMidiDevicesReader_MidiThread.Unlock();
560 }
561 }
562
563 /**
564 * Will be called by the MIDIIn Thread to signal the audio thread to release
565 * voice(s) on the given key. This method is meant for offline rendering
566 * and / or for cases where the exact position of the event in the current
567 * audio fragment is already known.
568 *
569 * @param Key - MIDI key number of the released key
570 * @param Velocity - MIDI release velocity value of the released key
571 * @param FragmentPos - sample point position in the current audio
572 * fragment to which this event belongs to
573 */
574 void AbstractEngineChannel::SendNoteOff(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel, int32_t FragmentPos) {
575 if (FragmentPos < 0) {
576 dmsg(1,("EngineChannel::SendNoteOff(): negative FragmentPos! Seems MIDI driver is buggy!"));
577 }
578 else if (pEngine) {
579 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
580 LockGuard g;
581 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
582
583 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
584 event.Type = Event::type_note_off;
585 event.Param.Note.Key = Key;
586 event.Param.Note.Velocity = Velocity;
587 event.Param.Note.Channel = MidiChannel;
588 event.pEngineChannel = this;
589 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
590 else dmsg(1,("EngineChannel: Input event queue full!"));
591 // inform connected virtual MIDI devices if any ...
592 // (e.g. virtual MIDI keyboard in instrument editor(s))
593 ArrayList<VirtualMidiDevice*>& devices =
594 const_cast<ArrayList<VirtualMidiDevice*>&>(
595 virtualMidiDevicesReader_MidiThread.Lock()
596 );
597 for (int i = 0; i < devices.size(); i++) {
598 devices[i]->SendNoteOffToDevice(Key, Velocity);
599 }
600 virtualMidiDevicesReader_MidiThread.Unlock();
601 }
602 }
603
604 /**
605 * Will be called by the MIDIIn Thread to signal the audio thread to change
606 * the pitch value for all voices. This method is meant for real time
607 * rendering, that is an event will immediately be created with the
608 * current system time as time stamp.
609 *
610 * @param Pitch - MIDI pitch value (-8192 ... +8191)
611 */
612 void AbstractEngineChannel::SendPitchbend(int Pitch, uint8_t MidiChannel) {
613 if (pEngine) {
614 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
615 LockGuard g;
616 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
617
618 Event event = pEngine->pEventGenerator->CreateEvent();
619 event.Type = Event::type_pitchbend;
620 event.Param.Pitch.Pitch = Pitch;
621 event.Param.Pitch.Channel = MidiChannel;
622 event.pEngineChannel = this;
623 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
624 else dmsg(1,("EngineChannel: Input event queue full!"));
625 }
626 }
627
628 /**
629 * Will be called by the MIDIIn Thread to signal the audio thread to change
630 * the pitch value for all voices. This method is meant for offline
631 * rendering and / or for cases where the exact position of the event in
632 * the current audio fragment is already known.
633 *
634 * @param Pitch - MIDI pitch value (-8192 ... +8191)
635 * @param FragmentPos - sample point position in the current audio
636 * fragment to which this event belongs to
637 */
638 void AbstractEngineChannel::SendPitchbend(int Pitch, uint8_t MidiChannel, int32_t FragmentPos) {
639 if (FragmentPos < 0) {
640 dmsg(1,("AbstractEngineChannel::SendPitchBend(): negative FragmentPos! Seems MIDI driver is buggy!"));
641 }
642 else if (pEngine) {
643 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
644 LockGuard g;
645 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
646
647 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
648 event.Type = Event::type_pitchbend;
649 event.Param.Pitch.Pitch = Pitch;
650 event.Param.Pitch.Channel = MidiChannel;
651 event.pEngineChannel = this;
652 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
653 else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
654 }
655 }
656
657 /**
658 * Will be called by the MIDIIn Thread to signal the audio thread that a
659 * continuous controller value has changed. This method is meant for real
660 * time rendering, that is an event will immediately be created with the
661 * current system time as time stamp.
662 *
663 * @param Controller - MIDI controller number of the occured control change
664 * @param Value - value of the control change
665 */
666 void AbstractEngineChannel::SendControlChange(uint8_t Controller, uint8_t Value, uint8_t MidiChannel) {
667 if (pEngine) {
668 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
669 LockGuard g;
670 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
671
672 Event event = pEngine->pEventGenerator->CreateEvent();
673 event.Type = Event::type_control_change;
674 event.Param.CC.Controller = Controller;
675 event.Param.CC.Value = Value;
676 event.Param.CC.Channel = MidiChannel;
677 event.pEngineChannel = this;
678 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
679 else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
680 }
681 }
682
683 /**
684 * Will be called by the MIDIIn Thread to signal the audio thread that a
685 * continuous controller value has changed. This method is meant for
686 * offline rendering and / or for cases where the exact position of the
687 * event in the current audio fragment is already known.
688 *
689 * @param Controller - MIDI controller number of the occured control change
690 * @param Value - value of the control change
691 * @param FragmentPos - sample point position in the current audio
692 * fragment to which this event belongs to
693 */
694 void AbstractEngineChannel::SendControlChange(uint8_t Controller, uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) {
695 if (FragmentPos < 0) {
696 dmsg(1,("AbstractEngineChannel::SendControlChange(): negative FragmentPos! Seems MIDI driver is buggy!"));
697 }
698 else if (pEngine) {
699 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
700 LockGuard g;
701 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
702
703 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
704 event.Type = Event::type_control_change;
705 event.Param.CC.Controller = Controller;
706 event.Param.CC.Value = Value;
707 event.Param.CC.Channel = MidiChannel;
708 event.pEngineChannel = this;
709 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
710 else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
711 }
712 }
713
714 void AbstractEngineChannel::SendChannelPressure(uint8_t Value, uint8_t MidiChannel) {
715 if (pEngine) {
716 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
717 LockGuard g;
718 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
719
720 Event event = pEngine->pEventGenerator->CreateEvent();
721 event.Type = Event::type_channel_pressure;
722 event.Param.ChannelPressure.Value = Value;
723 event.Param.ChannelPressure.Channel = MidiChannel;
724 event.pEngineChannel = this;
725 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
726 else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
727 }
728 }
729
730 void AbstractEngineChannel::SendChannelPressure(uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) {
731 if (pEngine) {
732 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
733 LockGuard g;
734 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
735
736 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
737 event.Type = Event::type_channel_pressure;
738 event.Param.ChannelPressure.Value = Value;
739 event.Param.ChannelPressure.Channel = MidiChannel;
740 event.pEngineChannel = this;
741 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
742 else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
743 }
744 }
745
746 void AbstractEngineChannel::SendPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint8_t MidiChannel) {
747 if (pEngine) {
748 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
749 LockGuard g;
750 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
751
752 Event event = pEngine->pEventGenerator->CreateEvent();
753 event.Type = Event::type_note_pressure;
754 event.Param.NotePressure.Key = Key;
755 event.Param.NotePressure.Value = Value;
756 event.Param.NotePressure.Channel = MidiChannel;
757 event.pEngineChannel = this;
758 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
759 else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
760 }
761 }
762
763 void AbstractEngineChannel::SendPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) {
764 if (pEngine) {
765 // protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel
766 LockGuard g;
767 if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex);
768
769 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
770 event.Type = Event::type_note_pressure;
771 event.Param.NotePressure.Key = Key;
772 event.Param.NotePressure.Value = Value;
773 event.Param.NotePressure.Channel = MidiChannel;
774 event.pEngineChannel = this;
775 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
776 else dmsg(1,("AbstractEngineChannel: Input event queue full!"));
777 }
778 }
779
780 /**
781 * Copy all events from the engine channel's input event queue buffer to
782 * the internal event list. This will be done at the beginning of each
783 * audio cycle (that is each RenderAudio() call) to distinguish all
784 * events which have to be processed in the current audio cycle. Each
785 * EngineChannel has it's own input event queue for the common channel
786 * specific events (like NoteOn, NoteOff and ControlChange events).
787 * Beside that, the engine also has a input event queue for global
788 * events (usually SysEx messages).
789 *
790 * @param Samples - number of sample points to be processed in the
791 * current audio cycle
792 */
793 void AbstractEngineChannel::ImportEvents(uint Samples) {
794 // import events from pure software MIDI "devices"
795 // (e.g. virtual keyboard in instrument editor)
796 {
797 const uint8_t channel = MidiChannel() == midi_chan_all ? 0 : MidiChannel();
798 const int FragmentPos = 0; // randomly chosen, we don't care about jitter for virtual MIDI devices
799 Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
800 VirtualMidiDevice::event_t devEvent; // the event format we get from the virtual MIDI device
801 // as we're going to (carefully) write some status to the
802 // synchronized struct, we cast away the const
803 ArrayList<VirtualMidiDevice*>& devices =
804 const_cast<ArrayList<VirtualMidiDevice*>&>(virtualMidiDevicesReader_AudioThread.Lock());
805 // iterate through all virtual MIDI devices
806 for (int i = 0; i < devices.size(); i++) {
807 VirtualMidiDevice* pDev = devices[i];
808 // I think we can simply flush the whole FIFO(s), the user shouldn't be so fast ;-)
809 while (pDev->GetMidiEventFromDevice(devEvent)) {
810 switch (devEvent.Type) {
811 case VirtualMidiDevice::EVENT_TYPE_NOTEON:
812 event.Type = Event::type_note_on;
813 event.Param.Note.Key = devEvent.Arg1;
814 event.Param.Note.Velocity = devEvent.Arg2;
815 event.Param.Note.Channel = channel;
816 break;
817 case VirtualMidiDevice::EVENT_TYPE_NOTEOFF:
818 event.Type = Event::type_note_off;
819 event.Param.Note.Key = devEvent.Arg1;
820 event.Param.Note.Velocity = devEvent.Arg2;
821 event.Param.Note.Channel = channel;
822 break;
823 case VirtualMidiDevice::EVENT_TYPE_CC:
824 switch (devEvent.Arg1) {
825 case 0: // bank select MSB ...
826 SetMidiBankMsb(devEvent.Arg2);
827 continue; // don't push this event into FIFO
828 case 32: // bank select LSB ...
829 SetMidiBankLsb(devEvent.Arg2);
830 continue; // don't push this event into FIFO
831 default: // regular MIDI CC ...
832 event.Type = Event::type_control_change;
833 event.Param.CC.Controller = devEvent.Arg1;
834 event.Param.CC.Value = devEvent.Arg2;
835 event.Param.CC.Channel = channel;
836 }
837 break;
838 case VirtualMidiDevice::EVENT_TYPE_PITCHBEND:
839 event.Type = Event::type_pitchbend;
840 event.Param.Pitch.Pitch = int(devEvent.Arg2 << 7 | devEvent.Arg1) - 8192;
841 event.Param.Pitch.Channel = channel;
842 break;
843 case VirtualMidiDevice::EVENT_TYPE_PROGRAM:
844 SendProgramChange(devEvent.Arg1);
845 continue; // don't push this event into FIFO
846 default:
847 std::cerr << "AbstractEngineChannel::ImportEvents() ERROR: unknown event type ("
848 << devEvent.Type << "). This is a bug!";
849 continue;
850 }
851 event.pEngineChannel = this;
852 // copy event to internal event list
853 if (pEvents->poolIsEmpty()) {
854 dmsg(1,("Event pool emtpy!\n"));
855 goto exitVirtualDevicesLoop;
856 }
857 *pEvents->allocAppend() = event;
858 }
859 }
860 }
861 exitVirtualDevicesLoop:
862 virtualMidiDevicesReader_AudioThread.Unlock();
863
864 // import events from the regular MIDI devices
865 RingBuffer<Event,false>::NonVolatileReader eventQueueReader = pEventQueue->get_non_volatile_reader();
866 Event* pEvent;
867 while (true) {
868 // get next event from input event queue
869 if (!(pEvent = eventQueueReader.pop())) break;
870 // if younger event reached, ignore that and all subsequent ones for now
871 if (pEvent->FragmentPos() >= Samples) {
872 eventQueueReader--;
873 dmsg(2,("Younger Event, pos=%d ,Samples=%d!\n",pEvent->FragmentPos(),Samples));
874 pEvent->ResetFragmentPos();
875 break;
876 }
877 // copy event to internal event list
878 if (pEvents->poolIsEmpty()) {
879 dmsg(1,("Event pool emtpy!\n"));
880 break;
881 }
882 *pEvents->allocAppend() = *pEvent;
883 }
884 eventQueueReader.free(); // free all copied events from input queue
885 }
886
887 FxSend* AbstractEngineChannel::AddFxSend(uint8_t MidiCtrl, String Name) throw (Exception) {
888 if (pEngine) pEngine->DisableAndLock();
889 FxSend* pFxSend = new FxSend(this, MidiCtrl, Name);
890 if (fxSends.empty()) {
891 if (pEngine && pEngine->pAudioOutputDevice) {
892 AudioOutputDevice* pDevice = pEngine->pAudioOutputDevice;
893 // create local render buffers
894 pChannelLeft = new AudioChannel(0, pDevice->MaxSamplesPerCycle());
895 pChannelRight = new AudioChannel(1, pDevice->MaxSamplesPerCycle());
896 } else {
897 // postpone local render buffer creation until audio device is assigned
898 pChannelLeft = NULL;
899 pChannelRight = NULL;
900 }
901 }
902 fxSends.push_back(pFxSend);
903 if (pEngine) pEngine->Enable();
904 fireFxSendCountChanged(GetSamplerChannel()->Index(), GetFxSendCount());
905
906 return pFxSend;
907 }
908
909 FxSend* AbstractEngineChannel::GetFxSend(uint FxSendIndex) {
910 return (FxSendIndex < fxSends.size()) ? fxSends[FxSendIndex] : NULL;
911 }
912
913 uint AbstractEngineChannel::GetFxSendCount() {
914 return fxSends.size();
915 }
916
917 void AbstractEngineChannel::RemoveFxSend(FxSend* pFxSend) {
918 if (pEngine) pEngine->DisableAndLock();
919 for (
920 std::vector<FxSend*>::iterator iter = fxSends.begin();
921 iter != fxSends.end(); iter++
922 ) {
923 if (*iter == pFxSend) {
924 delete pFxSend;
925 fxSends.erase(iter);
926 if (fxSends.empty()) {
927 // destroy local render buffers
928 if (pChannelLeft) delete pChannelLeft;
929 if (pChannelRight) delete pChannelRight;
930 // fallback to render directly into AudioOutputDevice's buffers
931 if (pEngine && pEngine->pAudioOutputDevice) {
932 pChannelLeft = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelLeft);
933 pChannelRight = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelRight);
934 } else { // we update the pointers later
935 pChannelLeft = NULL;
936 pChannelRight = NULL;
937 }
938 }
939 break;
940 }
941 }
942 if (pEngine) pEngine->Enable();
943 fireFxSendCountChanged(GetSamplerChannel()->Index(), GetFxSendCount());
944 }
945
946 void AbstractEngineChannel::RemoveAllFxSends() {
947 if (pEngine) pEngine->DisableAndLock();
948 if (!fxSends.empty()) { // free local render buffers
949 if (pChannelLeft) {
950 delete pChannelLeft;
951 if (pEngine && pEngine->pAudioOutputDevice) {
952 // fallback to render directly to the AudioOutputDevice's buffer
953 pChannelLeft = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelLeft);
954 } else pChannelLeft = NULL;
955 }
956 if (pChannelRight) {
957 delete pChannelRight;
958 if (pEngine && pEngine->pAudioOutputDevice) {
959 // fallback to render directly to the AudioOutputDevice's buffer
960 pChannelRight = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelRight);
961 } else pChannelRight = NULL;
962 }
963 }
964 for (int i = 0; i < fxSends.size(); i++) delete fxSends[i];
965 fxSends.clear();
966 if (pEngine) pEngine->Enable();
967 }
968
969 /**
970 * Add a group number to the set of key groups. Should be called
971 * when an instrument is loaded to make sure there are event lists
972 * for all key groups.
973 */
974 void AbstractEngineChannel::AddGroup(uint group) {
975 if (group) {
976 std::pair<ActiveKeyGroupMap::iterator, bool> p =
977 ActiveKeyGroups.insert(ActiveKeyGroupMap::value_type(group, 0));
978 if (p.second) {
979 // If the engine channel is pending deletion (see bug
980 // #113), pEngine will be null, so we can't use
981 // pEngine->pEventPool here. Instead we're using a
982 // specialized RTList that allows specifying the pool
983 // later.
984 (*p.first).second = new LazyList<Event>;
985 }
986 }
987 }
988
989 /**
990 * Handle key group (a.k.a. exclusive group) conflicts.
991 */
992 void AbstractEngineChannel::HandleKeyGroupConflicts(uint KeyGroup, Pool<Event>::Iterator& itNoteOnEvent) {
993 dmsg(4,("HandelKeyGroupConflicts KeyGroup=%d\n", KeyGroup));
994 if (KeyGroup) {
995 // send a release event to all active voices in the group
996 RTList<Event>::Iterator itEvent = ActiveKeyGroups[KeyGroup]->allocAppend(pEngine->pEventPool);
997 *itEvent = *itNoteOnEvent;
998 }
999 }
1000
1001 /**
1002 * Empty the lists of group events. Should be called from the
1003 * audio thread, after all voices have been rendered.
1004 */
1005 void AbstractEngineChannel::ClearGroupEventLists() {
1006 for (ActiveKeyGroupMap::iterator iter = ActiveKeyGroups.begin();
1007 iter != ActiveKeyGroups.end(); iter++) {
1008 if (iter->second) {
1009 iter->second->clear();
1010 } else {
1011 dmsg(1,("EngineChannel: group event list was NULL"));
1012 }
1013 }
1014 }
1015
1016 /**
1017 * Remove all lists with group events.
1018 */
1019 void AbstractEngineChannel::DeleteGroupEventLists() {
1020 for (ActiveKeyGroupMap::iterator iter = ActiveKeyGroups.begin();
1021 iter != ActiveKeyGroups.end(); iter++) {
1022 delete iter->second;
1023 }
1024 ActiveKeyGroups.clear();
1025 }
1026
1027 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC