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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2871 - (hide annotations) (download)
Sun Apr 10 18:22:23 2016 UTC (8 years ago) by schoenebeck
File size: 46636 byte(s)
* All engines: Implemented scheduler for delayed MIDI events and for
  suspended real-time instrument scripts.
* Real-Time instrument scripts: Implemented support for built-in "wait()"
  function's "duration-us" argument, thus scripts using this function are
  now correctly resumed after the requested amount of microseconds.
* Real-Time instrument scripts: Implemented support for built-in
  "play_note()" function's "duration-us" argument, thus notes triggered
  with this argument are now correctly released after the requested amount
  of microseconds.
* Real-Time instrument scripts: Fixed crash which happened when trying to
  reference an undeclared script variable.
* Real-Time instrument scripts: Script events were not cleared when
  engine channel was reset, potentially causing undefined behavior.
* All engines: Attempt to partly fix resetting engine channels vs.
  resetting engine, an overall cleanup of the Reset*(),
  ConnectAudioDevice(), DisconnectAudioDevice() API methods would still be
  desirable though, because the current situation is still inconsistent
  and error prone.
* Bumped version (2.0.0.svn2).

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

  ViewVC Help
Powered by ViewVC