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

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

  ViewVC Help
Powered by ViewVC