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

Diff of /linuxsampler/trunk/src/engines/AbstractEngineChannel.cpp

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

revision 2596 by schoenebeck, Thu Jun 5 19:39:12 2014 UTC revision 2948 by schoenebeck, Fri Jul 15 15:29:04 2016 UTC
# Line 5  Line 5 
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-2012 Christian Schoenebeck and Grigor Iliev        *   *   Copyright (C) 2009-2012 Christian Schoenebeck and Grigor Iliev        *
8   *   Copyright (C) 2013-2014 Christian Schoenebeck and Andreas Persson     *   *   Copyright (C) 2012-2016 Christian Schoenebeck and Andreas Persson     *
9   *                                                                         *   *                                                                         *
10   *   This program is free software; you can redistribute it and/or modify  *   *   This program is free software; you can redistribute it and/or modify  *
11   *   it under the terms of the GNU General Public License as published by  *   *   it under the terms of the GNU General Public License as published by  *
# Line 35  namespace LinuxSampler { Line 35  namespace LinuxSampler {
35      {      {
36          pEngine      = NULL;          pEngine      = NULL;
37          pEvents      = NULL; // we allocate when we retrieve the right Engine object          pEvents      = NULL; // we allocate when we retrieve the right Engine object
38            delayedEvents.pList = NULL;
39          pEventQueue  = new RingBuffer<Event,false>(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0);          pEventQueue  = new RingBuffer<Event,false>(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0);
40          InstrumentIdx  = -1;          InstrumentIdx  = -1;
41          InstrumentStat = -1;          InstrumentStat = -1;
# Line 46  namespace LinuxSampler { Line 47  namespace LinuxSampler {
47          ResetControllers();          ResetControllers();
48          PortamentoMode = false;          PortamentoMode = false;
49          PortamentoTime = CONFIG_PORTAMENTO_TIME_DEFAULT;          PortamentoTime = CONFIG_PORTAMENTO_TIME_DEFAULT;
50          pScriptEvents = NULL;          pScript = NULL;
51      }      }
52    
53      AbstractEngineChannel::~AbstractEngineChannel() {      AbstractEngineChannel::~AbstractEngineChannel() {
         unloadCurrentInstrumentScript();  
         if (pScriptEvents) delete pScriptEvents;  
54          delete pEventQueue;          delete pEventQueue;
55          DeleteGroupEventLists();          DeleteGroupEventLists();
56          RemoveAllFxSends();          RemoveAllFxSends();
# Line 103  namespace LinuxSampler { Line 102  namespace LinuxSampler {
102    
103      void AbstractEngineChannel::Reset() {      void AbstractEngineChannel::Reset() {
104          if (pEngine) pEngine->DisableAndLock();          if (pEngine) pEngine->DisableAndLock();
105          ResetInternal();          ResetInternal(false/*don't reset engine*/);
106          ResetControllers();          ResetControllers();
107          if (pEngine) {          if (pEngine) {
108              pEngine->Enable();              pEngine->Enable();
# Line 131  namespace LinuxSampler { Line 130  namespace LinuxSampler {
130      /**      /**
131       * This method is not thread safe!       * This method is not thread safe!
132       */       */
133      void AbstractEngineChannel::ResetInternal() {      void AbstractEngineChannel::ResetInternal(bool bResetEngine) {
134          CurrentKeyDimension = 0;          CurrentKeyDimension = 0;
135          PortamentoPos = -1.0f; // no portamento active yet          PortamentoPos = -1.0f; // no portamento active yet
136    
137            // delete all active instrument script events
138            if (pScript) pScript->resetEvents();
139    
140            // free all delayed MIDI events
141            delayedEvents.clear();
142    
143          // delete all input events          // delete all input events
144          pEventQueue->init();          pEventQueue->init();
145    
146          if (pEngine) pEngine->ResetInternal();          if (bResetEngine && pEngine) pEngine->ResetInternal();
147    
148          // status of engine channel has changed, so set notify flag          // status of engine channel has changed, so set notify flag
149          bStatusChanged = true;          bStatusChanged = true;
150      }      }
151    
152      /**      /**
      * Loads the real-time instrument script given by @a text on this engine  
      * channel. A resource manager is used to allocate and share equivalent  
      * scripts on multiple engine channels.  
      *  
      * @param text - source code of script  
      */  
     void AbstractEngineChannel::loadInstrumentScript(const String& text) {  
         dmsg(1,("Loading real-time instrument script ... "));  
   
         // hand back old script reference and VM execution contexts  
         // (if not done already)  
         unloadCurrentInstrumentScript();  
   
         // get new script reference  
         script.parserContext = pEngine->scripts.Borrow(text, this);  
         if (!script.parserContext->errors().empty()) {  
             std::vector<ParserIssue> errors = script.parserContext->errors();  
             std::cerr << "[ScriptVM] Could not load instrument script, there were "  
                       << errors.size() << " parser errors:\n";  
             for (int i = 0; i < errors.size(); ++i)  
                 errors[i].dump();  
             return; // stop here if there were any parser errors  
         }  
   
         script.handlerInit = script.parserContext->eventHandlerByName("init");  
         script.handlerNote = script.parserContext->eventHandlerByName("note");  
         script.handlerRelease = script.parserContext->eventHandlerByName("release");  
         script.handlerController = script.parserContext->eventHandlerByName("controller");  
         script.bHasValidScript =  
             script.handlerInit || script.handlerNote || script.handlerRelease ||  
             script.handlerController;  
   
         // amount of script handlers each script event has to execute  
         int handlerExecCount = 0;  
         if (script.handlerInit) handlerExecCount++; // "init" handler is always executed before the actual event handler  
         if (script.handlerNote || script.handlerRelease || script.handlerController) // only one of these are executed after "init" handler  
             handlerExecCount++;  
   
         // create script event pool (if it doesn't exist already)  
         if (!pScriptEvents)  
             pScriptEvents = new Pool<ScriptEvent>(CONFIG_MAX_EVENTS_PER_FRAGMENT);  
   
         // create new VM execution contexts for new script  
         while (!pScriptEvents->poolIsEmpty()) {  
             RTList<ScriptEvent>::Iterator it = pScriptEvents->allocAppend();  
             it->execCtx = pEngine->pScriptVM->createExecContext(  
                 script.parserContext  
             );  
             it->handlers = new VMEventHandler*[handlerExecCount+1];  
         }  
         pScriptEvents->clear();  
   
         dmsg(1,("Done\n"));  
     }  
   
     /**  
      * Unloads the currently used real-time instrument script on this sampler  
      * channel. A resource manager is used to share equivalent scripts among  
      * multiple sampler channels, and to deallocate the parsed script once not  
      * used on any engine channel anymore.  
      */  
     void AbstractEngineChannel::unloadCurrentInstrumentScript() {  
         if (script.parserContext)  
             dmsg(1,("Unloading current instrument script."));  
   
         // free allocated VM execution contexts  
         if (pScriptEvents) {  
             pScriptEvents->clear();  
             while (!pScriptEvents->poolIsEmpty()) {  
                 RTList<ScriptEvent>::Iterator it = pScriptEvents->allocAppend();  
                 if (it->execCtx) {  
                     // free VM execution context object  
                     delete it->execCtx;  
                     it->execCtx = NULL;  
                     // free C array of handler pointers  
                     delete [] it->handlers;  
                 }  
             }  
             pScriptEvents->clear();  
         }  
         // hand back VM representation of script  
         if (script.parserContext) {  
             pEngine->scripts.HandBack(script.parserContext, this);  
             script.parserContext = NULL;  
             script.handlerInit = NULL;  
             script.handlerNote = NULL;  
             script.handlerRelease = NULL;  
             script.handlerController = NULL;  
         }  
         script.bHasValidScript = false;  
     }  
   
     /**  
153       * Implementation of virtual method from abstract EngineChannel interface.       * Implementation of virtual method from abstract EngineChannel interface.
154       * This method will periodically be polled (e.g. by the LSCP server) to       * This method will periodically be polled (e.g. by the LSCP server) to
155       * check if some engine channel parameter has changed since the last       * check if some engine channel parameter has changed since the last
# Line 723  namespace LinuxSampler { Line 635  namespace LinuxSampler {
635    
636              Event event = pEngine->pEventGenerator->CreateEvent();              Event event = pEngine->pEventGenerator->CreateEvent();
637              event.Type                          = Event::type_channel_pressure;              event.Type                          = Event::type_channel_pressure;
638                event.Param.ChannelPressure.Controller = CTRL_TABLE_IDX_AFTERTOUCH; // required for instrument scripts
639              event.Param.ChannelPressure.Value   = Value;              event.Param.ChannelPressure.Value   = Value;
640              event.Param.ChannelPressure.Channel = MidiChannel;              event.Param.ChannelPressure.Channel = MidiChannel;
641              event.pEngineChannel                = this;              event.pEngineChannel                = this;
# Line 739  namespace LinuxSampler { Line 652  namespace LinuxSampler {
652    
653              Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);              Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos);
654              event.Type                          = Event::type_channel_pressure;              event.Type                          = Event::type_channel_pressure;
655                event.Param.ChannelPressure.Controller = CTRL_TABLE_IDX_AFTERTOUCH; // required for instrument scripts
656              event.Param.ChannelPressure.Value   = Value;              event.Param.ChannelPressure.Value   = Value;
657              event.Param.ChannelPressure.Channel = MidiChannel;              event.Param.ChannelPressure.Channel = MidiChannel;
658              event.pEngineChannel                = this;              event.pEngineChannel                = this;
# Line 781  namespace LinuxSampler { Line 695  namespace LinuxSampler {
695          }          }
696      }      }
697    
698        bool AbstractEngineChannel::applyTranspose(Event* event) {
699            if (event->Type != Event::type_note_on && event->Type != Event::type_note_off)
700                return true; // event OK (not a note event, nothing to do with it here)
701    
702            //HACK: we should better add the transpose value only to the most mandatory places (like for retrieving the region and calculating the tuning), because otherwise voices will unintendedly survive when changing transpose while playing
703            const int k = event->Param.Note.Key + GlobalTranspose;
704            if (k < 0 || k > 127)
705                return false; // bad event, drop it
706    
707            event->Param.Note.Key = k;
708    
709            return true; // event OK
710        }
711    
712      /**      /**
713       * Copy all events from the engine channel's input event queue buffer to       * Copy all events from the engine channel's input event queue buffer to
714       * the internal event list. This will be done at the beginning of each       * the internal event list. This will be done at the beginning of each
# Line 817  namespace LinuxSampler { Line 745  namespace LinuxSampler {
745                              event.Param.Note.Key      = devEvent.Arg1;                              event.Param.Note.Key      = devEvent.Arg1;
746                              event.Param.Note.Velocity = devEvent.Arg2;                              event.Param.Note.Velocity = devEvent.Arg2;
747                              event.Param.Note.Channel  = channel;                              event.Param.Note.Channel  = channel;
748                                // apply transpose setting to (note on/off) event
749                                if (!applyTranspose(&event))
750                                    continue; // note value is out of range, so drop this event
751                                // assign a new note to this note-on event
752                                if (!pEngine->LaunchNewNote(this, &event))
753                                    continue; // failed launching new note, so drop this event
754                              break;                              break;
755                          case VirtualMidiDevice::EVENT_TYPE_NOTEOFF:                          case VirtualMidiDevice::EVENT_TYPE_NOTEOFF:
756                              event.Type = Event::type_note_off;                              event.Type = Event::type_note_off;
757                              event.Param.Note.Key      = devEvent.Arg1;                              event.Param.Note.Key      = devEvent.Arg1;
758                              event.Param.Note.Velocity = devEvent.Arg2;                              event.Param.Note.Velocity = devEvent.Arg2;
759                              event.Param.Note.Channel  = channel;                              event.Param.Note.Channel  = channel;
760                                if (!applyTranspose(&event))
761                                    continue; // note value is out of range, so drop this event
762                              break;                              break;
763                          case VirtualMidiDevice::EVENT_TYPE_CC:                          case VirtualMidiDevice::EVENT_TYPE_CC:
764                              switch (devEvent.Arg1) {                              switch (devEvent.Arg1) {
# Line 878  namespace LinuxSampler { Line 814  namespace LinuxSampler {
814                  pEvent->ResetFragmentPos();                  pEvent->ResetFragmentPos();
815                  break;                  break;
816              }              }
             // copy event to internal event list  
817              if (pEvents->poolIsEmpty()) {              if (pEvents->poolIsEmpty()) {
818                  dmsg(1,("Event pool emtpy!\n"));                  dmsg(1,("Event pool emtpy!\n"));
819                  break;                  break;
820              }              }
821                // apply transpose setting to (note on/off) event
822                if (!applyTranspose(pEvent))
823                    continue; // it's a note event which has a note value out of range, so drop this event
824                // assign a new note to this event (if its a note-on event)
825                if (pEvent->Type == Event::type_note_on)
826                    if (!pEngine->LaunchNewNote(this, pEvent))
827                        continue; // failed launching new note, so drop this event
828                // copy event to internal event list
829              *pEvents->allocAppend() = *pEvent;              *pEvents->allocAppend() = *pEvent;
830          }          }
831          eventQueueReader.free(); // free all copied events from input queue          eventQueueReader.free(); // free all copied events from input queue
832      }      }
833        
834      /**      /**
835       * Called by real-time instrument script functions to schedule a new event       * Called by real-time instrument script functions to schedule a new event
836       * somewhere in future.       * @a delay microseconds in future.
837         *
838         * @b IMPORTANT: for the supplied @a delay to be scheduled correctly, the
839         * passed @a pEvent must be assigned a valid fragment time within the
840         * current audio fragment boundaries. That fragment time will be used by
841         * this method as basis for interpreting what "now" acutally is, and thus
842         * it will be used as basis for calculating the precise scheduling time
843         * for @a delay. The easiest way to achieve this is by copying a recent
844         * event which happened within the current audio fragment cycle: i.e. the
845         * original event which caused calling this method here.
846         *
847         * @param pEvent - event to be scheduled in future (event data will be copied)
848         * @param delay - amount of microseconds in future (from now) when event shall be processed
849         * @returns unique event ID of scheduled new event, or NULL on error
850       */       */
851      void AbstractEngineChannel::ScheduleEvent(const Event* pEvent, int delay) { //TODO: delay not implemented yet      event_id_t AbstractEngineChannel::ScheduleEventMicroSec(const Event* pEvent, int delay) {
852          // since delay is not implemented yet, we simply add the new event          dmsg(3,("AbstractEngineChannel::ScheduleEventMicroSec(Event.Type=%d,delay=%d)\n", pEvent->Type, delay));
         // to the event list of the current audio fragmet cycle for now  
853          RTList<Event>::Iterator itEvent = pEvents->allocAppend();          RTList<Event>::Iterator itEvent = pEvents->allocAppend();
854          if (itEvent) *itEvent = *pEvent; // copy event          if (!itEvent) {
855                dmsg(1,("AbstractEngineChannel::ScheduleEventMicroSec(): Event pool emtpy!\n"));
856                return 0;
857            }
858            RTList<ScheduledEvent>::Iterator itNode = delayedEvents.schedulerNodes.allocAppend();
859            if (!itNode) { // scheduler node pool empty ...
860                dmsg(1,("AbstractEngineChannel::ScheduleEventMicroSec(): ScheduledEvent pool empty!\n"));
861                pEvents->free(itEvent);
862                return 0;
863            }
864            // copy passed event
865            *itEvent = *pEvent;
866            // move copied event to list of delayed events
867            itEvent = itEvent.moveToEndOf(delayedEvents.pList);
868            // connect scheduler node with the copied event
869            itNode->itEvent = itEvent;
870            // add entry to time sorted scheduler queue for copied event
871            pEngine->pEventGenerator->scheduleAheadMicroSec(
872                delayedEvents.queue, *itNode, itEvent->FragmentPos(), delay
873            );
874            //dmsg(5,("ScheduledEvent queue size: %d\n", delayedEvents.queue.size()));
875            return pEvents->getID(itEvent);
876        }
877    
878        /**
879         * Called by real-time instrument script functions to ignore the event
880         * reflected by given event ID. The event will be freed immediately to its
881         * pool and cannot be dereferenced by its old ID anymore. Even if its
882         * allocated back from the Pool later on, it will have a different ID.
883         *
884         * @param id - unique ID of event to be dropped
885         */
886        void AbstractEngineChannel::IgnoreEvent(event_id_t id) {
887            RTList<Event>::Iterator it = pEvents->fromID(id);
888            if (it) pEvents->free(it);
889        }
890    
891        /** @brief Drop the requested event.
892         *
893         * Called by real-time instrument script functions to ignore the event
894         * reflected by the given event @a id. This method detects whether the
895         * passed ID is actually a @c Note ID or a regular @c Event ID and act
896         * accordingly.
897         *
898         * @param id - event id (from script scope)
899         * @see ScriptID
900         */
901        void AbstractEngineChannel::IgnoreEventByScriptID(const ScriptID& id) {
902            switch (id.type()) {
903                case ScriptID::EVENT:
904                    IgnoreEvent( id.eventID() );
905                    break;
906                case ScriptID::NOTE:
907                    IgnoreNote( id.noteID() );
908                    break;
909            }
910        }
911    
912        /** @brief Order resuming of script execution instance "now".
913         *
914         * Called by real-time instrument script function stop_wait() to resume a
915         * script callback currently being suspended (i.e. due to a wait() script
916         * function call).
917         *
918         * @param itCallback - suspended script callback to be resumed
919         * @param now - current scheduler time to be "now"
920         * @param forever - whether this particulare script callback should ignore
921         *                  all subsequent wait*() script function calls
922         */
923        void AbstractEngineChannel::ScheduleResumeOfScriptCallback(RTList<ScriptEvent>::Iterator& itCallback, sched_time_t now, bool forever) {
924            // ignore if invalid iterator was passed
925            if (!itCallback) return;
926    
927            ScriptEvent* pCallback = &*itCallback;
928    
929            // mark this callback to ignore all subsequent built-in wait*() script function calls
930            if (forever) pCallback->ignoreAllWaitCalls = true;
931    
932            // ignore if callback is not in the scheduler queue
933            if (pCallback->currentSchedulerQueue() != &pScript->suspendedEvents) return;
934    
935            // ignore if callback is already scheduled to be resumed "now"
936            if (pCallback->scheduleTime <= now) return;
937    
938            // take it out from the scheduler queue and re-insert callback
939            // to schedule the script callback for resuming execution "now"
940            pScript->suspendedEvents.erase(*pCallback);
941            pCallback->scheduleTime = now + 1;
942            pScript->suspendedEvents.insert(*pCallback);
943      }      }
944    
945      FxSend* AbstractEngineChannel::AddFxSend(uint8_t MidiCtrl, String Name) throw (Exception) {      FxSend* AbstractEngineChannel::AddFxSend(uint8_t MidiCtrl, String Name) throw (Exception) {

Legend:
Removed from v.2596  
changed lines
  Added in v.2948

  ViewVC Help
Powered by ViewVC