/[svn]/linuxsampler/trunk/src/engines/EngineBase.h
ViewVC logotype

Diff of /linuxsampler/trunk/src/engines/EngineBase.h

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

revision 2027 by iliev, Tue Nov 3 19:27:42 2009 UTC revision 2618 by schoenebeck, Wed Jun 11 11:39:44 2014 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-2009 Christian Schoenebeck                         *   *   Copyright (C) 2005-2008 Christian Schoenebeck                         *
7   *   Copyright (C) 2009 Grigor Iliev                                       *   *   Copyright (C) 2009-2013 Christian Schoenebeck and Grigor Iliev        *
8   *                                                                         *   *                                                                         *
9   *   This program is free software; you can redistribute it and/or modify  *   *   This program is free software; you can redistribute it and/or modify  *
10   *   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 104  namespace LinuxSampler { Line 104  namespace LinuxSampler {
104               *  @param Samples - number of sample points to be rendered               *  @param Samples - number of sample points to be rendered
105               *  @returns       0 on success               *  @returns       0 on success
106               */               */
107              virtual int RenderAudio(uint Samples) {              virtual int RenderAudio(uint Samples) OVERRIDE {
108                  dmsg(8,("RenderAudio(Samples=%d)\n", Samples));                  dmsg(8,("RenderAudio(Samples=%d)\n", Samples));
109    
110                  // return if engine disabled                  // return if engine disabled
# Line 143  namespace LinuxSampler { Line 143  namespace LinuxSampler {
143                          }                          }
144                      }                      }
145                  }                  }
146                    
147                    // In case scale tuning has been changed, recalculate pitch for
148                    // all active voices.
149                    ProcessScaleTuningChange();
150    
151                  // reset internal voice counter (just for statistic of active voices)                  // reset internal voice counter (just for statistic of active voices)
152                  ActiveVoiceCountTemp = 0;                  ActiveVoiceCountTemp = 0;
# Line 190  namespace LinuxSampler { Line 194  namespace LinuxSampler {
194                  // been deleted by the disk thread                  // been deleted by the disk thread
195                  if (iPendingStreamDeletions) ProcessPendingStreamDeletions();                  if (iPendingStreamDeletions) ProcessPendingStreamDeletions();
196    
197                    // Release the instrument change command. (This has to
198                    // be done after all voices have been rendered and not
199                    // in HandleInstrumentChanges, as the RegionsInUse
200                    // list has been built up by the voice renderers.)
201                    for (int i = 0; i < engineChannels.size(); i++) {
202                        EngineChannelBase<V, R, I>* channel =
203                            static_cast<EngineChannelBase<V, R, I>*>(engineChannels[i]);
204                        channel->InstrumentChangeCommandReader.Unlock();
205                    }
206                  FrameTime += Samples;                  FrameTime += Samples;
207    
208                  EngineDisabled.RttDone();                  EngineDisabled.RttDone();
209                  return 0;                  return 0;
210              }              }
211    
212              virtual int MaxVoices() { return pVoicePool->poolSize(); }              virtual int MaxVoices() OVERRIDE { return pVoicePool->poolSize(); }
213    
214              virtual void SetMaxVoices(int iVoices) throw (Exception) {              virtual void SetMaxVoices(int iVoices) throw (Exception) OVERRIDE {
215                  if (iVoices < 1)                  if (iVoices < 1)
216                      throw Exception("Maximum voices for an engine cannot be set lower than 1");                      throw Exception("Maximum voices for an engine cannot be set lower than 1");
217    
# Line 234  namespace LinuxSampler { Line 247  namespace LinuxSampler {
247                  }                  }
248                  pVoicePool->clear();                  pVoicePool->clear();
249    
250                    PostSetMaxVoices(iVoices);
251                  ResumeAll();                  ResumeAll();
252              }              }
253                
254                /** Called after the new max number of voices is set and before resuming the engine. */
255                virtual void PostSetMaxVoices(int iVoices) { }
256    
257              virtual uint DiskStreamCount() { return (pDiskThread) ? pDiskThread->GetActiveStreamCount() : 0; }              virtual uint DiskStreamCount() OVERRIDE { return (pDiskThread) ? pDiskThread->GetActiveStreamCount() : 0; }
258              virtual uint DiskStreamCountMax() { return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0; }              virtual uint DiskStreamCountMax() OVERRIDE { return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0; }
259              virtual int  MaxDiskStreams() { return iMaxDiskStreams; }              virtual int  MaxDiskStreams() OVERRIDE { return iMaxDiskStreams; }
260    
261              virtual void SetMaxDiskStreams(int iStreams) throw (Exception) {              virtual void SetMaxDiskStreams(int iStreams) throw (Exception) OVERRIDE {
262                  if (iStreams < 0)                  if (iStreams < 0)
263                      throw Exception("Maximum disk streams for an engine cannot be set lower than 0");                      throw Exception("Maximum disk streams for an engine cannot be set lower than 0");
264    
# Line 256  namespace LinuxSampler { Line 273  namespace LinuxSampler {
273                  ResumeAll();                  ResumeAll();
274              }              }
275    
276              virtual String DiskStreamBufferFillBytes() { return (pDiskThread) ? pDiskThread->GetBufferFillBytes() : ""; }              virtual String DiskStreamBufferFillBytes() OVERRIDE { return (pDiskThread) ? pDiskThread->GetBufferFillBytes() : ""; }
277              virtual String DiskStreamBufferFillPercentage() { return (pDiskThread) ? pDiskThread->GetBufferFillPercentage() : ""; }              virtual String DiskStreamBufferFillPercentage() OVERRIDE { return (pDiskThread) ? pDiskThread->GetBufferFillPercentage() : ""; }
278              virtual InstrumentManager* GetInstrumentManager() { return &instruments; }              virtual InstrumentManager* GetInstrumentManager() OVERRIDE { return &instruments; }
279    
280              /**              /**
281               * Connect this engine instance with the given audio output device.               * Connect this engine instance with the given audio output device.
# Line 269  namespace LinuxSampler { Line 286  namespace LinuxSampler {
286               *               *
287               * @param pAudioOut - audio output device to connect to               * @param pAudioOut - audio output device to connect to
288               */               */
289              virtual void Connect(AudioOutputDevice* pAudioOut) {              virtual void Connect(AudioOutputDevice* pAudioOut) OVERRIDE {
290                  // caution: don't ignore if connecting to the same device here,                  // caution: don't ignore if connecting to the same device here,
291                  // because otherwise SetMaxDiskStreams() implementation won't work anymore!                  // because otherwise SetMaxDiskStreams() implementation won't work anymore!
292    
# Line 299  namespace LinuxSampler { Line 316  namespace LinuxSampler {
316                      // lower minimum release time                      // lower minimum release time
317                      const float minReleaseTime = (float) MaxSamplesPerCycle / (float) SampleRate;                      const float minReleaseTime = (float) MaxSamplesPerCycle / (float) SampleRate;
318                      for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {                      for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
319                          iterVoice->EG1.CalculateFadeOutCoeff(minReleaseTime, SampleRate);                          iterVoice->CalculateFadeOutCoeff(minReleaseTime, SampleRate);
320                      }                      }
321                      pVoicePool->clear();                      pVoicePool->clear();
322                  }                  }
# Line 332  namespace LinuxSampler { Line 349  namespace LinuxSampler {
349                  pDiskThread->StartThread();                  pDiskThread->StartThread();
350                  dmsg(1,("OK\n"));                  dmsg(1,("OK\n"));
351    
352                    bool printEqInfo = true;
353                  for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {                  for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
354                      if (!iterVoice->pDiskThread) {                      if (!iterVoice->pDiskThread) {
355                          dmsg(0,("Engine -> voice::trigger: !pDiskThread\n"));                          dmsg(0,("Engine -> voice::trigger: !pDiskThread\n"));
356                          exit(EXIT_FAILURE);                          exit(EXIT_FAILURE);
357                      }                      }
358                        
359                        iterVoice->CreateEq();
360                        
361                        if(printEqInfo) {
362                            iterVoice->PrintEqInfo();
363                            printEqInfo = false;
364                        }
365                  }                  }
366                  pVoicePool->clear();                  pVoicePool->clear();
367                    
368                    // (re)create dedicated voice audio buffers
369                    //TODO: we could optimize resource usage a bit by just allocating these dedicated voice buffers when there is at least one engine channel with FX sends, because only in this case those special buffers are used actually, but since it would usually only save couple bytes in total, its probably not worth it
370                    if (pDedicatedVoiceChannelLeft)  delete pDedicatedVoiceChannelLeft;
371                    if (pDedicatedVoiceChannelRight) delete pDedicatedVoiceChannelRight;
372                    pDedicatedVoiceChannelLeft  = new AudioChannel(0, MaxSamplesPerCycle);
373                    pDedicatedVoiceChannelRight = new AudioChannel(1, MaxSamplesPerCycle);
374                }
375            
376                // Implementattion for abstract method derived from Engine.
377                virtual void ReconnectAudioOutputDevice() OVERRIDE {
378                    SuspendAll();
379                    if (pAudioOutputDevice) Connect(pAudioOutputDevice);
380                    ResumeAll();
381              }              }
382    
383              /**              /**
# Line 394  namespace LinuxSampler { Line 433  namespace LinuxSampler {
433               */               */
434              virtual void Suspend(RR* pRegion) {              virtual void Suspend(RR* pRegion) {
435                  dmsg(2,("EngineBase: Suspending Region %x ...\n",pRegion));                  dmsg(2,("EngineBase: Suspending Region %x ...\n",pRegion));
436                  SuspendedRegionsMutex.Lock();                  {
437                  SuspensionChangeOngoing.Set(true);                      LockGuard lock(SuspendedRegionsMutex);
438                  pPendingRegionSuspension = pRegion;                      SuspensionChangeOngoing.Set(true);
439                  SuspensionChangeOngoing.WaitAndUnlockIf(true);                      pPendingRegionSuspension = pRegion;
440                  SuspendedRegionsMutex.Unlock();                      SuspensionChangeOngoing.WaitAndUnlockIf(true);
441                    }
442                  dmsg(2,("EngineBase: Region %x suspended.",pRegion));                  dmsg(2,("EngineBase: Region %x suspended.",pRegion));
443              }              }
444    
# Line 410  namespace LinuxSampler { Line 450  namespace LinuxSampler {
450               */               */
451              virtual void Resume(RR* pRegion) {              virtual void Resume(RR* pRegion) {
452                  dmsg(2,("EngineBase: Resuming Region %x ...\n",pRegion));                  dmsg(2,("EngineBase: Resuming Region %x ...\n",pRegion));
453                  SuspendedRegionsMutex.Lock();                  {
454                  SuspensionChangeOngoing.Set(true);                      LockGuard lock(SuspendedRegionsMutex);
455                  pPendingRegionResumption = pRegion;                      SuspensionChangeOngoing.Set(true);
456                  SuspensionChangeOngoing.WaitAndUnlockIf(true);                      pPendingRegionResumption = pRegion;
457                  SuspendedRegionsMutex.Unlock();                      SuspensionChangeOngoing.WaitAndUnlockIf(true);
458                    }
459                  dmsg(2,("EngineBase: Region %x resumed.\n",pRegion));                  dmsg(2,("EngineBase: Region %x resumed.\n",pRegion));
460              }              }
461    
# Line 530  namespace LinuxSampler { Line 571  namespace LinuxSampler {
571    
572              //friend class EngineChannelBase<V, R, I>;              //friend class EngineChannelBase<V, R, I>;
573    
574                static IM instruments;
575    
576          protected:          protected:
577              class SuspensionVoiceHandler : public MidiKeyboardManager<V>::VoiceHandler {              class SuspensionVoiceHandler : public MidiKeyboardManager<V>::VoiceHandler {
578              public:              public:
579                  int PendingStreamDeletions;                  int PendingStreamDeletions;
580                  RR* pPendingRegionSuspension;                  RR* pPendingRegionSuspension;
581    
582                  SuspensionVoiceHandler(RR* pPendingRegionSuspension) {                  SuspensionVoiceHandler(RR* pPendingRegionSuspension) {
583                      PendingStreamDeletions = 0;                      PendingStreamDeletions = 0;
584                      this->pPendingRegionSuspension = pPendingRegionSuspension;                      this->pPendingRegionSuspension = pPendingRegionSuspension;
585                  }                  }
586    
587                  virtual bool Process(MidiKey* pMidiKey) {                  virtual bool Process(MidiKey* pMidiKey) OVERRIDE {
588                      VoiceIterator itVoice = pMidiKey->pActiveVoices->first();                      VoiceIterator itVoice = pMidiKey->pActiveVoices->first();
589                      // if current key is not associated with this region, skip this key                      // if current key is not associated with this region, skip this key
590                      if (itVoice->GetRegion()->GetParent() != pPendingRegionSuspension) return false;                      if (itVoice->GetRegion()->GetParent() != pPendingRegionSuspension) return false;
# Line 548  namespace LinuxSampler { Line 592  namespace LinuxSampler {
592                      return true;                      return true;
593                  }                  }
594    
595                  virtual void Process(VoiceIterator& itVoice) {                  virtual void Process(VoiceIterator& itVoice) OVERRIDE {
596                      // request a notification from disk thread side for stream deletion                      // request a notification from disk thread side for stream deletion
597                      const Stream::Handle hStream = itVoice->KillImmediately(true);                      const Stream::Handle hStream = itVoice->KillImmediately(true);
598                      if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream                      if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream
# Line 558  namespace LinuxSampler { Line 602  namespace LinuxSampler {
602                  }                  }
603              };              };
604    
             static IM instruments;  
   
