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

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

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

revision 411 by schoenebeck, Sat Feb 26 02:01:14 2005 UTC revision 412 by schoenebeck, Sat Feb 26 22:44:51 2005 UTC
# Line 40  namespace LinuxSampler { namespace gig { Line 40  namespace LinuxSampler { namespace gig {
40    
41      std::map<AudioOutputDevice*,Engine*> Engine::engines;      std::map<AudioOutputDevice*,Engine*> Engine::engines;
42    
43        /**
44         * Get a gig::Engine object for the given gig::EngineChannel and the
45         * given AudioOutputDevice. All engine channels which are connected to
46         * the same audio output device will use the same engine instance. This
47         * method will be called by a gig::EngineChannel whenever it's
48         * connecting to a audio output device.
49         *
50         * @param pChannel - engine channel which acquires an engine object
51         * @param pDevice  - the audio output device \a pChannel is connected to
52         */
53      Engine* Engine::AcquireEngine(LinuxSampler::gig::EngineChannel* pChannel, AudioOutputDevice* pDevice) {      Engine* Engine::AcquireEngine(LinuxSampler::gig::EngineChannel* pChannel, AudioOutputDevice* pDevice) {
54          Engine* pEngine;          Engine* pEngine = NULL;
55            // check if there's already an engine for the given audio output device
56          if (engines.count(pDevice)) {          if (engines.count(pDevice)) {
57              pEngine = engines[pDevice];              dmsg(4,("Using existing gig::Engine.\n"));
58          } else {              pEngine = engines[pDevice];            
59            } else { // create a new engine (and disk thread) instance for the given audio output device
60                dmsg(4,("Creating new gig::Engine.\n"));
61              pEngine = new Engine;              pEngine = new Engine;
62              pEngine->Connect(pDevice);              pEngine->Connect(pDevice);
63                engines[pDevice] = pEngine;            
64          }          }
65          pEngine->samplerChannels.push_back(pChannel);          // register engine channel to the engine instance
66            pEngine->engineChannels.push_back(pChannel);
67            dmsg(4,("This gig::Engine has now %d EngineChannels.\n",pEngine->engineChannels.size()));
68          return pEngine;          return pEngine;
69      }      }
70    
71        /**
72         * Once an engine channel is disconnected from an audio output device,
73         * it wil immediately call this method to unregister itself from the
74         * engine instance and if that engine instance is not used by any other
75         * engine channel anymore, then that engine instance will be destroyed.
76         *
77         * @param pChannel - engine channel which wants to disconnect from it's
78         *                   engine instance
79         * @param pDevice  - audio output device \a pChannel was connected to
80         */
81      void Engine::FreeEngine(LinuxSampler::gig::EngineChannel* pChannel, AudioOutputDevice* pDevice) {      void Engine::FreeEngine(LinuxSampler::gig::EngineChannel* pChannel, AudioOutputDevice* pDevice) {
82            dmsg(4,("Disconnecting EngineChannel from gig::Engine.\n"));
83          Engine* pEngine = engines[pDevice];          Engine* pEngine = engines[pDevice];
84          pEngine->samplerChannels.remove(pChannel);          // unregister EngineChannel from the Engine instance
85          if (pEngine->samplerChannels.empty()) delete pEngine;          pEngine->engineChannels.remove(pChannel);
86            // if the used Engine instance is not used anymore, then destroy it
87            if (pEngine->engineChannels.empty()) {
88                pDevice->Disconnect(pEngine);
89                engines.erase(pDevice);
90                delete pEngine;
91                dmsg(4,("Destroying gig::Engine.\n"));
92            }
93            else dmsg(4,("This gig::Engine has now %d EngineChannels.\n",pEngine->engineChannels.size()));
94      }      }
95    
96      Engine::Engine() {      Engine::Engine() {
# Line 148  namespace LinuxSampler { namespace gig { Line 183  namespace LinuxSampler { namespace gig {
183          ActiveVoiceCountMax = 0;          ActiveVoiceCountMax = 0;
184    
185          // reset voice stealing parameters          // reset voice stealing parameters
         itLastStolenVoice = RTList<Voice>::Iterator();  
         iuiLastStolenKey  = RTList<uint>::Iterator();  
186          pVoiceStealingQueue->clear();          pVoiceStealingQueue->clear();
187    
188          // reset to normal chromatic scale (means equal temper)          // reset to normal chromatic scale (means equal temper)
# Line 166  namespace LinuxSampler { namespace gig { Line 199  namespace LinuxSampler { namespace gig {
199    
200          // delete all input events          // delete all input events
201          pEventQueue->init();          pEventQueue->init();
202      }      }    
203    
204      void Engine::Connect(AudioOutputDevice* pAudioOut) {      void Engine::Connect(AudioOutputDevice* pAudioOut) {
205          pAudioOutputDevice = pAudioOut;          pAudioOutputDevice = pAudioOut;
# Line 242  namespace LinuxSampler { namespace gig { Line 275  namespace LinuxSampler { namespace gig {
275          }          }
276      }      }
277    
278        void Engine::ClearEventLists() {
279            pEvents->clear();
280            pCCEvents->clear();
281            for (uint i = 0; i < Event::destination_count; i++) {
282                pSynthesisEvents[i]->clear();
283            }
284        }
285    
286        /**
287         * Copy all events from the given input queue buffer to the engine's
288         * internal event list. This will be done at the beginning of each audio
289         * cycle (that is each RenderAudio() call) to get all events which have
290         * to be processed in the current audio cycle. Each EngineChannel has
291         * it's own input event queue for the common channel specific events
292         * (like NoteOn, NoteOff and ControlChange events). Beside that, the
293         * engine also has a input event queue for global events (usually SysEx
294         * message).
295         *
296         * @param pEventQueue - input event buffer to read from
297         * @param Samples     - number of sample points to be processed in the
298         *                      current audio cycle
299         */
300        void Engine::ImportEvents(RingBuffer<Event>* pEventQueue, uint Samples) {
301            RingBuffer<Event>::NonVolatileReader eventQueueReader = pEventQueue->get_non_volatile_reader();
302            Event* pEvent;
303            while (true) {
304                // get next event from input event queue
305                if (!(pEvent = eventQueueReader.pop())) break;
306                // if younger event reached, ignore that and all subsequent ones for now
307                if (pEvent->FragmentPos() >= Samples) {
308                    eventQueueReader--;
309                    dmsg(2,("Younger Event, pos=%d ,Samples=%d!\n",pEvent->FragmentPos(),Samples));
310                    pEvent->ResetFragmentPos();
311                    break;
312                }
313                // copy event to internal event list
314                if (pEvents->poolIsEmpty()) {
315                    dmsg(1,("Event pool emtpy!\n"));
316                    break;
317                }
318                *pEvents->allocAppend() = *pEvent;
319            }
320            eventQueueReader.free(); // free all copied events from input queue
321        }    
322    
323      /**      /**
324       *  Let this engine proceed to render the given amount of sample points. The       *  Let this engine proceed to render the given amount of sample points. The
325       *  calculated audio data of all voices of this engine will be placed into       *  calculated audio data of all voices of this engine will be placed into
# Line 249  namespace LinuxSampler { namespace gig { Line 327  namespace LinuxSampler { namespace gig {
327       *  converted to the appropriate value range by the audio output class (e.g.       *  converted to the appropriate value range by the audio output class (e.g.
328       *  AlsaIO or JackIO) right after.       *  AlsaIO or JackIO) right after.
329       *       *
      *  @param pEngineChannel - the engine's channel to be rendered  
330       *  @param Samples - number of sample points to be rendered       *  @param Samples - number of sample points to be rendered
331       *  @returns       0 on success       *  @returns       0 on success
332       */       */
333      int Engine::RenderAudio(LinuxSampler::gig::EngineChannel* pEngineChannel, uint Samples) {      int Engine::RenderAudio(uint Samples) {
334          dmsg(5,("RenderAudio(Samples=%d)\n", Samples));          dmsg(5,("RenderAudio(Samples=%d)\n", Samples));
335    
336          // return if no instrument loaded or engine disabled          // return if engine disabled
337          if (EngineDisabled.Pop()) {          if (EngineDisabled.Pop()) {
338              dmsg(5,("gig::Engine: engine disabled (val=%d)\n",EngineDisabled.GetUnsafe()));              dmsg(5,("gig::Engine: engine disabled (val=%d)\n",EngineDisabled.GetUnsafe()));
339              return 0;              return 0;
340          }          }
         if (!pEngineChannel->pInstrument) {  
             dmsg(5,("gig::Engine: no instrument loaded\n"));  
             return 0;  
         }  
   
341    
342          // update time of start and end of this audio fragment (as events' time stamps relate to this)          // update time of start and end of this audio fragment (as events' time stamps relate to this)
343          pEventGenerator->UpdateFragmentTime(Samples);          pEventGenerator->UpdateFragmentTime(Samples);
344    
345            // empty the engine's event lists for the new fragment
346            ClearEventLists();
347    
348          // empty the event lists for the new fragment          // get all events from the engine's global input event queue which belong to the current fragment
349          pEvents->clear();          // (these are usually just SysEx messages)
350          pCCEvents->clear();          ImportEvents(this->pEventQueue, Samples);
351          for (uint i = 0; i < Event::destination_count; i++) {  
352              pSynthesisEvents[i]->clear();          // process engine global events (these are currently only MIDI System Exclusive messages)
353            {
354                RTList<Event>::Iterator itEvent = pEvents->first();
355                RTList<Event>::Iterator end     = pEvents->end();
356                for (; itEvent != end; ++itEvent) {
357                    switch (itEvent->Type) {
358                        case Event::type_sysex:
359                            dmsg(5,("Engine: Sysex received\n"));
360                            ProcessSysex(itEvent);
361                            break;
362                    }
363                }
364          }          }
365    
366            // reset internal voice counter (just for statistic of active voices)
367            ActiveVoiceCountTemp = 0;
368    
369            // render audio for all engine channels
370            // TODO: should we make voice stealing engine globally? unfortunately this would mean other disadvantages so I left voice stealing in the engine channel space for now
371            {
372                std::list<EngineChannel*>::iterator itChannel = engineChannels.begin();
373                std::list<EngineChannel*>::iterator end       = engineChannels.end();
374                for (; itChannel != end; itChannel++) {
375                    if (!(*itChannel)->pInstrument) continue; // ignore if no instrument loaded
376                    RenderAudio(*itChannel, Samples);
377                }
378            }
379    
380            // just some statistics about this engine instance
381            ActiveVoiceCount = ActiveVoiceCountTemp;
382            if (ActiveVoiceCount > ActiveVoiceCountMax) ActiveVoiceCountMax = ActiveVoiceCount;
383    
384            return 0;
385        }
386    
387        void Engine::RenderAudio(EngineChannel* pEngineChannel, uint Samples) {
388            // empty the engine's event lists for the new fragment
389            ClearEventLists();
390            // empty the engine channel's, MIDI key specific event lists
391          {          {
392              RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();              RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
393              RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();              RTList<uint>::Iterator end    = pEngineChannel->pActiveKeys->end();
# Line 286  namespace LinuxSampler { namespace gig { Line 397  namespace LinuxSampler { namespace gig {
397          }          }
398    
399    
400          // get all events from the input event queue which belong to the current fragment          // get all events from the engine channels's input event queue which belong to the current fragment
401          {          // (these are the common events like NoteOn, NoteOff, ControlChange, etc.)
402              RingBuffer<Event>::NonVolatileReader eventQueueReader = pEventQueue->get_non_volatile_reader();          ImportEvents(pEngineChannel->pEventQueue, Samples);      
             Event* pEvent;  
             while (true) {  
                 // get next event from input event queue  
                 if (!(pEvent = eventQueueReader.pop())) break;  
                 // if younger event reached, ignore that and all subsequent ones for now  
                 if (pEvent->FragmentPos() >= Samples) {  
                     eventQueueReader--;  
                     dmsg(2,("Younger Event, pos=%d ,Samples=%d!\n",pEvent->FragmentPos(),Samples));  
                     pEvent->ResetFragmentPos();  
                     break;  
                 }  
                 // copy event to internal event list  
                 if (pEvents->poolIsEmpty()) {  
                     dmsg(1,("Event pool emtpy!\n"));  
                     break;  
                 }  
                 *pEvents->allocAppend() = *pEvent;  
             }  
             eventQueueReader.free(); // free all copied events from input queue  
         }  
403    
404    
405          // process events          // process events
# Line 319  namespace LinuxSampler { namespace gig { Line 410  namespace LinuxSampler { namespace gig {
410                  switch (itEvent->Type) {                  switch (itEvent->Type) {
411                      case Event::type_note_on:                      case Event::type_note_on:
412                          dmsg(5,("Engine: Note on received\n"));                          dmsg(5,("Engine: Note on received\n"));
413                          ProcessNoteOn(pEngineChannel, itEvent);                          ProcessNoteOn((EngineChannel*)itEvent->pEngineChannel, itEvent);
414                          break;                          break;
415                      case Event::type_note_off:                      case Event::type_note_off:
416                          dmsg(5,("Engine: Note off received\n"));                          dmsg(5,("Engine: Note off received\n"));
417                          ProcessNoteOff(pEngineChannel, itEvent);                          ProcessNoteOff((EngineChannel*)itEvent->pEngineChannel, itEvent);
418                          break;                          break;
419                      case Event::type_control_change:                      case Event::type_control_change:
420                          dmsg(5,("Engine: MIDI CC received\n"));                          dmsg(5,("Engine: MIDI CC received\n"));
421                          ProcessControlChange(pEngineChannel, itEvent);                          ProcessControlChange((EngineChannel*)itEvent->pEngineChannel, itEvent);
422                          break;                          break;
423                      case Event::type_pitchbend:                      case Event::type_pitchbend:
424                          dmsg(5,("Engine: Pitchbend received\n"));                          dmsg(5,("Engine: Pitchbend received\n"));
425                          ProcessPitchbend(pEngineChannel, itEvent);                          ProcessPitchbend((EngineChannel*)itEvent->pEngineChannel, itEvent);
                         break;  
                     case Event::type_sysex:  
                         dmsg(5,("Engine: Sysex received\n"));  
                         ProcessSysex(itEvent);  
426                          break;                          break;
427                  }                  }
428              }              }
429          }          }
430    
431    
         int active_voices = 0;  
   
