/[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 2410 by schoenebeck, Sat Feb 2 18:52:15 2013 UTC revision 2962 by schoenebeck, Sun Jul 17 17:54: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) 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 32  Line 33 
33  #include "InstrumentManager.h"  #include "InstrumentManager.h"
34  #include "../common/global_private.h"  #include "../common/global_private.h"
35    
36    // a bit headroom over CONFIG_MAX_VOICES to avoid minor complications i.e. under voice stealing conditions
37    #define MAX_NOTES_HEADROOM  3
38    #define GLOBAL_MAX_NOTES    (GLOBAL_MAX_VOICES * MAX_NOTES_HEADROOM)
39    
40  namespace LinuxSampler {  namespace LinuxSampler {
41    
# Line 45  namespace LinuxSampler { Line 49  namespace LinuxSampler {
49          class IM  /* Instrument Manager */,          class IM  /* Instrument Manager */,
50          class I   /* Instrument */          class I   /* Instrument */
51      >      >
52      class EngineBase: public AbstractEngine, public RegionPools<R>, public VoicePool<V> {      class EngineBase: public AbstractEngine, public RegionPools<R>, public NotePool<V> {
53    
54          public:          public:
55                typedef typename RTList< Note<V> >::Iterator NoteIterator;
56              typedef typename RTList<V>::Iterator VoiceIterator;              typedef typename RTList<V>::Iterator VoiceIterator;
57              typedef typename Pool<V>::Iterator PoolVoiceIterator;              typedef typename Pool<V>::Iterator PoolVoiceIterator;
58              typedef typename RTList<RR*>::Iterator RootRegionIterator;              typedef typename RTList<RR*>::Iterator RootRegionIterator;
59              typedef typename MidiKeyboardManager<V>::MidiKey MidiKey;              typedef typename MidiKeyboardManager<V>::MidiKey MidiKey;
60                            
61              EngineBase() : SuspendedRegions(128) {              EngineBase() : SuspendedRegions(128), noteIDPool(GLOBAL_MAX_NOTES) {
62                  pDiskThread          = NULL;                  pDiskThread          = NULL;
63                    pNotePool            = new Pool< Note<V> >(GLOBAL_MAX_NOTES);
64                    pNotePool->setPoolElementIDsReservedBits(INSTR_SCRIPT_EVENT_ID_RESERVED_BITS);
65                  pVoicePool           = new Pool<V>(GLOBAL_MAX_VOICES);                  pVoicePool           = new Pool<V>(GLOBAL_MAX_VOICES);
66                  pRegionPool[0]       = new Pool<R*>(GLOBAL_MAX_VOICES);                  pRegionPool[0]       = new Pool<R*>(GLOBAL_MAX_VOICES);
67                  pRegionPool[1]       = new Pool<R*>(GLOBAL_MAX_VOICES);                  pRegionPool[1]       = new Pool<R*>(GLOBAL_MAX_VOICES);
68                  pVoiceStealingQueue  = new RTList<Event>(pEventPool);                  pVoiceStealingQueue  = new RTList<Event>(pEventPool);
69                  iMaxDiskStreams      = GLOBAL_MAX_STREAMS;                  iMaxDiskStreams      = GLOBAL_MAX_STREAMS;
70    
71                  for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {                  // init all Voice objects in voice pool
72                    for (VoiceIterator iterVoice = pVoicePool->allocAppend();
73                         iterVoice; iterVoice = pVoicePool->allocAppend())
74                    {
75                      iterVoice->SetEngine(this);                      iterVoice->SetEngine(this);
76                  }                  }
77                  pVoicePool->clear();                  pVoicePool->clear();
78    
79                    // init all Note objects in note pool
80                    for (NoteIterator itNote = pNotePool->allocAppend(); itNote;
81                         itNote = pNotePool->allocAppend())
82                    {
83                        itNote->init(pVoicePool, &noteIDPool);
84                    }
85                    pNotePool->clear();
86    
87                  ResetInternal();                  ResetInternal();
88                  ResetScaleTuning();                  ResetScaleTuning();
89                  ResetSuspendedRegions();                  ResetSuspendedRegions();
# Line 79  namespace LinuxSampler { Line 97  namespace LinuxSampler {
97                      dmsg(1,("OK\n"));                      dmsg(1,("OK\n"));
98                  }                  }
99    
100                    if (pNotePool) {
101                        pNotePool->clear();
102                        delete pNotePool;
103                    }
104    
105                  if (pVoicePool) {                  if (pVoicePool) {
106                      pVoicePool->clear();                      pVoicePool->clear();
107                      delete pVoicePool;                      delete pVoicePool;
# Line 104  namespace LinuxSampler { Line 127  namespace LinuxSampler {
127               *  @param Samples - number of sample points to be rendered               *  @param Samples - number of sample points to be rendered
128               *  @returns       0 on success               *  @returns       0 on success
129               */               */
130              virtual int RenderAudio(uint Samples) {              virtual int RenderAudio(uint Samples) OVERRIDE {
131                  dmsg(8,("RenderAudio(Samples=%d)\n", Samples));                  dmsg(8,("RenderAudio(Samples=%d)\n", Samples));
132    
133                  // return if engine disabled                  // return if engine disabled
# Line 143  namespace LinuxSampler { Line 166  namespace LinuxSampler {
166                          }                          }
167                      }                      }
168                  }                  }
169                    
170                    // In case scale tuning has been changed, recalculate pitch for
171                    // all active voices.
172                    ProcessScaleTuningChange();
173    
174                  // reset internal voice counter (just for statistic of active voices)                  // reset internal voice counter (just for statistic of active voices)
175                  ActiveVoiceCountTemp = 0;                  ActiveVoiceCountTemp = 0;
# Line 205  namespace LinuxSampler { Line 232  namespace LinuxSampler {
232                  return 0;                  return 0;
233              }              }
234    
235              virtual int MaxVoices() { return pVoicePool->poolSize(); }              virtual int MaxVoices() OVERRIDE { return pVoicePool->poolSize(); }
236    
237              virtual void SetMaxVoices(int iVoices) throw (Exception) {              virtual void SetMaxVoices(int iVoices) throw (Exception) OVERRIDE {
238                  if (iVoices < 1)                  if (iVoices < 1)
239                      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");
240    
# Line 231  namespace LinuxSampler { Line 258  namespace LinuxSampler {
258                      pChannel->ResetRegionsInUse(pRegionPool);                      pChannel->ResetRegionsInUse(pRegionPool);
259                  }                  }
260    
261                    // FIXME: Shouldn't all those pool elements be freed before resizing the pools?
262                  try {                  try {
263                      pVoicePool->resizePool(iVoices);                      pVoicePool->resizePool(iVoices);
264                        pNotePool->resizePool(iVoices * MAX_NOTES_HEADROOM);
265                        noteIDPool.resizePool(iVoices * MAX_NOTES_HEADROOM);
266                  } catch (...) {                  } catch (...) {
267                      throw Exception("FATAL: Could not resize voice pool!");                      throw Exception("FATAL: Could not resize voice pool!");
268                  }                  }
269    
270                  for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {                  for (VoiceIterator iterVoice = pVoicePool->allocAppend();
271                         iterVoice; iterVoice = pVoicePool->allocAppend())
272                    {
273                      iterVoice->SetEngine(this);                      iterVoice->SetEngine(this);
274                      iterVoice->pDiskThread = this->pDiskThread;                      iterVoice->pDiskThread = this->pDiskThread;
275                  }                  }
276                  pVoicePool->clear();                  pVoicePool->clear();
277    
278                    for (NoteIterator itNote = pNotePool->allocAppend(); itNote;
279                         itNote = pNotePool->allocAppend())
280                    {
281                        itNote->init(pVoicePool, &noteIDPool);
282                    }
283                    pNotePool->clear();
284    
285                  PostSetMaxVoices(iVoices);                  PostSetMaxVoices(iVoices);
286                  ResumeAll();                  ResumeAll();
287              }              }
# Line 250  namespace LinuxSampler { Line 289  namespace LinuxSampler {
289              /** Called after the new max number of voices is set and before resuming the engine. */              /** Called after the new max number of voices is set and before resuming the engine. */
290              virtual void PostSetMaxVoices(int iVoices) { }              virtual void PostSetMaxVoices(int iVoices) { }
291    
292              virtual uint DiskStreamCount() { return (pDiskThread) ? pDiskThread->GetActiveStreamCount() : 0; }              virtual uint DiskStreamCount() OVERRIDE { return (pDiskThread) ? pDiskThread->GetActiveStreamCount() : 0; }
293              virtual uint DiskStreamCountMax() { return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0; }              virtual uint DiskStreamCountMax() OVERRIDE { return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0; }
294              virtual int  MaxDiskStreams() { return iMaxDiskStreams; }              virtual int  MaxDiskStreams() OVERRIDE { return iMaxDiskStreams; }
295    
296              virtual void SetMaxDiskStreams(int iStreams) throw (Exception) {              virtual void SetMaxDiskStreams(int iStreams) throw (Exception) OVERRIDE {
297                  if (iStreams < 0)                  if (iStreams < 0)
298                      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");
299    
# Line 269  namespace LinuxSampler { Line 308  namespace LinuxSampler {
308                  ResumeAll();                  ResumeAll();
309              }              }
310    
311              virtual String DiskStreamBufferFillBytes() { return (pDiskThread) ? pDiskThread->GetBufferFillBytes() : ""; }              virtual String DiskStreamBufferFillBytes() OVERRIDE { return (pDiskThread) ? pDiskThread->GetBufferFillBytes() : ""; }
312              virtual String DiskStreamBufferFillPercentage() { return (pDiskThread) ? pDiskThread->GetBufferFillPercentage() : ""; }              virtual String DiskStreamBufferFillPercentage() OVERRIDE { return (pDiskThread) ? pDiskThread->GetBufferFillPercentage() : ""; }
313              virtual InstrumentManager* GetInstrumentManager() { return &instruments; }              virtual InstrumentManager* GetInstrumentManager() OVERRIDE { return &instruments; }
314    
315              /**              /**
316               * Connect this engine instance with the given audio output device.               * Connect this engine instance with the given audio output device.
# Line 282  namespace LinuxSampler { Line 321  namespace LinuxSampler {
321               *               *
322               * @param pAudioOut - audio output device to connect to               * @param pAudioOut - audio output device to connect to
323               */               */
324              virtual void Connect(AudioOutputDevice* pAudioOut) {              virtual void Connect(AudioOutputDevice* pAudioOut) OVERRIDE {
325                  // caution: don't ignore if connecting to the same device here,                  // caution: don't ignore if connecting to the same device here,
326                  // because otherwise SetMaxDiskStreams() implementation won't work anymore!                  // because otherwise SetMaxDiskStreams() implementation won't work anymore!
327    
# Line 311  namespace LinuxSampler { Line 350  namespace LinuxSampler {
350                      MinFadeOutSamples = MaxSamplesPerCycle;                      MinFadeOutSamples = MaxSamplesPerCycle;
351                      // lower minimum release time                      // lower minimum release time
352                      const float minReleaseTime = (float) MaxSamplesPerCycle / (float) SampleRate;                      const float minReleaseTime = (float) MaxSamplesPerCycle / (float) SampleRate;
353                        pVoicePool->clear();
354                      for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {                      for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
355                          iterVoice->CalculateFadeOutCoeff(minReleaseTime, SampleRate);                          iterVoice->CalculateFadeOutCoeff(minReleaseTime, SampleRate);
356                      }                      }
# Line 331  namespace LinuxSampler { Line 371  namespace LinuxSampler {
371                      exit(EXIT_FAILURE);                      exit(EXIT_FAILURE);
372                  }                  }
373    
374                    pVoicePool->clear();
375                  for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {                  for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
376                      iterVoice->pDiskThread = this->pDiskThread;                      iterVoice->pDiskThread = this->pDiskThread;
377                      dmsg(3,("d"));                      dmsg(3,("d"));
# Line 370  namespace LinuxSampler { Line 411  namespace LinuxSampler {
411              }              }
412                    
413              // Implementattion for abstract method derived from Engine.              // Implementattion for abstract method derived from Engine.
414              virtual void ReconnectAudioOutputDevice() {              virtual void ReconnectAudioOutputDevice() OVERRIDE {
415                  SuspendAll();                  SuspendAll();
416                  if (pAudioOutputDevice) Connect(pAudioOutputDevice);                  if (pAudioOutputDevice) Connect(pAudioOutputDevice);
417                  ResumeAll();                  ResumeAll();
# Line 428  namespace LinuxSampler { Line 469  namespace LinuxSampler {
469               * @param pRegion - region the engine shall stop using               * @param pRegion - region the engine shall stop using
470               */               */
471              virtual void Suspend(RR* pRegion) {              virtual void Suspend(RR* pRegion) {
472                  dmsg(2,("EngineBase: Suspending Region %x ...\n",pRegion));                  dmsg(2,("EngineBase: Suspending Region %p ...\n",(void*)pRegion));
473                  SuspendedRegionsMutex.Lock();                  {
474                  SuspensionChangeOngoing.Set(true);                      LockGuard lock(SuspendedRegionsMutex);
475                  pPendingRegionSuspension = pRegion;                      SuspensionChangeOngoing.Set(true);
476                  SuspensionChangeOngoing.WaitAndUnlockIf(true);                      pPendingRegionSuspension = pRegion;
477                  SuspendedRegionsMutex.Unlock();                      SuspensionChangeOngoing.WaitAndUnlockIf(true);
478                  dmsg(2,("EngineBase: Region %x suspended.",pRegion));                  }
479                    dmsg(2,("EngineBase: Region %p suspended.",(void*)pRegion));
480              }              }
481    
482              /**              /**
# Line 444  namespace LinuxSampler { Line 486  namespace LinuxSampler {
486               * @param pRegion - region the engine shall be allowed to use again               * @param pRegion - region the engine shall be allowed to use again
487               */               */
488              virtual void Resume(RR* pRegion) {              virtual void Resume(RR* pRegion) {
489                  dmsg(2,("EngineBase: Resuming Region %x ...\n",pRegion));                  dmsg(2,("EngineBase: Resuming Region %p ...\n",(void*)pRegion));
490                  SuspendedRegionsMutex.Lock();                  {
491                  SuspensionChangeOngoing.Set(true);                      LockGuard lock(SuspendedRegionsMutex);
492                  pPendingRegionResumption = pRegion;                      SuspensionChangeOngoing.Set(true);
493                  SuspensionChangeOngoing.WaitAndUnlockIf(true);                      pPendingRegionResumption = pRegion;
494                  SuspendedRegionsMutex.Unlock();                      SuspensionChangeOngoing.WaitAndUnlockIf(true);
495                  dmsg(2,("EngineBase: Region %x resumed.\n",pRegion));                  }
496                    dmsg(2,("EngineBase: Region %p resumed.\n",(void*)pRegion));
497              }              }
498    
499              virtual void ResetSuspendedRegions() {              virtual void ResetSuspendedRegions() {
# Line 558  namespace LinuxSampler { Line 601  namespace LinuxSampler {
601                  return pRegionPool[index];                  return pRegionPool[index];
602              }              }
603    
604              // implementation of abstract method derived from class 'LinuxSampler::VoicePool'              // implementation of abstract methods derived from class 'LinuxSampler::NotePool'
605              virtual Pool<V>* GetVoicePool() { return pVoicePool; }              virtual Pool<V>* GetVoicePool() OVERRIDE { return pVoicePool; }
606                virtual Pool< Note<V> >* GetNotePool() OVERRIDE { return pNotePool; }
607                virtual Pool<note_id_t>* GetNodeIDPool() OVERRIDE { return &noteIDPool; }
608    
609              D* GetDiskThread() { return pDiskThread; }              D* GetDiskThread() { return pDiskThread; }
610    
# Line 572  namespace LinuxSampler { Line 617  namespace LinuxSampler {
617              public:              public:
618                  int PendingStreamDeletions;                  int PendingStreamDeletions;
619                  RR* pPendingRegionSuspension;                  RR* pPendingRegionSuspension;
620    
621                  SuspensionVoiceHandler(RR* pPendingRegionSuspension) {                  SuspensionVoiceHandler(RR* pPendingRegionSuspension) {
622                      PendingStreamDeletions = 0;                      PendingStreamDeletions = 0;
623                      this->pPendingRegionSuspension = pPendingRegionSuspension;                      this->pPendingRegionSuspension = pPendingRegionSuspension;
624                  }                  }
625    
626                  virtual bool Process(MidiKey* pMidiKey) {                  virtual bool Process(MidiKey* pMidiKey) OVERRIDE {
627                      VoiceIterator itVoice = pMidiKey->pActiveVoices->first();                      NoteIterator  itNote  = pMidiKey->pActiveNotes->first();
628                        VoiceIterator itVoice = itNote->pActiveVoices->first();
629                      // if current key is not associated with this region, skip this key                      // if current key is not associated with this region, skip this key
630                      if (itVoice->GetRegion()->GetParent() != pPendingRegionSuspension) return false;                      if (itVoice->GetRegion()->GetParent() != pPendingRegionSuspension) return false;
631    
632                      return true;                      return true;
633                  }                  }
634    
635                  virtual void Process(VoiceIterator& itVoice) {                  virtual void Process(VoiceIterator& itVoice) OVERRIDE {
636                      // request a notification from disk thread side for stream deletion                      // request a notification from disk thread side for stream deletion
637                      const Stream::Handle hStream = itVoice->KillImmediately(true);                      const Stream::Handle hStream = itVoice->KillImmediately(true);
638                      if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream                      if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream
# Line 601  namespace LinuxSampler { Line 648  namespace LinuxSampler {
648    
649              int                          ActiveVoiceCountTemp;  ///< number of currently active voices (for internal usage, will be used for incrementation)              int                          ActiveVoiceCountTemp;  ///< number of currently active voices (for internal usage, will be used for incrementation)
650              VoiceIterator                itLastStolenVoice;     ///< Only for voice stealing: points to the last voice which was theft in current audio fragment, NULL otherwise.              VoiceIterator                itLastStolenVoice;     ///< Only for voice stealing: points to the last voice which was theft in current audio fragment, NULL otherwise.
651                NoteIterator                 itLastStolenNote;      ///< Only for voice stealing: points to the last note from which was theft in current audio fragment, NULL otherwise.
652              RTList<uint>::Iterator       iuiLastStolenKey;      ///< Only for voice stealing: key number of last key on which the last voice was theft in current audio fragment, NULL otherwise.              RTList<uint>::Iterator       iuiLastStolenKey;      ///< Only for voice stealing: key number of last key on which the last voice was theft in current audio fragment, NULL otherwise.
653              EngineChannelBase<V, R, I>*  pLastStolenChannel;    ///< Only for voice stealing: points to the engine channel on which the previous voice was stolen in this audio fragment.              EngineChannelBase<V, R, I>*  pLastStolenChannel;    ///< Only for voice stealing: points to the engine channel on which the previous voice was stolen in this audio fragment.
654              VoiceIterator                itLastStolenVoiceGlobally; ///< Same as itLastStolenVoice, but engine globally              VoiceIterator                itLastStolenVoiceGlobally; ///< Same as itLastStolenVoice, but engine globally
655                NoteIterator                 itLastStolenNoteGlobally; ///< Same as itLastStolenNote, but engine globally
656              RTList<uint>::Iterator       iuiLastStolenKeyGlobally;  ///< Same as iuiLastStolenKey, but engine globally              RTList<uint>::Iterator       iuiLastStolenKeyGlobally;  ///< Same as iuiLastStolenKey, but engine globally
657              RTList<Event>*               pVoiceStealingQueue;   ///< All voice-launching events which had to be postponed due to free voice shortage.              RTList<Event>*               pVoiceStealingQueue;   ///< All voice-launching events which had to be postponed due to free voice shortage.
658              Mutex                        ResetInternalMutex;    ///< Mutex to protect the ResetInternal function for concurrent usage (e.g. by the lscp and instrument loader threads).              Mutex                        ResetInternalMutex;    ///< Mutex to protect the ResetInternal function for concurrent usage (e.g. by the lscp and instrument loader threads).
659              int iMaxDiskStreams;              int iMaxDiskStreams;
660    
661                NoteBase* NoteByID(note_id_t id) OVERRIDE {
662                    NoteIterator itNote = GetNotePool()->fromID(id);
663                    if (!itNote) return NULL;
664                    return &*itNote;
665                }
666    
667                /**
668                 * Gets a new @c Note object from the note pool, initializes it
669                 * appropriately, links it with requested parent note (if
670                 * requested), moves it to the appropriate key's list of active
671                 * notes it, and sticks the new note's unique ID to the
672                 * passed @a pNoteOnEvent.
673                 *
674                 * @param pEngineChannel - engine channel on which this event happened
675                 * @param pNoteOnEvent - event which caused this
676                 * @returns new note's unique ID (or zero on error)
677                 */
678                note_id_t LaunchNewNote(LinuxSampler::EngineChannel* pEngineChannel, Event* pNoteOnEvent) OVERRIDE {
679                    EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
680                    Pool< Note<V> >* pNotePool = GetNotePool();
681    
682                    if (pNotePool->poolIsEmpty()) {
683                        dmsg(1,("Engine: Could not launch new note; Note pool empty!\n"));
684                        return 0; // error
685                    }
686    
687                    // create a new note (for new voices to be assigned to)
688                    //NoteIterator itNewNote = pKey->pActiveNotes->allocAppend();
689                    NoteIterator itNewNote = pNotePool->allocAppend();
690                    const note_id_t newNoteID = pNotePool->getID(itNewNote);
691    
692                    // remember the engine's time when this note was triggered exactly
693                    itNewNote->triggerSchedTime = pNoteOnEvent->SchedTime();
694    
695                    // usually the new note (and its subsequent voices) will be
696                    // allocated on the key provided by the event's note number,
697                    // however if this new note is requested not to be a regular
698                    // note, but rather a child note, then this new note will be
699                    // allocated on the parent note's key instead in order to
700                    // release the child note simultaniously with its parent note
701                    itNewNote->hostKey = pNoteOnEvent->Param.Note.Key;
702    
703                    // in case this new note was requested to be a child note,
704                    // then retrieve its parent note and link them with each other
705                    const note_id_t parentNoteID = pNoteOnEvent->Param.Note.ParentNoteID;
706                    if (parentNoteID) {
707                        NoteIterator itParentNote = pNotePool->fromID(parentNoteID);                        
708                        if (itParentNote) {
709                            RTList<note_id_t>::Iterator itChildNoteID = itParentNote->pChildNotes->allocAppend();
710                            if (itChildNoteID) {
711                                // link parent and child note with each other
712                                *itChildNoteID = newNoteID;
713                                itNewNote->parentNoteID = parentNoteID;
714                                itNewNote->hostKey = itParentNote->hostKey;
715                            } else {    
716                                dmsg(1,("Engine: Could not assign new note as child note; Note ID pool empty!\n"));
717                                pNotePool->free(itNewNote);
718                                return 0; // error
719                            }
720                        } else {
721                            // the parent note was apparently released already, so
722                            // free the new note again and inform caller that it
723                            // should drop the event
724                            dmsg(3,("Engine: Could not assign new note as child note; Parent note is gone!\n"));
725                            pNotePool->free(itNewNote);
726                            return 0; // error
727                        }
728                    }
729    
730                    dmsg(2,("Launched new note on host key %d\n", itNewNote->hostKey));
731    
732                    // copy event which caused this note
733                    itNewNote->cause = *pNoteOnEvent;
734                    itNewNote->eventID = pEventPool->getID(pNoteOnEvent);
735    
736                    // move new note to its host key
737                    MidiKey* pKey = &pChannel->pMIDIKeyInfo[itNewNote->hostKey];
738                    itNewNote.moveToEndOf(pKey->pActiveNotes);
739    
740                    // assign unique note ID of this new note to the original note on event
741                    pNoteOnEvent->Param.Note.ID = newNoteID;
742    
743                    return newNoteID; // success
744                }
745    
746              /**              /**
747               * Dispatch and handle all events in this audio fragment for the given               * Dispatch and handle all events in this audio fragment for the given
748               * engine channel.               * engine channel.
# Line 624  namespace LinuxSampler { Line 758  namespace LinuxSampler {
758                  AbstractEngineChannel* pChannel = static_cast<AbstractEngineChannel*>(pEngineChannel);                  AbstractEngineChannel* pChannel = static_cast<AbstractEngineChannel*>(pEngineChannel);
759                  pChannel->ImportEvents(Samples);                  pChannel->ImportEvents(Samples);
760    
761                  // process events                  // if a valid real-time instrument script is loaded, pre-process
762                    // the event list by running the script now, since the script
763                    // might filter events or add new ones for this cycle
764                    if (pChannel->pScript) {
765                        const sched_time_t fragmentEndTime = pEventGenerator->schedTimeAtCurrentFragmentEnd();
766    
767                        // resume suspended script executions been scheduled for
768                        // this audio fragment cycle (which were suspended in a
769                        // previous audio fragment cycle)
770                        ProcessSuspendedScriptEvents(pChannel, fragmentEndTime);
771    
772                        // spawn new script executions for the new MIDI events of
773                        // this audio fragment cycle
774                        //
775                        // FIXME: it would probably be better to just schedule newly spawned script executions here and then execute them altogether with already suspended ones all at once in order of all their scheduled timing
776                        for (RTList<Event>::Iterator itEvent = pChannel->pEvents->first(),
777                            end = pChannel->pEvents->end(); itEvent != end; )
778                        {
779                            //HACK: avoids iterator invalidation which might happen below since an instrument script might drop an event by direct raw pointer access (it would be considerable to extend the Iterator class to detect and circumvent this case by checking the "reincarnation" member variable).
780                            RTList<Event>::Iterator itNext = itEvent;
781                            ++itNext;
782    
783                            switch (itEvent->Type) {
784                                case Event::type_note_on:
785                                    if (pChannel->pScript->handlerNote)
786                                        ProcessEventByScript(pChannel, itEvent, pChannel->pScript->handlerNote);
787                                    break;
788                                case Event::type_note_off:
789                                    if (pChannel->pScript->handlerRelease)
790                                        ProcessEventByScript(pChannel, itEvent, pChannel->pScript->handlerRelease);
791                                    break;
792                                case Event::type_control_change:
793                                case Event::type_channel_pressure:
794                                case Event::type_pitchbend:
795                                    if (pChannel->pScript->handlerController)
796                                        ProcessEventByScript(pChannel, itEvent, pChannel->pScript->handlerController);                            
797                                    break;
798                                case Event::type_note_pressure:
799                                    //TODO: ...
800                                    break;
801                            }
802    
803                            // see HACK comment above
804                            itEvent = itNext;
805                        }
806    
807                        // this has to be run again, since the newly spawned scripts
808                        // above may have cause suspended scripts that must be
809                        // resumed within this same audio fragment cycle
810                        //
811                        // FIXME: see FIXME comment above
812                        ProcessSuspendedScriptEvents(pChannel, fragmentEndTime);
813                    }
814    
815                    // if there are any delayed events scheduled for the current
816                    // audio fragment cycle, then move and sort them into the main
817                    // event list
818                    if (!pChannel->delayedEvents.queue.isEmpty()) {
819                        dmsg(5,("Engine: There are delayed MIDI events (total queue size: %d) ...\n", pChannel->delayedEvents.queue.size()));
820                        const sched_time_t fragmentEndTime = pEventGenerator->schedTimeAtCurrentFragmentEnd();
821                        RTList<Event>::Iterator itEvent = pChannel->pEvents->first();
822                        while (true) {
823                            RTList<ScheduledEvent>::Iterator itDelayedEventNode =
824                                pEventGenerator->popNextScheduledEvent(
825                                    pChannel->delayedEvents.queue,
826                                    pChannel->delayedEvents.schedulerNodes,
827                                    fragmentEndTime
828                                );
829                            if (!itDelayedEventNode) break;
830                            // get the actual delayed event object and free the used scheduler node
831                            RTList<Event>::Iterator itDelayedEvent = itDelayedEventNode->itEvent;
832                            pChannel->delayedEvents.schedulerNodes.free(itDelayedEventNode);
833                            if (!itDelayedEvent) { // should never happen, but just to be sure ...
834                                dmsg(1,("Engine: Oops, invalid delayed event!\n"));
835                                continue;
836                            }
837                            // skip all events on main event list which have a time
838                            // before (or equal to) the delayed event to be inserted
839                            for (; itEvent && itEvent->FragmentPos() <= itDelayedEvent->FragmentPos();
840                                 ++itEvent);
841                            // now move delayed event from delayedEvents.pList to
842                            // the current position on the main event list
843                            itEvent = itDelayedEvent.moveBefore(itEvent);
844                            dmsg(5,("Engine: Inserted event of type %d into main event list (queue size: %d).\n", itEvent->Type, pChannel->delayedEvents.queue.size()));
845                        }
846                        dmsg(5,("Engine: End of delayed events (total queue size: %d).\n", pChannel->delayedEvents.queue.size()));
847                    }
848    
849                    // now process all events regularly
850                  {                  {
851                      RTList<Event>::Iterator itEvent = pChannel->pEvents->first();                      RTList<Event>::Iterator itEvent = pChannel->pEvents->first();
852                      RTList<Event>::Iterator end     = pChannel->pEvents->end();                      RTList<Event>::Iterator end     = pChannel->pEvents->end();
# Line 634  namespace LinuxSampler { Line 856  namespace LinuxSampler {
856                                  dmsg(5,("Engine: Note on received\n"));                                  dmsg(5,("Engine: Note on received\n"));
857                                  ProcessNoteOn((EngineChannel*)itEvent->pEngineChannel, itEvent);                                  ProcessNoteOn((EngineChannel*)itEvent->pEngineChannel, itEvent);
858                                  break;                                  break;
859                                case Event::type_play_note:
860                                    dmsg(5,("Engine: Play Note received\n"));
861                                    ProcessNoteOn((EngineChannel*)itEvent->pEngineChannel, itEvent);
862                                    break;
863                              case Event::type_note_off:                              case Event::type_note_off:
864                                  dmsg(5,("Engine: Note off received\n"));                                  dmsg(5,("Engine: Note off received\n"));
865                                  ProcessNoteOff((EngineChannel*)itEvent->pEngineChannel, itEvent);                                  ProcessNoteOff((EngineChannel*)itEvent->pEngineChannel, itEvent);
866                                  break;                                  break;
867                                case Event::type_stop_note:
868                                    dmsg(5,("Engine: Stop Note received\n"));
869                                    ProcessNoteOff((EngineChannel*)itEvent->pEngineChannel, itEvent);
870                                    break;
871                              case Event::type_control_change:                              case Event::type_control_change:
872                                  dmsg(5,("Engine: MIDI CC received\n"));                                  dmsg(5,("Engine: MIDI CC received\n"));
873                                  ProcessControlChange((EngineChannel*)itEvent->pEngineChannel, itEvent);                                  ProcessControlChange((EngineChannel*)itEvent->pEngineChannel, itEvent);
874                                  break;                                  break;
875                                case Event::type_channel_pressure:
876                                    dmsg(5,("Engine: MIDI Chan. Pressure received\n"));
877                                    ProcessChannelPressure((EngineChannel*)itEvent->pEngineChannel, itEvent);
878                                    break;
879                                case Event::type_note_pressure:
880                                    dmsg(5,("Engine: MIDI Note Pressure received\n"));
881                                    ProcessPolyphonicKeyPressure((EngineChannel*)itEvent->pEngineChannel, itEvent);
882                                    break;
883                              case Event::type_pitchbend:                              case Event::type_pitchbend:
884                                  dmsg(5,("Engine: Pitchbend received\n"));                                  dmsg(5,("Engine: Pitchbend received\n"));
885                                  ProcessPitchbend(static_cast<AbstractEngineChannel*>(itEvent->pEngineChannel), itEvent);                                  ProcessPitchbend(static_cast<AbstractEngineChannel*>(itEvent->pEngineChannel), itEvent);
886                                  break;                                  break;
887                                case Event::type_note_synth_param:
888                                    dmsg(5,("Engine: Note Synth Param received\n"));
889                                    ProcessNoteSynthParam(itEvent->pEngineChannel, itEvent);
890                                    break;
891                          }                          }
892                      }                      }
893                  }                  }
# Line 653  namespace LinuxSampler { Line 895  namespace LinuxSampler {
895                  // reset voice stealing for the next engine channel (or next audio fragment)                  // reset voice stealing for the next engine channel (or next audio fragment)
896                  itLastStolenVoice         = VoiceIterator();                  itLastStolenVoice         = VoiceIterator();
897                  itLastStolenVoiceGlobally = VoiceIterator();                  itLastStolenVoiceGlobally = VoiceIterator();
898                    itLastStolenNote          = NoteIterator();
899                    itLastStolenNoteGlobally  = NoteIterator();
900                  iuiLastStolenKey          = RTList<uint>::Iterator();                  iuiLastStolenKey          = RTList<uint>::Iterator();
901                  iuiLastStolenKeyGlobally  = RTList<uint>::Iterator();                  iuiLastStolenKeyGlobally  = RTList<uint>::Iterator();
902                  pLastStolenChannel        = NULL;                  pLastStolenChannel        = NULL;
903              }              }
904    
905              /**              /**
906                 * Run all suspended script execution instances which are scheduled
907                 * to be resumed for the current audio fragment cycle.
908                 *
909                 * @param pChannel - engine channel on which suspended events occurred
910                 */
911                void ProcessSuspendedScriptEvents(AbstractEngineChannel* pChannel, const sched_time_t fragmentEndTime) {
912                    while (true) {
913                        RTList<ScriptEvent>::Iterator itEvent =
914                            pEventGenerator->popNextScheduledScriptEvent(
915                                pChannel->pScript->suspendedEvents,
916                                *pChannel->pScript->pEvents, fragmentEndTime
917                            );
918                        if (!itEvent) break;
919                        ResumeScriptEvent(pChannel, itEvent);
920                    }
921                }
922    
923                /** @brief Call instrument script's event handler for this event.
924                 *
925                 * Causes a new execution instance of the currently loaded real-time
926                 * instrument script's event handler (callback) to be spawned for
927                 * the given MIDI event.
928                 *
929                 * @param pChannel - engine channel on which the MIDI event occurred
930                 * @param itEvent - MIDI event that causes this new script execution
931                 * @param pEventHandler - script's event handler to be executed
932                 */
933                void ProcessEventByScript(AbstractEngineChannel* pChannel, RTList<Event>::Iterator& itEvent, VMEventHandler* pEventHandler) {
934                    const int key = itEvent->Param.Note.Key; // even if this is not a note on/off event, accessing it does not mean any harm
935                    // check if polyphonic data is passed from "note" to "release"
936                    // script event handlers
937                    if (pEventHandler == pChannel->pScript->handlerRelease &&
938                        pChannel->pScript->handlerNote &&
939                        pChannel->pScript->handlerNote->isPolyphonic() &&
940                        pChannel->pScript->handlerRelease->isPolyphonic() &&
941                        !pChannel->pScript->pKeyEvents[key]->isEmpty())
942                    {
943                        // polyphonic variable data is used/passed from "note" to
944                        // "release" script callback, so we have to recycle the
945                        // original "note on" script event(s)
946                        RTList<ScriptEvent>::Iterator it  = pChannel->pScript->pKeyEvents[key]->first();
947                        RTList<ScriptEvent>::Iterator end = pChannel->pScript->pKeyEvents[key]->end();
948                        for (; it != end; ++it) {
949                            ProcessScriptEvent(
950                                pChannel, itEvent, pEventHandler, it
951                            );
952                        }
953                    } else {
954                        // no polyphonic data is used/passed from "note" to
955                        // "release" script callback, so just use a new fresh
956                        // script event object
957                        RTList<ScriptEvent>::Iterator itScriptEvent =
958                            pChannel->pScript->pEvents->allocAppend();
959                        ProcessScriptEvent(
960                            pChannel, itEvent, pEventHandler, itScriptEvent
961                        );
962                    }
963                }
964    
965                /** @brief Spawn new execution instance of an instrument script handler.
966                 *
967                 * Will be called to initiate a new execution of a real-time
968                 * instrument script event right from the start of the script's
969                 * respective handler. If script execution did not complete after
970                 * calling this method, the respective script exeuction is then
971                 * suspended and a call to ResumeScriptEvent() will be used next
972                 * time to continue its execution.
973                 *
974                 * @param pChannel - engine channel this script is running for
975                 * @param itEvent - event which caused execution of this script
976                 *                  event handler
977                 * @param pEventHandler - VM representation of event handler to be
978                 *                        executed
979                 * @param itScriptEvent - script event that shall be processed
980                 */
981                void ProcessScriptEvent(AbstractEngineChannel* pChannel, RTList<Event>::Iterator& itEvent, VMEventHandler* pEventHandler, RTList<ScriptEvent>::Iterator& itScriptEvent) {
982                    if (!itScriptEvent) return; // not a valid script event (i.e. because no free script event was left in the script event pool)
983    
984                    // fill the list of script handlers to be executed by this event
985                    int i = 0;
986                    itScriptEvent->handlers[i++] = pEventHandler; // actual event handler (i.e. note, controller)
987                    itScriptEvent->handlers[i] = NULL; // NULL termination of list
988    
989                    // initialize/reset other members
990                    itScriptEvent->cause = *itEvent;
991                    itScriptEvent->currentHandler = 0;
992                    itScriptEvent->executionSlices = 0;
993                    itScriptEvent->ignoreAllWaitCalls = false;
994                    itScriptEvent->handlerType = pEventHandler->eventHandlerType();
995                    // this is the native representation of the $EVENT_ID script variable
996                    itScriptEvent->id =
997                        (itEvent->Type == Event::type_note_on)
998                            ? ScriptID::fromNoteID( itEvent->Param.Note.ID )
999                            : ScriptID::fromEventID( pEventPool->getID(itEvent) );
1000    
1001                    // run script handler(s)
1002                    VMExecStatus_t res = pScriptVM->exec(
1003                        pChannel->pScript->parserContext, &*itScriptEvent
1004                    );
1005    
1006                    // was the script suspended?
1007                    if (res & VM_EXEC_SUSPENDED) { // script was suspended ...
1008                        // in case the script was suspended, keep it on the allocated
1009                        // ScriptEvent list to be resume at the scheduled time in future,
1010                        // additionally insert it into a sorted time queue
1011                        pEventGenerator->scheduleAheadMicroSec(
1012                            pChannel->pScript->suspendedEvents, // scheduler queue
1013                            *itScriptEvent, // script event
1014                            itScriptEvent->cause.FragmentPos(), // current time of script event (basis for its next execution)
1015                            itScriptEvent->execCtx->suspensionTimeMicroseconds() // how long shall it be suspended
1016                        );
1017                    } else { // script execution has finished without 'suspended' status ...
1018                        // if "polyphonic" variable data is passed from script's
1019                        // "note" event handler to its "release" event handler, then
1020                        // the script event must be kept and recycled for the later
1021                        // occuring "release" script event ...
1022                        if (pEventHandler == pChannel->pScript->handlerNote &&
1023                            pChannel->pScript->handlerRelease &&
1024                            pChannel->pScript->handlerNote->isPolyphonic() &&
1025                            pChannel->pScript->handlerRelease->isPolyphonic())
1026                        {
1027                            const int key = itEvent->Param.Note.Key;
1028                            itScriptEvent.moveToEndOf(pChannel->pScript->pKeyEvents[key & 127]);
1029                        } else {
1030                            // ... otherwise if no polyphonic data is passed and
1031                            // script's execution has finished without suspension
1032                            // status, then free the script event for a new future
1033                            // script event to be triggered from start
1034                            pChannel->pScript->pEvents->free(itScriptEvent);
1035                        }
1036                    }
1037                }
1038    
1039                /** @brief Resume execution of instrument script.
1040                 *
1041                 * Will be called to resume execution of a real-time instrument
1042                 * script event which has been suspended previously.
1043                 *
1044                 * Script execution might be suspended for various reasons. Usually
1045                 * a script will be suspended if the script called the built-in
1046                 * "wait()" function, but it might also be suspended automatically
1047                 * if the script took too much execution time in an audio fragment
1048                 * cycle. So in the latter case automatic suspension is performed in
1049                 * order to avoid harm for the sampler's overall real-time
1050                 * requirements.
1051                 *
1052                 * @param pChannel - engine channel this script is running for
1053                 * @param itScriptEvent - script execution that shall be resumed
1054                 */
1055                void ResumeScriptEvent(AbstractEngineChannel* pChannel, RTList<ScriptEvent>::Iterator& itScriptEvent) {
1056                    VMEventHandler* handler = itScriptEvent->handlers[itScriptEvent->currentHandler];
1057    
1058                    // run script
1059                    VMExecStatus_t res = pScriptVM->exec(
1060                        pChannel->pScript->parserContext, &*itScriptEvent
1061                    );
1062    
1063                    // was the script suspended?
1064                    if (res & VM_EXEC_SUSPENDED) {
1065                        // in case the script was suspended, keep it on the allocated
1066                        // ScriptEvent list to be resume at the scheduled time in future,
1067                        // additionally insert it into a sorted time queue
1068                        pEventGenerator->scheduleAheadMicroSec(
1069                            pChannel->pScript->suspendedEvents, // scheduler queue
1070                            *itScriptEvent, // script event
1071                            itScriptEvent->cause.FragmentPos(), // current time of script event (basis for its next execution)
1072                            itScriptEvent->execCtx->suspensionTimeMicroseconds() // how long shall it be suspended
1073                        );
1074                    } else { // script execution has finished without 'suspended' status ...
1075                        // if "polyphonic" variable data is passed from script's
1076                        // "note" event handler to its "release" event handler, then
1077                        // the script event must be kept and recycled for the later
1078                        // occuring "release" script event ...
1079                        if (handler && handler == pChannel->pScript->handlerNote &&
1080                            pChannel->pScript->handlerRelease &&
1081                            pChannel->pScript->handlerNote->isPolyphonic() &&
1082                            pChannel->pScript->handlerRelease->isPolyphonic())
1083                        {
1084                            const int key = itScriptEvent->cause.Param.Note.Key;
1085                            itScriptEvent.moveToEndOf(pChannel->pScript->pKeyEvents[key & 127]);
1086                        } else {
1087                            // ... otherwise if no polyphonic data is passed and
1088                            // script's execution has finished without suspension
1089                            // status, then free the script event for a new future
1090                            // script event to be triggered from start
1091                            pChannel->pScript->pEvents->free(itScriptEvent);
1092                        }
1093                    }
1094                }
1095    
1096                /**
1097               *  Will be called by LaunchVoice() method in case there are no free               *  Will be called by LaunchVoice() method in case there are no free
1098               *  voices left. This method will select and kill one old voice for               *  voices left. This method will select and kill one old voice for
1099               *  voice stealing and postpone the note-on event until the selected               *  voice stealing and postpone the note-on event until the selected
1100               *  voice actually died.               *  voice actually died.
1101               *               *
1102               *  @param pEngineChannel - engine channel on which this event occured on               *  @param pEngineChannel - engine channel on which this event occurred on
1103               *  @param itNoteOnEvent - key, velocity and time stamp of the event               *  @param itNoteOnEvent - key, velocity and time stamp of the event
1104               *  @returns 0 on success, a value < 0 if no active voice could be picked for voice stealing               *  @returns 0 on success, a value < 0 if no active voice could be picked for voice stealing
1105               */               */
1106              int  StealVoice(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) {              int StealVoice(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) {
1107                  if (VoiceSpawnsLeft <= 0) {                  if (VoiceSpawnsLeft <= 0) {
1108                      dmsg(1,("Max. voice thefts per audio fragment reached (you may raise CONFIG_MAX_VOICES).\n"));                      dmsg(1,("Max. voice thefts per audio fragment reached (you may raise CONFIG_MAX_VOICES).\n"));
1109                      return -1;                      return -1;
# Line 676  namespace LinuxSampler { Line 1111  namespace LinuxSampler {
1111    
1112                  EngineChannelBase<V, R, I>* pEngineChn = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);                  EngineChannelBase<V, R, I>* pEngineChn = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
1113    
1114                  if (!pEventPool->poolIsEmpty()) {                  if (pEventPool->poolIsEmpty()) {
1115                        dmsg(1,("Event pool emtpy!\n"));
1116                        return -1;
1117                    }
1118    
1119                      if(!pEngineChn->StealVoice(itNoteOnEvent, &itLastStolenVoice, &iuiLastStolenKey)) {                  if (!pEngineChn->StealVoice(itNoteOnEvent, &itLastStolenVoice, &itLastStolenNote, &iuiLastStolenKey)) {
1120                          --VoiceSpawnsLeft;                      --VoiceSpawnsLeft;
1121                          return 0;                      return 0;
1122                      }                  }
1123    
1124                      // if we couldn't steal a voice from the same engine channel then                  // if we couldn't steal a voice from the same engine channel then
1125                      // steal oldest voice on the oldest key from any other engine channel                  // steal oldest voice on the oldest key from any other engine channel
1126                      // (the smaller engine channel number, the higher priority)                  // (the smaller engine channel number, the higher priority)
1127                      EngineChannelBase<V, R, I>*  pSelectedChannel;                  EngineChannelBase<V, R, I>*  pSelectedChannel;
1128                      int                       iChannelIndex;                  int                          iChannelIndex;
1129                      VoiceIterator             itSelectedVoice;                  VoiceIterator                itSelectedVoice;
1130    
1131                      // select engine channel                  // select engine channel
1132                      if (pLastStolenChannel) {                  if (pLastStolenChannel) {
1133                          pSelectedChannel = pLastStolenChannel;                      pSelectedChannel = pLastStolenChannel;
1134                          iChannelIndex    = pSelectedChannel->iEngineIndexSelf;                      iChannelIndex    = pSelectedChannel->iEngineIndexSelf;
1135                      } else { // pick the engine channel followed by this engine channel                  } else { // pick the engine channel followed by this engine channel
1136                          iChannelIndex    = (pEngineChn->iEngineIndexSelf + 1) % engineChannels.size();                      iChannelIndex    = (pEngineChn->iEngineIndexSelf + 1) % engineChannels.size();
1137                          pSelectedChannel = static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannelIndex]);                      pSelectedChannel = static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannelIndex]);
1138                      }                  }
1139    
1140                      // if we already stole in this fragment, try to proceed on same key                  // if we already stole in this fragment, try to proceed on same note
1141                      if (this->itLastStolenVoiceGlobally) {                  if (this->itLastStolenVoiceGlobally) {
1142                          itSelectedVoice = this->itLastStolenVoiceGlobally;                      itSelectedVoice = this->itLastStolenVoiceGlobally;
1143                          do {                      do {
1144                              ++itSelectedVoice;                          ++itSelectedVoice;
1145                          } while (itSelectedVoice && !itSelectedVoice->IsStealable()); // proceed iterating if voice was created in this fragment cycle                      } while (itSelectedVoice && !itSelectedVoice->IsStealable()); // proceed iterating if voice was created in this fragment cycle
1146                    }
1147                    // did we find a 'stealable' voice?
1148                    if (itSelectedVoice && itSelectedVoice->IsStealable()) {
1149                        // remember which voice we stole, so we can simply proceed on next voice stealing
1150                        this->itLastStolenVoiceGlobally = itSelectedVoice;
1151                        // done
1152                        goto stealable_voice_found;
1153                    }
1154    
1155                    // get (next) oldest note
1156                    if (this->itLastStolenNoteGlobally) {
1157                        for (NoteIterator itNote = ++this->itLastStolenNoteGlobally;
1158                             itNote; ++itNote)
1159                        {
1160                            for (itSelectedVoice = itNote->pActiveVoices->first(); itSelectedVoice; ++itSelectedVoice) {
1161                                // proceed iterating if voice was created in this audio fragment cycle
1162                                if (itSelectedVoice->IsStealable()) {
1163                                    // remember which voice of which note we stole, so we can simply proceed on next voice stealing
1164                                    this->itLastStolenNoteGlobally  = itNote;
1165                                    this->itLastStolenVoiceGlobally = itSelectedVoice;
1166                                    goto stealable_voice_found; // selection succeeded
1167                                }
1168                            }
1169                      }                      }
1170                    }
1171    
1172                      #if CONFIG_DEVMODE                  #if CONFIG_DEVMODE
1173                      EngineChannel* pBegin = pSelectedChannel; // to detect endless loop                  EngineChannel* pBegin = pSelectedChannel; // to detect endless loop
1174                      #endif // CONFIG_DEVMODE                  #endif // CONFIG_DEVMODE
1175    
1176                      // did we find a 'stealable' voice?                  while (true) { // iterate through engine channels                        
1177                      if (itSelectedVoice && itSelectedVoice->IsStealable()) {                      // get (next) oldest key
1178                          // remember which voice we stole, so we can simply proceed on next voice stealing                      RTList<uint>::Iterator iuiSelectedKey = (this->iuiLastStolenKeyGlobally) ? ++this->iuiLastStolenKeyGlobally : pSelectedChannel->pActiveKeys->first();
1179                          this->itLastStolenVoiceGlobally = itSelectedVoice;                      this->iuiLastStolenKeyGlobally = RTList<uint>::Iterator(); // to prevent endless loop (see line above)
1180                      } else while (true) { // iterate through engine channels                      while (iuiSelectedKey) {
1181                          // get (next) oldest key                          MidiKey* pSelectedKey = &pSelectedChannel->pMIDIKeyInfo[*iuiSelectedKey];
1182                          RTList<uint>::Iterator iuiSelectedKey = (this->iuiLastStolenKeyGlobally) ? ++this->iuiLastStolenKeyGlobally : pSelectedChannel->pActiveKeys->first();  
1183                          this->iuiLastStolenKeyGlobally = RTList<uint>::Iterator(); // to prevent endless loop (see line above)                          for (NoteIterator itNote = pSelectedKey->pActiveNotes->first(),
1184                          while (iuiSelectedKey) {                               itNotesEnd = pSelectedKey->pActiveNotes->end();
1185                              MidiKey* pSelectedKey = &pSelectedChannel->pMIDIKeyInfo[*iuiSelectedKey];                               itNote != itNotesEnd; ++itNote)
1186                              itSelectedVoice = pSelectedKey->pActiveVoices->first();                          {
1187                                itSelectedVoice = itNote->pActiveVoices->first();
1188                              // proceed iterating if voice was created in this fragment cycle                              // proceed iterating if voice was created in this fragment cycle
1189                              while (itSelectedVoice && !itSelectedVoice->IsStealable()) ++itSelectedVoice;                              while (itSelectedVoice && !itSelectedVoice->IsStealable()) ++itSelectedVoice;
1190                              // found a "stealable" voice ?                              // found a "stealable" voice ?
1191                              if (itSelectedVoice && itSelectedVoice->IsStealable()) {                              if (itSelectedVoice && itSelectedVoice->IsStealable()) {
1192                                  // remember which voice on which key on which engine channel we stole, so we can simply proceed on next voice stealing                                  // remember which voice of which note on which key on which engine channel we stole, so we can simply proceed on next voice stealing
1193                                  this->iuiLastStolenKeyGlobally  = iuiSelectedKey;                                  this->iuiLastStolenKeyGlobally  = iuiSelectedKey;
1194                                    this->itLastStolenNoteGlobally  = itNote;
1195                                  this->itLastStolenVoiceGlobally = itSelectedVoice;                                  this->itLastStolenVoiceGlobally = itSelectedVoice;
1196                                  this->pLastStolenChannel        = pSelectedChannel;                                  this->pLastStolenChannel        = pSelectedChannel;
1197                                  goto stealable_voice_found; // selection succeeded                                  goto stealable_voice_found; // selection succeeded
1198                              }                              }
                             ++iuiSelectedKey; // get next key on current engine channel  
                         }  
                         // get next engine channel  
                         iChannelIndex    = (iChannelIndex + 1) % engineChannels.size();  
                         pSelectedChannel = static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannelIndex]);  
   
                         #if CONFIG_DEVMODE  
                         if (pSelectedChannel == pBegin) {  
                             dmsg(1,("FATAL ERROR: voice stealing endless loop!\n"));  
                             dmsg(1,("VoiceSpawnsLeft=%d.\n", VoiceSpawnsLeft));  
                             dmsg(1,("Exiting.\n"));  
                             exit(-1);  
1199                          }                          }
1200                          #endif // CONFIG_DEVMODE                          ++iuiSelectedKey; // get next key on current engine channel
1201                      }                      }
1202                        // get next engine channel
1203                      // jump point if a 'stealable' voice was found                      iChannelIndex    = (iChannelIndex + 1) % engineChannels.size();
1204                      stealable_voice_found:                      pSelectedChannel = static_cast<EngineChannelBase<V, R, I>*>(engineChannels[iChannelIndex]);
1205    
1206                      #if CONFIG_DEVMODE                      #if CONFIG_DEVMODE
1207                      if (!itSelectedVoice->IsActive()) {                      if (pSelectedChannel == pBegin) {
1208                          dmsg(1,("EngineBase: ERROR, tried to steal a voice which was not active !!!\n"));                          dmsg(1,("FATAL ERROR: voice stealing endless loop!\n"));
1209                          return -1;                          dmsg(1,("VoiceSpawnsLeft=%d.\n", VoiceSpawnsLeft));
1210                            dmsg(1,("Exiting.\n"));
1211                            exit(-1);
1212                      }                      }
1213                      #endif // CONFIG_DEVMODE                      #endif // CONFIG_DEVMODE
1214                    }
1215    
1216                      // now kill the selected voice                  // jump point if a 'stealable' voice was found
1217                      itSelectedVoice->Kill(itNoteOnEvent);                  stealable_voice_found:
   
                     --VoiceSpawnsLeft;  
