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

Annotation of /linuxsampler/trunk/src/engines/gig/EngineChannel.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1761 - (hide annotations) (download)
Fri Aug 29 15:42:06 2008 UTC (15 years, 7 months ago) by iliev
File size: 39589 byte(s)
* fixed a crash which occurs when removing a sampler channel waiting
  to start instrument loading after another channel

1 schoenebeck 411 /***************************************************************************
2     * *
3     * LinuxSampler - modular, streaming capable sampler *
4     * *
5     * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 persson 1646 * Copyright (C) 2005 - 2008 Christian Schoenebeck *
7 schoenebeck 411 * *
8     * This program is free software; you can redistribute it and/or modify *
9     * it under the terms of the GNU General Public License as published by *
10     * the Free Software Foundation; either version 2 of the License, or *
11     * (at your option) any later version. *
12     * *
13     * This program is distributed in the hope that it will be useful, *
14     * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16     * GNU General Public License for more details. *
17     * *
18     * You should have received a copy of the GNU General Public License *
19     * along with this program; if not, write to the Free Software *
20     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21     * MA 02111-1307 USA *
22     ***************************************************************************/
23    
24     #include "EngineChannel.h"
25    
26 schoenebeck 1424 #include "../../common/global_private.h"
27 iliev 1761 #include "../../Sampler.h"
28 schoenebeck 1424
29 persson 438 namespace LinuxSampler { namespace gig {
30 schoenebeck 411
31 persson 1646 EngineChannel::EngineChannel() :
32 schoenebeck 1659 InstrumentChangeCommandReader(InstrumentChangeCommand),
33 schoenebeck 1662 virtualMidiDevicesReader_AudioThread(virtualMidiDevices),
34     virtualMidiDevicesReader_MidiThread(virtualMidiDevices)
35 schoenebeck 1659 {
36 schoenebeck 411 pMIDIKeyInfo = new midi_key_info_t[128];
37     pEngine = NULL;
38     pInstrument = NULL;
39 schoenebeck 460 pEvents = NULL; // we allocate when we retrieve the right Engine object
40 schoenebeck 970 pEventQueue = new RingBuffer<Event,false>(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0);
41 schoenebeck 411 pActiveKeys = new Pool<uint>(128);
42     for (uint i = 0; i < 128; i++) {
43     pMIDIKeyInfo[i].pActiveVoices = NULL; // we allocate when we retrieve the right Engine object
44     pMIDIKeyInfo[i].KeyPressed = false;
45     pMIDIKeyInfo[i].Active = false;
46     pMIDIKeyInfo[i].ReleaseTrigger = false;
47     pMIDIKeyInfo[i].pEvents = NULL; // we allocate when we retrieve the right Engine object
48 schoenebeck 473 pMIDIKeyInfo[i].VoiceTheftsQueued = 0;
49 persson 438 pMIDIKeyInfo[i].RoundRobinIndex = 0;
50 schoenebeck 411 }
51     InstrumentIdx = -1;
52     InstrumentStat = -1;
53 schoenebeck 1001 pChannelLeft = NULL;
54     pChannelRight = NULL;
55 schoenebeck 411 AudioDeviceChannelLeft = -1;
56     AudioDeviceChannelRight = -1;
57 schoenebeck 675 pMidiInputPort = NULL;
58     midiChannel = midi_chan_all;
59 schoenebeck 670 ResetControllers();
60 schoenebeck 829 SoloMode = false;
61     PortamentoMode = false;
62     PortamentoTime = CONFIG_PORTAMENTO_TIME_DEFAULT;
63 schoenebeck 411 }
64    
65     EngineChannel::~EngineChannel() {
66 schoenebeck 460 DisconnectAudioOutputDevice();
67 schoenebeck 411 if (pEventQueue) delete pEventQueue;
68     if (pActiveKeys) delete pActiveKeys;
69     if (pMIDIKeyInfo) delete[] pMIDIKeyInfo;
70 schoenebeck 1001 RemoveAllFxSends();
71 schoenebeck 411 }
72    
73     /**
74 schoenebeck 660 * Implementation of virtual method from abstract EngineChannel interface.
75     * This method will periodically be polled (e.g. by the LSCP server) to
76     * check if some engine channel parameter has changed since the last
77     * StatusChanged() call.
78     *
79 schoenebeck 670 * This method can also be used to mark the engine channel as changed
80     * from outside, e.g. by a MIDI input device. The optional argument
81     * \a nNewStatus can be used for this.
82     *
83 schoenebeck 660 * TODO: This "poll method" is just a lazy solution and might be
84     * replaced in future.
85 schoenebeck 670 * @param bNewStatus - (optional, default: false) sets the new status flag
86 schoenebeck 660 * @returns true if engine channel status has changed since last
87     * StatusChanged() call
88     */
89 schoenebeck 670 bool EngineChannel::StatusChanged(bool bNewStatus) {
90 schoenebeck 660 bool b = bStatusChanged;
91 schoenebeck 670 bStatusChanged = bNewStatus;
92 schoenebeck 660 return b;
93     }
94    
95 schoenebeck 670 void EngineChannel::Reset() {
96     if (pEngine) pEngine->DisableAndLock();
97     ResetInternal();
98     ResetControllers();
99     if (pEngine) {
100     pEngine->Enable();
101     pEngine->Reset();
102     }
103     }
104    
105 schoenebeck 660 /**
106 schoenebeck 411 * This method is not thread safe!
107     */
108     void EngineChannel::ResetInternal() {
109     CurrentKeyDimension = 0;
110    
111     // reset key info
112     for (uint i = 0; i < 128; i++) {
113     if (pMIDIKeyInfo[i].pActiveVoices)
114     pMIDIKeyInfo[i].pActiveVoices->clear();
115     if (pMIDIKeyInfo[i].pEvents)
116     pMIDIKeyInfo[i].pEvents->clear();
117     pMIDIKeyInfo[i].KeyPressed = false;
118     pMIDIKeyInfo[i].Active = false;
119     pMIDIKeyInfo[i].ReleaseTrigger = false;
120     pMIDIKeyInfo[i].itSelf = Pool<uint>::Iterator();
121 schoenebeck 473 pMIDIKeyInfo[i].VoiceTheftsQueued = 0;
122 schoenebeck 411 }
123 schoenebeck 829 SoloKey = -1; // no solo key active yet
124     PortamentoPos = -1.0f; // no portamento active yet
125 schoenebeck 411
126     // reset all key groups
127     std::map<uint,uint*>::iterator iter = ActiveKeyGroups.begin();
128     for (; iter != ActiveKeyGroups.end(); iter++) iter->second = NULL;
129    
130     // free all active keys
131     pActiveKeys->clear();
132    
133     // delete all input events
134     pEventQueue->init();
135    
136     if (pEngine) pEngine->ResetInternal();
137 schoenebeck 660
138     // status of engine channel has changed, so set notify flag
139     bStatusChanged = true;
140 schoenebeck 411 }
141    
142     LinuxSampler::Engine* EngineChannel::GetEngine() {
143     return pEngine;
144     }
145    
146     /**
147     * More or less a workaround to set the instrument name, index and load
148     * status variable to zero percent immediately, that is without blocking
149     * the calling thread. It might be used in future for other preparations
150     * as well though.
151     *
152     * @param FileName - file name of the Gigasampler instrument file
153     * @param Instrument - index of the instrument in the .gig file
154     * @see LoadInstrument()
155     */
156     void EngineChannel::PrepareLoadInstrument(const char* FileName, uint Instrument) {
157     InstrumentFile = FileName;
158     InstrumentIdx = Instrument;
159     InstrumentStat = 0;
160     }
161    
162     /**
163     * Load an instrument from a .gig file. PrepareLoadInstrument() has to
164     * be called first to provide the information which instrument to load.
165     * This method will then actually start to load the instrument and block
166     * the calling thread until loading was completed.
167     *
168     * @see PrepareLoadInstrument()
169     */
170     void EngineChannel::LoadInstrument() {
171 persson 1646 // make sure we don't trigger any new notes with an old
172     // instrument
173     instrument_change_command_t& cmd = ChangeInstrument(0);
174     if (cmd.pInstrument) {
175     // give old instrument back to instrument manager, but
176     // keep the dimension regions and samples that are in use
177     Engine::instruments.HandBackInstrument(cmd.pInstrument, this, cmd.pDimRegionsInUse);
178 schoenebeck 411 }
179 persson 1646 cmd.pDimRegionsInUse->clear();
180 schoenebeck 411
181     // delete all key groups
182     ActiveKeyGroups.clear();
183    
184     // request gig instrument from instrument manager
185 persson 1038 ::gig::Instrument* newInstrument;
186 schoenebeck 411 try {
187 schoenebeck 947 InstrumentManager::instrument_id_t instrid;
188     instrid.FileName = InstrumentFile;
189     instrid.Index = InstrumentIdx;
190 persson 1038 newInstrument = Engine::instruments.Borrow(instrid, this);
191     if (!newInstrument) {
192 schoenebeck 1212 throw InstrumentManagerException("resource was not created");
193 schoenebeck 411 }
194     }
195     catch (RIFF::Exception e) {
196     InstrumentStat = -2;
197 iliev 1309 StatusChanged(true);
198 schoenebeck 411 String msg = "gig::Engine error: Failed to load instrument, cause: " + e.Message;
199 schoenebeck 880 throw Exception(msg);
200 schoenebeck 411 }
201 schoenebeck 1212 catch (InstrumentManagerException e) {
202 schoenebeck 411 InstrumentStat = -3;
203 iliev 1309 StatusChanged(true);
204 schoenebeck 411 String msg = "gig::Engine error: Failed to load instrument, cause: " + e.Message();
205 schoenebeck 880 throw Exception(msg);
206 schoenebeck 411 }
207     catch (...) {
208     InstrumentStat = -4;
209 iliev 1309 StatusChanged(true);
210 schoenebeck 880 throw Exception("gig::Engine error: Failed to load instrument, cause: Unknown exception while trying to parse gig file.");
211 schoenebeck 411 }
212    
213     // rebuild ActiveKeyGroups map with key groups of current instrument
214 persson 1038 for (::gig::Region* pRegion = newInstrument->GetFirstRegion(); pRegion; pRegion = newInstrument->GetNextRegion())
215 schoenebeck 411 if (pRegion->KeyGroup) ActiveKeyGroups[pRegion->KeyGroup] = NULL;
216    
217 persson 1038 InstrumentIdxName = newInstrument->pInfo->Name;
218 schoenebeck 411 InstrumentStat = 100;
219    
220 persson 1646 ChangeInstrument(newInstrument);
221    
222 iliev 1298 StatusChanged(true);
223 schoenebeck 411 }
224    
225 persson 1646
226 schoenebeck 411 /**
227 persson 1646 * Changes the instrument for an engine channel.
228     *
229     * @param pInstrument - new instrument
230     * @returns the resulting instrument change command after the
231     * command switch, containing the old instrument and
232     * the dimregions it is using
233     */
234     EngineChannel::instrument_change_command_t& EngineChannel::ChangeInstrument(::gig::Instrument* pInstrument) {
235     instrument_change_command_t& cmd = InstrumentChangeCommand.GetConfigForUpdate();
236     cmd.pInstrument = pInstrument;
237     cmd.bChangeInstrument = true;
238    
239     return InstrumentChangeCommand.SwitchConfig();
240     }
241    
242     /**
243 schoenebeck 411 * Will be called by the InstrumentResourceManager when the instrument
244 schoenebeck 517 * we are currently using on this EngineChannel is going to be updated,
245     * so we can stop playback before that happens.
246 schoenebeck 411 */
247     void EngineChannel::ResourceToBeUpdated(::gig::Instrument* pResource, void*& pUpdateArg) {
248     dmsg(3,("gig::Engine: Received instrument update message.\n"));
249     if (pEngine) pEngine->DisableAndLock();
250     ResetInternal();
251     this->pInstrument = NULL;
252     }
253    
254     /**
255     * Will be called by the InstrumentResourceManager when the instrument
256     * update process was completed, so we can continue with playback.
257     */
258     void EngineChannel::ResourceUpdated(::gig::Instrument* pOldResource, ::gig::Instrument* pNewResource, void* pUpdateArg) {
259     this->pInstrument = pNewResource; //TODO: there are couple of engine parameters we should update here as well if the instrument was updated (see LoadInstrument())
260     if (pEngine) pEngine->Enable();
261 schoenebeck 660 bStatusChanged = true; // status of engine has changed, so set notify flag
262 schoenebeck 411 }
263    
264 schoenebeck 517 /**
265     * Will be called by the InstrumentResourceManager on progress changes
266     * while loading or realoading an instrument for this EngineChannel.
267     *
268     * @param fProgress - current progress as value between 0.0 and 1.0
269     */
270     void EngineChannel::OnResourceProgress(float fProgress) {
271     this->InstrumentStat = int(fProgress * 100.0f);
272     dmsg(7,("gig::EngineChannel: progress %d%", InstrumentStat));
273 schoenebeck 660 bStatusChanged = true; // status of engine has changed, so set notify flag
274 schoenebeck 517 }
275    
276 schoenebeck 412 void EngineChannel::Connect(AudioOutputDevice* pAudioOut) {
277 schoenebeck 460 if (pEngine) {
278     if (pEngine->pAudioOutputDevice == pAudioOut) return;
279 schoenebeck 412 DisconnectAudioOutputDevice();
280     }
281 schoenebeck 411 pEngine = Engine::AcquireEngine(this, pAudioOut);
282 persson 438 ResetInternal();
283 schoenebeck 738 pEvents = new RTList<Event>(pEngine->pEventPool);
284 persson 1646
285     // reset the instrument change command struct (need to be done
286     // twice, as it is double buffered)
287     {
288     instrument_change_command_t& cmd = InstrumentChangeCommand.GetConfigForUpdate();
289     cmd.pDimRegionsInUse = new RTList< ::gig::DimensionRegion*>(pEngine->pDimRegionPool[0]);
290     cmd.pInstrument = 0;
291     cmd.bChangeInstrument = false;
292     }
293     {
294     instrument_change_command_t& cmd = InstrumentChangeCommand.SwitchConfig();
295     cmd.pDimRegionsInUse = new RTList< ::gig::DimensionRegion*>(pEngine->pDimRegionPool[1]);
296     cmd.pInstrument = 0;
297     cmd.bChangeInstrument = false;
298     }
299    
300 schoenebeck 411 for (uint i = 0; i < 128; i++) {
301     pMIDIKeyInfo[i].pActiveVoices = new RTList<Voice>(pEngine->pVoicePool);
302     pMIDIKeyInfo[i].pEvents = new RTList<Event>(pEngine->pEventPool);
303     }
304     AudioDeviceChannelLeft = 0;
305     AudioDeviceChannelRight = 1;
306 schoenebeck 1001 if (fxSends.empty()) { // render directly into the AudioDevice's output buffers
307     pChannelLeft = pAudioOut->Channel(AudioDeviceChannelLeft);
308     pChannelRight = pAudioOut->Channel(AudioDeviceChannelRight);
309     } else { // use local buffers for rendering and copy later
310     // ensure the local buffers have the correct size
311     if (pChannelLeft) delete pChannelLeft;
312     if (pChannelRight) delete pChannelRight;
313     pChannelLeft = new AudioChannel(0, pAudioOut->MaxSamplesPerCycle());
314     pChannelRight = new AudioChannel(1, pAudioOut->MaxSamplesPerCycle());
315     }
316 persson 1039 if (pEngine->EngineDisabled.GetUnsafe()) pEngine->Enable();
317 persson 846 MidiInputPort::AddSysexListener(pEngine);
318 schoenebeck 411 }
319    
320     void EngineChannel::DisconnectAudioOutputDevice() {
321     if (pEngine) { // if clause to prevent disconnect loops
322 persson 1646
323     // delete the structures used for instrument change
324     RTList< ::gig::DimensionRegion*>* d = InstrumentChangeCommand.GetConfigForUpdate().pDimRegionsInUse;
325     if (d) delete d;
326     EngineChannel::instrument_change_command_t& cmd = InstrumentChangeCommand.SwitchConfig();
327     d = cmd.pDimRegionsInUse;
328    
329     if (cmd.pInstrument) {
330     // release the currently loaded instrument
331     Engine::instruments.HandBackInstrument(cmd.pInstrument, this, d);
332     }
333 iliev 1761
334 persson 1646 if (d) delete d;
335    
336     // release all active dimension regions to resource
337     // manager
338     RTList<uint>::Iterator iuiKey = pActiveKeys->first();
339     RTList<uint>::Iterator end = pActiveKeys->end();
340     while (iuiKey != end) { // iterate through all active keys
341     midi_key_info_t* pKey = &pMIDIKeyInfo[*iuiKey];
342     ++iuiKey;
343    
344     RTList<Voice>::Iterator itVoice = pKey->pActiveVoices->first();
345     RTList<Voice>::Iterator itVoicesEnd = pKey->pActiveVoices->end();
346     for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
347     Engine::instruments.HandBackDimReg(itVoice->pDimRgn);
348     }
349     }
350    
351 schoenebeck 411 ResetInternal();
352 schoenebeck 460 if (pEvents) {
353     delete pEvents;
354     pEvents = NULL;
355     }
356 schoenebeck 411 for (uint i = 0; i < 128; i++) {
357 schoenebeck 420 if (pMIDIKeyInfo[i].pActiveVoices) {
358     delete pMIDIKeyInfo[i].pActiveVoices;
359     pMIDIKeyInfo[i].pActiveVoices = NULL;
360     }
361     if (pMIDIKeyInfo[i].pEvents) {
362     delete pMIDIKeyInfo[i].pEvents;
363     pMIDIKeyInfo[i].pEvents = NULL;
364     }
365 schoenebeck 411 }
366     Engine* oldEngine = pEngine;
367     AudioOutputDevice* oldAudioDevice = pEngine->pAudioOutputDevice;
368     pEngine = NULL;
369     Engine::FreeEngine(this, oldAudioDevice);
370     AudioDeviceChannelLeft = -1;
371 persson 438 AudioDeviceChannelRight = -1;
372 schoenebeck 1001 if (!fxSends.empty()) { // free the local rendering buffers
373     if (pChannelLeft) delete pChannelLeft;
374     if (pChannelRight) delete pChannelRight;
375     }
376     pChannelLeft = NULL;
377     pChannelRight = NULL;
378 schoenebeck 411 }
379     }
380    
381 schoenebeck 1001 AudioOutputDevice* EngineChannel::GetAudioOutputDevice() {
382     return (pEngine) ? pEngine->pAudioOutputDevice : NULL;
383     }
384    
385 schoenebeck 411 void EngineChannel::SetOutputChannel(uint EngineAudioChannel, uint AudioDeviceChannel) {
386     if (!pEngine || !pEngine->pAudioOutputDevice) throw AudioOutputException("No audio output device connected yet.");
387 persson 438
388 schoenebeck 411 AudioChannel* pChannel = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannel);
389     if (!pChannel) throw AudioOutputException("Invalid audio output device channel " + ToString(AudioDeviceChannel));
390     switch (EngineAudioChannel) {
391     case 0: // left output channel
392 schoenebeck 1001 if (fxSends.empty()) pChannelLeft = pChannel;
393 schoenebeck 411 AudioDeviceChannelLeft = AudioDeviceChannel;
394     break;
395     case 1: // right output channel
396 schoenebeck 1001 if (fxSends.empty()) pChannelRight = pChannel;
397 schoenebeck 411 AudioDeviceChannelRight = AudioDeviceChannel;
398     break;
399     default:
400     throw AudioOutputException("Invalid engine audio channel " + ToString(EngineAudioChannel));
401     }
402 iliev 1267
403     bStatusChanged = true;
404 schoenebeck 411 }
405    
406     int EngineChannel::OutputChannel(uint EngineAudioChannel) {
407     switch (EngineAudioChannel) {
408     case 0: // left channel
409     return AudioDeviceChannelLeft;
410     case 1: // right channel
411     return AudioDeviceChannelRight;
412     default:
413     throw AudioOutputException("Invalid engine audio channel " + ToString(EngineAudioChannel));
414     }
415     }
416    
417 schoenebeck 675 void EngineChannel::Connect(MidiInputPort* pMidiPort, midi_chan_t MidiChannel) {
418     if (!pMidiPort || pMidiPort == this->pMidiInputPort) return;
419     DisconnectMidiInputPort();
420     this->pMidiInputPort = pMidiPort;
421     this->midiChannel = MidiChannel;
422     pMidiPort->Connect(this, MidiChannel);
423     }
424    
425     void EngineChannel::DisconnectMidiInputPort() {
426     MidiInputPort* pOldPort = this->pMidiInputPort;
427     this->pMidiInputPort = NULL;
428     if (pOldPort) pOldPort->Disconnect(this);
429     }
430    
431     MidiInputPort* EngineChannel::GetMidiInputPort() {
432     return pMidiInputPort;
433     }
434    
435     midi_chan_t EngineChannel::MidiChannel() {
436     return midiChannel;
437     }
438    
439 schoenebeck 1001 FxSend* EngineChannel::AddFxSend(uint8_t MidiCtrl, String Name) throw (Exception) {
440     if (pEngine) pEngine->DisableAndLock();
441     FxSend* pFxSend = new FxSend(this, MidiCtrl, Name);
442     if (fxSends.empty()) {
443     if (pEngine && pEngine->pAudioOutputDevice) {
444     AudioOutputDevice* pDevice = pEngine->pAudioOutputDevice;
445     // create local render buffers
446     pChannelLeft = new AudioChannel(0, pDevice->MaxSamplesPerCycle());
447     pChannelRight = new AudioChannel(1, pDevice->MaxSamplesPerCycle());
448     } else {
449     // postpone local render buffer creation until audio device is assigned
450     pChannelLeft = NULL;
451     pChannelRight = NULL;
452     }
453     }
454     fxSends.push_back(pFxSend);
455     if (pEngine) pEngine->Enable();
456 iliev 1761 fireFxSendCountChanged(GetSamplerChannel()->Index(), GetFxSendCount());
457 persson 1646
458 schoenebeck 1001 return pFxSend;
459     }
460    
461     FxSend* EngineChannel::GetFxSend(uint FxSendIndex) {
462     return (FxSendIndex < fxSends.size()) ? fxSends[FxSendIndex] : NULL;
463     }
464    
465     uint EngineChannel::GetFxSendCount() {
466     return fxSends.size();
467     }
468    
469     void EngineChannel::RemoveFxSend(FxSend* pFxSend) {
470     if (pEngine) pEngine->DisableAndLock();
471     for (
472     std::vector<FxSend*>::iterator iter = fxSends.begin();
473     iter != fxSends.end(); iter++
474     ) {
475     if (*iter == pFxSend) {
476     delete pFxSend;
477     fxSends.erase(iter);
478     if (fxSends.empty()) {
479     // destroy local render buffers
480     if (pChannelLeft) delete pChannelLeft;
481     if (pChannelRight) delete pChannelRight;
482     // fallback to render directly into AudioOutputDevice's buffers
483     if (pEngine && pEngine->pAudioOutputDevice) {
484     pChannelLeft = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelLeft);
485     pChannelRight = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelRight);
486     } else { // we update the pointers later
487     pChannelLeft = NULL;
488     pChannelRight = NULL;
489     }
490     }
491     break;
492     }
493     }
494     if (pEngine) pEngine->Enable();
495 iliev 1761 fireFxSendCountChanged(GetSamplerChannel()->Index(), GetFxSendCount());
496 schoenebeck 1001 }
497    
498 schoenebeck 411 /**
499     * Will be called by the MIDIIn Thread to let the audio thread trigger a new
500 schoenebeck 906 * voice for the given key. This method is meant for real time rendering,
501     * that is an event will immediately be created with the current system
502     * time as time stamp.
503 schoenebeck 411 *
504     * @param Key - MIDI key number of the triggered key
505     * @param Velocity - MIDI velocity value of the triggered key
506     */
507     void EngineChannel::SendNoteOn(uint8_t Key, uint8_t Velocity) {
508     if (pEngine) {
509     Event event = pEngine->pEventGenerator->CreateEvent();
510     event.Type = Event::type_note_on;
511     event.Param.Note.Key = Key;
512     event.Param.Note.Velocity = Velocity;
513 persson 438 event.pEngineChannel = this;
514 schoenebeck 411 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
515 schoenebeck 412 else dmsg(1,("EngineChannel: Input event queue full!"));
516 schoenebeck 1662 // inform connected virtual MIDI devices if any ...
517     // (e.g. virtual MIDI keyboard in instrument editor(s))
518     ArrayList<VirtualMidiDevice*>& devices =
519     const_cast<ArrayList<VirtualMidiDevice*>&>(
520     virtualMidiDevicesReader_MidiThread.Lock()
521     );
522     for (int i = 0; i < devices.size(); i++) {
523     devices[i]->SendNoteOnToDevice(Key, Velocity);
524     }
525     virtualMidiDevicesReader_MidiThread.Unlock();
526 schoenebeck 411 }
527     }
528    
529     /**
530 schoenebeck 906 * Will be called by the MIDIIn Thread to let the audio thread trigger a new
531     * voice for the given key. This method is meant for offline rendering
532     * and / or for cases where the exact position of the event in the current
533     * audio fragment is already known.
534     *
535     * @param Key - MIDI key number of the triggered key
536     * @param Velocity - MIDI velocity value of the triggered key
537     * @param FragmentPos - sample point position in the current audio
538     * fragment to which this event belongs to
539     */
540     void EngineChannel::SendNoteOn(uint8_t Key, uint8_t Velocity, int32_t FragmentPos) {
541     if (FragmentPos < 0) {
542     dmsg(1,("EngineChannel::SendNoteOn(): negative FragmentPos! Seems MIDI driver is buggy!"));
543     }
544     else if (pEngine) {
545     Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
546     event.Type = Event::type_note_on;
547     event.Param.Note.Key = Key;
548     event.Param.Note.Velocity = Velocity;
549     event.pEngineChannel = this;
550     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
551     else dmsg(1,("EngineChannel: Input event queue full!"));
552 schoenebeck 1662 // inform connected virtual MIDI devices if any ...
553     // (e.g. virtual MIDI keyboard in instrument editor(s))
554     ArrayList<VirtualMidiDevice*>& devices =
555     const_cast<ArrayList<VirtualMidiDevice*>&>(
556     virtualMidiDevicesReader_MidiThread.Lock()
557     );
558     for (int i = 0; i < devices.size(); i++) {
559     devices[i]->SendNoteOnToDevice(Key, Velocity);
560     }
561     virtualMidiDevicesReader_MidiThread.Unlock();
562 schoenebeck 906 }
563     }
564    
565     /**
566 schoenebeck 411 * Will be called by the MIDIIn Thread to signal the audio thread to release
567 schoenebeck 906 * voice(s) on the given key. This method is meant for real time rendering,
568     * that is an event will immediately be created with the current system
569     * time as time stamp.
570 schoenebeck 411 *
571     * @param Key - MIDI key number of the released key
572     * @param Velocity - MIDI release velocity value of the released key
573     */
574     void EngineChannel::SendNoteOff(uint8_t Key, uint8_t Velocity) {
575     if (pEngine) {
576     Event event = pEngine->pEventGenerator->CreateEvent();
577     event.Type = Event::type_note_off;
578     event.Param.Note.Key = Key;
579     event.Param.Note.Velocity = Velocity;
580 schoenebeck 412 event.pEngineChannel = this;
581 schoenebeck 411 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
582 schoenebeck 412 else dmsg(1,("EngineChannel: Input event queue full!"));
583 schoenebeck 1662 // inform connected virtual MIDI devices if any ...
584     // (e.g. virtual MIDI keyboard in instrument editor(s))
585     ArrayList<VirtualMidiDevice*>& devices =
586     const_cast<ArrayList<VirtualMidiDevice*>&>(
587     virtualMidiDevicesReader_MidiThread.Lock()
588     );
589     for (int i = 0; i < devices.size(); i++) {
590     devices[i]->SendNoteOffToDevice(Key, Velocity);
591     }
592     virtualMidiDevicesReader_MidiThread.Unlock();
593 schoenebeck 411 }
594     }
595    
596     /**
597 schoenebeck 906 * Will be called by the MIDIIn Thread to signal the audio thread to release
598     * voice(s) on the given key. This method is meant for offline rendering
599     * and / or for cases where the exact position of the event in the current
600     * audio fragment is already known.
601     *
602     * @param Key - MIDI key number of the released key
603     * @param Velocity - MIDI release velocity value of the released key
604     * @param FragmentPos - sample point position in the current audio
605     * fragment to which this event belongs to
606     */
607     void EngineChannel::SendNoteOff(uint8_t Key, uint8_t Velocity, int32_t FragmentPos) {
608     if (FragmentPos < 0) {
609     dmsg(1,("EngineChannel::SendNoteOff(): negative FragmentPos! Seems MIDI driver is buggy!"));
610     }
611     else if (pEngine) {
612     Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
613     event.Type = Event::type_note_off;
614     event.Param.Note.Key = Key;
615     event.Param.Note.Velocity = Velocity;
616     event.pEngineChannel = this;
617     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
618     else dmsg(1,("EngineChannel: Input event queue full!"));
619 schoenebeck 1662 // inform connected virtual MIDI devices if any ...
620     // (e.g. virtual MIDI keyboard in instrument editor(s))
621     ArrayList<VirtualMidiDevice*>& devices =
622     const_cast<ArrayList<VirtualMidiDevice*>&>(
623     virtualMidiDevicesReader_MidiThread.Lock()
624     );
625     for (int i = 0; i < devices.size(); i++) {
626     devices[i]->SendNoteOffToDevice(Key, Velocity);
627     }
628     virtualMidiDevicesReader_MidiThread.Unlock();
629 schoenebeck 906 }
630     }
631    
632     /**
633 schoenebeck 411 * Will be called by the MIDIIn Thread to signal the audio thread to change
634 schoenebeck 906 * the pitch value for all voices. This method is meant for real time
635     * rendering, that is an event will immediately be created with the
636     * current system time as time stamp.
637 schoenebeck 411 *
638     * @param Pitch - MIDI pitch value (-8192 ... +8191)
639     */
640     void EngineChannel::SendPitchbend(int Pitch) {
641 persson 438 if (pEngine) {
642 schoenebeck 411 Event event = pEngine->pEventGenerator->CreateEvent();
643     event.Type = Event::type_pitchbend;
644     event.Param.Pitch.Pitch = Pitch;
645 schoenebeck 412 event.pEngineChannel = this;
646 schoenebeck 411 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
647 schoenebeck 412 else dmsg(1,("EngineChannel: Input event queue full!"));
648 schoenebeck 411 }
649     }
650    
651     /**
652 schoenebeck 906 * Will be called by the MIDIIn Thread to signal the audio thread to change
653     * the pitch value for all voices. This method is meant for offline
654     * rendering and / or for cases where the exact position of the event in
655     * the current audio fragment is already known.
656     *
657     * @param Pitch - MIDI pitch value (-8192 ... +8191)
658     * @param FragmentPos - sample point position in the current audio
659     * fragment to which this event belongs to
660     */
661     void EngineChannel::SendPitchbend(int Pitch, int32_t FragmentPos) {
662     if (FragmentPos < 0) {
663     dmsg(1,("EngineChannel::SendPitchBend(): negative FragmentPos! Seems MIDI driver is buggy!"));
664     }
665     else if (pEngine) {
666     Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
667     event.Type = Event::type_pitchbend;
668     event.Param.Pitch.Pitch = Pitch;
669     event.pEngineChannel = this;
670     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
671     else dmsg(1,("EngineChannel: Input event queue full!"));
672     }
673     }
674    
675     /**
676 schoenebeck 411 * Will be called by the MIDIIn Thread to signal the audio thread that a
677 schoenebeck 906 * continuous controller value has changed. This method is meant for real
678     * time rendering, that is an event will immediately be created with the
679     * current system time as time stamp.
680 schoenebeck 411 *
681     * @param Controller - MIDI controller number of the occured control change
682     * @param Value - value of the control change
683     */
684     void EngineChannel::SendControlChange(uint8_t Controller, uint8_t Value) {
685     if (pEngine) {
686     Event event = pEngine->pEventGenerator->CreateEvent();
687     event.Type = Event::type_control_change;
688     event.Param.CC.Controller = Controller;
689     event.Param.CC.Value = Value;
690 schoenebeck 412 event.pEngineChannel = this;
691 schoenebeck 411 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
692 schoenebeck 412 else dmsg(1,("EngineChannel: Input event queue full!"));
693 schoenebeck 411 }
694     }
695    
696 schoenebeck 906 /**
697     * Will be called by the MIDIIn Thread to signal the audio thread that a
698     * continuous controller value has changed. This method is meant for
699     * offline rendering and / or for cases where the exact position of the
700     * event in the current audio fragment is already known.
701     *
702     * @param Controller - MIDI controller number of the occured control change
703     * @param Value - value of the control change
704     * @param FragmentPos - sample point position in the current audio
705     * fragment to which this event belongs to
706     */
707     void EngineChannel::SendControlChange(uint8_t Controller, uint8_t Value, int32_t FragmentPos) {
708     if (FragmentPos < 0) {
709     dmsg(1,("EngineChannel::SendControlChange(): negative FragmentPos! Seems MIDI driver is buggy!"));
710     }
711     else if (pEngine) {
712     Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
713     event.Type = Event::type_control_change;
714     event.Param.CC.Controller = Controller;
715     event.Param.CC.Value = Value;
716     event.pEngineChannel = this;
717     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
718     else dmsg(1,("EngineChannel: Input event queue full!"));
719     }
720     }
721    
722 schoenebeck 460 void EngineChannel::ClearEventLists() {
723     pEvents->clear();
724     // empty MIDI key specific event lists
725     {
726     RTList<uint>::Iterator iuiKey = pActiveKeys->first();
727     RTList<uint>::Iterator end = pActiveKeys->end();
728     for(; iuiKey != end; ++iuiKey) {
729     pMIDIKeyInfo[*iuiKey].pEvents->clear(); // free all events on the key
730     }
731     }
732     }
733    
734 schoenebeck 473 void EngineChannel::ResetControllers() {
735 schoenebeck 670 Pitch = 0;
736     SustainPedal = false;
737 iliev 776 SostenutoPedal = false;
738 schoenebeck 1005 GlobalVolume = 1.0f;
739 schoenebeck 947 MidiVolume = 1.0;
740 schoenebeck 670 GlobalPanLeft = 1.0f;
741     GlobalPanRight = 1.0f;
742 schoenebeck 1723 iLastPanRequest = 64;
743 schoenebeck 1041 GlobalTranspose = 0;
744 schoenebeck 473 // set all MIDI controller values to zero
745 persson 903 memset(ControllerTable, 0x00, 129);
746 schoenebeck 1040 // reset all FX Send levels
747     for (
748     std::vector<FxSend*>::iterator iter = fxSends.begin();
749     iter != fxSends.end(); iter++
750     ) {
751     (*iter)->Reset();
752     }
753 schoenebeck 473 }
754    
755 schoenebeck 460 /**
756     * Copy all events from the engine channel's input event queue buffer to
757     * the internal event list. This will be done at the beginning of each
758     * audio cycle (that is each RenderAudio() call) to distinguish all
759     * events which have to be processed in the current audio cycle. Each
760     * EngineChannel has it's own input event queue for the common channel
761     * specific events (like NoteOn, NoteOff and ControlChange events).
762     * Beside that, the engine also has a input event queue for global
763     * events (usually SysEx messages).
764     *
765     * @param Samples - number of sample points to be processed in the
766     * current audio cycle
767     */
768     void EngineChannel::ImportEvents(uint Samples) {
769 schoenebeck 1659 // import events from pure software MIDI "devices"
770     // (e.g. virtual keyboard in instrument editor)
771     {
772     const int FragmentPos = 0; // randomly chosen, we don't care about jitter for virtual MIDI devices
773     Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
774     VirtualMidiDevice::event_t devEvent; // the event format we get from the virtual MIDI device
775     // as we're going to (carefully) write some status to the
776     // synchronized struct, we cast away the const
777     ArrayList<VirtualMidiDevice*>& devices =
778 schoenebeck 1662 const_cast<ArrayList<VirtualMidiDevice*>&>(virtualMidiDevicesReader_AudioThread.Lock());
779 schoenebeck 1659 // iterate through all virtual MIDI devices
780     for (int i = 0; i < devices.size(); i++) {
781     VirtualMidiDevice* pDev = devices[i];
782     // I think we can simply flush the whole FIFO(s), the user shouldn't be so fast ;-)
783     while (pDev->GetMidiEventFromDevice(devEvent)) {
784     event.Type =
785     (devEvent.Type == VirtualMidiDevice::EVENT_TYPE_NOTEON) ?
786     Event::type_note_on : Event::type_note_off;
787     event.Param.Note.Key = devEvent.Key;
788     event.Param.Note.Velocity = devEvent.Velocity;
789     event.pEngineChannel = this;
790     // copy event to internal event list
791     if (pEvents->poolIsEmpty()) {
792     dmsg(1,("Event pool emtpy!\n"));
793     goto exitVirtualDevicesLoop;
794     }
795     *pEvents->allocAppend() = event;
796     }
797     }
798     }
799     exitVirtualDevicesLoop:
800 schoenebeck 1662 virtualMidiDevicesReader_AudioThread.Unlock();
801 schoenebeck 1659
802     // import events from the regular MIDI devices
803 schoenebeck 970 RingBuffer<Event,false>::NonVolatileReader eventQueueReader = pEventQueue->get_non_volatile_reader();
804 schoenebeck 460 Event* pEvent;
805     while (true) {
806     // get next event from input event queue
807     if (!(pEvent = eventQueueReader.pop())) break;
808     // if younger event reached, ignore that and all subsequent ones for now
809     if (pEvent->FragmentPos() >= Samples) {
810     eventQueueReader--;
811     dmsg(2,("Younger Event, pos=%d ,Samples=%d!\n",pEvent->FragmentPos(),Samples));
812     pEvent->ResetFragmentPos();
813     break;
814     }
815     // copy event to internal event list
816     if (pEvents->poolIsEmpty()) {
817     dmsg(1,("Event pool emtpy!\n"));
818     break;
819     }
820     *pEvents->allocAppend() = *pEvent;
821     }
822     eventQueueReader.free(); // free all copied events from input queue
823     }
824    
825 schoenebeck 1001 void EngineChannel::RemoveAllFxSends() {
826     if (pEngine) pEngine->DisableAndLock();
827     if (!fxSends.empty()) { // free local render buffers
828     if (pChannelLeft) {
829     delete pChannelLeft;
830     if (pEngine && pEngine->pAudioOutputDevice) {
831     // fallback to render directly to the AudioOutputDevice's buffer
832     pChannelLeft = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelLeft);
833     } else pChannelLeft = NULL;
834     }
835     if (pChannelRight) {
836     delete pChannelRight;
837     if (pEngine && pEngine->pAudioOutputDevice) {
838     // fallback to render directly to the AudioOutputDevice's buffer
839     pChannelRight = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannelRight);
840 persson 1646 } else pChannelRight = NULL;
841 schoenebeck 1001 }
842     }
843     for (int i = 0; i < fxSends.size(); i++) delete fxSends[i];
844     fxSends.clear();
845     if (pEngine) pEngine->Enable();
846     }
847    
848 schoenebeck 1659 void EngineChannel::Connect(VirtualMidiDevice* pDevice) {
849     // double buffer ... double work ...
850     {
851     ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.GetConfigForUpdate();
852     devices.add(pDevice);
853     }
854     {
855     ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.SwitchConfig();
856     devices.add(pDevice);
857     }
858     }
859    
860     void EngineChannel::Disconnect(VirtualMidiDevice* pDevice) {
861     // double buffer ... double work ...
862     {
863     ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.GetConfigForUpdate();
864     devices.remove(pDevice);
865     }
866     {
867     ArrayList<VirtualMidiDevice*>& devices = virtualMidiDevices.SwitchConfig();
868     devices.remove(pDevice);
869     }
870     }
871    
872 schoenebeck 411 float EngineChannel::Volume() {
873     return GlobalVolume;
874     }
875    
876     void EngineChannel::Volume(float f) {
877     GlobalVolume = f;
878 schoenebeck 660 bStatusChanged = true; // status of engine channel has changed, so set notify flag
879 schoenebeck 411 }
880    
881 schoenebeck 1723 float EngineChannel::Pan() {
882     return float(iLastPanRequest - 64) / 64.0f;
883     }
884    
885     void EngineChannel::Pan(float f) {
886     int iMidiPan = int(f * 64.0f) + 64;
887     if (iMidiPan > 127) iMidiPan = 127;
888     else if (iMidiPan < 0) iMidiPan = 0;
889     GlobalPanLeft = Engine::PanCurve[128 - iMidiPan];
890     GlobalPanRight = Engine::PanCurve[iMidiPan];
891     iLastPanRequest = iMidiPan;
892     }
893    
894 schoenebeck 411 uint EngineChannel::Channels() {
895     return 2;
896     }
897    
898     String EngineChannel::InstrumentFileName() {
899     return InstrumentFile;
900     }
901    
902     String EngineChannel::InstrumentName() {
903     return InstrumentIdxName;
904     }
905    
906     int EngineChannel::InstrumentIndex() {
907     return InstrumentIdx;
908     }
909    
910     int EngineChannel::InstrumentStatus() {
911     return InstrumentStat;
912 persson 438 }
913 schoenebeck 411
914 schoenebeck 475 String EngineChannel::EngineName() {
915     return LS_GIG_ENGINE_NAME;
916     }
917 schoenebeck 660
918 schoenebeck 411 }} // namespace LinuxSampler::gig

  ViewVC Help
Powered by ViewVC