/[svn]/linuxsampler/trunk/src/voice.cpp
ViewVC logotype

Diff of /linuxsampler/trunk/src/voice.cpp

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

revision 13 by schoenebeck, Fri Nov 21 15:07:23 2003 UTC revision 35 by schoenebeck, Fri Mar 5 13:46:15 2004 UTC
# Line 24  Line 24 
24    
25  // FIXME: no support for layers (nor crossfades) yet  // FIXME: no support for layers (nor crossfades) yet
26    
27  DiskThread* Voice::pDiskThread = NULL;  DiskThread*  Voice::pDiskThread = NULL;
28    AudioThread* Voice::pEngine     = NULL;
29    
30  Voice::Voice(DiskThread* pDiskThread) {  Voice::Voice() {
31      Active             = false;      Active = false;
     Voice::pDiskThread = pDiskThread;  
32  }  }
33    
34  Voice::~Voice() {  Voice::~Voice() {
35  }  }
36    
37  void Voice::Trigger(int MIDIKey, uint8_t Velocity, gig::Instrument* Instrument) {  /**
38      Active        = true;   *  Initializes and triggers the voice, a disk stream will be launched if
39      this->MIDIKey = MIDIKey;   *  needed.
40      pRegion       = Instrument->GetRegion(MIDIKey);   *
41      PlaybackState = playback_state_ram; // we always start playback from RAM cache and switch then to disk if needed   *  @param pNoteOnEvent - event that caused triggering of this voice
42      Pos           = 0;   *  @param Pitch        - MIDI detune factor (-8192 ... +8191)
43     *  @param pInstrument  - points to the loaded instrument which provides sample wave(s) and articulation data
44     *  @returns            0 on success, a value < 0 if something failed
45     */
46    int Voice::Trigger(ModulationSystem::Event* pNoteOnEvent, int Pitch, gig::Instrument* pInstrument) {
47        Active          = true;
48        MIDIKey         = pNoteOnEvent->Key;
49        pRegion         = pInstrument->GetRegion(MIDIKey);
50        PlaybackState   = playback_state_ram; // we always start playback from RAM cache and switch then to disk if needed
51        Pos             = 0;
52        ReleaseVelocity = 127; // default release velocity value
53        Delay           = pNoteOnEvent->FragmentPos();
54        pTriggerEvent   = pNoteOnEvent;
55    
56      if (!pRegion) {      if (!pRegion) {
57          std::cerr << "Audio Thread: No Region defined for MIDI key " << MIDIKey << std::endl << std::flush;          std::cerr << "Audio Thread: No Region defined for MIDI key " << MIDIKey << std::endl << std::flush;
58          Active = false;          Kill();
59          return;          return -1;
60      }      }
61    
62      //TODO: current MIDI controller values are not taken into account yet      //TODO: current MIDI controller values are not taken into account yet
# Line 52  void Voice::Trigger(int MIDIKey, uint8_t Line 64  void Voice::Trigger(int MIDIKey, uint8_t
64      for (int i = pRegion->Dimensions - 1; i >= 0; i--) { // Check if instrument has a velocity split      for (int i = pRegion->Dimensions - 1; i >= 0; i--) { // Check if instrument has a velocity split
65          if (pRegion->pDimensionDefinitions[i].dimension == gig::dimension_velocity) {          if (pRegion->pDimensionDefinitions[i].dimension == gig::dimension_velocity) {
66              uint DimValues[5] = {0,0,0,0,0};              uint DimValues[5] = {0,0,0,0,0};
67                   DimValues[i] = Velocity;                   DimValues[i] = pNoteOnEvent->Velocity;
68              pDimRgn = pRegion->GetDimensionRegionByValue(DimValues[4],DimValues[3],DimValues[2],DimValues[1],DimValues[0]);              pDimRgn = pRegion->GetDimensionRegionByValue(DimValues[4],DimValues[3],DimValues[2],DimValues[1],DimValues[0]);
69              break;              break;
70          }          }
# Line 67  void Voice::Trigger(int MIDIKey, uint8_t Line 79  void Voice::Trigger(int MIDIKey, uint8_t
79      long cachedsamples = pSample->GetCache().Size / pSample->FrameSize;      long cachedsamples = pSample->GetCache().Size / pSample->FrameSize;
80      DiskVoice          = cachedsamples < pSample->SamplesTotal;      DiskVoice          = cachedsamples < pSample->SamplesTotal;
81    
82      if (DiskVoice) {      if (DiskVoice) { // voice to be streamed from disk
83          MaxRAMPos = cachedsamples - (OutputBufferSize << MAX_PITCH) / pSample->Channels;          MaxRAMPos = cachedsamples - (MaxSamplesPerCycle << MAX_PITCH) / pSample->Channels; //TODO: this calculation is too pessimistic and may better be moved to Render() method, so it calculates MaxRAMPos dependent to the current demand of sample points to be rendered (e.g. in case of JACK)
84          pDiskThread->OrderNewStream(&DiskStreamRef, pSample, MaxRAMPos);  
85          dmsg(5,("Disk voice launched (cached samples: %d, total Samples: %d, MaxRAMPos: %d\n", cachedsamples, pSample->SamplesTotal, MaxRAMPos));          // check if there's a loop defined which completely fits into the cached (RAM) part of the sample
86            if (pSample->Loops && pSample->LoopEnd <= MaxRAMPos) {
87                RAMLoop        = true;
88                LoopCyclesLeft = pSample->LoopPlayCount;
89            }
90            else RAMLoop = false;
91    
92            if (pDiskThread->OrderNewStream(&DiskStreamRef, pSample, MaxRAMPos, !RAMLoop) < 0) {
93                dmsg(1,("Disk stream order failed!\n"));
94                Kill();
95                return -1;
96            }
97            dmsg(4,("Disk voice launched (cached samples: %d, total Samples: %d, MaxRAMPos: %d, RAMLooping: %s)\n", cachedsamples, pSample->SamplesTotal, MaxRAMPos, (RAMLoop) ? "yes" : "no"));
98      }      }
99      else {      else { // RAM only voice
100          MaxRAMPos = cachedsamples;          MaxRAMPos = cachedsamples;
101          dmsg(5,("RAM only voice launched\n"));          if (pSample->Loops) {
102                RAMLoop        = true;
103                LoopCyclesLeft = pSample->LoopPlayCount;
104            }
105            else RAMLoop = false;
106            dmsg(4,("RAM only voice launched (Looping: %s)\n", (RAMLoop) ? "yes" : "no"));
107      }      }
108    
109      CurrentPitch = pow(2, (double) (MIDIKey - (int) pSample->MIDIUnityNote) / (double) 12);  
110        // Pitch according to keyboard position (if 'PitchTrack' is set) and given detune factor
111        this->Pitch = ((double) Pitch / 8192.0) / 12.0 + (pDimRgn->PitchTrack) ? pow(2, ((double) (MIDIKey - (int) pDimRgn->UnityNote) + (double) pDimRgn->FineTune / 100.0) / 12.0)
112                                                                               : pow(2, ((double) pDimRgn->FineTune / 100.0) / 12.0);
113    
114        Volume = pDimRgn->GetVelocityAttenuation(pNoteOnEvent->Velocity);
115    
116        EG1.Trigger(pDimRgn->EG1PreAttack,
117                    pDimRgn->EG1Attack,
118                    pDimRgn->EG1Hold,
119                    pSample->LoopStart,
120                    pDimRgn->EG1Decay1,
121                    pDimRgn->EG1Decay2,
122                    pDimRgn->EG1InfiniteSustain,
123                    pDimRgn->EG1Sustain,
124                    pDimRgn->EG1Release,
125                    Delay);
126    
127      // ************************************************      // ************************************************
128      // TODO: ARTICULATION DATA HANDLING IS MISSING HERE      // TODO: ARTICULATION DATA HANDLING IS MISSING HERE
129      // ************************************************      // ************************************************
130    
131        return 0; // success
132  }  }
133    
134  void Voice::RenderAudio() {  /**
135     *  Renders the audio data for this voice for the current audio fragment.
136     *  The sample input data can either come from RAM (cached sample or sample
137     *  part) or directly from disk. The output signal will be rendered by
138     *  resampling / interpolation. If this voice is a disk streaming voice and
139     *  the voice completely played back the cached RAM part of the sample, it
140     *  will automatically switch to disk playback for the next RenderAudio()
141     *  call.
142     *
143     *  @param Samples - number of samples to be rendered in this audio fragment cycle
144     */
145    void Voice::Render(uint Samples) {
146    
147        // Reset the synthesis parameter matrix
148        ModulationSystem::ResetDestinationParameter(ModulationSystem::destination_vca, this->Volume);
149        ModulationSystem::ResetDestinationParameter(ModulationSystem::destination_vco, this->Pitch);
150    
151    
152        // Apply events to the synthesis parameter matrix
153        ProcessEvents(Samples);
154    
155    
156        // Let all modulators throw their parameter changes for the current audio fragment
157        EG1.Process(Samples, pEngine->pMIDIKeyInfo[MIDIKey].pEvents, pTriggerEvent, this->Pos, this->Pitch);
158    
159    
160      switch (this->PlaybackState) {      switch (this->PlaybackState) {
161    
162          case playback_state_ram: {          case playback_state_ram: {
163                  Interpolate((sample_t*) pSample->GetCache().pStart);                  if (RAMLoop) InterpolateAndLoop(Samples, (sample_t*) pSample->GetCache().pStart, Delay);
164                    else         Interpolate(Samples, (sample_t*) pSample->GetCache().pStart, Delay);
165                  if (DiskVoice) {                  if (DiskVoice) {
166                      // check if we reached the allowed limit of the sample RAM cache                      // check if we reached the allowed limit of the sample RAM cache
167                      if (Pos > MaxRAMPos) {                      if (Pos > MaxRAMPos) {
# Line 109  void Voice::RenderAudio() { Line 181  void Voice::RenderAudio() {
181                      DiskStreamRef.pStream = pDiskThread->AskForCreatedStream(DiskStreamRef.OrderID);                      DiskStreamRef.pStream = pDiskThread->AskForCreatedStream(DiskStreamRef.OrderID);
182                      if (!DiskStreamRef.pStream) {                      if (!DiskStreamRef.pStream) {
183                          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;
184                          pDiskThread->OrderDeletionOfStream(&DiskStreamRef);                          Kill();
                         this->Active = false;  
185                          return;                          return;
186                      }                      }
187                      DiskStreamRef.pStream->IncrementReadPos(pSample->Channels * (double_to_int(Pos) - MaxRAMPos));                      DiskStreamRef.pStream->IncrementReadPos(pSample->Channels * (double_to_int(Pos) - MaxRAMPos));
# Line 118  void Voice::RenderAudio() { Line 189  void Voice::RenderAudio() {
189                  }                  }
190    
191                  // add silence sample at the end if we reached the end of the stream (for the interpolator)                  // add silence sample at the end if we reached the end of the stream (for the interpolator)
192                  if (DiskStreamRef.State == Stream::state_end && DiskStreamRef.pStream->GetReadSpace() < (OutputBufferSize << MAX_PITCH) / pSample->Channels) {                  if (DiskStreamRef.State == Stream::state_end && DiskStreamRef.pStream->GetReadSpace() < (MaxSamplesPerCycle << MAX_PITCH) / pSample->Channels) {
193                      DiskStreamRef.pStream->WriteSilence((OutputBufferSize << MAX_PITCH) / pSample->Channels);                      DiskStreamRef.pStream->WriteSilence((MaxSamplesPerCycle << MAX_PITCH) / pSample->Channels);
194                      this->PlaybackState = playback_state_end;                      this->PlaybackState = playback_state_end;
195                  }                  }
196    
197                  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
198                  Interpolate(ptr);                  Interpolate(Samples, ptr, Delay);
199                  DiskStreamRef.pStream->IncrementReadPos(double_to_int(Pos) * pSample->Channels);                  DiskStreamRef.pStream->IncrementReadPos(double_to_int(Pos) * pSample->Channels);
200                  Pos -= double_to_int(Pos);                  Pos -= double_to_int(Pos);
201              }              }
# Line 134  void Voice::RenderAudio() { Line 205  void Voice::RenderAudio() {
205              Kill(); // free voice              Kill(); // free voice
206              break;              break;
207      }      }
208    
209    
210        // Reset delay
211        Delay = 0;
212    
213        pTriggerEvent = NULL;
214    
215        // If release stage finished, let the voice be killed
216        if (EG1.GetStage() == EG_VCA::stage_end) this->PlaybackState = playback_state_end;
217  }  }
218    
219  void Voice::Interpolate(sample_t* pSrc) {  /**
220      float effective_volume = 1;  // TODO: use the art. data instead   *  Resets voice variables. Should only be called if rendering process is
221      int   i = 0;   *  suspended / not running.
222     */
223    void Voice::Reset() {
224        DiskStreamRef.pStream = NULL;
225        DiskStreamRef.hStream = 0;
226        DiskStreamRef.State   = Stream::state_unused;
227        DiskStreamRef.OrderID = 0;
228        Active = false;
229    }
230    
231      // ************************************************  /**
232      // TODO: ARTICULATION DATA HANDLING IS MISSING HERE   *  Process the control change event lists of the engine for the current
233      // ************************************************   *  audio fragment. Event values will be applied to the synthesis parameter
234     *  matrix.
235     *
236     *  @param Samples - number of samples to be rendered in this audio fragment cycle
237     */
238    void Voice::ProcessEvents(uint Samples) {
239        // process pitch events
240        RTEList<ModulationSystem::Event>* pEventList = pEngine->pCCEvents[ModulationSystem::destination_vco];
241        ModulationSystem::Event* pEvent = pEventList->first();;
242        while (pEvent) {
243            ModulationSystem::Event* pNextEvent = pEventList->next();
244    
245            // calculate the influence length of this event (in sample points)
246            uint duration = (pNextEvent) ? pNextEvent->FragmentPos() - pEvent->FragmentPos()
247                                         : Samples                   - pEvent->FragmentPos();
248    
249            // calculate actual pitch value
250            switch (pEvent->Type) {
251                case ModulationSystem::event_type_pitchbend:
252                    this->Pitch += ((double) pEvent->Pitch / 8192.0) / 12.0; // +- one semitone
253                    break;
254            }
255    
256            // apply pitch value to the pitch parameter sequence
257            for (uint i = pEvent->FragmentPos(); i < duration; i++) {
258                ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i] = this->Pitch;
259            }
260    
261            pEvent = pNextEvent;
262        }
263    }
264    
265    /**
266     *  Interpolates the input audio data (no loop).
267     *
268     *  @param Samples - number of sample points to be rendered in this audio
269     *                   fragment cycle
270     *  @param pSrc    - pointer to input sample data
271     *  @param Skip    - number of sample points to skip in output buffer
272     */
273    void Voice::Interpolate(uint Samples, sample_t* pSrc, uint Skip) {
274        int i = Skip;
275    
276      // FIXME: assuming either mono or stereo      // FIXME: assuming either mono or stereo
277      if (this->pSample->Channels == 2) { // Stereo Sample      if (this->pSample->Channels == 2) { // Stereo Sample
278          while (i < this->OutputBufferSize) {          while (i < Samples) {
279              #ifdef USE_LINEAR_INTERPOLATION              InterpolateOneStep_Stereo(pSrc, i,
280                  int   pos_int   = double_to_int(this->Pos);  // integer position                                        ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
281                  float pos_fract = this->Pos - pos_int;       // fractional part of position                                        ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
                 pos_int <<= 1;  
                 // left channel  
                 this->pOutput[i++] += effective_volume * (pSrc[pos_int]   + pos_fract * (pSrc[pos_int+2] - pSrc[pos_int]));  
                 // right channel  
                 this->pOutput[i++] += effective_volume * (pSrc[pos_int+1] + pos_fract * (pSrc[pos_int+3] - pSrc[pos_int+1]));  
             #else // polynomial interpolation  
                 //FIXME: !!!THIS WON'T WORK!!! needs to be adjusted for stereo, use linear interpolation meanwhile  
                 xm1 = pSrc[pos_int];  
                 x0  = pSrc[pos_int+1];  
                 x1  = pSrc[pos_int+2];  
                 x2  = pSrc[pos_int+3];  
                 a   = (3 * (x0-x1) - xm1 + x2) / 2;  
                 b   = 2 * x1 + xm1 - (5 * x0 + x2) / 2;  
                 c   = (x1 - xm1) / 2;  
                 this->pOutput[u] += effective_volume*((((a * pos_fract) + b) * pos_fract + c) * pos_fract + x0);  
             #endif // USE_LINEAR_INTERPOLATION  
   
             this->Pos += this->CurrentPitch;  
282          }          }
283      }      }
284      else { // Mono Sample      else { // Mono Sample
285          while (i < this->OutputBufferSize) {          while (i < Samples) {
286              #ifdef USE_LINEAR_INTERPOLATION              InterpolateOneStep_Mono(pSrc, i,
287                  int   pos_int       = double_to_int(this->Pos);  // integer position                                      ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
288                  float pos_fract     = this->Pos - pos_int;       // fractional part of position                                      ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
289                  float sample_point  = effective_volume * (pSrc[pos_int] + pos_fract * (pSrc[pos_int+1] - pSrc[pos_int]));          }
290                  this->pOutput[i]   += sample_point;      }
291                  this->pOutput[i+1] += sample_point;  }
                 i += 2;  
             #else // polynomial interpolation  
                 //FIXME: !!!THIS WON'T WORK!!! needs to be adjusted for stereo, use linear interpolation meanwhile  
                 xm1 = pSrc[pos_int];  
                 x0  = pSrc[pos_int+1];  
                 x1  = pSrc[pos_int+2];  
                 x2  = pSrc[pos_int+3];  
                 a   = (3 * (x0-x1) - xm1 + x2) / 2;  
                 b   = 2 * x1 + xm1 - (5 * x0 + x2) / 2;  
                 c   = (x1 - xm1) / 2;  
                this->pOutput[u] += effective_volume*((((a * pos_fract) + b) * pos_fract + c) * pos_fract + x0);  
             #endif  
292    
293              this->Pos += this->CurrentPitch;  /**
294     *  Interpolates the input audio data, this method honors looping.
295     *
296     *  @param Samples - number of sample points to be rendered in this audio
297     *                   fragment cycle
298     *  @param pSrc    - pointer to input sample data
299     *  @param Skip    - number of sample points to skip in output buffer
300     */
301    void Voice::InterpolateAndLoop(uint Samples, sample_t* pSrc, uint Skip) {
302        int i = Skip;
303    
304        // FIXME: assuming either mono or stereo
305        if (pSample->Channels == 2) { // Stereo Sample
306            if (pSample->LoopPlayCount) {
307                // render loop (loop count limited)
308                while (i < Samples && LoopCyclesLeft) {
309                    InterpolateOneStep_Stereo(pSrc, i,
310                                              ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
311                                              ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
312                    if (Pos > pSample->LoopEnd) {
313                        Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);;
314                        LoopCyclesLeft--;
315                    }
316                }
317                // render on without loop
318                while (i < Samples) {
319                    InterpolateOneStep_Stereo(pSrc, i,
320                                              ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
321                                              ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
322                }
323            }
324            else { // render loop (endless loop)
325                while (i < Samples) {
326                    InterpolateOneStep_Stereo(pSrc, i,
327                                              ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
328                                              ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
329                    if (Pos > pSample->LoopEnd) {
330                        Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);
331                    }
332                }
333            }
334        }
335        else { // Mono Sample
336            if (pSample->LoopPlayCount) {
337                // render loop (loop count limited)
338                while (i < Samples && LoopCyclesLeft) {
339                    InterpolateOneStep_Mono(pSrc, i,
340                                            ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
341                                            ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
342                    if (Pos > pSample->LoopEnd) {
343                        Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);;
344                        LoopCyclesLeft--;
345                    }
346                }
347                // render on without loop
348                while (i < Samples) {
349                    InterpolateOneStep_Mono(pSrc, i,
350                                            ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
351                                            ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
352                }
353            }
354            else { // render loop (endless loop)
355                while (i < Samples) {
356                    InterpolateOneStep_Mono(pSrc, i,
357                                            ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
358                                            ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
359                    if (Pos > pSample->LoopEnd) {
360                        Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);;
361                    }
362                }
363          }          }
364      }      }
365  }  }
366    
367    /**
368     *  Immediately kill the voice.
369     */
370  void Voice::Kill() {  void Voice::Kill() {
371      if (DiskVoice && DiskStreamRef.State != Stream::state_unused) {      if (DiskVoice && DiskStreamRef.State != Stream::state_unused) {
372          pDiskThread->OrderDeletionOfStream(&DiskStreamRef);          pDiskThread->OrderDeletionOfStream(&DiskStreamRef);
373      }      }
374      Active = false;      Reset();
375  }  }

Legend:
Removed from v.13  
changed lines
  Added in v.35

  ViewVC Help
Powered by ViewVC