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

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

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

revision 225 by schoenebeck, Sun Aug 22 14:46:47 2004 UTC revision 285 by schoenebeck, Thu Oct 14 21:31:26 2004 UTC
# Line 27  Line 27 
27    
28  namespace LinuxSampler { namespace gig {  namespace LinuxSampler { namespace gig {
29    
     // FIXME: no support for layers (nor crossfades) yet  
   
30      const float Voice::FILTER_CUTOFF_COEFF(CalculateFilterCutoffCoeff());      const float Voice::FILTER_CUTOFF_COEFF(CalculateFilterCutoffCoeff());
31    
32      const int Voice::FILTER_UPDATE_MASK(CalculateFilterUpdateMask());      const int Voice::FILTER_UPDATE_MASK(CalculateFilterUpdateMask());
# Line 47  namespace LinuxSampler { namespace gig { Line 45  namespace LinuxSampler { namespace gig {
45      Voice::Voice() {      Voice::Voice() {
46          pEngine     = NULL;          pEngine     = NULL;
47          pDiskThread = NULL;          pDiskThread = NULL;
48          Active = false;          PlaybackState = playback_state_end;
49          pEG1   = NULL;          pEG1   = NULL;
50          pEG2   = NULL;          pEG2   = NULL;
51          pEG3   = NULL;          pEG3   = NULL;
# Line 57  namespace LinuxSampler { namespace gig { Line 55  namespace LinuxSampler { namespace gig {
55          pLFO1  = NULL;          pLFO1  = NULL;
56          pLFO2  = NULL;          pLFO2  = NULL;
57          pLFO3  = NULL;          pLFO3  = NULL;
58            KeyGroup = 0;
59      }      }
60    
61      Voice::~Voice() {      Voice::~Voice() {
# Line 104  namespace LinuxSampler { namespace gig { Line 103  namespace LinuxSampler { namespace gig {
103       *  Initializes and triggers the voice, a disk stream will be launched if       *  Initializes and triggers the voice, a disk stream will be launched if
104       *  needed.       *  needed.
105       *       *
106       *  @param pNoteOnEvent - event that caused triggering of this voice       *  @param itNoteOnEvent       - event that caused triggering of this voice
107       *  @param PitchBend    - MIDI detune factor (-8192 ... +8191)       *  @param PitchBend           - MIDI detune factor (-8192 ... +8191)
108       *  @param pInstrument  - points to the loaded instrument which provides sample wave(s) and articulation data       *  @param pInstrument         - points to the loaded instrument which provides sample wave(s) and articulation data
109       *  @returns            0 on success, a value < 0 if something failed       *  @param iLayer              - layer number this voice refers to (only if this is a layered sound of course)
110         *  @param ReleaseTriggerVoice - if this new voice is a release trigger voice (optional, default = false)
111         *  @returns 0 on success, a value < 0 if something failed
112       */       */
113      int Voice::Trigger(Event* pNoteOnEvent, int PitchBend, ::gig::Instrument* pInstrument) {      int Voice::Trigger(Pool<Event>::Iterator& itNoteOnEvent, int PitchBend, ::gig::Instrument* pInstrument, int iLayer, bool ReleaseTriggerVoice) {
114          if (!pInstrument) {          if (!pInstrument) {
115             dmsg(1,("voice::trigger: !pInstrument\n"));             dmsg(1,("voice::trigger: !pInstrument\n"));
116             exit(EXIT_FAILURE);             exit(EXIT_FAILURE);
117          }          }
118    
119          Active          = true;          Type            = type_normal;
120          MIDIKey         = pNoteOnEvent->Key;          MIDIKey         = itNoteOnEvent->Param.Note.Key;
121          pRegion         = pInstrument->GetRegion(MIDIKey);          pRegion         = pInstrument->GetRegion(MIDIKey);
122          PlaybackState   = playback_state_ram; // we always start playback from RAM cache and switch then to disk if needed          PlaybackState   = playback_state_ram; // we always start playback from RAM cache and switch then to disk if needed
123          Pos             = 0;          Delay           = itNoteOnEvent->FragmentPos();
124          Delay           = pNoteOnEvent->FragmentPos();          itTriggerEvent  = itNoteOnEvent;
125          pTriggerEvent   = pNoteOnEvent;          itKillEvent     = Pool<Event>::Iterator();
126            itChildVoice    = Pool<Voice>::Iterator();
127    
128          if (!pRegion) {          if (!pRegion) {
129              std::cerr << "Audio Thread: No Region defined for MIDI key " << MIDIKey << std::endl << std::flush;              std::cerr << "gig::Voice: No Region defined for MIDI key " << MIDIKey << std::endl << std::flush;
130              Kill();              KillImmediately();
131              return -1;              return -1;
132          }          }
133    
134          //TODO: current MIDI controller values are not taken into account yet          KeyGroup = pRegion->KeyGroup;
135          ::gig::DimensionRegion* pDimRgn = NULL;  
136          for (int i = pRegion->Dimensions - 1; i >= 0; i--) { // Check if instrument has a velocity split          // get current dimension values to select the right dimension region
137              if (pRegion->pDimensionDefinitions[i].dimension == ::gig::dimension_velocity) {          //FIXME: controller values for selecting the dimension region here are currently not sample accurate
138                  uint DimValues[5] = {0,0,0,0,0};          uint DimValues[5] = {0,0,0,0,0};
139                      DimValues[i] = pNoteOnEvent->Velocity;          for (int i = pRegion->Dimensions - 1; i >= 0; i--) {
140                  pDimRgn = pRegion->GetDimensionRegionByValue(DimValues[4],DimValues[3],DimValues[2],DimValues[1],DimValues[0]);              switch (pRegion->pDimensionDefinitions[i].dimension) {
141                  break;                  case ::gig::dimension_samplechannel:
142                        DimValues[i] = 0; //TODO: we currently ignore this dimension
143                        break;
144                    case ::gig::dimension_layer:
145                        DimValues[i] = iLayer;
146                        // if this is the 1st layer then spawn further voices for all the other layers
147                        if (iLayer == 0)
148                            for (int iNewLayer = 1; iNewLayer < pRegion->pDimensionDefinitions[i].zones; iNewLayer++)
149                                itChildVoice = pEngine->LaunchVoice(itNoteOnEvent, iNewLayer, ReleaseTriggerVoice);
150                        break;
151                    case ::gig::dimension_velocity:
152                        DimValues[i] = itNoteOnEvent->Param.Note.Velocity;
153                        break;
154                    case ::gig::dimension_channelaftertouch:
155                        DimValues[i] = 0; //TODO: we currently ignore this dimension
156                        break;
157                    case ::gig::dimension_releasetrigger:
158                        Type = (ReleaseTriggerVoice) ? type_release_trigger : (!iLayer) ? type_release_trigger_required : type_normal;
159                        DimValues[i] = (uint) ReleaseTriggerVoice;
160                        break;
161                    case ::gig::dimension_keyboard:
162                        DimValues[i] = (uint) itNoteOnEvent->Param.Note.Key;
163                        break;
164                    case ::gig::dimension_modwheel:
165                        DimValues[i] = pEngine->ControllerTable[1];
166                        break;
167                    case ::gig::dimension_breath:
168                        DimValues[i] = pEngine->ControllerTable[2];
169                        break;
170                    case ::gig::dimension_foot:
171                        DimValues[i] = pEngine->ControllerTable[4];
172                        break;
173                    case ::gig::dimension_portamentotime:
174                        DimValues[i] = pEngine->ControllerTable[5];
175                        break;
176                    case ::gig::dimension_effect1:
177                        DimValues[i] = pEngine->ControllerTable[12];
178                        break;
179                    case ::gig::dimension_effect2:
180                        DimValues[i] = pEngine->ControllerTable[13];
181                        break;
182                    case ::gig::dimension_genpurpose1:
183                        DimValues[i] = pEngine->ControllerTable[16];
184                        break;
185                    case ::gig::dimension_genpurpose2:
186                        DimValues[i] = pEngine->ControllerTable[17];
187                        break;
188                    case ::gig::dimension_genpurpose3:
189                        DimValues[i] = pEngine->ControllerTable[18];
190                        break;
191                    case ::gig::dimension_genpurpose4:
192                        DimValues[i] = pEngine->ControllerTable[19];
193                        break;
194                    case ::gig::dimension_sustainpedal:
195                        DimValues[i] = pEngine->ControllerTable[64];
196                        break;
197                    case ::gig::dimension_portamento:
198                        DimValues[i] = pEngine->ControllerTable[65];
199                        break;
200                    case ::gig::dimension_sostenutopedal:
201                        DimValues[i] = pEngine->ControllerTable[66];
202                        break;
203                    case ::gig::dimension_softpedal:
204                        DimValues[i] = pEngine->ControllerTable[67];
205                        break;
206                    case ::gig::dimension_genpurpose5:
207                        DimValues[i] = pEngine->ControllerTable[80];
208                        break;
209                    case ::gig::dimension_genpurpose6:
210                        DimValues[i] = pEngine->ControllerTable[81];
211                        break;
212                    case ::gig::dimension_genpurpose7:
213                        DimValues[i] = pEngine->ControllerTable[82];
214                        break;
215                    case ::gig::dimension_genpurpose8:
216                        DimValues[i] = pEngine->ControllerTable[83];
217                        break;
218                    case ::gig::dimension_effect1depth:
219                        DimValues[i] = pEngine->ControllerTable[91];
220                        break;
221                    case ::gig::dimension_effect2depth:
222                        DimValues[i] = pEngine->ControllerTable[92];
223                        break;
224                    case ::gig::dimension_effect3depth:
225                        DimValues[i] = pEngine->ControllerTable[93];
226                        break;
227                    case ::gig::dimension_effect4depth:
228                        DimValues[i] = pEngine->ControllerTable[94];
229                        break;
230                    case ::gig::dimension_effect5depth:
231                        DimValues[i] = pEngine->ControllerTable[95];
232                        break;
233                    case ::gig::dimension_none:
234                        std::cerr << "gig::Voice::Trigger() Error: dimension=none\n" << std::flush;
235                        break;
236                    default:
237                        std::cerr << "gig::Voice::Trigger() Error: Unknown dimension\n" << std::flush;
238              }              }
239          }          }
240          if (!pDimRgn) { // if there was no velocity split          pDimRgn = pRegion->GetDimensionRegionByValue(DimValues[4],DimValues[3],DimValues[2],DimValues[1],DimValues[0]);
241              pDimRgn = pRegion->GetDimensionRegionByValue(0,0,0,0,0);  
242            // get starting crossfade volume level
243            switch (pDimRgn->AttenuationController.type) {
244                case ::gig::attenuation_ctrl_t::type_channelaftertouch:
245                    CrossfadeVolume = 1.0f; //TODO: aftertouch not supported yet
246                    break;
247                case ::gig::attenuation_ctrl_t::type_velocity:
248                    CrossfadeVolume = CrossfadeAttenuation(itNoteOnEvent->Param.Note.Velocity);
249                    break;
250                case ::gig::attenuation_ctrl_t::type_controlchange: //FIXME: currently not sample accurate
251                    CrossfadeVolume = CrossfadeAttenuation(pEngine->ControllerTable[pDimRgn->AttenuationController.controller_number]);
252                    break;
253                case ::gig::attenuation_ctrl_t::type_none: // no crossfade defined
254                default:
255                    CrossfadeVolume = 1.0f;
256          }          }
257    
258            PanLeft  = 1.0f - float(RTMath::Max(pDimRgn->Pan, 0)) /  63.0f;
259            PanRight = 1.0f - float(RTMath::Min(pDimRgn->Pan, 0)) / -64.0f;
260    
261          pSample = pDimRgn->pSample; // sample won't change until the voice is finished          pSample = pDimRgn->pSample; // sample won't change until the voice is finished
262    
263            Pos = pDimRgn->SampleStartOffset; // offset where we should start playback of sample (0 - 2000 sample points)
264    
265          // Check if the sample needs disk streaming or is too short for that          // Check if the sample needs disk streaming or is too short for that
266          long cachedsamples = pSample->GetCache().Size / pSample->FrameSize;          long cachedsamples = pSample->GetCache().Size / pSample->FrameSize;
267          DiskVoice          = cachedsamples < pSample->SamplesTotal;          DiskVoice          = cachedsamples < pSample->SamplesTotal;
# Line 161  namespace LinuxSampler { namespace gig { Line 278  namespace LinuxSampler { namespace gig {
278    
279              if (pDiskThread->OrderNewStream(&DiskStreamRef, pSample, MaxRAMPos, !RAMLoop) < 0) {              if (pDiskThread->OrderNewStream(&DiskStreamRef, pSample, MaxRAMPos, !RAMLoop) < 0) {
280                  dmsg(1,("Disk stream order failed!\n"));                  dmsg(1,("Disk stream order failed!\n"));
281                  Kill();                  KillImmediately();
282                  return -1;                  return -1;
283              }              }
284              dmsg(4,("Disk voice launched (cached samples: %d, total Samples: %d, MaxRAMPos: %d, RAMLooping: %s)\n", cachedsamples, pSample->SamplesTotal, MaxRAMPos, (RAMLoop) ? "yes" : "no"));              dmsg(4,("Disk voice launched (cached samples: %d, total Samples: %d, MaxRAMPos: %d, RAMLooping: %s)\n", cachedsamples, pSample->SamplesTotal, MaxRAMPos, (RAMLoop) ? "yes" : "no"));
# Line 179  namespace LinuxSampler { namespace gig { Line 296  namespace LinuxSampler { namespace gig {
296    
297          // calculate initial pitch value          // calculate initial pitch value
298          {          {
299              double pitchbasecents = pDimRgn->FineTune * 10;              double pitchbasecents = pDimRgn->FineTune * 10 + (int) pEngine->ScaleTuning[MIDIKey % 12];
300              if (pDimRgn->PitchTrack) pitchbasecents += (MIDIKey - (int) pDimRgn->UnityNote) * 100;              if (pDimRgn->PitchTrack) pitchbasecents += (MIDIKey - (int) pDimRgn->UnityNote) * 100;
301              this->PitchBase = RTMath::CentsToFreqRatio(pitchbasecents);              this->PitchBase = RTMath::CentsToFreqRatio(pitchbasecents) * (double(pSample->SamplesPerSecond) / double(pEngine->pAudioOutputDevice->SampleRate()));
302              this->PitchBend = RTMath::CentsToFreqRatio(((double) PitchBend / 8192.0) * 200.0); // pitchbend wheel +-2 semitones = 200 cents              this->PitchBend = RTMath::CentsToFreqRatio(((double) PitchBend / 8192.0) * 200.0); // pitchbend wheel +-2 semitones = 200 cents
303          }          }
304    
305    
306          Volume = pDimRgn->GetVelocityAttenuation(pNoteOnEvent->Velocity) / 32768.0f; // we downscale by 32768 to convert from int16 value range to DSP value range (which is -1.0..1.0)          Volume = pDimRgn->GetVelocityAttenuation(itNoteOnEvent->Param.Note.Velocity) / 32768.0f; // we downscale by 32768 to convert from int16 value range to DSP value range (which is -1.0..1.0)
307    
308    
309          // setup EG 1 (VCA EG)          // setup EG 1 (VCA EG)
# Line 201  namespace LinuxSampler { namespace gig { Line 318  namespace LinuxSampler { namespace gig {
318                      eg1controllervalue = 0; // TODO: aftertouch not yet supported                      eg1controllervalue = 0; // TODO: aftertouch not yet supported
319                      break;                      break;
320                  case ::gig::eg1_ctrl_t::type_velocity:                  case ::gig::eg1_ctrl_t::type_velocity:
321                      eg1controllervalue = pNoteOnEvent->Velocity;                      eg1controllervalue = itNoteOnEvent->Param.Note.Velocity;
322                      break;                      break;
323                  case ::gig::eg1_ctrl_t::type_controlchange: // MIDI control change controller                  case ::gig::eg1_ctrl_t::type_controlchange: // MIDI control change controller
324                      eg1controllervalue = pEngine->ControllerTable[pDimRgn->EG1Controller.controller_number];                      eg1controllervalue = pEngine->ControllerTable[pDimRgn->EG1Controller.controller_number];
# Line 240  namespace LinuxSampler { namespace gig { Line 357  namespace LinuxSampler { namespace gig {
357                      eg2controllervalue = 0; // TODO: aftertouch not yet supported                      eg2controllervalue = 0; // TODO: aftertouch not yet supported
358                      break;                      break;
359                  case ::gig::eg2_ctrl_t::type_velocity:                  case ::gig::eg2_ctrl_t::type_velocity:
360                      eg2controllervalue = pNoteOnEvent->Velocity;                      eg2controllervalue = itNoteOnEvent->Param.Note.Velocity;
361                      break;                      break;
362                  case ::gig::eg2_ctrl_t::type_controlchange: // MIDI control change controller                  case ::gig::eg2_ctrl_t::type_controlchange: // MIDI control change controller
363                      eg2controllervalue = pEngine->ControllerTable[pDimRgn->EG2Controller.controller_number];                      eg2controllervalue = pEngine->ControllerTable[pDimRgn->EG2Controller.controller_number];
# Line 468  namespace LinuxSampler { namespace gig { Line 585  namespace LinuxSampler { namespace gig {
585    
586              // calculate cutoff frequency              // calculate cutoff frequency
587              float cutoff = (!VCFCutoffCtrl.controller)              float cutoff = (!VCFCutoffCtrl.controller)
588                  ? exp((float) (127 - pNoteOnEvent->Velocity) * (float) pDimRgn->VCFVelocityScale * 6.2E-5f * FILTER_CUTOFF_COEFF) * FILTER_CUTOFF_MAX                  ? exp((float) (127 - itNoteOnEvent->Param.Note.Velocity) * (float) pDimRgn->VCFVelocityScale * 6.2E-5f * FILTER_CUTOFF_COEFF) * FILTER_CUTOFF_MAX
589                  : exp((float) VCFCutoffCtrl.value * 0.00787402f * FILTER_CUTOFF_COEFF) * FILTER_CUTOFF_MAX;                  : exp((float) VCFCutoffCtrl.value * 0.00787402f * FILTER_CUTOFF_COEFF) * FILTER_CUTOFF_MAX;
590    
591              // calculate resonance              // calculate resonance
592              float resonance = (float) VCFResonanceCtrl.value * 0.00787f;   // 0.0..1.0              float resonance = (float) VCFResonanceCtrl.value * 0.00787f;   // 0.0..1.0
593              if (pDimRgn->VCFKeyboardTracking) {              if (pDimRgn->VCFKeyboardTracking) {
594                  resonance += (float) (pNoteOnEvent->Key - pDimRgn->VCFKeyboardTrackingBreakpoint) * 0.00787f;                  resonance += (float) (itNoteOnEvent->Param.Note.Key - pDimRgn->VCFKeyboardTrackingBreakpoint) * 0.00787f;
595              }              }
596              Constrain(resonance, 0.0, 1.0); // correct resonance if outside allowed value range (0.0..1.0)              Constrain(resonance, 0.0, 1.0); // correct resonance if outside allowed value range (0.0..1.0)
597    
# Line 492  namespace LinuxSampler { namespace gig { Line 609  namespace LinuxSampler { namespace gig {
609          }          }
610      #endif // ENABLE_FILTER      #endif // ENABLE_FILTER
611    
         // ************************************************  
         // TODO: ARTICULATION DATA HANDLING IS MISSING HERE  
         // ************************************************  
   
612          return 0; // success          return 0; // success
613      }      }
614    
# Line 513  namespace LinuxSampler { namespace gig { Line 626  namespace LinuxSampler { namespace gig {
626      void Voice::Render(uint Samples) {      void Voice::Render(uint Samples) {
627    
628          // Reset the synthesis parameter matrix          // Reset the synthesis parameter matrix
629          pEngine->ResetSynthesisParameters(Event::destination_vca, this->Volume * pEngine->GlobalVolume);          pEngine->ResetSynthesisParameters(Event::destination_vca, this->Volume * this->CrossfadeVolume * pEngine->GlobalVolume);
630          pEngine->ResetSynthesisParameters(Event::destination_vco, this->PitchBase);          pEngine->ResetSynthesisParameters(Event::destination_vco, this->PitchBase);
631      #if ENABLE_FILTER      #if ENABLE_FILTER
632          pEngine->ResetSynthesisParameters(Event::destination_vcfc, VCFCutoffCtrl.fvalue);          pEngine->ResetSynthesisParameters(Event::destination_vcfc, VCFCutoffCtrl.fvalue);
# Line 526  namespace LinuxSampler { namespace gig { Line 639  namespace LinuxSampler { namespace gig {
639    
640    
641          // Let all modulators write their parameter changes to the synthesis parameter matrix for the current audio fragment          // Let all modulators write their parameter changes to the synthesis parameter matrix for the current audio fragment
642          pEG1->Process(Samples, pEngine->pMIDIKeyInfo[MIDIKey].pEvents, pTriggerEvent, this->Pos, this->PitchBase * this->PitchBend);          pEG1->Process(Samples, pEngine->pMIDIKeyInfo[MIDIKey].pEvents, itTriggerEvent, this->Pos, this->PitchBase * this->PitchBend, itKillEvent);
643      #if ENABLE_FILTER      #if ENABLE_FILTER
644          pEG2->Process(Samples, pEngine->pMIDIKeyInfo[MIDIKey].pEvents, pTriggerEvent, this->Pos, this->PitchBase * this->PitchBend);          pEG2->Process(Samples, pEngine->pMIDIKeyInfo[MIDIKey].pEvents, itTriggerEvent, this->Pos, this->PitchBase * this->PitchBend);
645      #endif // ENABLE_FILTER      #endif // ENABLE_FILTER
646          pEG3->Process(Samples);          pEG3->Process(Samples);
647          pLFO1->Process(Samples);          pLFO1->Process(Samples);
# Line 547  namespace LinuxSampler { namespace gig { Line 660  namespace LinuxSampler { namespace gig {
660    
661              case playback_state_ram: {              case playback_state_ram: {
662                      if (RAMLoop) InterpolateAndLoop(Samples, (sample_t*) pSample->GetCache().pStart, Delay);                      if (RAMLoop) InterpolateAndLoop(Samples, (sample_t*) pSample->GetCache().pStart, Delay);
663                      else         Interpolate(Samples, (sample_t*) pSample->GetCache().pStart, Delay);                      else         InterpolateNoLoop(Samples, (sample_t*) pSample->GetCache().pStart, Delay);
664                      if (DiskVoice) {                      if (DiskVoice) {
665                          // check if we reached the allowed limit of the sample RAM cache                          // check if we reached the allowed limit of the sample RAM cache
666                          if (Pos > MaxRAMPos) {                          if (Pos > MaxRAMPos) {
# Line 567  namespace LinuxSampler { namespace gig { Line 680  namespace LinuxSampler { namespace gig {
680                          DiskStreamRef.pStream = pDiskThread->AskForCreatedStream(DiskStreamRef.OrderID);                          DiskStreamRef.pStream = pDiskThread->AskForCreatedStream(DiskStreamRef.OrderID);
681                          if (!DiskStreamRef.pStream) {                          if (!DiskStreamRef.pStream) {
682                              std::cout << stderr << "Disk stream not available in time!" << std::endl << std::flush;                              std::cout << stderr << "Disk stream not available in time!" << std::endl << std::flush;
683                              Kill();                              KillImmediately();
684                              return;                              return;
685                          }                          }
686                          DiskStreamRef.pStream->IncrementReadPos(pSample->Channels * (RTMath::DoubleToInt(Pos) - MaxRAMPos));                          DiskStreamRef.pStream->IncrementReadPos(pSample->Channels * (RTMath::DoubleToInt(Pos) - MaxRAMPos));
# Line 581  namespace LinuxSampler { namespace gig { Line 694  namespace LinuxSampler { namespace gig {
694                      }                      }
695    
696                      sample_t* ptr = DiskStreamRef.pStream->GetReadPtr(); // get the current read_ptr within the ringbuffer where we read the samples from                      sample_t* ptr = DiskStreamRef.pStream->GetReadPtr(); // get the current read_ptr within the ringbuffer where we read the samples from
697                      Interpolate(Samples, ptr, Delay);                      InterpolateNoLoop(Samples, ptr, Delay);
698                      DiskStreamRef.pStream->IncrementReadPos(RTMath::DoubleToInt(Pos) * pSample->Channels);                      DiskStreamRef.pStream->IncrementReadPos(RTMath::DoubleToInt(Pos) * pSample->Channels);
699                      Pos -= RTMath::DoubleToInt(Pos);                      Pos -= RTMath::DoubleToInt(Pos);
700                  }                  }
701                  break;                  break;
702    
703              case playback_state_end:              case playback_state_end:
704                  Kill(); // free voice                  std::cerr << "gig::Voice::Render(): entered with playback_state_end, this is a bug!\n" << std::flush;
705                  break;                  break;
706          }          }
707    
708    
     #if ENABLE_FILTER  
709          // Reset synthesis event lists (except VCO, as VCO events apply channel wide currently)          // Reset synthesis event lists (except VCO, as VCO events apply channel wide currently)
710            pEngine->pSynthesisEvents[Event::destination_vca]->clear();
711        #if ENABLE_FILTER
712          pEngine->pSynthesisEvents[Event::destination_vcfc]->clear();          pEngine->pSynthesisEvents[Event::destination_vcfc]->clear();
713          pEngine->pSynthesisEvents[Event::destination_vcfr]->clear();          pEngine->pSynthesisEvents[Event::destination_vcfr]->clear();
714      #endif // ENABLE_FILTER      #endif // ENABLE_FILTER
# Line 602  namespace LinuxSampler { namespace gig { Line 716  namespace LinuxSampler { namespace gig {
716          // Reset delay          // Reset delay
717          Delay = 0;          Delay = 0;
718    
719          pTriggerEvent = NULL;          itTriggerEvent = Pool<Event>::Iterator();
720    
721          // If release stage finished, let the voice be killed          // If sample stream or release stage finished, kill the voice
722          if (pEG1->GetStage() == EGADSR::stage_end) this->PlaybackState = playback_state_end;          if (PlaybackState == playback_state_end || pEG1->GetStage() == EGADSR::stage_end) KillImmediately();
723      }      }
724    
725      /**      /**
# Line 620  namespace LinuxSampler { namespace gig { Line 734  namespace LinuxSampler { namespace gig {
734          DiskStreamRef.hStream = 0;          DiskStreamRef.hStream = 0;
735          DiskStreamRef.State   = Stream::state_unused;          DiskStreamRef.State   = Stream::state_unused;
736          DiskStreamRef.OrderID = 0;          DiskStreamRef.OrderID = 0;
737          Active = false;          PlaybackState = playback_state_end;
738            itTriggerEvent = Pool<Event>::Iterator();
739            itKillEvent    = Pool<Event>::Iterator();
740      }      }
741    
742      /**      /**
# Line 633  namespace LinuxSampler { namespace gig { Line 749  namespace LinuxSampler { namespace gig {
749      void Voice::ProcessEvents(uint Samples) {      void Voice::ProcessEvents(uint Samples) {
750    
751          // dispatch control change events          // dispatch control change events
752          Event* pCCEvent = pEngine->pCCEvents->first();          RTList<Event>::Iterator itCCEvent = pEngine->pCCEvents->first();
753          if (Delay) { // skip events that happened before this voice was triggered          if (Delay) { // skip events that happened before this voice was triggered
754              while (pCCEvent && pCCEvent->FragmentPos() <= Delay) pCCEvent = pEngine->pCCEvents->next();              while (itCCEvent && itCCEvent->FragmentPos() <= Delay) ++itCCEvent;
755          }          }
756          while (pCCEvent) {          while (itCCEvent) {
757              if (pCCEvent->Controller) { // if valid MIDI controller              if (itCCEvent->Param.CC.Controller) { // if valid MIDI controller
758                  #if ENABLE_FILTER                  #if ENABLE_FILTER
759                  if (pCCEvent->Controller == VCFCutoffCtrl.controller) {                  if (itCCEvent->Param.CC.Controller == VCFCutoffCtrl.controller) {
760                      pEngine->pSynthesisEvents[Event::destination_vcfc]->alloc_assign(*pCCEvent);                      *pEngine->pSynthesisEvents[Event::destination_vcfc]->allocAppend() = *itCCEvent;
761                  }                  }
762                  if (pCCEvent->Controller == VCFResonanceCtrl.controller) {                  if (itCCEvent->Param.CC.Controller == VCFResonanceCtrl.controller) {
763                      pEngine->pSynthesisEvents[Event::destination_vcfr]->alloc_assign(*pCCEvent);                      *pEngine->pSynthesisEvents[Event::destination_vcfr]->allocAppend() = *itCCEvent;
764                  }                  }
765                  #endif // ENABLE_FILTER                  #endif // ENABLE_FILTER
766                  if (pCCEvent->Controller == pLFO1->ExtController) {                  if (itCCEvent->Param.CC.Controller == pLFO1->ExtController) {
767                      pLFO1->SendEvent(pCCEvent);                      pLFO1->SendEvent(itCCEvent);
768                  }                  }
769                  #if ENABLE_FILTER                  #if ENABLE_FILTER
770                  if (pCCEvent->Controller == pLFO2->ExtController) {                  if (itCCEvent->Param.CC.Controller == pLFO2->ExtController) {
771                      pLFO2->SendEvent(pCCEvent);                      pLFO2->SendEvent(itCCEvent);
772                  }                  }
773                  #endif // ENABLE_FILTER                  #endif // ENABLE_FILTER
774                  if (pCCEvent->Controller == pLFO3->ExtController) {                  if (itCCEvent->Param.CC.Controller == pLFO3->ExtController) {
775                      pLFO3->SendEvent(pCCEvent);                      pLFO3->SendEvent(itCCEvent);
776                    }
777                    if (pDimRgn->AttenuationController.type == ::gig::attenuation_ctrl_t::type_controlchange &&
778                        itCCEvent->Param.CC.Controller == pDimRgn->AttenuationController.controller_number) { // if crossfade event
779                        *pEngine->pSynthesisEvents[Event::destination_vca]->allocAppend() = *itCCEvent;
780                  }                  }
781              }              }
782    
783              pCCEvent = pEngine->pCCEvents->next();              ++itCCEvent;
784          }          }
785    
786    
787          // process pitch events          // process pitch events
788          {          {
789              RTEList<Event>* pVCOEventList = pEngine->pSynthesisEvents[Event::destination_vco];              RTList<Event>* pVCOEventList = pEngine->pSynthesisEvents[Event::destination_vco];
790              Event* pVCOEvent = pVCOEventList->first();              RTList<Event>::Iterator itVCOEvent = pVCOEventList->first();
791              if (Delay) { // skip events that happened before this voice was triggered              if (Delay) { // skip events that happened before this voice was triggered
792                  while (pVCOEvent && pVCOEvent->FragmentPos() <= Delay) pVCOEvent = pVCOEventList->next();                  while (itVCOEvent && itVCOEvent->FragmentPos() <= Delay) ++itVCOEvent;
793              }              }
794              // apply old pitchbend value until first pitch event occurs              // apply old pitchbend value until first pitch event occurs
795              if (this->PitchBend != 1.0) {              if (this->PitchBend != 1.0) {
796                  uint end = (pVCOEvent) ? pVCOEvent->FragmentPos() : Samples;                  uint end = (itVCOEvent) ? itVCOEvent->FragmentPos() : Samples;
797                  for (uint i = Delay; i < end; i++) {                  for (uint i = Delay; i < end; i++) {
798                      pEngine->pSynthesisParameters[Event::destination_vco][i] *= this->PitchBend;                      pEngine->pSynthesisParameters[Event::destination_vco][i] *= this->PitchBend;
799                  }                  }
800              }              }
801              float pitch;              float pitch;
802              while (pVCOEvent) {              while (itVCOEvent) {
803                  Event* pNextVCOEvent = pVCOEventList->next();                  RTList<Event>::Iterator itNextVCOEvent = itVCOEvent;
804                    ++itNextVCOEvent;
805    
806                  // calculate the influence length of this event (in sample points)                  // calculate the influence length of this event (in sample points)
807                  uint end = (pNextVCOEvent) ? pNextVCOEvent->FragmentPos() : Samples;                  uint end = (itNextVCOEvent) ? itNextVCOEvent->FragmentPos() : Samples;
808    
809                  pitch = RTMath::CentsToFreqRatio(((double) pVCOEvent->Pitch / 8192.0) * 200.0); // +-two semitones = +-200 cents                  pitch = RTMath::CentsToFreqRatio(((double) itVCOEvent->Param.Pitch.Pitch / 8192.0) * 200.0); // +-two semitones = +-200 cents
810    
811                  // apply pitch value to the pitch parameter sequence                  // apply pitch value to the pitch parameter sequence
812                  for (uint i = pVCOEvent->FragmentPos(); i < end; i++) {                  for (uint i = itVCOEvent->FragmentPos(); i < end; i++) {
813                      pEngine->pSynthesisParameters[Event::destination_vco][i] *= pitch;                      pEngine->pSynthesisParameters[Event::destination_vco][i] *= pitch;
814                  }                  }
815    
816                  pVCOEvent = pNextVCOEvent;                  itVCOEvent = itNextVCOEvent;
817              }              }
818              if (pVCOEventList->last()) this->PitchBend = pitch;              if (!pVCOEventList->isEmpty()) this->PitchBend = pitch;
819          }          }
820    
821            // process volume / attenuation events (TODO: we only handle and _expect_ crossfade events here ATM !)
822            {
823                RTList<Event>* pVCAEventList = pEngine->pSynthesisEvents[Event::destination_vca];
824                RTList<Event>::Iterator itVCAEvent = pVCAEventList->first();
825                if (Delay) { // skip events that happened before this voice was triggered
826                    while (itVCAEvent && itVCAEvent->FragmentPos() <= Delay) ++itVCAEvent;
827                }
828                float crossfadevolume;
829                while (itVCAEvent) {
830                    RTList<Event>::Iterator itNextVCAEvent = itVCAEvent;
831                    ++itNextVCAEvent;
832    
833                    // calculate the influence length of this event (in sample points)
834                    uint end = (itNextVCAEvent) ? itNextVCAEvent->FragmentPos() : Samples;
835    
836                    crossfadevolume = CrossfadeAttenuation(itVCAEvent->Param.CC.Value);
837    
838                    float effective_volume = crossfadevolume * this->Volume * pEngine->GlobalVolume;
839    
840                    // apply volume value to the volume parameter sequence
841                    for (uint i = itVCAEvent->FragmentPos(); i < end; i++) {
842                        pEngine->pSynthesisParameters[Event::destination_vca][i] = effective_volume;
843                    }
844    
845                    itVCAEvent = itNextVCAEvent;
846                }
847                if (!pVCAEventList->isEmpty()) this->CrossfadeVolume = crossfadevolume;
848            }
849    
850      #if ENABLE_FILTER      #if ENABLE_FILTER
851          // process filter cutoff events          // process filter cutoff events
852          {          {
853              RTEList<Event>* pCutoffEventList = pEngine->pSynthesisEvents[Event::destination_vcfc];              RTList<Event>* pCutoffEventList = pEngine->pSynthesisEvents[Event::destination_vcfc];
854              Event* pCutoffEvent = pCutoffEventList->first();              RTList<Event>::Iterator itCutoffEvent = pCutoffEventList->first();
855              if (Delay) { // skip events that happened before this voice was triggered              if (Delay) { // skip events that happened before this voice was triggered
856                  while (pCutoffEvent && pCutoffEvent->FragmentPos() <= Delay) pCutoffEvent = pCutoffEventList->next();                  while (itCutoffEvent && itCutoffEvent->FragmentPos() <= Delay) ++itCutoffEvent;
857              }              }
858              float cutoff;              float cutoff;
859              while (pCutoffEvent) {              while (itCutoffEvent) {
860                  Event* pNextCutoffEvent = pCutoffEventList->next();                  RTList<Event>::Iterator itNextCutoffEvent = itCutoffEvent;
861                    ++itNextCutoffEvent;
862    
863                  // calculate the influence length of this event (in sample points)                  // calculate the influence length of this event (in sample points)
864                  uint end = (pNextCutoffEvent) ? pNextCutoffEvent->FragmentPos() : Samples;                  uint end = (itNextCutoffEvent) ? itNextCutoffEvent->FragmentPos() : Samples;
865    
866                  cutoff = exp((float) pCutoffEvent->Value * 0.00787402f * FILTER_CUTOFF_COEFF) * FILTER_CUTOFF_MAX - FILTER_CUTOFF_MIN;                  cutoff = exp((float) itCutoffEvent->Param.CC.Value * 0.00787402f * FILTER_CUTOFF_COEFF) * FILTER_CUTOFF_MAX - FILTER_CUTOFF_MIN;
867    
868                  // apply cutoff frequency to the cutoff parameter sequence                  // apply cutoff frequency to the cutoff parameter sequence
869                  for (uint i = pCutoffEvent->FragmentPos(); i < end; i++) {                  for (uint i = itCutoffEvent->FragmentPos(); i < end; i++) {
870                      pEngine->pSynthesisParameters[Event::destination_vcfc][i] = cutoff;                      pEngine->pSynthesisParameters[Event::destination_vcfc][i] = cutoff;
871                  }                  }
872    
873                  pCutoffEvent = pNextCutoffEvent;                  itCutoffEvent = itNextCutoffEvent;
874              }              }
875              if (pCutoffEventList->last()) VCFCutoffCtrl.fvalue = cutoff; // needed for initialization of parameter matrix next time              if (!pCutoffEventList->isEmpty()) VCFCutoffCtrl.fvalue = cutoff; // needed for initialization of parameter matrix next time
876          }          }
877    
878          // process filter resonance events          // process filter resonance events
879          {          {
880              RTEList<Event>* pResonanceEventList = pEngine->pSynthesisEvents[Event::destination_vcfr];              RTList<Event>* pResonanceEventList = pEngine->pSynthesisEvents[Event::destination_vcfr];
881              Event* pResonanceEvent = pResonanceEventList->first();              RTList<Event>::Iterator itResonanceEvent = pResonanceEventList->first();
882              if (Delay) { // skip events that happened before this voice was triggered              if (Delay) { // skip events that happened before this voice was triggered
883                  while (pResonanceEvent && pResonanceEvent->FragmentPos() <= Delay) pResonanceEvent = pResonanceEventList->next();                  while (itResonanceEvent && itResonanceEvent->FragmentPos() <= Delay) ++itResonanceEvent;
884              }              }
885              while (pResonanceEvent) {              while (itResonanceEvent) {
886                  Event* pNextResonanceEvent = pResonanceEventList->next();                  RTList<Event>::Iterator itNextResonanceEvent = itResonanceEvent;
887                    ++itNextResonanceEvent;
888    
889                  // calculate the influence length of this event (in sample points)                  // calculate the influence length of this event (in sample points)
890                  uint end = (pNextResonanceEvent) ? pNextResonanceEvent->FragmentPos() : Samples;                  uint end = (itNextResonanceEvent) ? itNextResonanceEvent->FragmentPos() : Samples;
891    
892                  // convert absolute controller value to differential                  // convert absolute controller value to differential
893                  int ctrldelta = pResonanceEvent->Value - VCFResonanceCtrl.value;                  int ctrldelta = itResonanceEvent->Param.CC.Value - VCFResonanceCtrl.value;
894                  VCFResonanceCtrl.value = pResonanceEvent->Value;                  VCFResonanceCtrl.value = itResonanceEvent->Param.CC.Value;
895    
896                  float resonancedelta = (float) ctrldelta * 0.00787f; // 0.0..1.0                  float resonancedelta = (float) ctrldelta * 0.00787f; // 0.0..1.0
897    
898                  // apply cutoff frequency to the cutoff parameter sequence                  // apply cutoff frequency to the cutoff parameter sequence
899                  for (uint i = pResonanceEvent->FragmentPos(); i < end; i++) {                  for (uint i = itResonanceEvent->FragmentPos(); i < end; i++) {
900                      pEngine->pSynthesisParameters[Event::destination_vcfr][i] += resonancedelta;                      pEngine->pSynthesisParameters[Event::destination_vcfr][i] += resonancedelta;
901                  }                  }
902    
903                  pResonanceEvent = pNextResonanceEvent;                  itResonanceEvent = itNextResonanceEvent;
904              }              }
905              if (pResonanceEventList->last()) VCFResonanceCtrl.fvalue = pResonanceEventList->last()->Value * 0.00787f; // needed for initialization of parameter matrix next time              if (!pResonanceEventList->isEmpty()) VCFResonanceCtrl.fvalue = pResonanceEventList->last()->Param.CC.Value * 0.00787f; // needed for initialization of parameter matrix next time
906          }          }
907      #endif // ENABLE_FILTER      #endif // ENABLE_FILTER
908      }      }
# Line 803  namespace LinuxSampler { namespace gig { Line 954  namespace LinuxSampler { namespace gig {
954      #endif // ENABLE_FILTER      #endif // ENABLE_FILTER
955    
956      /**      /**
957       *  Interpolates the input audio data (no loop).       *  Interpolates the input audio data (without looping).
958       *       *
959       *  @param Samples - number of sample points to be rendered in this audio       *  @param Samples - number of sample points to be rendered in this audio
960       *                   fragment cycle       *                   fragment cycle
961       *  @param pSrc    - pointer to input sample data       *  @param pSrc    - pointer to input sample data
962       *  @param Skip    - number of sample points to skip in output buffer       *  @param Skip    - number of sample points to skip in output buffer
963       */       */
964      void Voice::Interpolate(uint Samples, sample_t* pSrc, uint Skip) {      void Voice::InterpolateNoLoop(uint Samples, sample_t* pSrc, uint Skip) {
965          int i = Skip;          int i = Skip;
966    
967          // FIXME: assuming either mono or stereo          // FIXME: assuming either mono or stereo
968          if (this->pSample->Channels == 2) { // Stereo Sample          if (this->pSample->Channels == 2) { // Stereo Sample
969              while (i < Samples) {              while (i < Samples) InterpolateStereo(pSrc, i);
                 InterpolateOneStep_Stereo(pSrc, i,  
                                           pEngine->pSynthesisParameters[Event::destination_vca][i],  
                                           pEngine->pSynthesisParameters[Event::destination_vco][i],  
                                           pEngine->pBasicFilterParameters[i],  
                                           pEngine->pMainFilterParameters[i]);  
             }  
970          }          }
971          else { // Mono Sample          else { // Mono Sample
972              while (i < Samples) {              while (i < Samples) InterpolateMono(pSrc, i);
                 InterpolateOneStep_Mono(pSrc, i,  
                                         pEngine->pSynthesisParameters[Event::destination_vca][i],  
                                         pEngine->pSynthesisParameters[Event::destination_vco][i],  
                                         pEngine->pBasicFilterParameters[i],  
                                         pEngine->pMainFilterParameters[i]);  
             }  
973          }          }
974      }      }
975    
# Line 850  namespace LinuxSampler { namespace gig { Line 989  namespace LinuxSampler { namespace gig {
989              if (pSample->LoopPlayCount) {              if (pSample->LoopPlayCount) {
990                  // render loop (loop count limited)                  // render loop (loop count limited)
991                  while (i < Samples && LoopCyclesLeft) {                  while (i < Samples && LoopCyclesLeft) {
992                      InterpolateOneStep_Stereo(pSrc, i,                      InterpolateStereo(pSrc, i);
                                               pEngine->pSynthesisParameters[Event::destination_vca][i],  
                                               pEngine->pSynthesisParameters[Event::destination_vco][i],  
                                               pEngine->pBasicFilterParameters[i],  
                                               pEngine->pMainFilterParameters[i]);  
993                      if (Pos > pSample->LoopEnd) {                      if (Pos > pSample->LoopEnd) {
994                          Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);;                          Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);;
995                          LoopCyclesLeft--;                          LoopCyclesLeft--;
996                      }                      }
997                  }                  }
998                  // render on without loop                  // render on without loop
999                  while (i < Samples) {                  while (i < Samples) InterpolateStereo(pSrc, i);
                     InterpolateOneStep_Stereo(pSrc, i,  
                                               pEngine->pSynthesisParameters[Event::destination_vca][i],  
                                               pEngine->pSynthesisParameters[Event::destination_vco][i],  
                                               pEngine->pBasicFilterParameters[i],  
                                               pEngine->pMainFilterParameters[i]);  
                 }  
1000              }              }
1001              else { // render loop (endless loop)              else { // render loop (endless loop)
1002                  while (i < Samples) {                  while (i < Samples) {
1003                      InterpolateOneStep_Stereo(pSrc, i,                      InterpolateStereo(pSrc, i);
                                               pEngine->pSynthesisParameters[Event::destination_vca][i],  
                                               pEngine->pSynthesisParameters[Event::destination_vco][i],  
                                               pEngine->pBasicFilterParameters[i],  
                                               pEngine->pMainFilterParameters[i]);  
1004                      if (Pos > pSample->LoopEnd) {                      if (Pos > pSample->LoopEnd) {
1005                          Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);                          Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);
1006                      }                      }
# Line 886  namespace LinuxSampler { namespace gig { Line 1011  namespace LinuxSampler { namespace gig {
1011              if (pSample->LoopPlayCount) {              if (pSample->LoopPlayCount) {
1012                  // render loop (loop count limited)                  // render loop (loop count limited)
1013                  while (i < Samples && LoopCyclesLeft) {                  while (i < Samples && LoopCyclesLeft) {
1014                      InterpolateOneStep_Mono(pSrc, i,                      InterpolateMono(pSrc, i);
                                             pEngine->pSynthesisParameters[Event::destination_vca][i],  
                                             pEngine->pSynthesisParameters[Event::destination_vco][i],  
                                             pEngine->pBasicFilterParameters[i],  
                                             pEngine->pMainFilterParameters[i]);  
1015                      if (Pos > pSample->LoopEnd) {                      if (Pos > pSample->LoopEnd) {
1016                          Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);;                          Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);;
1017                          LoopCyclesLeft--;                          LoopCyclesLeft--;
1018                      }                      }
1019                  }                  }
1020                  // render on without loop                  // render on without loop
1021                  while (i < Samples) {                  while (i < Samples) InterpolateMono(pSrc, i);
                     InterpolateOneStep_Mono(pSrc, i,  
                                             pEngine->pSynthesisParameters[Event::destination_vca][i],  
                                             pEngine->pSynthesisParameters[Event::destination_vco][i],  
                                             pEngine->pBasicFilterParameters[i],  
                                             pEngine->pMainFilterParameters[i]);  
                 }  
1022              }              }
1023              else { // render loop (endless loop)              else { // render loop (endless loop)
1024                  while (i < Samples) {                  while (i < Samples) {
1025                      InterpolateOneStep_Mono(pSrc, i,                      InterpolateMono(pSrc, i);
                                             pEngine->pSynthesisParameters[Event::destination_vca][i],  
                                             pEngine->pSynthesisParameters[Event::destination_vco][i],  
                                             pEngine->pBasicFilterParameters[i],  
                                             pEngine->pMainFilterParameters[i]);  
1026                      if (Pos > pSample->LoopEnd) {                      if (Pos > pSample->LoopEnd) {
1027                          Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);;                          Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);;
1028                      }                      }
# Line 921  namespace LinuxSampler { namespace gig { Line 1032  namespace LinuxSampler { namespace gig {
1032      }      }
1033    
1034      /**      /**
1035       *  Immediately kill the voice.       *  Immediately kill the voice. This method should not be used to kill
1036         *  a normal, active voice, because it doesn't take care of things like
1037         *  fading down the volume level to avoid clicks and regular processing
1038         *  until the kill event actually occured!
1039         *
1040         *  @see Kill()
1041       */       */
1042      void Voice::Kill() {      void Voice::KillImmediately() {
1043          if (DiskVoice && DiskStreamRef.State != Stream::state_unused) {          if (DiskVoice && DiskStreamRef.State != Stream::state_unused) {
1044              pDiskThread->OrderDeletionOfStream(&DiskStreamRef);              pDiskThread->OrderDeletionOfStream(&DiskStreamRef);
1045          }          }
1046          Reset();          Reset();
1047      }      }
1048    
1049        /**
1050         *  Kill the voice in regular sense. Let the voice render audio until
1051         *  the kill event actually occured and then fade down the volume level
1052         *  very quickly and let the voice die finally. Unlike a normal release
1053         *  of a voice, a kill process cannot be cancalled and is therefore
1054         *  usually used for voice stealing and key group conflicts.
1055         *
1056         *  @param itKillEvent - event which caused the voice to be killed
1057         */
1058        void Voice::Kill(Pool<Event>::Iterator& itKillEvent) {
1059            if (itTriggerEvent && itKillEvent->FragmentPos() <= itTriggerEvent->FragmentPos()) return;
1060            this->itKillEvent = itKillEvent;
1061        }
1062    
1063  }} // namespace LinuxSampler::gig  }} // namespace LinuxSampler::gig

Legend:
Removed from v.225  
changed lines
  Added in v.285

  ViewVC Help
Powered by ViewVC