1218    
1219                      return 0; // success                  #if CONFIG_DEVMODE
1220                  }                  if (!itSelectedVoice->IsActive()) {
1221                  else {                      dmsg(1,("EngineBase: ERROR, tried to steal a voice which was not active !!!\n"));
                     dmsg(1,("Event pool emtpy!\n"));  
1222                      return -1;                      return -1;
1223                  }                  }
1224                    #endif // CONFIG_DEVMODE
1225    
1226                    // now kill the selected voice
1227                    itSelectedVoice->Kill(itNoteOnEvent);
1228    
1229                    --VoiceSpawnsLeft;
1230    
1231                    return 0; // success
1232              }              }
1233    
1234              void HandleInstrumentChanges() {              void HandleInstrumentChanges() {
# Line 790  namespace LinuxSampler { Line 1250  namespace LinuxSampler {
1250                          dmsg(5,("Engine: instrument change command received\n"));                          dmsg(5,("Engine: instrument change command received\n"));
1251                          cmd.bChangeInstrument = false;                          cmd.bChangeInstrument = false;
1252                          pEngineChannel->pInstrument = cmd.pInstrument;                          pEngineChannel->pInstrument = cmd.pInstrument;
1253                            pEngineChannel->pScript =
1254                                cmd.pScript->bHasValidScript ? cmd.pScript : NULL;
1255                          instrumentChanged = true;                          instrumentChanged = true;
1256    
1257                          pEngineChannel->MarkAllActiveVoicesAsOrphans();                          pEngineChannel->MarkAllActiveVoicesAsOrphans();
1258    
1259                            // the script's "init" event handler is only executed
1260                            // once (when the script is loaded or reloaded)
1261                            if (pEngineChannel->pScript && pEngineChannel->pScript->handlerInit) {
1262                                RTList<ScriptEvent>::Iterator itScriptEvent =
1263                                    pEngineChannel->pScript->pEvents->allocAppend();
1264    
1265                                itScriptEvent->cause.pEngineChannel = pEngineChannel;
1266                                itScriptEvent->handlers[0] = pEngineChannel->pScript->handlerInit;
1267                                itScriptEvent->handlers[1] = NULL;
1268    
1269                                VMExecStatus_t res = pScriptVM->exec(
1270                                    pEngineChannel->pScript->parserContext, &*itScriptEvent
1271                                );
1272    
1273                                pEngineChannel->pScript->pEvents->free(itScriptEvent);
1274                            }
1275                      }                      }
1276                  }                  }
1277    
# Line 843  namespace LinuxSampler { Line 1322  namespace LinuxSampler {
1322                      EngineChannelBase<V, R, I>* pEngineChannel =                      EngineChannelBase<V, R, I>* pEngineChannel =
1323                          static_cast<EngineChannelBase<V, R, I>*>(itVoiceStealEvent->pEngineChannel);;                          static_cast<EngineChannelBase<V, R, I>*>(itVoiceStealEvent->pEngineChannel);;
1324                      if (!pEngineChannel->pInstrument) continue; // ignore if no instrument loaded                      if (!pEngineChannel->pInstrument) continue; // ignore if no instrument loaded
1325                        
1326                      PoolVoiceIterator itNewVoice =                      PoolVoiceIterator itNewVoice =
1327                          LaunchVoice(pEngineChannel, itVoiceStealEvent, itVoiceStealEvent->Param.Note.Layer, itVoiceStealEvent->Param.Note.ReleaseTrigger, false, false);                          LaunchVoice(pEngineChannel, itVoiceStealEvent, itVoiceStealEvent->Param.Note.Layer, itVoiceStealEvent->Param.Note.ReleaseTrigger, false, false);
1328                      if (itNewVoice) {                      if (itNewVoice) {
1329                            // usually there should already be a new Note object
1330                            NoteIterator itNote = GetNotePool()->fromID(itVoiceStealEvent->Param.Note.ID);
1331                            if (!itNote) { // should not happen, but just to be sure ...
1332                                const note_id_t noteID = LaunchNewNote(pEngineChannel, &*itVoiceStealEvent);
1333                                if (!noteID) {
1334                                    dmsg(1,("Engine: Voice stealing failed; No Note object and Note pool empty!\n"));
1335                                    continue;
1336                                }
1337                                itNote = GetNotePool()->fromID(noteID);
1338                            }
1339                            // move voice from whereever it was, to the new note's list of active voices
1340                            itNewVoice = itNewVoice.moveToEndOf(itNote->pActiveVoices);
1341                            // render audio of this new voice for the first time
1342                          itNewVoice->Render(Samples);                          itNewVoice->Render(Samples);
1343                          if (itNewVoice->IsActive()) { // still active                          if (itNewVoice->IsActive()) { // still active
1344                              *(pEngineChannel->pRegionsInUse->allocAppend()) = itNewVoice->GetRegion();                              *(pEngineChannel->pRegionsInUse->allocAppend()) = itNewVoice->GetRegion();
# Line 883  namespace LinuxSampler { Line 1376  namespace LinuxSampler {
1376                   pChannel->FreeAllInactiveKyes();                   pChannel->FreeAllInactiveKyes();
1377    
1378                  // empty the engine channel's own event lists                  // empty the engine channel's own event lists
1379                  pChannel->ClearEventLists();                  // (only events of the current audio fragment cycle)
1380                    pChannel->ClearEventListsOfCurrentFragment();
1381              }              }
1382    
1383              /**              /**
# Line 948  namespace LinuxSampler { Line 1442  namespace LinuxSampler {
1442                                  case 0x1d: { // reverb send of note (Roland GS NRPN)                                  case 0x1d: { // reverb send of note (Roland GS NRPN)
1443                                      const uint note = NrpnCtrlLSB;                                      const uint note = NrpnCtrlLSB;
1444                                      const float reverb = float(itControlChangeEvent->Param.CC.Value) / 127.0f;                                      const float reverb = float(itControlChangeEvent->Param.CC.Value) / 127.0f;
1445                                      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));
1446                                      if (note < 128)                                      if (note < 128)
1447                                          pChannel->pMIDIKeyInfo[note].ReverbSend = reverb;                                          pChannel->pMIDIKeyInfo[note].ReverbSend = reverb;
1448                                      break;                                      break;
# Line 956  namespace LinuxSampler { Line 1450  namespace LinuxSampler {
1450                                  case 0x1e: { // chorus send of note (Roland GS NRPN)                                  case 0x1e: { // chorus send of note (Roland GS NRPN)
1451                                      const uint note = NrpnCtrlLSB;                                      const uint note = NrpnCtrlLSB;
1452                                      const float chorus = float(itControlChangeEvent->Param.CC.Value) / 127.0f;                                      const float chorus = float(itControlChangeEvent->Param.CC.Value) / 127.0f;
1453                                      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));
1454                                      if (note < 128)                                      if (note < 128)
1455                                          pChannel->pMIDIKeyInfo[note].ChorusSend = chorus;                                          pChannel->pMIDIKeyInfo[note].ChorusSend = chorus;
1456                                      break;                                      break;
# Line 1109  namespace LinuxSampler { Line 1603  namespace LinuxSampler {
1603              /**              /**
1604               *  Assigns and triggers a new voice for the respective MIDI key.               *  Assigns and triggers a new voice for the respective MIDI key.
1605               *               *
1606               *  @param pEngineChannel - engine channel on which this event occured on               *  @param pEngineChannel - engine channel on which this event occurred on
1607               *  @param itNoteOnEvent - key, velocity and time stamp of the event               *  @param itNoteOnEvent - key, velocity and time stamp of the event
1608               */               */
1609              virtual void ProcessNoteOn(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) {              virtual void ProcessNoteOn(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent) {
1610                  EngineChannelBase<V, R, I>* pChannel =                  EngineChannelBase<V, R, I>* pChannel =
1611                          static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);                          static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
1612    
                 //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  
                 int k = itNoteOnEvent->Param.Note.Key + pChannel->GlobalTranspose;  
                 if (k < 0 || k > 127) return; //ignore keys outside the key range  
   
                 itNoteOnEvent->Param.Note.Key += pChannel->GlobalTranspose;  
                 int vel = itNoteOnEvent->Param.Note.Velocity;  
   
1613                  const int key = itNoteOnEvent->Param.Note.Key;                  const int key = itNoteOnEvent->Param.Note.Key;
1614                    const int vel = itNoteOnEvent->Param.Note.Velocity;
1615                    if (key < 0 || key > 127) return; // ignore event, key outside allowed key range
1616    
1617                  MidiKey* pKey = &pChannel->pMIDIKeyInfo[key];                  MidiKey* pKey = &pChannel->pMIDIKeyInfo[key];
1618    
1619                  pChannel->listeners.PreProcessNoteOn(key, vel);                  // There are real MIDI note-on events (Event::type_note_on) and
1620                    // programmatically spawned notes (Event::type_play_note). We have
1621                    // to distinguish between them, since certain processing below
1622                    // must only be done on real MIDI note-on events (i.e. for
1623                    // correctly updating which MIDI keys are currently pressed down).
1624                    const bool isRealMIDINoteOnEvent = itNoteOnEvent->Type == Event::type_note_on;
1625    
1626                    if (isRealMIDINoteOnEvent)
1627                        pChannel->listeners.PreProcessNoteOn(key, vel);
1628    
1629                  #if !CONFIG_PROCESS_MUTED_CHANNELS                  #if !CONFIG_PROCESS_MUTED_CHANNELS
1630                  if (pEngineChannel->GetMute()) { // skip if sampler channel is muted                  if (pEngineChannel->GetMute()) { // skip if sampler channel is muted
1631                      pChannel->listeners.PostProcessNoteOn(key, vel);                      if (isRealMIDINoteOnEvent)
1632                            pChannel->listeners.PostProcessNoteOn(key, vel);
1633                      return;                      return;
1634                  }                  }
1635                  #endif                  #endif
1636    
1637                  if (!pChannel->pInstrument) {                  if (!pChannel->pInstrument) {
1638                      pChannel->listeners.PostProcessNoteOn(key, vel);                      if (isRealMIDINoteOnEvent)
1639                            pChannel->listeners.PostProcessNoteOn(key, vel);
1640                      return; // ignore if no instrument loaded                      return; // ignore if no instrument loaded
1641                  }                  }
1642    
# Line 1143  namespace LinuxSampler { Line 1644  namespace LinuxSampler {
1644                  RTList<Event>::Iterator itNoteOnEventOnKeyList = itNoteOnEvent.moveToEndOf(pKey->pEvents);                  RTList<Event>::Iterator itNoteOnEventOnKeyList = itNoteOnEvent.moveToEndOf(pKey->pEvents);
1645    
1646                  // if Solo Mode then kill all already active voices                  // if Solo Mode then kill all already active voices
1647                  if (pChannel->SoloMode) {                  if (pChannel->SoloMode && isRealMIDINoteOnEvent) {
1648                      Pool<uint>::Iterator itYoungestKey = pChannel->pActiveKeys->last();                      Pool<uint>::Iterator itYoungestKey = pChannel->pActiveKeys->last();
1649                      if (itYoungestKey) {                      if (itYoungestKey) {
1650                          const int iYoungestKey = *itYoungestKey;                          const int iYoungestKey = *itYoungestKey;
# Line 1151  namespace LinuxSampler { Line 1652  namespace LinuxSampler {
1652                          if (pOtherKey->Active) {                          if (pOtherKey->Active) {
1653                              // get final portamento position of currently active voice                              // get final portamento position of currently active voice
1654                              if (pChannel->PortamentoMode) {                              if (pChannel->PortamentoMode) {
1655                                  VoiceIterator itVoice = pOtherKey->pActiveVoices->last();                                  NoteIterator itNote = pOtherKey->pActiveNotes->last();
1656                                  if (itVoice) itVoice->UpdatePortamentoPos(itNoteOnEventOnKeyList);                                  if (itNote) {
1657                                        VoiceIterator itVoice = itNote->pActiveVoices->last();
1658                                        if (itVoice) itVoice->UpdatePortamentoPos(itNoteOnEventOnKeyList);
1659                                    }
1660                              }                              }
1661                              // kill all voices on the (other) key                              // kill all voices on the (other) key
1662                              VoiceIterator itVoiceToBeKilled = pOtherKey->pActiveVoices->first();                              for (NoteIterator itNote = pOtherKey->pActiveNotes->first(); itNote; ++itNote) {
1663                              VoiceIterator end               = pOtherKey->pActiveVoices->end();                                  VoiceIterator itVoiceToBeKilled = itNote->pActiveVoices->first();
1664                              for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) {                                  VoiceIterator end               = itNote->pActiveVoices->end();
1665                                  if (!(itVoiceToBeKilled->Type & Voice::type_release_trigger))                                  for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) {
1666                                      itVoiceToBeKilled->Kill(itNoteOnEventOnKeyList);                                      if (!(itVoiceToBeKilled->Type & Voice::type_release_trigger))
1667                                            itVoiceToBeKilled->Kill(itNoteOnEventOnKeyList);
1668                                    }
1669                              }                              }
1670                          }                          }
1671                      }                      }
# Line 1167  namespace LinuxSampler { Line 1673  namespace LinuxSampler {
1673                      pChannel->SoloKey = key;                      pChannel->SoloKey = key;
1674                  }                  }
1675    
1676                  pChannel->ProcessKeySwitchChange(key);                  if (isRealMIDINoteOnEvent) {
1677                        pChannel->ProcessKeySwitchChange(key);
1678    
1679                  pKey->KeyPressed = true; // the MIDI key was now pressed down                      pKey->KeyPressed = true; // the MIDI key was now pressed down
1680                  pKey->Velocity   = itNoteOnEventOnKeyList->Param.Note.Velocity;                      pChannel->KeyDown[key] = true; // just used as built-in %KEY_DOWN script variable
1681                  pKey->NoteOnTime = FrameTime + itNoteOnEventOnKeyList->FragmentPos(); // will be used to calculate note length                      pKey->Velocity   = itNoteOnEventOnKeyList->Param.Note.Velocity;
1682                        pKey->NoteOnTime = FrameTime + itNoteOnEventOnKeyList->FragmentPos(); // will be used to calculate note length
1683                    }
1684    
1685                  // cancel release process of voices on this key if needed                  // cancel release process of voices on this key if needed
1686                  if (pKey->Active && !pChannel->SustainPedal) {                  if (pKey->Active && !pChannel->SustainPedal && isRealMIDINoteOnEvent) {
1687                      RTList<Event>::Iterator itCancelReleaseEvent = pKey->pEvents->allocAppend();                      RTList<Event>::Iterator itCancelReleaseEvent = pKey->pEvents->allocAppend();
1688                      if (itCancelReleaseEvent) {                      if (itCancelReleaseEvent) {
1689                          *itCancelReleaseEvent = *itNoteOnEventOnKeyList;         // copy event                          *itCancelReleaseEvent = *itNoteOnEventOnKeyList;         // copy event
1690                          itCancelReleaseEvent->Type = Event::type_cancel_release; // transform event type                          itCancelReleaseEvent->Type = Event::type_cancel_release_key; // transform event type
1691                      }                      }
1692                      else dmsg(1,("Event pool emtpy!\n"));                      else dmsg(1,("Event pool emtpy!\n"));
1693                  }                  }
# Line 1189  namespace LinuxSampler { Line 1698  namespace LinuxSampler {
1698                  if (!pKey->Active && !pKey->VoiceTheftsQueued)                  if (!pKey->Active && !pKey->VoiceTheftsQueued)
1699                      pKey->pEvents->free(itNoteOnEventOnKeyList);                      pKey->pEvents->free(itNoteOnEventOnKeyList);
1700    
1701                  if (!pChannel->SoloMode || pChannel->PortamentoPos < 0.0f) pChannel->PortamentoPos = (float) key;                  if (isRealMIDINoteOnEvent && (!pChannel->SoloMode || pChannel->PortamentoPos < 0.0f))
1702                        pChannel->PortamentoPos = (float) key;
1703    
1704                    //NOTE: Hmm, I guess its a matter of taste whether round robin should be advanced only on real MIDI note-on events, isn't it?
1705                  if (pKey->pRoundRobinIndex) {                  if (pKey->pRoundRobinIndex) {
1706                      (*pKey->pRoundRobinIndex)++; // counter specific for the key or region                      (*pKey->pRoundRobinIndex)++; // counter specific for the key or region
1707                      pChannel->RoundRobinIndex++; // common counter for the channel                      pChannel->RoundRobinIndex++; // common counter for the channel
1708                  }                  }
1709                  pChannel->listeners.PostProcessNoteOn(key, vel);  
1710                    if (isRealMIDINoteOnEvent)
1711                        pChannel->listeners.PostProcessNoteOn(key, vel);
1712              }              }
1713    
1714              /**              /**
# Line 1220  namespace LinuxSampler { Line 1734  namespace LinuxSampler {
1734               *  sustain pedal will be released or voice turned inactive by itself (e.g.               *  sustain pedal will be released or voice turned inactive by itself (e.g.
1735               *  due to completion of sample playback).               *  due to completion of sample playback).
1736               *               *
1737               *  @param pEngineChannel - engine channel on which this event occured on               *  @param pEngineChannel - engine channel on which this event occurred on
1738               *  @param itNoteOffEvent - key, velocity and time stamp of the event               *  @param itNoteOffEvent - key, velocity and time stamp of the event
1739               */               */
1740              virtual void ProcessNoteOff(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOffEvent) {              virtual void ProcessNoteOff(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOffEvent) {
1741                  EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);                  EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
1742    
                 int k = itNoteOffEvent->Param.Note.Key + pChannel->GlobalTranspose;  
                 if (k < 0 || k > 127) return; //ignore keys outside the key range  
   
                 //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  
                 itNoteOffEvent->Param.Note.Key += pChannel->GlobalTranspose;  
                 int vel = itNoteOffEvent->Param.Note.Velocity;  
   
1743                  const int iKey = itNoteOffEvent->Param.Note.Key;                  const int iKey = itNoteOffEvent->Param.Note.Key;
1744                    const int vel  = itNoteOffEvent->Param.Note.Velocity;
1745                    if (iKey < 0 || iKey > 127) return; // ignore event, key outside allowed key range
1746    
1747                  MidiKey* pKey = &pChannel->pMIDIKeyInfo[iKey];                  MidiKey* pKey = &pChannel->pMIDIKeyInfo[iKey];
1748    
1749                  pChannel->listeners.PreProcessNoteOff(iKey, vel);                  // There are real MIDI note-off events (Event::type_note_off) and
1750                    // programmatically spawned notes (Event::type_stop_note). We have
1751                    // to distinguish between them, since certain processing below
1752                    // must only be done on real MIDI note-off events (i.e. for
1753                    // correctly updating which MIDI keys are currently pressed down),
1754                    // plus a stop-note event just releases voices of one particular
1755                    // note, whereas a note-off event releases all voices on a
1756                    // particular MIDI key instead.
1757                    const bool isRealMIDINoteOffEvent = itNoteOffEvent->Type == Event::type_note_off;
1758    
1759                    if (isRealMIDINoteOffEvent)
1760                        pChannel->listeners.PreProcessNoteOff(iKey, vel);
1761    
1762                  #if !CONFIG_PROCESS_MUTED_CHANNELS                  #if !CONFIG_PROCESS_MUTED_CHANNELS
1763                  if (pEngineChannel->GetMute()) { // skip if sampler channel is muted                  if (pEngineChannel->GetMute()) { // skip if sampler channel is muted
1764                      pChannel->listeners.PostProcessNoteOff(iKey, vel);                      if (isRealMIDINoteOffEvent)
1765                            pChannel->listeners.PostProcessNoteOff(iKey, vel);
1766                      return;                      return;
1767                  }                  }
1768                  #endif                  #endif
1769    
1770                  pKey->KeyPressed = false; // the MIDI key was now released                  if (isRealMIDINoteOffEvent) {
1771                        pKey->KeyPressed = false; // the MIDI key was now released
1772                        pChannel->KeyDown[iKey] = false; // just used as built-in %KEY_DOWN script variable
1773                    }
1774    
1775                  // move event to the key's own event list                  // move event to the key's own event list
1776                  RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents);                  RTList<Event>::Iterator itNoteOffEventOnKeyList = itNoteOffEvent.moveToEndOf(pKey->pEvents);
1777    
1778                  bool bShouldRelease = pKey->Active && pChannel->ShouldReleaseVoice(itNoteOffEventOnKeyList->Param.Note.Key);                  if (isRealMIDINoteOffEvent) {
1779                        bool bShouldRelease = pKey->Active && pChannel->ShouldReleaseVoice(itNoteOffEventOnKeyList->Param.Note.Key);
1780    
1781                  // in case Solo Mode is enabled, kill all voices on this key and respawn a voice on the highest pressed key (if any)                      // in case Solo Mode is enabled, kill all voices on this key and respawn a voice on the highest pressed key (if any)
1782                  if (pChannel->SoloMode && pChannel->pInstrument) { //TODO: this feels like too much code just for handling solo mode :P                      if (pChannel->SoloMode && pChannel->pInstrument) { //TODO: this feels like too much code just for handling solo mode :P
1783                      bool bOtherKeysPressed = false;                          bool bOtherKeysPressed = false;
1784                      if (iKey == pChannel->SoloKey) {                          if (iKey == pChannel->SoloKey) {
1785                          pChannel->SoloKey = -1;                              pChannel->SoloKey = -1;
1786                          // if there's still a key pressed down, respawn a voice (group) on the highest key                              // if there's still a key pressed down, respawn a voice (group) on the highest key
1787                          for (int i = 127; i > 0; i--) {                              for (int i = 127; i > 0; i--) {
1788                              MidiKey* pOtherKey = &pChannel->pMIDIKeyInfo[i];                                  MidiKey* pOtherKey = &pChannel->pMIDIKeyInfo[i];
1789                              if (pOtherKey->KeyPressed) {                                  if (pOtherKey->KeyPressed) {
1790                                  bOtherKeysPressed = true;                                      bOtherKeysPressed = true;
1791                                  // make the other key the new 'currently active solo key'                                      // make the other key the new 'currently active solo key'
1792                                  pChannel->SoloKey = i;                                      pChannel->SoloKey = i;
1793                                  // get final portamento position of currently active voice                                      // get final portamento position of currently active voice
1794                                  if (pChannel->PortamentoMode) {                                      if (pChannel->PortamentoMode) {
1795                                      VoiceIterator itVoice = pKey->pActiveVoices->first();                                          NoteIterator itNote = pKey->pActiveNotes->first();
1796                                      if (itVoice) itVoice->UpdatePortamentoPos(itNoteOffEventOnKeyList);                                          VoiceIterator itVoice = itNote->pActiveVoices->first();
1797                                  }                                          if (itVoice) itVoice->UpdatePortamentoPos(itNoteOffEventOnKeyList);
1798                                  // create a pseudo note on event                                      }
1799                                  RTList<Event>::Iterator itPseudoNoteOnEvent = pOtherKey->pEvents->allocAppend();                                      // create a pseudo note on event
1800                                  if (itPseudoNoteOnEvent) {                                      RTList<Event>::Iterator itPseudoNoteOnEvent = pOtherKey->pEvents->allocAppend();
1801                                      // copy event                                      if (itPseudoNoteOnEvent) {
1802                                      *itPseudoNoteOnEvent = *itNoteOffEventOnKeyList;                                          // copy event
1803                                      // transform event to a note on event                                          *itPseudoNoteOnEvent = *itNoteOffEventOnKeyList;
1804                                      itPseudoNoteOnEvent->Type                = Event::type_note_on;                                          // transform event to a note on event
1805                                      itPseudoNoteOnEvent->Param.Note.Key      = i;                                          itPseudoNoteOnEvent->Type                = Event::type_note_on; //FIXME: should probably use Event::type_play_note instead (to avoid i.e. hanging notes)
1806                                      itPseudoNoteOnEvent->Param.Note.Velocity = pOtherKey->Velocity;                                          itPseudoNoteOnEvent->Param.Note.Key      = i;
1807                                      // allocate and trigger new voice(s) for the other key                                          itPseudoNoteOnEvent->Param.Note.Velocity = pOtherKey->Velocity;
1808                                      TriggerNewVoices(pChannel, itPseudoNoteOnEvent, false);                                          // assign a new note to this note-on event
1809                                      // if neither a voice was spawned or postponed then remove note on event from key again                                          if (LaunchNewNote(pChannel, &*itPseudoNoteOnEvent)) {
1810                                      if (!pOtherKey->Active && !pOtherKey->VoiceTheftsQueued)                                              // allocate and trigger new voice(s) for the other key
1811                                          pOtherKey->pEvents->free(itPseudoNoteOnEvent);                                              TriggerNewVoices(pChannel, itPseudoNoteOnEvent, false);
1812                                            }
1813                                            // if neither a voice was spawned or postponed then remove note on event from key again
1814                                            if (!pOtherKey->Active && !pOtherKey->VoiceTheftsQueued)
1815                                                pOtherKey->pEvents->free(itPseudoNoteOnEvent);
1816    
1817                                  } else dmsg(1,("Could not respawn voice, no free event left\n"));                                      } else dmsg(1,("Could not respawn voice, no free event left\n"));
1818                                  break; // done                                      break; // done
1819                                    }
1820                              }                              }
1821                          }                          }
1822                      }                          if (bOtherKeysPressed) {
1823                      if (bOtherKeysPressed) {                              if (pKey->Active) { // kill all voices on this key
1824                          if (pKey->Active) { // kill all voices on this key                                  bShouldRelease = false; // no need to release, as we kill it here
1825                              bShouldRelease = false; // no need to release, as we kill it here                                  for (NoteIterator itNote = pKey->pActiveNotes->first(); itNote; ++itNote) {
1826                              VoiceIterator itVoiceToBeKilled = pKey->pActiveVoices->first();                                      VoiceIterator itVoiceToBeKilled = itNote->pActiveVoices->first();
1827                              VoiceIterator end               = pKey->pActiveVoices->end();                                      VoiceIterator end               = itNote->pActiveVoices->end();
1828                              for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) {                                      for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) {
1829                                  if (!(itVoiceToBeKilled->Type & Voice::type_release_trigger))                                          if (!(itVoiceToBeKilled->Type & Voice::type_release_trigger))
1830                                      itVoiceToBeKilled->Kill(itNoteOffEventOnKeyList);                                              itVoiceToBeKilled->Kill(itNoteOffEventOnKeyList);
1831                                        }
1832                                    }
1833                              }                              }
1834                          }                          } else pChannel->PortamentoPos = -1.0f;
1835                      } else pChannel->PortamentoPos = -1.0f;                      }
                 }  
   
                 // if no solo mode (the usual case) or if solo mode and no other key pressed, then release voices on this key if needed  
                 if (bShouldRelease) {  
                     itNoteOffEventOnKeyList->Type = Event::type_release; // transform event type  
1836    
1837                      // spawn release triggered voice(s) if needed                      // if no solo mode (the usual case) or if solo mode and no other key pressed, then release voices on this key if needed
1838                      if (pKey->ReleaseTrigger && pChannel->pInstrument) {                      if (bShouldRelease) {
1839                          TriggerReleaseVoices(pChannel, itNoteOffEventOnKeyList);                          itNoteOffEventOnKeyList->Type = Event::type_release_key; // transform event type
1840                          pKey->ReleaseTrigger = false;                          // spawn release triggered voice(s) if needed
1841                            ProcessReleaseTrigger(pChannel, itNoteOffEventOnKeyList, pKey);
1842                        }
1843                    } else if (itNoteOffEventOnKeyList->Type == Event::type_stop_note) {
1844                        // This programmatically caused event is caused by a call to
1845                        // the built-in instrument script function note_off(). In
1846                        // contrast to a real MIDI note-off event the stop-note
1847                        // event just intends to release voices of one particular note.
1848                        NoteBase* pNote = pChannel->pEngine->NoteByID( itNoteOffEventOnKeyList->Param.Note.ID );
1849                        if (pNote) { // the requested note is still alive ...
1850                            itNoteOffEventOnKeyList->Type = Event::type_release_note; // transform event type
1851                        } else { // note is dead and gone ..
1852                            pKey->pEvents->free(itNoteOffEventOnKeyList); // remove stop-note event from key again
1853                            return; // prevent event to be removed a 2nd time below
1854                      }                      }
1855                  }                  }
1856    
# Line 1317  namespace LinuxSampler { Line 1858  namespace LinuxSampler {
1858                  if (!pKey->Active && !pKey->VoiceTheftsQueued)                  if (!pKey->Active && !pKey->VoiceTheftsQueued)
1859                      pKey->pEvents->free(itNoteOffEventOnKeyList);                      pKey->pEvents->free(itNoteOffEventOnKeyList);
1860    
1861                  pChannel->listeners.PostProcessNoteOff(iKey, vel);                  if (isRealMIDINoteOffEvent)
1862                        pChannel->listeners.PostProcessNoteOff(iKey, vel);
1863                }
1864    
1865                /**
1866                 * Called on sustain pedal up events to check and if required,
1867                 * launch release trigger voices on the respective active key.
1868                 *
1869                 * @param pEngineChannel - engine channel on which this event occurred on
1870                 * @param itEvent - release trigger event (contains note number)
1871                 */
1872                virtual void ProcessReleaseTrigger(EngineChannel* pEngineChannel, RTList<Event>::Iterator& itEvent) OVERRIDE {
1873                    EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
1874    
1875                    const int iKey = itEvent->Param.Note.Key;
1876                    if (iKey < 0 || iKey > 127) return; // ignore event, key outside allowed key range
1877    
1878                    MidiKey* pKey = &pChannel->pMIDIKeyInfo[iKey];
1879    
1880                    ProcessReleaseTrigger(pChannel, itEvent, pKey);
1881                }
1882    
1883                /**
1884                 * Called on note-off and sustain pedal up events to check and if
1885                 * required, launch release trigger voices on the respective active
1886                 * key.
1887                 *
1888                 * @param pEngineChannel - engine channel on which this event occurred on
1889                 * @param itEvent - note off event / release trigger event
1890                 * @param pKey - key on which the release trigger voices shall be spawned
1891                 */
1892                inline void ProcessReleaseTrigger(EngineChannelBase<V, R, I>* pChannel, RTList<Event>::Iterator& itEvent, MidiKey* pKey) {
1893                    // spawn release triggered voice(s) if needed
1894                    if (pKey->ReleaseTrigger && pChannel->pInstrument) {
1895                        // assign a new note to this release event
1896                        if (LaunchNewNote(pChannel, &*itEvent)) {
1897                            // allocate and trigger new release voice(s)
1898                            TriggerReleaseVoices(pChannel, itEvent);
1899                        }
1900                        pKey->ReleaseTrigger = false;
1901                    }
1902                }
1903    
1904                /**
1905                 * Called on note synthesis parameter change events. These are
1906                 * internal events caused by calling built-in real-time instrument
1907                 * script functions like change_vol(), change_pitch(), etc.
1908                 *
1909                 * This method performs two tasks:
1910                 *
1911                 * - It converts the event's relative values changes (Deltas) to
1912                 *   the respective final new synthesis parameter value (AbsValue),
1913                 *   for that particular moment of the event that is.
1914                 *
1915                 * - It moves the individual events to the Note's own event list
1916                 *   (or actually to the event list of the MIDI key), so that
1917                 *   voices can process those events sample accurately.
1918                 *
1919                 * @param pEngineChannel - engine channel on which this event occurred on
1920                 * @param itEvent - note synthesis parameter change event
1921                 */
1922                virtual void ProcessNoteSynthParam(EngineChannel* pEngineChannel, RTList<Event>::Iterator& itEvent) {
1923                    EngineChannelBase<V, R, I>* pChannel = static_cast<EngineChannelBase<V, R, I>*>(pEngineChannel);
1924    
1925                    NoteBase* pNote = pChannel->pEngine->NoteByID( itEvent->Param.NoteSynthParam.NoteID );
1926                    if (!pNote || pNote->hostKey < 0 || pNote->hostKey >= 128) return;
1927    
1928                    const bool& relative = itEvent->Param.NoteSynthParam.Relative;
1929    
1930                    switch (itEvent->Param.NoteSynthParam.Type) {
1931                        case Event::synth_param_volume:
1932                            if (relative)
1933                                pNote->Override.Volume *= itEvent->Param.NoteSynthParam.Delta;
1934                            else
1935                                pNote->Override.Volume = itEvent->Param.NoteSynthParam.Delta;
1936                            itEvent->Param.NoteSynthParam.AbsValue = pNote->Override.Volume;
1937                            break;
1938                        case Event::synth_param_pitch:
1939                            if (relative)
1940                                pNote->Override.Pitch *= itEvent->Param.NoteSynthParam.Delta;
1941                            else
1942                                pNote->Override.Pitch = itEvent->Param.NoteSynthParam.Delta;
1943                            itEvent->Param.NoteSynthParam.AbsValue = pNote->Override.Pitch;
1944                            break;
1945                        case Event::synth_param_pan:
1946                            if (relative) {
1947                                pNote->Override.Pan = RTMath::RelativeSummedAvg(pNote->Override.Pan, itEvent->Param.NoteSynthParam.Delta, ++pNote->Override.PanSources);
1948                            } else {
1949                                pNote->Override.Pan = itEvent->Param.NoteSynthParam.Delta;
1950                                pNote->Override.PanSources = 1; // only relevant on subsequent change_pan() instrument script calls on same note with 'relative' argument being set
1951                            }
1952                            itEvent->Param.NoteSynthParam.AbsValue = pNote->Override.Pan;
1953                            break;
1954                        case Event::synth_param_cutoff:
1955                            pNote->Override.Cutoff = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta;
1956                            break;
1957                        case Event::synth_param_resonance:
1958                            pNote->Override.Resonance = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta;
1959                            break;
1960                        case Event::synth_param_attack:
1961                            pNote->Override.Attack = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta;
1962                            break;
1963                        case Event::synth_param_decay:
1964                            pNote->Override.Decay = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta;
1965                            break;
1966                        case Event::synth_param_release:
1967                            pNote->Override.Release = itEvent->Param.NoteSynthParam.AbsValue = itEvent->Param.NoteSynthParam.Delta;
1968                            break;
1969                    }
1970    
1971                    // move note parameter event to its MIDI key
1972                    MidiKey* pKey = &pChannel->pMIDIKeyInfo[pNote->hostKey];
1973                    itEvent.moveToEndOf(pKey->pEvents);
1974              }              }
1975    
1976              /**              /**
# Line 1325  namespace LinuxSampler { Line 1978  namespace LinuxSampler {
1978               *  control and status variables. This method is protected by a mutex.               *  control and status variables. This method is protected by a mutex.
1979               */               */
1980              virtual void ResetInternal() {              virtual void ResetInternal() {
1981                  ResetInternalMutex.Lock();                  LockGuard lock(ResetInternalMutex);
1982    
1983                  // make sure that the engine does not get any sysex messages                  // make sure that the engine does not get any sysex messages
1984                  // while it's reseting                  // while it's reseting
# Line 1337  namespace LinuxSampler { Line 1990  namespace LinuxSampler {
1990                  pVoiceStealingQueue->clear();                  pVoiceStealingQueue->clear();
1991                  itLastStolenVoice          = VoiceIterator();                  itLastStolenVoice          = VoiceIterator();
1992                  itLastStolenVoiceGlobally  = VoiceIterator();                  itLastStolenVoiceGlobally  = VoiceIterator();
1993                    itLastStolenNote           = NoteIterator();
1994                    itLastStolenNoteGlobally   = NoteIterator();
1995                  iuiLastStolenKey           = RTList<uint>::Iterator();                  iuiLastStolenKey           = RTList<uint>::Iterator();
1996                  iuiLastStolenKeyGlobally   = RTList<uint>::Iterator();                  iuiLastStolenKeyGlobally   = RTList<uint>::Iterator();
1997                  pLastStolenChannel         = NULL;                  pLastStolenChannel         = NULL;
1998    
1999                    // reset all notes
2000                    pNotePool->clear();
2001                    for (NoteIterator itNote = pNotePool->allocAppend(); itNote;
2002                         itNote = pNotePool->allocAppend())
2003                    {
2004                        itNote->reset();
2005                    }
2006                    pNotePool->clear();
2007    
2008                  // reset all voices                  // reset all voices
2009                    pVoicePool->clear();
2010                  for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {                  for (VoiceIterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) {
2011                      iterVoice->Reset();                      iterVoice->Reset();
2012                  }                  }
2013                  pVoicePool->clear();                  pVoicePool->clear();
2014    
2015                    // reset all engine channels
2016                    for (int i = 0; i < engineChannels.size(); i++) {
2017                        AbstractEngineChannel* pEngineChannel =
2018                            static_cast<AbstractEngineChannel*>(engineChannels[i]);
2019                        pEngineChannel->ResetInternal(false/*don't reset engine*/);
2020                    }
2021    
2022                  // reset disk thread                  // reset disk thread
2023                  if (pDiskThread) pDiskThread->Reset();                  if (pDiskThread) pDiskThread->Reset();
2024    
# Line 1354  namespace LinuxSampler { Line 2026  namespace LinuxSampler {
2026                  pEventQueue->init();                  pEventQueue->init();
2027                  pSysexBuffer->init();                  pSysexBuffer->init();
2028                  if (sysexDisabled) MidiInputPort::AddSysexListener(this);                  if (sysexDisabled) MidiInputPort::AddSysexListener(this);
                 ResetInternalMutex.Unlock();  
2029              }              }
2030    
2031              /**              /**
# Line 1376  namespace LinuxSampler { Line 2047  namespace LinuxSampler {
2047               *  called by the ProcessNoteOn() method and by the voices itself               *  called by the ProcessNoteOn() method and by the voices itself
2048               *  (e.g. to spawn further voices on the same key for layered sounds).               *  (e.g. to spawn further voices on the same key for layered sounds).
2049               *               *
2050               *  @param pEngineChannel      - engine channel on which this event occured on               *  @param pEngineChannel      - engine channel on which this event occurred on
2051               *  @param itNoteOnEvent       - key, velocity and time stamp of the event               *  @param itNoteOnEvent       - key, velocity and time stamp of the event
2052               *  @param iLayer              - layer index for the new voice (optional - only               *  @param iLayer              - layer index for the new voice (optional - only
2053               *                               in case of layered sounds of course)               *                               in case of layered sounds of course)
# Line 1419  namespace LinuxSampler { Line 2090  namespace LinuxSampler {
2090                      // launch the new voice                      // launch the new voice
2091                      if (itNewVoice->Trigger(pChannel, itNoteOnEvent, pChannel->Pitch, pRegion, VoiceType, iKeyGroup) < 0) {                      if (itNewVoice->Trigger(pChannel, itNoteOnEvent, pChannel->Pitch, pRegion, VoiceType, iKeyGroup) < 0) {
2092                          dmsg(4,("Voice not triggered\n"));                          dmsg(4,("Voice not triggered\n"));
2093                          pKey->pActiveVoices->free(itNewVoice);                          GetVoicePool()->free(itNewVoice);
2094                      }                      }
2095                      else { // on success                      else { // on success
2096                          --VoiceSpawnsLeft;                          --VoiceSpawnsLeft;
# Line 1450  namespace LinuxSampler { Line 2121  namespace LinuxSampler {
2121    
2122                  return -1;                  return -1;
2123              }              }
2124                
2125                /**
2126                 * Checks whether scale tuning setting has been changed since last
2127                 * time this method was called, if yes, it recalculates the pitch
2128                 * for all active voices.
2129                 */
2130                void ProcessScaleTuningChange() {
2131                    const bool changed = ScaleTuningChanged.readAndReset();
2132                    if (!changed) return;
2133                    
2134                    for (int i = 0; i < engineChannels.size(); i++) {
2135                        EngineChannelBase<V, R, I>* channel =
2136                            static_cast<EngineChannelBase<V, R, I>*>(engineChannels[i]);
2137                        channel->OnScaleTuningChanged();
2138                    }
2139                }
2140    
2141          private:          private:
2142                Pool< Note<V> >* pNotePool;
2143                Pool<note_id_t> noteIDPool;
2144              Pool<V>*    pVoicePool;            ///< Contains all voices that can be activated.              Pool<V>*    pVoicePool;            ///< Contains all voices that can be activated.
2145              Pool<RR*>   SuspendedRegions;              Pool<RR*>   SuspendedRegions;
2146              Mutex       SuspendedRegionsMutex;              Mutex       SuspendedRegionsMutex;

Legend:
Removed from v.2410  
changed lines
  Added in v.2962

  ViewVC Help
Powered by ViewVC