/[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 2559 by schoenebeck, Sun May 18 17:38:25 2014 UTC revision 2837 by persson, Sun Aug 23 06:14:00 2015 UTC
# Line 4  Line 4 
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-2008 Christian Schoenebeck                         *   *   Copyright (C) 2005-2008 Christian Schoenebeck                         *
7   *   Copyright (C) 2009-2013 Christian Schoenebeck and Grigor Iliev        *   *   Copyright (C) 2009-2015 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 432  namespace LinuxSampler { Line 432  namespace LinuxSampler {
432               * @param pRegion - region the engine shall stop using               * @param pRegion - region the engine shall stop using
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 %p ...\n",(void*)pRegion));
436                  {                  {
437                      LockGuard lock(SuspendedRegionsMutex);                      LockGuard lock(SuspendedRegionsMutex);
438                      SuspensionChangeOngoing.Set(true);                      SuspensionChangeOngoing.Set(true);
439                      pPendingRegionSuspension = pRegion;                      pPendingRegionSuspension = pRegion;
440                      SuspensionChangeOngoing.WaitAndUnlockIf(true);                      SuspensionChangeOngoing.WaitAndUnlockIf(true);
441                  }                  }
442                  dmsg(2,("EngineBase: Region %x suspended.",pRegion));                  dmsg(2,("EngineBase: Region %p suspended.",(void*)pRegion));
443              }              }
444    
445              /**              /**
# Line 449  namespace LinuxSampler { Line 449  namespace LinuxSampler {
449               * @param pRegion - region the engine shall be allowed to use again               * @param pRegion - region the engine shall be allowed to use again
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 %p ...\n",(void*)pRegion));
453                  {                  {
454                      LockGuard lock(SuspendedRegionsMutex);                      LockGuard lock(SuspendedRegionsMutex);
455                      SuspensionChangeOngoing.Set(true);                      SuspensionChangeOngoing.Set(true);
456                      pPendingRegionResumption = pRegion;                      pPendingRegionResumption = pRegion;
457                      SuspensionChangeOngoing.WaitAndUnlockIf(true);                      SuspensionChangeOngoing.WaitAndUnlockIf(true);
458                  }                  }
459                  dmsg(2,("EngineBase: Region %x resumed.\n",pRegion));                  dmsg(2,("EngineBase: Region %p resumed.\n",(void*)pRegion));
460              }              }
461    
462              virtual void ResetSuspendedRegions() {              virtual void ResetSuspendedRegions() {
# Line 631  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) {
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 673  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                    const int key = itEvent->Param.Note.Key; // even if this is not a note on/off event, accessing it does not mean any harm
727                    // check if polyphonic data is passed from "note" to "release"
728                    // script event handlers
729                    if (pEventHandler == pChannel->pScript->handlerRelease &&
730                        pChannel->pScript->handlerNote &&
731                        pChannel->pScript->handlerNote->isPolyphonic() &&
732                        pChannel->pScript->handlerRelease->isPolyphonic() &&
733                        !pChannel->pScript->pKeyEvents[key]->isEmpty())
734                    {
735                        // polyphonic variable data is used/passed from "note" to
736                        // "release" script callback, so we have to recycle the
737                        // original "note on" script event(s)
738                        RTList<ScriptEvent>::Iterator it  = pChannel->pScript->pKeyEvents[key]->first();
739                        RTList<ScriptEvent>::Iterator end = pChannel->pScript->pKeyEvents[key]->end();
740                        for (; it != end; ++it) {
741                            ProcessScriptEvent(
742                                pChannel, itEvent, pEventHandler, it
743                            );
744                        }
745                    } else {
746                        // no polyphonic data is used/passed from "note" to
747                        // "release" script callback, so just use a new fresh
748                        // script event object
749                        RTList<ScriptEvent>::Iterator itScriptEvent =
750                            pChannel->pScript->pEvents->allocAppend();
751                        ProcessScriptEvent(
752                            pChannel, itEvent, pEventHandler, itScriptEvent
753                        );
754                    }
755                }
756    
757                void ProcessScriptEvent(AbstractEngineChannel* pChannel, RTList<Event>::Iterator& itEvent, VMEventHandler* pEventHandler, RTList<ScriptEvent>::Iterator& itScriptEvent) {
758                    if (!itScriptEvent) return; // not a valid script event (i.e. because no free script event was left in the script event pool)
759    
760                    // fill the list of script handlers to be executed by this event
761                    int i = 0;
762                    itScriptEvent->handlers[i++] = pEventHandler; // actual event handler (i.e. note, controller)
763                    itScriptEvent->handlers[i] = NULL; // NULL termination of list
764    
765                    // initialize/reset other members
766                    itScriptEvent->cause = *itEvent;
767                    itScriptEvent->id = pEventPool->getID(itEvent);
768                    itScriptEvent->currentHandler = 0;
769                    itScriptEvent->executionSlices = 0;
770    
771                    // run script handler(s)
772                    VMExecStatus_t res = pScriptVM->exec(
773                        pChannel->pScript->parserContext, &*itScriptEvent
774                    );
775    
776                    // in case the script was suspended, keep it on the allocated
777                    // ScriptEvent list to be continued on the next audio cycle
778                    if (!(res & VM_EXEC_SUSPENDED)) { // script execution has finished without 'suspended' status ...
779                        // if "polyphonic" variable data is passed from script's
780                        // "note" event handler to its "release" event handler, then
781                        // the script event must be kept and recycled for the later
782                        // occuring "release" script event ...
783                        if (pEventHandler == pChannel->pScript->handlerNote &&
784                            pChannel->pScript->handlerRelease &&
785                            pChannel->pScript->handlerNote->isPolyphonic() &&
786                            pChannel->pScript->handlerRelease->isPolyphonic())
787                        {
788                            const int key = itEvent->Param.Note.Key;
789                            itScriptEvent.moveToEndOf(pChannel->pScript->pKeyEvents[key & 127]);
790                        } else {
791                            // ... otherwise if no polyphonic data is passed and
792                            // script's execution has finished without suspension
793                            // status, then free the script event for a new future
794                            // script event to be triggered from start
795                            pChannel->pScript->pEvents->free(itScriptEvent);
796                        }
797                    }
798                }
799    
800                /** @brief Resume execution of instrument script.
801                 *
802                 * Will be called to resume execution of a real-time instrument
803                 * script event which has been suspended in a previous audio
804                 * fragment cycle.
805                 *
806                 * Script execution might be suspended for various reasons. Usually
807                 * a script will be suspended if the script called the built-in
808                 * "wait()" function, but it might also be suspended automatically
809                 * if the script took too much execution time in an audio fragment
810                 * cycle. So in the latter case automatic suspension is performed in
811                 * order to avoid harm for the sampler's overall real-time
812                 * requirements.
813                 *
814                 * @param pChannel - engine channel this script is running for
815                 * @param itScriptEvent - script execution that shall be resumed
816                 */
817                void ResumeScriptEvent(AbstractEngineChannel* pChannel, RTList<ScriptEvent>::Iterator& itScriptEvent) {
818                    VMEventHandler* handler = itScriptEvent->handlers[itScriptEvent->currentHandler];
819    
820                    // run script
821                    VMExecStatus_t res = pScriptVM->exec(
822                        pChannel->pScript->parserContext, &*itScriptEvent
823                    );
824    
825                    // in case the script was suspended, keep it on the allocated
826                    // ScriptEvent list to be continued on the next audio cycle
827                    if (!(res & VM_EXEC_SUSPENDED)) { // script execution has finished without 'suspended' status ...
828                        // if "polyphonic" variable data is passed from script's
829                        // "note" event handler to its "release" event handler, then
830                        // the script event must be kept and recycled for the later
831                        // occuring "release" script event ...
832                        if (handler && handler == pChannel->pScript->handlerNote &&
833                            pChannel->pScript->handlerRelease &&
834                            pChannel->pScript->handlerNote->isPolyphonic() &&
835                            pChannel->pScript->handlerRelease->isPolyphonic())
836                        {
837                            const int key = itScriptEvent->cause.Param.Note.Key;
838                            itScriptEvent.moveToEndOf(pChannel->pScript->pKeyEvents[key & 127]);
839                        } else {
840                            // ... otherwise if no polyphonic data is passed and
841                            // script's execution has finished without suspension
842                            // status, then free the script event for a new future
843                            // script event to be triggered from start
844                            pChannel->pScript->pEvents->free(itScriptEvent);
845                        }
846                    }
847                }
848    
849              /**              /**
850               *  Will be called by LaunchVoice() method in case there are no free               *  Will be called by LaunchVoice() method in case there are no free
851               *  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 805  namespace LinuxSampler { Line 978  namespace LinuxSampler {
978                          dmsg(5,("Engine: instrument change command received\n"));                          dmsg(5,("Engine: instrument change command received\n"));
979                          cmd.bChangeInstrument = false;                          cmd.bChangeInstrument = false;
980                          pEngineChannel->pInstrument = cmd.pInstrument;                          pEngineChannel->pInstrument = cmd.pInstrument;
981                            pEngineChannel->pScript =
982                                cmd.pScript->bHasValidScript ? cmd.pScript : NULL;
983                          instrumentChanged = true;                          instrumentChanged = true;
984    
985                          pEngineChannel->MarkAllActiveVoicesAsOrphans();                          pEngineChannel->MarkAllActiveVoicesAsOrphans();
986    
987                            // the script's "init" event handler is only executed
988                            // once (when the script is loaded or reloaded)
989                            if (pEngineChannel->pScript && pEngineChannel->pScript->handlerInit) {
990                                RTList<ScriptEvent>::Iterator itScriptEvent =
991                                    pEngineChannel->pScript->pEvents->allocAppend();
992    
993                                itScriptEvent->cause.pEngineChannel = pEngineChannel;
994                                itScriptEvent->handlers[0] = pEngineChannel->pScript->handlerInit;
995                                itScriptEvent->handlers[1] = NULL;
996    
997                                VMExecStatus_t res = pScriptVM->exec(
998                                    pEngineChannel->pScript->parserContext, &*itScriptEvent
999                                );
1000    
1001                                pEngineChannel->pScript->pEvents->free(itScriptEvent);
1002                            }
1003                      }                      }
1004                  }                  }
1005    
# Line 963  namespace LinuxSampler { Line 1155  namespace LinuxSampler {
1155                                  case 0x1d: { // reverb send of note (Roland GS NRPN)                                  case 0x1d: { // reverb send of note (Roland GS NRPN)
1156                                      const uint note = NrpnCtrlLSB;                                      const uint note = NrpnCtrlLSB;
1157                                      const float reverb = float(itControlChangeEvent->Param.CC.Value) / 127.0f;                                      const float reverb = float(itControlChangeEvent->Param.CC.Value) / 127.0f;
1158                                      dmsg(4,("Note Reverb Send NRPN received (note=%d,send=%d).\n", note, reverb));                                      dmsg(4,("Note Reverb Send NRPN received (note=%d,send=%f).\n", note, reverb));
1159                                      if (note < 128)                                      if (note < 128)
1160                                          pChannel->pMIDIKeyInfo[note].ReverbSend = reverb;                                          pChannel->pMIDIKeyInfo[note].ReverbSend = reverb;
1161                                      break;                                      break;
# Line 971  namespace LinuxSampler { Line 1163  namespace LinuxSampler {
1163                                  case 0x1e: { // chorus send of note (Roland GS NRPN)                                  case 0x1e: { // chorus send of note (Roland GS NRPN)
1164                                      const uint note = NrpnCtrlLSB;                                      const uint note = NrpnCtrlLSB;
1165                                      const float chorus = float(itControlChangeEvent->Param.CC.Value) / 127.0f;                                      const float chorus = float(itControlChangeEvent->Param.CC.Value) / 127.0f;
1166                                      dmsg(4,("Note Chorus Send NRPN received (note=%d,send=%d).\n", note, chorus));                                      dmsg(4,("Note Chorus Send NRPN received (note=%d,send=%f).\n", note, chorus));
1167                                      if (note < 128)                                      if (note < 128)
1168                                          pChannel->pMIDIKeyInfo[note].ChorusSend = chorus;                                          pChannel->pMIDIKeyInfo[note].ChorusSend = chorus;
1169                                      break;                                      break;
# Line 1185  namespace LinuxSampler { Line 1377  namespace LinuxSampler {
1377                  pChannel->ProcessKeySwitchChange(key);                  pChannel->ProcessKeySwitchChange(key);
1378    
1379                  pKey->KeyPressed = true; // the MIDI key was now pressed down                  pKey->KeyPressed = true; // the MIDI key was now pressed down
1380                    pChannel->KeyDown[key] = true; // just used as built-in %KEY_DOWN script variable
1381                  pKey->Velocity   = itNoteOnEventOnKeyList->Param.Note.Velocity;                  pKey->Velocity   = itNoteOnEventOnKeyList->Param.Note.Velocity;
1382                  pKey->NoteOnTime = FrameTime + itNoteOnEventOnKeyList->FragmentPos(); // will be used to calculate note length                  pKey->NoteOnTime = FrameTime + itNoteOnEventOnKeyList->FragmentPos(); // will be used to calculate note length
1383    
# Line 1261  namespace LinuxSampler { Line 1454  namespace LinuxSampler {
1454                  #endif                  #endif
1455    
1456                  pKey->KeyPressed = false; // the MIDI key was now released                  pKey->KeyPressed = false; // the MIDI key was now released
1457                    pChannel->KeyDown[iKey] = false; // just used as built-in %KEY_DOWN script variable
1458    
1459                  // move event to the key's own event list                  // move event to the key's own event list
1460                  RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents);                  RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents);

Legend:
Removed from v.2559  
changed lines
  Added in v.2837

  ViewVC Help
Powered by ViewVC