605              Pool<R*>* pRegionPool[2]; ///< Double buffered pool, used by the engine channels to keep track of regions in use.              Pool<R*>* pRegionPool[2]; ///< Double buffered pool, used by the engine channels to keep track of regions in use.
606              int       MinFadeOutSamples;     ///< The number of samples needed to make an instant fade out (e.g. for voice stealing) without leading to clicks.              int       MinFadeOutSamples;     ///< The number of samples needed to make an instant fade out (e.g. for voice stealing) without leading to clicks.
607              D*        pDiskThread;              D*        pDiskThread;
# Line 589  namespace LinuxSampler { Line 631  namespace LinuxSampler {
631                  AbstractEngineChannel* pChannel = static_cast<AbstractEngineChannel*>(pEngineChannel);                  AbstractEngineChannel* pChannel = static_cast<AbstractEngineChannel*>(pEngineChannel);
632                  pChannel->ImportEvents(Samples);                  pChannel->ImportEvents(Samples);
633    
634                  // process events                  // if a valid real-time instrument script is loaded, pre-process
635                    // the event list by running the script now, since the script
636                    // might filter events or add new ones for this cycle
637                    if (pChannel->pScript && pChannel->pScript->bHasValidScript) {
638                        // resume any suspended script executions still hanging
639                        // around of previous audio fragment cycles
640                        for (RTList<ScriptEvent>::Iterator itEvent = pChannel->pScript->pEvents->first(),
641                            end = pChannel->pScript->pEvents->end(); itEvent != end; ++itEvent)
642                        {
643                            ResumeScriptEvent(pChannel, itEvent); //TODO: implement support for actual suspension time (i.e. passed to a script's wait() function call)
644                        }
645    
646                        // spawn new script executions for the new MIDI events of
647                        // this audio fragment cycle
648                        for (RTList<Event>::Iterator itEvent = pChannel->pEvents->first(),
649                            end = pChannel->pEvents->end(); itEvent != end; ++itEvent)
650                        {
651                            switch (itEvent->Type) {
652                                case Event::type_note_on:
653                                    if (pChannel->pScript->handlerNote)
654                                        ProcessEventByScript(pChannel, itEvent, pChannel->pScript->handlerNote);
655                                    break;
656                                case Event::type_note_off:
657                                    if (pChannel->pScript->handlerRelease)
658                                        ProcessEventByScript(pChannel, itEvent, pChannel->pScript->handlerRelease);
659                                    break;
660                                case Event::type_control_change:
661                                case Event::type_channel_pressure:
662                                case Event::type_pitchbend:
663                                    if (pChannel->pScript->handlerController)
664                                        ProcessEventByScript(pChannel, itEvent, pChannel->pScript->handlerController);                            
665                                    break;
666                                case Event::type_note_pressure:
667                                    //TODO: ...
668                                    break;
669                            }
670                        }
671                    }
672    
673                    // now process all events regularly
674                  {                  {
675                      RTList<Event>::Iterator itEvent = pChannel->pEvents->first();                      RTList<Event>::Iterator itEvent = pChannel->pEvents->first();
676                      RTList<Event>::Iterator end     = pChannel->pEvents->end();                      RTList<Event>::Iterator end     = pChannel->pEvents->end();
# Line 607  namespace LinuxSampler { Line 688  namespace LinuxSampler {
688                                  dmsg(5,("Engine: MIDI CC received\n"));                                  dmsg(5,("Engine: MIDI CC received\n"));
689                                  ProcessControlChange((EngineChannel*)itEvent->pEngineChannel, itEvent);                                  ProcessControlChange((EngineChannel*)itEvent->pEngineChannel, itEvent);
690                                  break;                                  break;
691                                case Event::type_channel_pressure:
692                                    dmsg(5,("Engine: MIDI Chan. Pressure received\n"));
693                                    ProcessChannelPressure((EngineChannel*)itEvent->pEngineChannel, itEvent);
694                                    break;
695                                case Event::type_note_pressure:
696                                    dmsg(5,("Engine: MIDI Note Pressure received\n"));
697                                    ProcessPolyphonicKeyPressure((EngineChannel*)itEvent->pEngineChannel, itEvent);
698                                    break;
699                              case Event::type_pitchbend:                              case Event::type_pitchbend:
700                                  dmsg(5,("Engine: Pitchbend received\n"));                                  dmsg(5,("Engine: Pitchbend received\n"));
701                                  ProcessPitchbend(static_cast<AbstractEngineChannel*>(itEvent->pEngineChannel), itEvent);                                  ProcessPitchbend(static_cast<AbstractEngineChannel*>(itEvent->pEngineChannel), itEvent);
# Line 623  namespace LinuxSampler { Line 712  namespace LinuxSampler {
712                  pLastStolenChannel        = NULL;                  pLastStolenChannel        = NULL;
713              }              }
714    
715                /** @brief Call instrument script's event handler for this event.
716                 *
717                 * Causes a new execution instance of the currently loaded real-time
718                 * instrument script's event handler (callback) to be spawned for
719                 * the given MIDI event.
720                 *
721                 * @param pChannel - engine channel on which the MIDI event occured
722                 * @param itEvent - MIDI event that causes this new script execution
723                 * @param pEventHandler - script's event handler to be executed
724                 */
725                void ProcessEventByScript(AbstractEngineChannel* pChannel, RTList<Event>::Iterator& itEvent, VMEventHandler* pEventHandler) {
726                    RTList<ScriptEvent>::Iterator itScriptEvent =
727                        pChannel->pScript->pEvents->allocAppend();
728    
729                    if (!itScriptEvent) return; // no free script event left for execution
730    
731                    // fill the list of script handlers to be executed by this event
732                    int i = 0;
733                    itScriptEvent->handlers[i++] = pEventHandler; // actual event handler (i.e. note, controller)
734                    itScriptEvent->handlers[i] = NULL; // NULL termination of list
735    
736                    // initialize/reset other members
737                    itScriptEvent->cause = *itEvent;
738                    itScriptEvent->id = pEventPool->getID(itEvent);
739                    itScriptEvent->currentHandler = 0;
740                    itScriptEvent->executionSlices = 0;
741    
742                    // run script handler(s)
743                    VMExecStatus_t res = pScriptVM->exec(
744                        pChannel->pScript->parserContext, &*itScriptEvent
745                    );
746    
747                    // in case the script was suspended, keep it on the allocated
748                    // ScriptEvent list to be continued on the next audio cycle,
749                    // otherwise if execution has been finished, free it for a new
750                    // future script event to be triggered from start
751                    if (!(res & VM_EXEC_SUSPENDED))
752                        pChannel->pScript->pEvents->free(itScriptEvent);
753                }
754    
755                /** @brief Resume execution of instrument script.
756                 *
757                 * Will be called to resume execution of a real-time instrument
758                 * script event which has been suspended in a previous audio
759                 * fragment cycle.
760                 *
761                 * Script execution might be suspended for various reasons. Usually
762                 * a script will be suspended if the script called the built-in
763                 * "wait()" function, but it might also be suspended automatically
764                 * if the script took too much execution time in an audio fragment
765                 * cycle. So in the latter case automatic suspension is performed in
766                 * order to avoid harm for the sampler's overall real-time
767                 * requirements.
768                 *
769                 * @param pChannel - engine channel this script is running for
770                 * @param itScriptEvent - script execution that shall be resumed
771                 */
772                void ResumeScriptEvent(AbstractEngineChannel* pChannel, RTList<ScriptEvent>::Iterator& itScriptEvent) {
773                    // run script
774                    VMExecStatus_t res = pScriptVM->exec(
775                        pChannel->pScript->parserContext, &*itScriptEvent
776                    );
777                    // in case the script was again suspended, keep it on the allocated
778                    // ScriptEvent list to be continued on the next audio cycle,
779                    // otherwise if execution has been finished, free it for a new
780                    // future script event to be triggered from start
781                    if (!(res & VM_EXEC_SUSPENDED))
782                        pChannel->pScript->pEvents->free(itScriptEvent);
783                }
784    
785              /**              /**
786               *  Will be called by LaunchVoice() method in case there are no free               *  Will be called by LaunchVoice() method in case there are no free
787               *  voices left. This method will select and kill one old voice for               *  voices left. This method will select and kill one old voice for
# Line 755  namespace LinuxSampler { Line 914  namespace LinuxSampler {
914                          dmsg(5,("Engine: instrument change command received\n"));                          dmsg(5,("Engine: instrument change command received\n"));
915                          cmd.bChangeInstrument = false;                          cmd.bChangeInstrument = false;
916                          pEngineChannel->pInstrument = cmd.pInstrument;                          pEngineChannel->pInstrument = cmd.pInstrument;
917                            pEngineChannel->pScript = cmd.pScript;
918                          instrumentChanged = true;                          instrumentChanged = true;
919    
920                          pEngineChannel->MarkAllActiveVoicesAsOrphans();                          pEngineChannel->MarkAllActiveVoicesAsOrphans();
921    
922                            // the script's "init" event handler is only executed
923                            // once (when the script is loaded or reloaded)
924                            if (pEngineChannel->pScript && pEngineChannel->pScript->handlerInit) {
925                                RTList<ScriptEvent>::Iterator itScriptEvent =
926                                    pEngineChannel->pScript->pEvents->allocAppend();
927    
928                                itScriptEvent->cause.pEngineChannel = pEngineChannel;
929                                itScriptEvent->handlers[0] = pEngineChannel->pScript->handlerInit;
930                                itScriptEvent->handlers[1] = NULL;
931    
932                                VMExecStatus_t res = pScriptVM->exec(
933                                    pEngineChannel->pScript->parserContext, &*itScriptEvent
934                                );
935    
936                                pEngineChannel->pScript->pEvents->free(itScriptEvent);
937                            }
938                      }                      }
939                  }                  }
940    
# Line 765  namespace LinuxSampler { Line 942  namespace LinuxSampler {
942                      //TODO: this is a lazy solution ATM and not safe in case somebody is currently editing the instrument we're currently switching to (we should store all suspended regions on instrument manager side and when switching to another instrument copy that list to the engine's local list of suspensions                      //TODO: this is a lazy solution ATM and not safe in case somebody is currently editing the instrument we're currently switching to (we should store all suspended regions on instrument manager side and when switching to another instrument copy that list to the engine's local list of suspensions
943                      ResetSuspendedRegions();                      ResetSuspendedRegions();
944                  }                  }
   
                 for (int i = 0; i < engineChannels.size(); i++) {  
                     EngineChannelBase<V, R, I>* channel =  
                         static_cast<EngineChannelBase<V, R, I>*>(engineChannels[i]);  
                     channel->InstrumentChangeCommandReader.Unlock();  
                 }  
945              }              }
946    
947              /**              /**
# Line 857  namespace LinuxSampler { Line 1028  namespace LinuxSampler {
1028                  pChannel->ClearEventLists();                  pChannel->ClearEventLists();
1029              }              }
1030    
1031                /**
1032                 * Process MIDI control change events with hard coded behavior,
1033                 * that is controllers whose behavior is defined independently
1034                 * of the actual sampler engine type and instrument.
1035                 *
1036                 * @param pEngineChannel - engine channel on which the MIDI CC event was received
1037                 * @param itControlChangeEvent - the actual MIDI CC event
1038                 */
1039              void ProcessHardcodedControllers (              void ProcessHardcodedControllers (
1040                  EngineChannel*          pEngineChannel,                  EngineChannel*          pEngineChannel,
1041                  Pool<Event>::Iterator&  itControlChangeEvent                  Pool<Event>::Iterator&  itControlChangeEvent
# Line 869  namespace LinuxSampler { Line 1048  namespace LinuxSampler {
1048                          pChannel->PortamentoTime = (float) itControlChangeEvent->Param.CC.Value / 127.0f * (float) CONFIG_PORTAMENTO_TIME_MAX + (float) CONFIG_PORTAMENTO_TIME_MIN;                          pChannel->PortamentoTime = (float) itControlChangeEvent->Param.CC.Value / 127.0f * (float) CONFIG_PORTAMENTO_TIME_MAX + (float) CONFIG_PORTAMENTO_TIME_MIN;
1049                          break;                          break;
1050                      }                      }
1051                      case 6: { // data entry (currently only used for RPN controllers)                      case 6: { // data entry (currently only used for RPN and NRPN controllers)
1052                          if (pChannel->GetMidiRpnController() == 2) { // coarse tuning in half tones                          //dmsg(1,("DATA ENTRY %d\n", itControlChangeEvent->Param.CC.Value));
1053                              int transpose = (int) itControlChangeEvent->Param.CC.Value - 64;                          if (pChannel->GetMidiRpnController() >= 0) { // RPN controller number was sent previously ...
1054                              // limit to +- two octaves for now                              dmsg(4,("Guess it's an RPN ...\n"));
1055                              transpose = RTMath::Min(transpose,  24);                              if (pChannel->GetMidiRpnController() == 2) { // coarse tuning in half tones
1056                              transpose = RTMath::Max(transpose, -24);                                  int transpose = (int) itControlChangeEvent->Param.CC.Value - 64;
1057                              pChannel->GlobalTranspose = transpose;                                  // limit to +- two octaves for now
1058                              // workaround, so we won't have hanging notes                                  transpose = RTMath::Min(transpose,  24);
1059                              pChannel->ReleaseAllVoices(itControlChangeEvent);                                  transpose = RTMath::Max(transpose, -24);
1060                                    pChannel->GlobalTranspose = transpose;
1061                                    // workaround, so we won't have hanging notes
1062                                    pChannel->ReleaseAllVoices(itControlChangeEvent);
1063                                }
1064                                // to prevent other MIDI CC #6 messages to be misenterpreted as RPN controller data
1065                                pChannel->ResetMidiRpnController();
1066                            } else if (pChannel->GetMidiNrpnController() >= 0) { // NRPN controller number was sent previously ...
1067                                dmsg(4,("Guess it's an NRPN ...\n"));
1068                                const int NrpnCtrlMSB = pChannel->GetMidiNrpnController() >> 8;
1069                                const int NrpnCtrlLSB = pChannel->GetMidiNrpnController() & 0xff;
1070                                dmsg(4,("NRPN MSB=%d LSB=%d Data=%d\n", NrpnCtrlMSB, NrpnCtrlLSB, itControlChangeEvent->Param.CC.Value));
1071                                switch (NrpnCtrlMSB) {
1072                                    case 0x1a: { // volume level of note (Roland GS NRPN)
1073                                        const uint note = NrpnCtrlLSB;
1074                                        const uint vol  = itControlChangeEvent->Param.CC.Value;
1075                                        dmsg(4,("Note Volume NRPN received (note=%d,vol=%d).\n", note, vol));
1076                                        if (note < 128 && vol < 128)
1077                                            pChannel->pMIDIKeyInfo[note].Volume = VolumeCurve[vol];
1078                                        break;
1079                                    }
1080                                    case 0x1c: { // panpot of note (Roland GS NRPN)
1081                                        const uint note = NrpnCtrlLSB;
1082                                        const uint pan  = itControlChangeEvent->Param.CC.Value;
1083                                        dmsg(4,("Note Pan NRPN received (note=%d,pan=%d).\n", note, pan));
1084                                        if (note < 128 && pan < 128) {
1085                                            pChannel->pMIDIKeyInfo[note].PanLeft  = PanCurve[128 - pan];
1086                                            pChannel->pMIDIKeyInfo[note].PanRight = PanCurve[pan];
1087                                        }
1088                                        break;
1089                                    }
1090                                    case 0x1d: { // reverb send of note (Roland GS NRPN)
1091                                        const uint note = NrpnCtrlLSB;
1092                                        const float reverb = float(itControlChangeEvent->Param.CC.Value) / 127.0f;
1093                                        dmsg(4,("Note Reverb Send NRPN received (note=%d,send=%d).\n", note, reverb));
1094                                        if (note < 128)
1095                                            pChannel->pMIDIKeyInfo[note].ReverbSend = reverb;
1096                                        break;
1097                                    }
1098                                    case 0x1e: { // chorus send of note (Roland GS NRPN)
1099                                        const uint note = NrpnCtrlLSB;
1100                                        const float chorus = float(itControlChangeEvent->Param.CC.Value) / 127.0f;
1101                                        dmsg(4,("Note Chorus Send NRPN received (note=%d,send=%d).\n", note, chorus));
1102                                        if (note < 128)
1103                                            pChannel->pMIDIKeyInfo[note].ChorusSend = chorus;
1104                                        break;
1105                                    }
1106                                }
1107                                // to prevent other MIDI CC #6 messages to be misenterpreted as NRPN controller data
1108                                pChannel->ResetMidiNrpnController();
1109                          }                          }
                         // to avoid other MIDI CC #6 messages to be misenterpreted as RPN controller data  
                         pChannel->ResetMidiRpnController();  
1110                          break;                          break;
1111                      }                      }
1112                      case 7: { // volume                      case 7: { // volume
# Line 891  namespace LinuxSampler { Line 1117  namespace LinuxSampler {
1117                      }                      }
1118                      case 10: { // panpot                      case 10: { // panpot
1119                          //TODO: not sample accurate yet                          //TODO: not sample accurate yet
                         pChannel->GlobalPanLeft  = PanCurve[128 - itControlChangeEvent->Param.CC.Value];  
                         pChannel->GlobalPanRight = PanCurve[itControlChangeEvent->Param.CC.Value];  
1120                          pChannel->iLastPanRequest = itControlChangeEvent->Param.CC.Value;                          pChannel->iLastPanRequest = itControlChangeEvent->Param.CC.Value;
1121                          break;                          break;
1122                      }                      }
# Line 969  namespace LinuxSampler { Line 1193  namespace LinuxSampler {
1193                          }                          }
1194                          break;                          break;
1195                      }                      }
1196                        case 98: { // NRPN controller LSB
1197                            dmsg(4,("NRPN LSB %d\n", itControlChangeEvent->Param.CC.Value));
1198                            pEngineChannel->SetMidiNrpnControllerLsb(itControlChangeEvent->Param.CC.Value);
1199                            break;
1200                        }
1201                        case 99: { // NRPN controller MSB
1202                            dmsg(4,("NRPN MSB %d\n", itControlChangeEvent->Param.CC.Value));
1203                            pEngineChannel->SetMidiNrpnControllerMsb(itControlChangeEvent->Param.CC.Value);
1204                            break;
1205                        }
1206                      case 100: { // RPN controller LSB                      case 100: { // RPN controller LSB
1207                            dmsg(4,("RPN LSB %d\n", itControlChangeEvent->Param.CC.Value));
1208                          pEngineChannel->SetMidiRpnControllerLsb(itControlChangeEvent->Param.CC.Value);                          pEngineChannel->SetMidiRpnControllerLsb(itControlChangeEvent->Param.CC.Value);
1209                          break;                          break;
1210                      }                      }
1211                      case 101: { // RPN controller MSB                      case 101: { // RPN controller MSB
1212                            dmsg(4,("RPN MSB %d\n", itControlChangeEvent->Param.CC.Value));
1213                          pEngineChannel->SetMidiRpnControllerMsb(itControlChangeEvent->Param.CC.Value);                          pEngineChannel->SetMidiRpnControllerMsb(itControlChangeEvent->Param.CC.Value);
1214                          break;                          break;
1215                      }                      }
# Line 1064  namespace LinuxSampler { Line 1300  namespace LinuxSampler {
1300                              VoiceIterator itVoiceToBeKilled = pOtherKey->pActiveVoices->first();                              VoiceIterator itVoiceToBeKilled = pOtherKey->pActiveVoices->first();
1301                              VoiceIterator end               = pOtherKey->pActiveVoices->end();                              VoiceIterator end               = pOtherKey->pActiveVoices->end();
1302                              for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) {                              for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) {
1303                                  if (itVoiceToBeKilled->Type != Voice::type_release_trigger)                                  if (!(itVoiceToBeKilled->Type & Voice::type_release_trigger))
1304                                      itVoiceToBeKilled->Kill(itNoteOnEventOnKeyList);                                      itVoiceToBeKilled->Kill(itNoteOnEventOnKeyList);
1305                              }                              }
1306                          }                          }
# Line 1076  namespace LinuxSampler { Line 1312  namespace LinuxSampler {
1312                  pChannel->ProcessKeySwitchChange(key);                  pChannel->ProcessKeySwitchChange(key);
1313    
1314                  pKey->KeyPressed = true; // the MIDI key was now pressed down                  pKey->KeyPressed = true; // the MIDI key was now pressed down
1315                    pChannel->KeyDown[key] = true; // just used as built-in %KEY_DOWN script variable
1316                  pKey->Velocity   = itNoteOnEventOnKeyList->Param.Note.Velocity;                  pKey->Velocity   = itNoteOnEventOnKeyList->Param.Note.Velocity;
1317                  pKey->NoteOnTime = FrameTime + itNoteOnEventOnKeyList->FragmentPos(); // will be used to calculate note length                  pKey->NoteOnTime = FrameTime + itNoteOnEventOnKeyList->FragmentPos(); // will be used to calculate note length
1318    
# Line 1096  namespace LinuxSampler { Line 1333  namespace LinuxSampler {
1333                      pKey->pEvents->free(itNoteOnEventOnKeyList);                      pKey->pEvents->free(itNoteOnEventOnKeyList);
1334    
1335                  if (!pChannel->SoloMode || pChannel->PortamentoPos < 0.0f) pChannel->PortamentoPos = (float) key;                  if (!pChannel->SoloMode || pChannel->PortamentoPos < 0.0f) pChannel->PortamentoPos = (float) key;
1336                  pKey->RoundRobinIndex++;                  if (pKey->pRoundRobinIndex) {
1337                        (*pKey->pRoundRobinIndex)++; // counter specific for the key or region
1338                        pChannel->RoundRobinIndex++; // common counter for the channel
1339                    }
1340                  pChannel->listeners.PostProcessNoteOn(key, vel);                  pChannel->listeners.PostProcessNoteOn(key, vel);
1341              }              }
1342    
# Line 1149  namespace LinuxSampler { Line 1389  namespace LinuxSampler {
1389                  #endif                  #endif
1390    
1391                  pKey->KeyPressed = false; // the MIDI key was now released                  pKey->KeyPressed = false; // the MIDI key was now released
1392                    pChannel->KeyDown[iKey] = false; // just used as built-in %KEY_DOWN script variable
1393    
1394                  // move event to the key's own event list                  // move event to the key's own event list
1395                  RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents);                  RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents);
# Line 1198  namespace LinuxSampler { Line 1439  namespace LinuxSampler {
1439                              VoiceIterator itVoiceToBeKilled = pKey->pActiveVoices->first();                              VoiceIterator itVoiceToBeKilled = pKey->pActiveVoices->first();
1440                              VoiceIterator end               = pKey->pActiveVoices->end();                              VoiceIterator end               = pKey->pActiveVoices->end();
1441                              for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) {                              for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) {
1442                                  if (itVoiceToBeKilled->Type != Voice::type_release_trigger)                                  if (!(itVoiceToBeKilled->Type & Voice::type_release_trigger))
1443                                      itVoiceToBeKilled->Kill(itNoteOffEventOnKeyList);                                      itVoiceToBeKilled->Kill(itNoteOffEventOnKeyList);
1444                              }                              }
1445                          }                          }
# Line 1228  namespace LinuxSampler { Line 1469  namespace LinuxSampler {
1469               *  control and status variables. This method is protected by a mutex.               *  control and status variables. This method is protected by a mutex.
1470               */               */
1471              virtual void ResetInternal() {              virtual void ResetInternal() {
1472                  ResetInternalMutex.Lock();                  LockGuard lock(ResetInternalMutex);
1473    
1474                  // make sure that the engine does not get any sysex messages                  // make sure that the engine does not get any sysex messages
1475                  // while it's reseting                  // while it's reseting
# Line 1257  namespace LinuxSampler { Line 1498  namespace LinuxSampler {
1498                  pEventQueue->init();                  pEventQueue->init();
1499                  pSysexBuffer->init();                  pSysexBuffer->init();
1500                  if (sysexDisabled) MidiInputPort::AddSysexListener(this);                  if (sysexDisabled) MidiInputPort::AddSysexListener(this);
                 ResetInternalMutex.Unlock();  
1501              }              }
1502    
1503              /**              /**
# Line 1331  namespace LinuxSampler { Line 1571  namespace LinuxSampler {
1571                              pKey->itSelf = pChannel->pActiveKeys->allocAppend();                              pKey->itSelf = pChannel->pActiveKeys->allocAppend();
1572                              *pKey->itSelf = itNoteOnEvent->Param.Note.Key;                              *pKey->itSelf = itNoteOnEvent->Param.Note.Key;
1573                          }                          }
1574                          if (itNewVoice->KeyGroup) {                          if (itNewVoice->Type & Voice::type_release_trigger_required) pKey->ReleaseTrigger = true; // mark key for the need of release triggered voice(s)
                             uint** ppKeyGroup = &pChannel->ActiveKeyGroups[itNewVoice->KeyGroup];  
                             *ppKeyGroup = &*pKey->itSelf; // put key as the (new) active key to its key group  
                         }  
                         if (itNewVoice->Type == Voice::type_release_trigger_required) pKey->ReleaseTrigger = true; // mark key for the need of release triggered voice(s)  
1575                          return 0; // success                          return 0; // success
1576                      }                      }
1577                  }                  }
# Line 1357  namespace LinuxSampler { Line 1593  namespace LinuxSampler {
1593    
1594                  return -1;                  return -1;
1595              }              }
1596                
1597                /**
1598                 * Checks whether scale tuning setting has been changed since last
1599                 * time this method was called, if yes, it recalculates the pitch
1600                 * for all active voices.
1601                 */
1602                void ProcessScaleTuningChange() {
1603                    const bool changed = ScaleTuningChanged.readAndReset();
1604                    if (!changed) return;
1605                    
1606                    for (int i = 0; i < engineChannels.size(); i++) {
1607                        EngineChannelBase<V, R, I>* channel =
1608                            static_cast<EngineChannelBase<V, R, I>*>(engineChannels[i]);
1609                        channel->OnScaleTuningChanged();
1610                    }
1611                }
1612    
1613          private:          private:
1614              Pool<V>*    pVoicePool;            ///< Contains all voices that can be activated.              Pool<V>*    pVoicePool;            ///< Contains all voices that can be activated.

Legend:
Removed from v.2027  
changed lines
  Added in v.2618

  ViewVC Help
Powered by ViewVC