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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 37 - (show annotations) (download)
Wed Mar 10 22:01:36 2004 UTC (20 years, 1 month ago) by schoenebeck
File size: 18269 byte(s)
* src/eg_vca.cpp: added following transitions to the envelope generator:
  'Attack_Hold' -> 'Release', 'Decay_1' -> 'Release' in case of a release
  event
* EG1 parameters 'Attack Time', 'Release Time' and 'Sustain Time' are now
  controllable by a MIDI controller defined in the .gig file
* src/voice.cpp: fixed bug in pitch calculation which caused new triggered
  voices to be played back without honoring the current pitch bend value

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003 by Benno Senoner and Christian Schoenebeck *
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program; if not, write to the Free Software *
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
20 * MA 02111-1307 USA *
21 ***************************************************************************/
22
23 #include "voice.h"
24
25 // FIXME: no support for layers (nor crossfades) yet
26
27 DiskThread* Voice::pDiskThread = NULL;
28 AudioThread* Voice::pEngine = NULL;
29
30 Voice::Voice() {
31 Active = false;
32 }
33
34 Voice::~Voice() {
35 }
36
37 /**
38 * Initializes and triggers the voice, a disk stream will be launched if
39 * needed.
40 *
41 * @param pNoteOnEvent - event that caused triggering of this voice
42 * @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) {
57 std::cerr << "Audio Thread: No Region defined for MIDI key " << MIDIKey << std::endl << std::flush;
58 Kill();
59 return -1;
60 }
61
62 //TODO: current MIDI controller values are not taken into account yet
63 gig::DimensionRegion* pDimRgn = NULL;
64 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) {
66 uint DimValues[5] = {0,0,0,0,0};
67 DimValues[i] = pNoteOnEvent->Velocity;
68 pDimRgn = pRegion->GetDimensionRegionByValue(DimValues[4],DimValues[3],DimValues[2],DimValues[1],DimValues[0]);
69 break;
70 }
71 }
72 if (!pDimRgn) { // if there was no velocity split
73 pDimRgn = pRegion->GetDimensionRegionByValue(0,0,0,0,0);
74 }
75
76 pSample = pDimRgn->pSample; // sample won't change until the voice is finished
77
78 // Check if the sample needs disk streaming or is too short for that
79 long cachedsamples = pSample->GetCache().Size / pSample->FrameSize;
80 DiskVoice = cachedsamples < pSample->SamplesTotal;
81
82 if (DiskVoice) { // voice to be streamed from disk
83 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
85 // 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 { // RAM only voice
100 MaxRAMPos = cachedsamples;
101 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
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 // get current value of EG1 controller
117 double eg1controllervalue;
118 switch (pDimRgn->EG1Controller.type) {
119 case gig::eg1_ctrl_t::type_none: // no controller defined
120 eg1controllervalue = 0;
121 break;
122 case gig::eg1_ctrl_t::type_channelaftertouch:
123 eg1controllervalue = 0; // TODO: aftertouch not yet supported
124 break;
125 case gig::eg1_ctrl_t::type_velocity:
126 eg1controllervalue = pNoteOnEvent->Velocity;
127 break;
128 case gig::eg1_ctrl_t::type_controlchange: // MIDI control change controller
129 eg1controllervalue = pEngine->ControllerTable[pDimRgn->EG1Controller.controller_number];
130 break;
131 }
132 if (pDimRgn->EG1ControllerInvert) eg1controllervalue = 127 - eg1controllervalue;
133
134 // calculate influence of EG1 controller on EG1's parameters (TODO: needs to be fine tuned)
135 double eg1attack = (pDimRgn->EG1ControllerAttackInfluence) ? 0.0001 * (double) (1 << pDimRgn->EG1ControllerAttackInfluence) * eg1controllervalue : 0.0;
136 double eg1decay = (pDimRgn->EG1ControllerDecayInfluence) ? 0.0001 * (double) (1 << pDimRgn->EG1ControllerDecayInfluence) * eg1controllervalue : 0.0;
137 double eg1release = (pDimRgn->EG1ControllerReleaseInfluence) ? 0.0001 * (double) (1 << pDimRgn->EG1ControllerReleaseInfluence) * eg1controllervalue : 0.0;
138
139 EG1.Trigger(pDimRgn->EG1PreAttack,
140 pDimRgn->EG1Attack + eg1attack,
141 pDimRgn->EG1Hold,
142 pSample->LoopStart,
143 pDimRgn->EG1Decay1 + eg1decay,
144 pDimRgn->EG1Decay2 + eg1decay,
145 pDimRgn->EG1InfiniteSustain,
146 pDimRgn->EG1Sustain,
147 pDimRgn->EG1Release + eg1release,
148 Delay);
149
150 // ************************************************
151 // TODO: ARTICULATION DATA HANDLING IS MISSING HERE
152 // ************************************************
153
154 return 0; // success
155 }
156
157 /**
158 * Renders the audio data for this voice for the current audio fragment.
159 * The sample input data can either come from RAM (cached sample or sample
160 * part) or directly from disk. The output signal will be rendered by
161 * resampling / interpolation. If this voice is a disk streaming voice and
162 * the voice completely played back the cached RAM part of the sample, it
163 * will automatically switch to disk playback for the next RenderAudio()
164 * call.
165 *
166 * @param Samples - number of samples to be rendered in this audio fragment cycle
167 */
168 void Voice::Render(uint Samples) {
169
170 // Reset the synthesis parameter matrix
171 ModulationSystem::ResetDestinationParameter(ModulationSystem::destination_vca, this->Volume);
172 ModulationSystem::ResetDestinationParameter(ModulationSystem::destination_vco, this->Pitch);
173
174
175 // Apply events to the synthesis parameter matrix
176 ProcessEvents(Samples);
177
178
179 // Let all modulators throw their parameter changes for the current audio fragment
180 EG1.Process(Samples, pEngine->pMIDIKeyInfo[MIDIKey].pEvents, pTriggerEvent, this->Pos, this->Pitch);
181
182
183 switch (this->PlaybackState) {
184
185 case playback_state_ram: {
186 if (RAMLoop) InterpolateAndLoop(Samples, (sample_t*) pSample->GetCache().pStart, Delay);
187 else Interpolate(Samples, (sample_t*) pSample->GetCache().pStart, Delay);
188 if (DiskVoice) {
189 // check if we reached the allowed limit of the sample RAM cache
190 if (Pos > MaxRAMPos) {
191 dmsg(5,("Voice: switching to disk playback (Pos=%f)\n", Pos));
192 this->PlaybackState = playback_state_disk;
193 }
194 }
195 else if (Pos >= pSample->GetCache().Size / pSample->FrameSize) {
196 this->PlaybackState = playback_state_end;
197 }
198 }
199 break;
200
201 case playback_state_disk: {
202 if (!DiskStreamRef.pStream) {
203 // check if the disk thread created our ordered disk stream in the meantime
204 DiskStreamRef.pStream = pDiskThread->AskForCreatedStream(DiskStreamRef.OrderID);
205 if (!DiskStreamRef.pStream) {
206 std::cout << stderr << "Disk stream not available in time!" << std::endl << std::flush;
207 Kill();
208 return;
209 }
210 DiskStreamRef.pStream->IncrementReadPos(pSample->Channels * (double_to_int(Pos) - MaxRAMPos));
211 Pos -= double_to_int(Pos);
212 }
213
214 // add silence sample at the end if we reached the end of the stream (for the interpolator)
215 if (DiskStreamRef.State == Stream::state_end && DiskStreamRef.pStream->GetReadSpace() < (MaxSamplesPerCycle << MAX_PITCH) / pSample->Channels) {
216 DiskStreamRef.pStream->WriteSilence((MaxSamplesPerCycle << MAX_PITCH) / pSample->Channels);
217 this->PlaybackState = playback_state_end;
218 }
219
220 sample_t* ptr = DiskStreamRef.pStream->GetReadPtr(); // get the current read_ptr within the ringbuffer where we read the samples from
221 Interpolate(Samples, ptr, Delay);
222 DiskStreamRef.pStream->IncrementReadPos(double_to_int(Pos) * pSample->Channels);
223 Pos -= double_to_int(Pos);
224 }
225 break;
226
227 case playback_state_end:
228 Kill(); // free voice
229 break;
230 }
231
232
233 // Reset delay
234 Delay = 0;
235
236 pTriggerEvent = NULL;
237
238 // If release stage finished, let the voice be killed
239 if (EG1.GetStage() == EG_VCA::stage_end) this->PlaybackState = playback_state_end;
240 }
241
242 /**
243 * Resets voice variables. Should only be called if rendering process is
244 * suspended / not running.
245 */
246 void Voice::Reset() {
247 DiskStreamRef.pStream = NULL;
248 DiskStreamRef.hStream = 0;
249 DiskStreamRef.State = Stream::state_unused;
250 DiskStreamRef.OrderID = 0;
251 Active = false;
252 }
253
254 /**
255 * Process the control change event lists of the engine for the current
256 * audio fragment. Event values will be applied to the synthesis parameter
257 * matrix.
258 *
259 * @param Samples - number of samples to be rendered in this audio fragment cycle
260 */
261 void Voice::ProcessEvents(uint Samples) {
262 // process pitch events
263 RTEList<ModulationSystem::Event>* pEventList = pEngine->pSynthesisEvents[ModulationSystem::destination_vco];
264 ModulationSystem::Event* pEvent = pEventList->first();;
265 while (pEvent) {
266 ModulationSystem::Event* pNextEvent = pEventList->next();
267
268 // calculate the influence length of this event (in sample points)
269 uint duration = (pNextEvent) ? pNextEvent->FragmentPos() - pEvent->FragmentPos()
270 : Samples - pEvent->FragmentPos();
271
272 // calculate actual pitch value
273 switch (pEvent->Type) {
274 case ModulationSystem::event_type_pitchbend:
275 this->Pitch += ((double) pEvent->Pitch / 8192.0) / 12.0; // +- one semitone
276 break;
277 }
278
279 // apply pitch value to the pitch parameter sequence
280 for (uint i = pEvent->FragmentPos(); i < duration; i++) {
281 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i] = this->Pitch;
282 }
283
284 pEvent = pNextEvent;
285 }
286 }
287
288 /**
289 * Interpolates the input audio data (no loop).
290 *
291 * @param Samples - number of sample points to be rendered in this audio
292 * fragment cycle
293 * @param pSrc - pointer to input sample data
294 * @param Skip - number of sample points to skip in output buffer
295 */
296 void Voice::Interpolate(uint Samples, sample_t* pSrc, uint Skip) {
297 int i = Skip;
298
299 // FIXME: assuming either mono or stereo
300 if (this->pSample->Channels == 2) { // Stereo Sample
301 while (i < Samples) {
302 InterpolateOneStep_Stereo(pSrc, i,
303 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
304 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
305 }
306 }
307 else { // Mono Sample
308 while (i < Samples) {
309 InterpolateOneStep_Mono(pSrc, i,
310 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
311 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
312 }
313 }
314 }
315
316 /**
317 * Interpolates the input audio data, this method honors looping.
318 *
319 * @param Samples - number of sample points to be rendered in this audio
320 * fragment cycle
321 * @param pSrc - pointer to input sample data
322 * @param Skip - number of sample points to skip in output buffer
323 */
324 void Voice::InterpolateAndLoop(uint Samples, sample_t* pSrc, uint Skip) {
325 int i = Skip;
326
327 // FIXME: assuming either mono or stereo
328 if (pSample->Channels == 2) { // Stereo Sample
329 if (pSample->LoopPlayCount) {
330 // render loop (loop count limited)
331 while (i < Samples && LoopCyclesLeft) {
332 InterpolateOneStep_Stereo(pSrc, i,
333 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
334 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
335 if (Pos > pSample->LoopEnd) {
336 Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);;
337 LoopCyclesLeft--;
338 }
339 }
340 // render on without loop
341 while (i < Samples) {
342 InterpolateOneStep_Stereo(pSrc, i,
343 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
344 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
345 }
346 }
347 else { // render loop (endless loop)
348 while (i < Samples) {
349 InterpolateOneStep_Stereo(pSrc, i,
350 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
351 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
352 if (Pos > pSample->LoopEnd) {
353 Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);
354 }
355 }
356 }
357 }
358 else { // Mono Sample
359 if (pSample->LoopPlayCount) {
360 // render loop (loop count limited)
361 while (i < Samples && LoopCyclesLeft) {
362 InterpolateOneStep_Mono(pSrc, i,
363 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
364 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
365 if (Pos > pSample->LoopEnd) {
366 Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);;
367 LoopCyclesLeft--;
368 }
369 }
370 // render on without loop
371 while (i < Samples) {
372 InterpolateOneStep_Mono(pSrc, i,
373 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
374 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
375 }
376 }
377 else { // render loop (endless loop)
378 while (i < Samples) {
379 InterpolateOneStep_Mono(pSrc, i,
380 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
381 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
382 if (Pos > pSample->LoopEnd) {
383 Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);;
384 }
385 }
386 }
387 }
388 }
389
390 /**
391 * Immediately kill the voice.
392 */
393 void Voice::Kill() {
394 if (DiskVoice && DiskStreamRef.State != Stream::state_unused) {
395 pDiskThread->OrderDeletionOfStream(&DiskStreamRef);
396 }
397 Reset();
398 }

  ViewVC Help
Powered by ViewVC