/[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 2596 - (show annotations) (download)
Thu Jun 5 19:39:12 2014 UTC (9 years, 10 months ago) by schoenebeck
File size: 46670 byte(s)
* ScriptVM (WIP): Implemented execution of script event
  handlers "note" and "release".
* ScriptVM (WIP): Implemented built-in script function
  "play_note()" (only two of the max. four function
  arguments are currently implemented yet though).
* ScriptVM (WIP): Fixed incorrect handling of
  suspended scripts.
* Bumped version (1.0.0.svn43).

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

  ViewVC Help
Powered by ViewVC