432          // render audio from all active voices          // render audio from all active voices
433          {          {
434              RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();              RTList<uint>::Iterator iuiKey = pEngineChannel->pActiveKeys->first();
# Line 357  namespace LinuxSampler { namespace gig { Line 442  namespace LinuxSampler { namespace gig {
442                  for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key                  for (; itVoice != itVoicesEnd; ++itVoice) { // iterate through all voices on this key
443                      // now render current voice                      // now render current voice
444                      itVoice->Render(Samples);                      itVoice->Render(Samples);
445                      if (itVoice->IsActive()) active_voices++; // still active                      if (itVoice->IsActive()) ActiveVoiceCountTemp++; // still active
446                      else { // voice reached end, is now inactive                      else { // voice reached end, is now inactive
447                          FreeVoice(pEngineChannel, itVoice); // remove voice from the list of active voices                          FreeVoice(pEngineChannel, itVoice); // remove voice from the list of active voices
448                      }                      }
# Line 365  namespace LinuxSampler { namespace gig { Line 450  namespace LinuxSampler { namespace gig {
450              }              }
451          }          }
452    
453            
454          // now render all postponed voices from voice stealing          // now render all postponed voices from voice stealing
455          {          {
456              RTList<Event>::Iterator itVoiceStealEvent = pVoiceStealingQueue->first();              RTList<Event>::Iterator itVoiceStealEvent = pVoiceStealingQueue->first();
# Line 376  namespace LinuxSampler { namespace gig { Line 461  namespace LinuxSampler { namespace gig {
461                  if (itNewVoice) {                  if (itNewVoice) {
462                      for (; itNewVoice; itNewVoice = itNewVoice->itChildVoice) {                      for (; itNewVoice; itNewVoice = itNewVoice->itChildVoice) {
463                          itNewVoice->Render(Samples);                          itNewVoice->Render(Samples);
464                          if (itNewVoice->IsActive()) active_voices++; // still active                          if (itNewVoice->IsActive()) ActiveVoiceCountTemp++; // still active
465                          else { // voice reached end, is now inactive                          else { // voice reached end, is now inactive
466                              FreeVoice(pEngineChannel, itNewVoice); // remove voice from the list of active voices                              FreeVoice(pEngineChannel, itNewVoice); // remove voice from the list of active voices
467                          }                          }
# Line 387  namespace LinuxSampler { namespace gig { Line 472  namespace LinuxSampler { namespace gig {
472          }          }
473          // reset voice stealing for the new fragment          // reset voice stealing for the new fragment
474          pVoiceStealingQueue->clear();          pVoiceStealingQueue->clear();
475          itLastStolenVoice = RTList<Voice>::Iterator();          pEngineChannel->itLastStolenVoice = RTList<Voice>::Iterator();
476          iuiLastStolenKey  = RTList<uint>::Iterator();          pEngineChannel->iuiLastStolenKey  = RTList<uint>::Iterator();
477            
478    
479          // free all keys which have no active voices left          // free all keys which have no active voices left
480          {          {
# Line 412  namespace LinuxSampler { namespace gig { Line 497  namespace LinuxSampler { namespace gig {
497                  #endif // DEVMODE                  #endif // DEVMODE
498              }              }
499          }          }
500        }
   
         // write that to the disk thread class so that it can print it  
         // on the console for debugging purposes  
         ActiveVoiceCount = active_voices;  
         if (ActiveVoiceCount > ActiveVoiceCountMax) ActiveVoiceCountMax = ActiveVoiceCount;  
   
   
         return 0;  
     }      
