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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 554 by schoenebeck, Thu May 19 19:25:14 2005 UTC revision 1761 by iliev, Fri Aug 29 15:42:06 2008 UTC
# Line 3  Line 3 
3   *   LinuxSampler - modular, streaming capable sampler                     *   *   LinuxSampler - modular, streaming capable sampler                     *
4   *                                                                         *   *                                                                         *
5   *   Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck   *   *   Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck   *
6   *   Copyright (C) 2005 Christian Schoenebeck                              *   *   Copyright (C) 2005 - 2008 Christian Schoenebeck                       *
7   *                                                                         *   *                                                                         *
8   *   This program is free software; you can redistribute it and/or modify  *   *   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  *   *   it under the terms of the GNU General Public License as published by  *
# Line 23  Line 23 
23    
24  #include "EngineChannel.h"  #include "EngineChannel.h"
25    
26    #include "../../common/global_private.h"
27    #include "../../Sampler.h"
28    
29  namespace LinuxSampler { namespace gig {  namespace LinuxSampler { namespace gig {
30    
31      EngineChannel::EngineChannel() {      EngineChannel::EngineChannel() :
32            InstrumentChangeCommandReader(InstrumentChangeCommand),
33            virtualMidiDevicesReader_AudioThread(virtualMidiDevices),
34            virtualMidiDevicesReader_MidiThread(virtualMidiDevices)
35        {
36          pMIDIKeyInfo = new midi_key_info_t[128];          pMIDIKeyInfo = new midi_key_info_t[128];
37          pEngine      = NULL;          pEngine      = NULL;
38          pInstrument  = NULL;          pInstrument  = NULL;
39          pEvents      = NULL; // we allocate when we retrieve the right Engine object          pEvents      = NULL; // we allocate when we retrieve the right Engine object
40          pCCEvents    = NULL; // we allocate when we retrieve the right Engine object          pEventQueue  = new RingBuffer<Event,false>(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0);
         pEventQueue  = new RingBuffer<Event>(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0);  
41          pActiveKeys  = new Pool<uint>(128);          pActiveKeys  = new Pool<uint>(128);
42          for (uint i = 0; i < 128; i++) {          for (uint i = 0; i < 128; i++) {
43              pMIDIKeyInfo[i].pActiveVoices  = NULL; // we allocate when we retrieve the right Engine object              pMIDIKeyInfo[i].pActiveVoices  = NULL; // we allocate when we retrieve the right Engine object
# Line 42  namespace LinuxSampler { namespace gig { Line 48  namespace LinuxSampler { namespace gig {
48              pMIDIKeyInfo[i].VoiceTheftsQueued = 0;              pMIDIKeyInfo[i].VoiceTheftsQueued = 0;
49              pMIDIKeyInfo[i].RoundRobinIndex = 0;              pMIDIKeyInfo[i].RoundRobinIndex = 0;
50          }          }
         for (uint i = 0; i < Event::destination_count; i++) {  
             pSynthesisEvents[i] = NULL; // we allocate when we retrieve the right Engine object  
         }  
51          InstrumentIdx  = -1;          InstrumentIdx  = -1;
52          InstrumentStat = -1;          InstrumentStat = -1;
53            pChannelLeft  = NULL;
54            pChannelRight = NULL;
55          AudioDeviceChannelLeft  = -1;          AudioDeviceChannelLeft  = -1;
56          AudioDeviceChannelRight = -1;          AudioDeviceChannelRight = -1;
57            pMidiInputPort = NULL;
58            midiChannel = midi_chan_all;
59            ResetControllers();
60            SoloMode       = false;
61            PortamentoMode = false;
62            PortamentoTime = CONFIG_PORTAMENTO_TIME_DEFAULT;
63      }      }
64    
65      EngineChannel::~EngineChannel() {      EngineChannel::~EngineChannel() {
66          DisconnectAudioOutputDevice();          DisconnectAudioOutputDevice();
         if (pInstrument) Engine::instruments.HandBack(pInstrument, this);  
67          if (pEventQueue) delete pEventQueue;          if (pEventQueue) delete pEventQueue;
68          if (pActiveKeys) delete pActiveKeys;          if (pActiveKeys) delete pActiveKeys;
69          if (pMIDIKeyInfo) delete[] pMIDIKeyInfo;          if (pMIDIKeyInfo) delete[] pMIDIKeyInfo;
70            RemoveAllFxSends();
71        }
72    
73        /**
74         * 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         * 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         * TODO: This "poll method" is just a lazy solution and might be
84         *       replaced in future.
85         * @param bNewStatus - (optional, default: false) sets the new status flag
86         * @returns true if engine channel status has changed since last
87         *          StatusChanged() call
88         */
89        bool EngineChannel::StatusChanged(bool bNewStatus) {
90            bool b = bStatusChanged;
91            bStatusChanged = bNewStatus;
92            return b;
93        }
94    
95        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      /**      /**
106       * This method is not thread safe!       * This method is not thread safe!
107       */       */
108      void EngineChannel::ResetInternal() {      void EngineChannel::ResetInternal() {
         Pitch               = 0;  
         SustainPedal        = false;  
         GlobalVolume        = 1.0;  
         GlobalPanLeft       = 1.0f;  
         GlobalPanRight      = 1.0f;  
109          CurrentKeyDimension = 0;          CurrentKeyDimension = 0;
110    
         ResetControllers();  
   
111          // reset key info          // reset key info
112          for (uint i = 0; i < 128; i++) {          for (uint i = 0; i < 128; i++) {
113              if (pMIDIKeyInfo[i].pActiveVoices)              if (pMIDIKeyInfo[i].pActiveVoices)
# Line 84  namespace LinuxSampler { namespace gig { Line 120  namespace LinuxSampler { namespace gig {
120              pMIDIKeyInfo[i].itSelf         = Pool<uint>::Iterator();              pMIDIKeyInfo[i].itSelf         = Pool<uint>::Iterator();
121              pMIDIKeyInfo[i].VoiceTheftsQueued = 0;              pMIDIKeyInfo[i].VoiceTheftsQueued = 0;
122          }          }
123            SoloKey       = -1;    // no solo key active yet
124            PortamentoPos = -1.0f; // no portamento active yet
125    
126          // reset all key groups          // reset all key groups
127          std::map<uint,uint*>::iterator iter = ActiveKeyGroups.begin();          std::map<uint,uint*>::iterator iter = ActiveKeyGroups.begin();
# Line 96  namespace LinuxSampler { namespace gig { Line 134  namespace LinuxSampler { namespace gig {
134          pEventQueue->init();          pEventQueue->init();
135    
136          if (pEngine) pEngine->ResetInternal();          if (pEngine) pEngine->ResetInternal();
137    
138            // status of engine channel has changed, so set notify flag
139            bStatusChanged = true;
140      }      }
141    
142      LinuxSampler::Engine* EngineChannel::GetEngine() {      LinuxSampler::Engine* EngineChannel::GetEngine() {
# Line 124  namespace LinuxSampler { namespace gig { Line 165  namespace LinuxSampler { namespace gig {
165       * This method will then actually start to load the instrument and block       * This method will then actually start to load the instrument and block
166       * the calling thread until loading was completed.       * the calling thread until loading was completed.
167       *       *
      * @returns detailed description of the method call result  
168       * @see PrepareLoadInstrument()       * @see PrepareLoadInstrument()
169       */       */
170      void EngineChannel::LoadInstrument() {      void EngineChannel::LoadInstrument() {
171            // make sure we don't trigger any new notes with an old
172          if (pEngine) pEngine->DisableAndLock();          // instrument
173            instrument_change_command_t& cmd = ChangeInstrument(0);
174          ResetInternal();          if (cmd.pInstrument) {
175                // give old instrument back to instrument manager, but
176          // free old instrument              // keep the dimension regions and samples that are in use
177          if (pInstrument) {              Engine::instruments.HandBackInstrument(cmd.pInstrument, this, cmd.pDimRegionsInUse);
             // give old instrument back to instrument manager  
             Engine::instruments.HandBack(pInstrument, this);  
178          }          }
179            cmd.pDimRegionsInUse->clear();
180    
181          // delete all key groups          // delete all key groups
182          ActiveKeyGroups.clear();          ActiveKeyGroups.clear();
183    
184          // request gig instrument from instrument manager          // request gig instrument from instrument manager
185            ::gig::Instrument* newInstrument;
186          try {          try {
187              instrument_id_t instrid;              InstrumentManager::instrument_id_t instrid;
188              instrid.FileName    = InstrumentFile;              instrid.FileName  = InstrumentFile;
189              instrid.iInstrument = InstrumentIdx;              instrid.Index     = InstrumentIdx;
190              pInstrument = Engine::instruments.Borrow(instrid, this);              newInstrument = Engine::instruments.Borrow(instrid, this);
191              if (!pInstrument) {              if (!newInstrument) {
192                  InstrumentStat = -1;                  throw InstrumentManagerException("resource was not created");
                 dmsg(1,("no instrument loaded!!!\n"));  
                 exit(EXIT_FAILURE);  
193              }              }
194          }          }
195          catch (RIFF::Exception e) {          catch (RIFF::Exception e) {
196              InstrumentStat = -2;              InstrumentStat = -2;
197                StatusChanged(true);
198              String msg = "gig::Engine error: Failed to load instrument, cause: " + e.Message;              String msg = "gig::Engine error: Failed to load instrument, cause: " + e.Message;
199              throw LinuxSamplerException(msg);              throw Exception(msg);
200          }          }
201          catch (InstrumentResourceManagerException e) {          catch (InstrumentManagerException e) {
202              InstrumentStat = -3;              InstrumentStat = -3;
203                StatusChanged(true);
204              String msg = "gig::Engine error: Failed to load instrument, cause: " + e.Message();              String msg = "gig::Engine error: Failed to load instrument, cause: " + e.Message();
205              throw LinuxSamplerException(msg);              throw Exception(msg);
206          }          }
207          catch (...) {          catch (...) {
208              InstrumentStat = -4;              InstrumentStat = -4;
209              throw LinuxSamplerException("gig::Engine error: Failed to load instrument, cause: Unknown exception while trying to parse gig file.");              StatusChanged(true);
210                throw Exception("gig::Engine error: Failed to load instrument, cause: Unknown exception while trying to parse gig file.");
211          }          }
212    
213          // rebuild ActiveKeyGroups map with key groups of current instrument          // rebuild ActiveKeyGroups map with key groups of current instrument
214          for (::gig::Region* pRegion = pInstrument->GetFirstRegion(); pRegion; pRegion = pInstrument->GetNextRegion())          for (::gig::Region* pRegion = newInstrument->GetFirstRegion(); pRegion; pRegion = newInstrument->GetNextRegion())
215              if (pRegion->KeyGroup) ActiveKeyGroups[pRegion->KeyGroup] = NULL;              if (pRegion->KeyGroup) ActiveKeyGroups[pRegion->KeyGroup] = NULL;
216    
217          InstrumentIdxName = pInstrument->pInfo->Name;          InstrumentIdxName = newInstrument->pInfo->Name;
218          InstrumentStat = 100;          InstrumentStat = 100;
219    
220          // inform audio driver for the need of two channels          ChangeInstrument(newInstrument);
         try {  
             if (pEngine && pEngine->pAudioOutputDevice)  
                 pEngine->pAudioOutputDevice->AcquireChannels(2); // gig Engine only stereo  
         }  
         catch (AudioOutputException e) {  
             String msg = "Audio output device unable to provide 2 audio channels, cause: " + e.Message();  
             throw LinuxSamplerException(msg);  
         }  
221    
222          if (pEngine) pEngine->Enable();          StatusChanged(true);
223        }
224    
225    
226        /**
227         * 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      /**      /**
# Line 208  namespace LinuxSampler { namespace gig { Line 258  namespace LinuxSampler { namespace gig {
258      void EngineChannel::ResourceUpdated(::gig::Instrument* pOldResource, ::gig::Instrument* pNewResource, void* pUpdateArg) {      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())          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();          if (pEngine) pEngine->Enable();
261            bStatusChanged = true; // status of engine has changed, so set notify flag
262      }      }
263    
264      /**      /**
# Line 219  namespace LinuxSampler { namespace gig { Line 270  namespace LinuxSampler { namespace gig {
270      void EngineChannel::OnResourceProgress(float fProgress) {      void EngineChannel::OnResourceProgress(float fProgress) {
271          this->InstrumentStat = int(fProgress * 100.0f);          this->InstrumentStat = int(fProgress * 100.0f);
272          dmsg(7,("gig::EngineChannel: progress %d%", InstrumentStat));          dmsg(7,("gig::EngineChannel: progress %d%", InstrumentStat));
273            bStatusChanged = true; // status of engine has changed, so set notify flag
274      }      }
275    
276      void EngineChannel::Connect(AudioOutputDevice* pAudioOut) {      void EngineChannel::Connect(AudioOutputDevice* pAudioOut) {
# Line 228  namespace LinuxSampler { namespace gig { Line 280  namespace LinuxSampler { namespace gig {
280          }          }
281          pEngine = Engine::AcquireEngine(this, pAudioOut);          pEngine = Engine::AcquireEngine(this, pAudioOut);
282          ResetInternal();          ResetInternal();
283          pEvents   = new RTList<Event>(pEngine->pEventPool);          pEvents = new RTList<Event>(pEngine->pEventPool);
284          pCCEvents = new RTList<Event>(pEngine->pEventPool);  
285          for (uint i = 0; i < Event::destination_count; i++) {          // reset the instrument change command struct (need to be done
286              pSynthesisEvents[i] = new RTList<Event>(pEngine->pEventPool);          // 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          for (uint i = 0; i < 128; i++) {          for (uint i = 0; i < 128; i++) {
301              pMIDIKeyInfo[i].pActiveVoices = new RTList<Voice>(pEngine->pVoicePool);              pMIDIKeyInfo[i].pActiveVoices = new RTList<Voice>(pEngine->pVoicePool);
302              pMIDIKeyInfo[i].pEvents       = new RTList<Event>(pEngine->pEventPool);              pMIDIKeyInfo[i].pEvents       = new RTList<Event>(pEngine->pEventPool);
303          }          }
304          AudioDeviceChannelLeft  = 0;          AudioDeviceChannelLeft  = 0;
305          AudioDeviceChannelRight = 1;          AudioDeviceChannelRight = 1;
306          pOutputLeft             = pAudioOut->Channel(0)->Buffer();          if (fxSends.empty()) { // render directly into the AudioDevice's output buffers
307          pOutputRight            = pAudioOut->Channel(1)->Buffer();              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            if (pEngine->EngineDisabled.GetUnsafe()) pEngine->Enable();
317            MidiInputPort::AddSysexListener(pEngine);
318      }      }
319    
320      void EngineChannel::DisconnectAudioOutputDevice() {      void EngineChannel::DisconnectAudioOutputDevice() {
321          if (pEngine) { // if clause to prevent disconnect loops          if (pEngine) { // if clause to prevent disconnect loops
322    
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    
334                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              ResetInternal();              ResetInternal();
352              if (pEvents) {              if (pEvents) {
353                  delete pEvents;                  delete pEvents;
354                  pEvents = NULL;                  pEvents = NULL;
355              }              }
             if (pCCEvents) {  
                 delete pCCEvents;  
                 pCCEvents = NULL;  
             }  
356              for (uint i = 0; i < 128; i++) {              for (uint i = 0; i < 128; i++) {
357                  if (pMIDIKeyInfo[i].pActiveVoices) {                  if (pMIDIKeyInfo[i].pActiveVoices) {
358                      delete pMIDIKeyInfo[i].pActiveVoices;                      delete pMIDIKeyInfo[i].pActiveVoices;
# Line 264  namespace LinuxSampler { namespace gig { Line 363  namespace LinuxSampler { namespace gig {
363                      pMIDIKeyInfo[i].pEvents = NULL;                      pMIDIKeyInfo[i].pEvents = NULL;
364                  }                  }
365              }              }
             for (uint i = 0; i < Event::destination_count; i++) {  
                 if (pSynthesisEvents[i]) {  
                     delete pSynthesisEvents[i];  
                     pSynthesisEvents[i] = NULL;  
                 }  
             }  
366              Engine* oldEngine = pEngine;              Engine* oldEngine = pEngine;
367              AudioOutputDevice* oldAudioDevice = pEngine->pAudioOutputDevice;              AudioOutputDevice* oldAudioDevice = pEngine->pAudioOutputDevice;
368              pEngine = NULL;              pEngine = NULL;
369              Engine::FreeEngine(this, oldAudioDevice);              Engine::FreeEngine(this, oldAudioDevice);
370              AudioDeviceChannelLeft  = -1;              AudioDeviceChannelLeft  = -1;
371              AudioDeviceChannelRight = -1;              AudioDeviceChannelRight = -1;
372                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          }          }
379      }      }
380    
381        AudioOutputDevice* EngineChannel::GetAudioOutputDevice() {
382            return (pEngine) ? pEngine->pAudioOutputDevice : NULL;
383        }
384    
385      void EngineChannel::SetOutputChannel(uint EngineAudioChannel, uint AudioDeviceChannel) {      void EngineChannel::SetOutputChannel(uint EngineAudioChannel, uint AudioDeviceChannel) {
386          if (!pEngine || !pEngine->pAudioOutputDevice) throw AudioOutputException("No audio output device connected yet.");          if (!pEngine || !pEngine->pAudioOutputDevice) throw AudioOutputException("No audio output device connected yet.");
387    
# Line 286  namespace LinuxSampler { namespace gig { Line 389  namespace LinuxSampler { namespace gig {
389          if (!pChannel) throw AudioOutputException("Invalid audio output device channel " + ToString(AudioDeviceChannel));          if (!pChannel) throw AudioOutputException("Invalid audio output device channel " + ToString(AudioDeviceChannel));
390          switch (EngineAudioChannel) {          switch (EngineAudioChannel) {
391              case 0: // left output channel              case 0: // left output channel
392                  pOutputLeft = pChannel->Buffer();                  if (fxSends.empty()) pChannelLeft = pChannel;
393                  AudioDeviceChannelLeft = AudioDeviceChannel;                  AudioDeviceChannelLeft = AudioDeviceChannel;
394                  break;                  break;
395              case 1: // right output channel              case 1: // right output channel
396                  pOutputRight = pChannel->Buffer();                  if (fxSends.empty()) pChannelRight = pChannel;
397                  AudioDeviceChannelRight = AudioDeviceChannel;                  AudioDeviceChannelRight = AudioDeviceChannel;
398                  break;                  break;
399              default:              default:
400                  throw AudioOutputException("Invalid engine audio channel " + ToString(EngineAudioChannel));                  throw AudioOutputException("Invalid engine audio channel " + ToString(EngineAudioChannel));
401          }          }
402    
403            bStatusChanged = true;
404      }      }
405    
406      int EngineChannel::OutputChannel(uint EngineAudioChannel) {      int EngineChannel::OutputChannel(uint EngineAudioChannel) {
# Line 309  namespace LinuxSampler { namespace gig { Line 414  namespace LinuxSampler { namespace gig {
414          }          }
415      }      }
416    
417        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        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            fireFxSendCountChanged(GetSamplerChannel()->Index(), GetFxSendCount());
457    
458            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            fireFxSendCountChanged(GetSamplerChannel()->Index(), GetFxSendCount());
496        }
497    
498      /**      /**
499       *  Will be called by the MIDIIn Thread to let the audio thread trigger a new       *  Will be called by the MIDIIn Thread to let the audio thread trigger a new
500       *  voice for the given key.       *  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       *       *
504       *  @param Key      - MIDI key number of the triggered key       *  @param Key      - MIDI key number of the triggered key
505       *  @param Velocity - MIDI velocity value of the triggered key       *  @param Velocity - MIDI velocity value of the triggered key
# Line 325  namespace LinuxSampler { namespace gig { Line 513  namespace LinuxSampler { namespace gig {
513              event.pEngineChannel      = this;              event.pEngineChannel      = this;
514              if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);              if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
515              else dmsg(1,("EngineChannel: Input event queue full!"));              else dmsg(1,("EngineChannel: Input event queue full!"));
516                // 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            }
527        }
528    
529        /**
530         *  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                // 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          }          }
563      }      }
564    
565      /**      /**
566       *  Will be called by the MIDIIn Thread to signal the audio thread to release       *  Will be called by the MIDIIn Thread to signal the audio thread to release
567       *  voice(s) on the given key.       *  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       *       *
571       *  @param Key      - MIDI key number of the released key       *  @param Key      - MIDI key number of the released key
572       *  @param Velocity - MIDI release velocity value of the released key       *  @param Velocity - MIDI release velocity value of the released key
# Line 344  namespace LinuxSampler { namespace gig { Line 580  namespace LinuxSampler { namespace gig {
580              event.pEngineChannel      = this;              event.pEngineChannel      = this;
581              if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);              if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
582              else dmsg(1,("EngineChannel: Input event queue full!"));              else dmsg(1,("EngineChannel: Input event queue full!"));
583                // 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            }
594        }
595    
596        /**
597         *  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                // 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          }          }
630      }      }
631    
632      /**      /**
633       *  Will be called by the MIDIIn Thread to signal the audio thread to change       *  Will be called by the MIDIIn Thread to signal the audio thread to change
634       *  the pitch value for all voices.       *  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       *       *
638       *  @param Pitch - MIDI pitch value (-8192 ... +8191)       *  @param Pitch - MIDI pitch value (-8192 ... +8191)
639       */       */
# Line 365  namespace LinuxSampler { namespace gig { Line 649  namespace LinuxSampler { namespace gig {
649      }      }
650    
651      /**      /**
652         *  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       *  Will be called by the MIDIIn Thread to signal the audio thread that a       *  Will be called by the MIDIIn Thread to signal the audio thread that a
677       *  continuous controller value has changed.       *  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       *       *
681       *  @param Controller - MIDI controller number of the occured control change       *  @param Controller - MIDI controller number of the occured control change
682       *  @param Value      - value of the control change       *  @param Value      - value of the control change
# Line 383  namespace LinuxSampler { namespace gig { Line 693  namespace LinuxSampler { namespace gig {
693          }          }
694      }      }
695    
696        /**
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      void EngineChannel::ClearEventLists() {      void EngineChannel::ClearEventLists() {
723          pEvents->clear();          pEvents->clear();
         pCCEvents->clear();  
         for (uint i = 0; i < Event::destination_count; i++) {  
             pSynthesisEvents[i]->clear();  
         }  
724          // empty MIDI key specific event lists          // empty MIDI key specific event lists
725          {          {
726              RTList<uint>::Iterator iuiKey = pActiveKeys->first();              RTList<uint>::Iterator iuiKey = pActiveKeys->first();
# Line 400  namespace LinuxSampler { namespace gig { Line 732  namespace LinuxSampler { namespace gig {
732      }      }
733    
734      void EngineChannel::ResetControllers() {      void EngineChannel::ResetControllers() {
735            Pitch          = 0;
736            SustainPedal   = false;
737            SostenutoPedal = false;
738            GlobalVolume   = 1.0f;
739            MidiVolume     = 1.0;
740            GlobalPanLeft  = 1.0f;
741            GlobalPanRight = 1.0f;
742            iLastPanRequest = 64;
743            GlobalTranspose = 0;
744          // set all MIDI controller values to zero          // set all MIDI controller values to zero
745          memset(ControllerTable, 0x00, 128);          memset(ControllerTable, 0x00, 129);
746            // 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      }      }
754    
755      /**      /**
# Line 418  namespace LinuxSampler { namespace gig { Line 766  namespace LinuxSampler { namespace gig {
766       *                  current audio cycle       *                  current audio cycle
767       */       */
768      void EngineChannel::ImportEvents(uint Samples) {      void EngineChannel::ImportEvents(uint Samples) {
769          RingBuffer<Event>::NonVolatileReader eventQueueReader = pEventQueue->get_non_volatile_reader();          // 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                    const_cast<ArrayList<VirtualMidiDevice*>&>(virtualMidiDevicesReader_AudioThread.Lock());
779                // 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            virtualMidiDevicesReader_AudioThread.Unlock();
801    
802            // import events from the regular MIDI devices
803            RingBuffer<Event,false>::NonVolatileReader eventQueueReader = pEventQueue->get_non_volatile_reader();
804          Event* pEvent;          Event* pEvent;
805          while (true) {          while (true) {
806              // get next event from input event queue              // get next event from input event queue
# Line 440  namespace LinuxSampler { namespace gig { Line 822  namespace LinuxSampler { namespace gig {
822          eventQueueReader.free(); // free all copied events from input queue          eventQueueReader.free(); // free all copied events from input queue
823      }      }
824    
825        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                    } else pChannelRight = NULL;
841                }
842            }
843            for (int i = 0; i < fxSends.size(); i++) delete fxSends[i];
844            fxSends.clear();
845            if (pEngine) pEngine->Enable();
846        }
847    
848        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      float EngineChannel::Volume() {      float EngineChannel::Volume() {
873          return GlobalVolume;          return GlobalVolume;
874      }      }
875    
876      void EngineChannel::Volume(float f) {      void EngineChannel::Volume(float f) {
877          GlobalVolume = f;          GlobalVolume = f;
878            bStatusChanged = true; // status of engine channel has changed, so set notify flag
879        }
880    
881        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      uint EngineChannel::Channels() {      uint EngineChannel::Channels() {
# Line 471  namespace LinuxSampler { namespace gig { Line 914  namespace LinuxSampler { namespace gig {
914      String EngineChannel::EngineName() {      String EngineChannel::EngineName() {
915          return LS_GIG_ENGINE_NAME;          return LS_GIG_ENGINE_NAME;
916      }      }
917        
918  }} // namespace LinuxSampler::gig  }} // namespace LinuxSampler::gig

Legend:
Removed from v.554  
changed lines
  Added in v.1761

  ViewVC Help
Powered by ViewVC