501    
502      /**      /**
503       *  Will be called by the MIDI input device whenever a MIDI system       *  Will be called by the MIDI input device whenever a MIDI system
# Line 434  namespace LinuxSampler { namespace gig { Line 510  namespace LinuxSampler { namespace gig {
510          Event event             = pEventGenerator->CreateEvent();          Event event             = pEventGenerator->CreateEvent();
511          event.Type              = Event::type_sysex;          event.Type              = Event::type_sysex;
512          event.Param.Sysex.Size  = Size;          event.Param.Sysex.Size  = Size;
513            event.pEngineChannel    = NULL; // as Engine global event
514          if (pEventQueue->write_space() > 0) {          if (pEventQueue->write_space() > 0) {
515              if (pSysexBuffer->write_space() >= Size) {              if (pSysexBuffer->write_space() >= Size) {
516                  // copy sysex data to input buffer                  // copy sysex data to input buffer
# Line 636  namespace LinuxSampler { namespace gig { Line 713  namespace LinuxSampler { namespace gig {
713                  // 'oldestkey' algorithm                  // 'oldestkey' algorithm
714                  case voice_steal_algo_keymask: {                  case voice_steal_algo_keymask: {
715                      midi_key_info_t* pOldestKey = &pEngineChannel->pMIDIKeyInfo[itNoteOnEvent->Param.Note.Key];                      midi_key_info_t* pOldestKey = &pEngineChannel->pMIDIKeyInfo[itNoteOnEvent->Param.Note.Key];
716                      if (itLastStolenVoice) {                      if (pEngineChannel->itLastStolenVoice) {
717                          itOldestVoice = itLastStolenVoice;                          itOldestVoice = pEngineChannel->itLastStolenVoice;
718                          ++itOldestVoice;                          ++itOldestVoice;
719                      }                      }
720                      else { // no voice stolen in this audio fragment cycle yet                      else { // no voice stolen in this audio fragment cycle yet
# Line 652  namespace LinuxSampler { namespace gig { Line 729  namespace LinuxSampler { namespace gig {
729                  // try to pick the oldest voice on the oldest active key                  // try to pick the oldest voice on the oldest active key
730                  // (caution: must stay after 'keymask' algorithm !)                  // (caution: must stay after 'keymask' algorithm !)
731                  case voice_steal_algo_oldestkey: {                  case voice_steal_algo_oldestkey: {
732                      if (itLastStolenVoice) {                      if (pEngineChannel->itLastStolenVoice) {
733                          midi_key_info_t* pOldestKey = &pEngineChannel->pMIDIKeyInfo[*iuiLastStolenKey];                          midi_key_info_t* pOldestKey = &pEngineChannel->pMIDIKeyInfo[*pEngineChannel->iuiLastStolenKey];
734                          itOldestVoice = itLastStolenVoice;                          itOldestVoice = pEngineChannel->itLastStolenVoice;
735                          ++itOldestVoice;                          ++itOldestVoice;
736                          if (!itOldestVoice) {                          if (!itOldestVoice) {
737                              iuiOldestKey = iuiLastStolenKey;                              iuiOldestKey = pEngineChannel->iuiLastStolenKey;
738                              ++iuiOldestKey;                              ++iuiOldestKey;
739                              if (iuiOldestKey) {                              if (iuiOldestKey) {
740                                  midi_key_info_t* pOldestKey = &pEngineChannel->pMIDIKeyInfo[*iuiOldestKey];                                  midi_key_info_t* pOldestKey = &pEngineChannel->pMIDIKeyInfo[*iuiOldestKey];
# Line 668  namespace LinuxSampler { namespace gig { Line 745  namespace LinuxSampler { namespace gig {
745                                  return;                                  return;
746                              }                              }
747                          }                          }
748                          else iuiOldestKey = iuiLastStolenKey;                          else iuiOldestKey = pEngineChannel->iuiLastStolenKey;
749                      }                      }
750                      else { // no voice stolen in this audio fragment cycle yet                      else { // no voice stolen in this audio fragment cycle yet
751                          iuiOldestKey = pEngineChannel->pActiveKeys->first();                          iuiOldestKey = pEngineChannel->pActiveKeys->first();
# Line 692  namespace LinuxSampler { namespace gig { Line 769  namespace LinuxSampler { namespace gig {
769              // now kill the selected voice              // now kill the selected voice
770              itOldestVoice->Kill(itNoteOnEvent);              itOldestVoice->Kill(itNoteOnEvent);
771              // remember which voice on which key we stole, so we can simply proceed for the next voice stealing              // remember which voice on which key we stole, so we can simply proceed for the next voice stealing
772              this->itLastStolenVoice = itOldestVoice;              pEngineChannel->itLastStolenVoice = itOldestVoice;
773              this->iuiLastStolenKey = iuiOldestKey;              pEngineChannel->iuiLastStolenKey = iuiOldestKey;
774          }          }
775          else dmsg(1,("Event pool emtpy!\n"));          else dmsg(1,("Event pool emtpy!\n"));
776      }      }
# Line 940  namespace LinuxSampler { namespace gig { Line 1017  namespace LinuxSampler { namespace gig {
1017      }      }
1018    
1019      String Engine::Version() {      String Engine::Version() {
1020          String s = "$Revision: 1.26 $";          String s = "$Revision: 1.27 $";
1021          return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword          return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
1022      }      }
1023    

Legend:
Removed from v.411  
changed lines
  Added in v.412

  ViewVC Help
Powered by ViewVC