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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 32 - (show annotations) (download)
Tue Feb 3 13:21:19 2004 UTC (20 years, 2 months ago) by schoenebeck
File size: 16789 byte(s)
* introduced time stamped events
* implemented jitter correction
* added pitchbend wheel support
* src/audiothread.cpp: using voice pool instead of a voice array, makes
  voice allocation more efficient and code more readable
* src/rtelmemorypool: redesigned, added some new methods and pool is now
  derived from list

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 MIDIKey - MIDI key number of the triggered key
42 * @param Velocity - MIDI velocity value of the triggered key
43 * @param Pitch - MIDI detune factor (-8192 ... +8191)
44 * @param pInstrument - points to the loaded instrument which provides sample wave(s) and articulation data
45 * @param Delay - number of sample points triggering should be delayed
46 * @returns 0 on success, a value < 0 if something failed
47 */
48 int Voice::Trigger(int MIDIKey, uint8_t Velocity, int Pitch, gig::Instrument* pInstrument, uint Delay) {
49 Active = true;
50 this->MIDIKey = MIDIKey;
51 pRegion = pInstrument->GetRegion(MIDIKey);
52 PlaybackState = playback_state_ram; // we always start playback from RAM cache and switch then to disk if needed
53 Pos = 0;
54 ReleaseVelocity = 127; // default release velocity value
55 this->Delay = Delay;
56 ReleaseSignalReceived = false;
57
58 if (!pRegion) {
59 std::cerr << "Audio Thread: No Region defined for MIDI key " << MIDIKey << std::endl << std::flush;
60 Kill();
61 return -1;
62 }
63
64 //TODO: current MIDI controller values are not taken into account yet
65 gig::DimensionRegion* pDimRgn = NULL;
66 for (int i = pRegion->Dimensions - 1; i >= 0; i--) { // Check if instrument has a velocity split
67 if (pRegion->pDimensionDefinitions[i].dimension == gig::dimension_velocity) {
68 uint DimValues[5] = {0,0,0,0,0};
69 DimValues[i] = Velocity;
70 pDimRgn = pRegion->GetDimensionRegionByValue(DimValues[4],DimValues[3],DimValues[2],DimValues[1],DimValues[0]);
71 break;
72 }
73 }
74 if (!pDimRgn) { // if there was no velocity split
75 pDimRgn = pRegion->GetDimensionRegionByValue(0,0,0,0,0);
76 }
77
78 pSample = pDimRgn->pSample; // sample won't change until the voice is finished
79
80 // Check if the sample needs disk streaming or is too short for that
81 long cachedsamples = pSample->GetCache().Size / pSample->FrameSize;
82 DiskVoice = cachedsamples < pSample->SamplesTotal;
83
84 if (DiskVoice) { // voice to be streamed from disk
85 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)
86
87 // check if there's a loop defined which completely fits into the cached (RAM) part of the sample
88 if (pSample->Loops && pSample->LoopEnd <= MaxRAMPos) {
89 RAMLoop = true;
90 LoopCyclesLeft = pSample->LoopPlayCount;
91 }
92 else RAMLoop = false;
93
94 if (pDiskThread->OrderNewStream(&DiskStreamRef, pSample, MaxRAMPos, !RAMLoop) < 0) {
95 dmsg(1,("Disk stream order failed!\n"));
96 Kill();
97 return -1;
98 }
99 dmsg(4,("Disk voice launched (cached samples: %d, total Samples: %d, MaxRAMPos: %d, RAMLooping: %s)\n", cachedsamples, pSample->SamplesTotal, MaxRAMPos, (RAMLoop) ? "yes" : "no"));
100 }
101 else { // RAM only voice
102 MaxRAMPos = cachedsamples;
103 if (pSample->Loops) {
104 RAMLoop = true;
105 LoopCyclesLeft = pSample->LoopPlayCount;
106 }
107 else RAMLoop = false;
108 dmsg(4,("RAM only voice launched (Looping: %s)\n", (RAMLoop) ? "yes" : "no"));
109 }
110
111
112 // Pitch according to keyboard position (if 'PitchTrack' is set) and given detune factor
113 this->Pitch = ((double) Pitch / 8192.0) / 12.0 + (pDimRgn->PitchTrack) ? pow(2, ((double) (MIDIKey - (int) pDimRgn->UnityNote) + (double) pDimRgn->FineTune / 100.0) / 12.0)
114 : pow(2, ((double) pDimRgn->FineTune / 100.0) / 12.0);
115
116 Volume = pDimRgn->GetVelocityAttenuation(Velocity);
117
118 EG1.Trigger(pDimRgn->EG1PreAttack, pDimRgn->EG1Attack, pDimRgn->EG1Release, Delay);
119
120 // ************************************************
121 // TODO: ARTICULATION DATA HANDLING IS MISSING HERE
122 // ************************************************
123
124 return 0; // success
125 }
126
127 /**
128 * Renders the audio data for this voice for the current audio fragment.
129 * The sample input data can either come from RAM (cached sample or sample
130 * part) or directly from disk. The output signal will be rendered by
131 * resampling / interpolation. If this voice is a disk streaming voice and
132 * the voice completely played back the cached RAM part of the sample, it
133 * will automatically switch to disk playback for the next RenderAudio()
134 * call.
135 *
136 * @param Samples - number of samples to be rendered in this audio fragment cycle
137 */
138 void Voice::Render(uint Samples) {
139
140 // Reset the synthesis parameter matrix
141 ModulationSystem::ResetDestinationParameter(ModulationSystem::destination_vca, this->Volume);
142 ModulationSystem::ResetDestinationParameter(ModulationSystem::destination_vco, this->Pitch);
143
144
145 // Apply events to the synthesis parameter matrix
146 ProcessEvents(Samples);
147
148
149 // Let all modulators throw their parameter changes for the current audio fragment
150 EG1.Process(Samples);
151
152
153 switch (this->PlaybackState) {
154
155 case playback_state_ram: {
156 if (RAMLoop) InterpolateAndLoop(Samples, (sample_t*) pSample->GetCache().pStart, Delay);
157 else Interpolate(Samples, (sample_t*) pSample->GetCache().pStart, Delay);
158 if (DiskVoice) {
159 // check if we reached the allowed limit of the sample RAM cache
160 if (Pos > MaxRAMPos) {
161 dmsg(5,("Voice: switching to disk playback (Pos=%f)\n", Pos));
162 this->PlaybackState = playback_state_disk;
163 }
164 }
165 else if (Pos >= pSample->GetCache().Size / pSample->FrameSize) {
166 this->PlaybackState = playback_state_end;
167 }
168 }
169 break;
170
171 case playback_state_disk: {
172 if (!DiskStreamRef.pStream) {
173 // check if the disk thread created our ordered disk stream in the meantime
174 DiskStreamRef.pStream = pDiskThread->AskForCreatedStream(DiskStreamRef.OrderID);
175 if (!DiskStreamRef.pStream) {
176 std::cout << stderr << "Disk stream not available in time!" << std::endl << std::flush;
177 Kill();
178 return;
179 }
180 DiskStreamRef.pStream->IncrementReadPos(pSample->Channels * (double_to_int(Pos) - MaxRAMPos));
181 Pos -= double_to_int(Pos);
182 }
183
184 // add silence sample at the end if we reached the end of the stream (for the interpolator)
185 if (DiskStreamRef.State == Stream::state_end && DiskStreamRef.pStream->GetReadSpace() < (MaxSamplesPerCycle << MAX_PITCH) / pSample->Channels) {
186 DiskStreamRef.pStream->WriteSilence((MaxSamplesPerCycle << MAX_PITCH) / pSample->Channels);
187 this->PlaybackState = playback_state_end;
188 }
189
190 sample_t* ptr = DiskStreamRef.pStream->GetReadPtr(); // get the current read_ptr within the ringbuffer where we read the samples from
191 Interpolate(Samples, ptr, Delay);
192 DiskStreamRef.pStream->IncrementReadPos(double_to_int(Pos) * pSample->Channels);
193 Pos -= double_to_int(Pos);
194 }
195 break;
196
197 case playback_state_end:
198 Kill(); // free voice
199 break;
200 }
201
202
203 // Reset delay
204 Delay = 0;
205
206
207 // If release stage finished, let the voice be killed
208 if (EG1.GetStage() == EG_VCA::stage_end) this->PlaybackState = playback_state_end;
209 }
210
211 /**
212 * Process the control change event lists of the engine for the current
213 * audio fragment. Event values will be applied to the synthesis parameter
214 * matrix.
215 *
216 * @param Samples - number of samples to be rendered in this audio fragment cycle
217 */
218 void Voice::ProcessEvents(uint Samples) {
219 // process pitch events
220 RTEList<ModulationSystem::Event>* pEventList = pEngine->pCCEvents[ModulationSystem::destination_vco];
221 ModulationSystem::Event* pEvent = pEventList->first();;
222 while (pEvent) {
223 ModulationSystem::Event* pNextEvent = pEventList->next();
224
225 // calculate the influence length of this event (in sample points)
226 uint duration = (pNextEvent) ? pNextEvent->FragmentPos() - pEvent->FragmentPos()
227 : Samples - pEvent->FragmentPos();
228
229 // calculate actual pitch value
230 switch (pEvent->Type) {
231 case ModulationSystem::event_type_pitchbend:
232 this->Pitch += ((double) pEvent->Pitch / 8192.0) / 12.0; // +- one semitone
233 break;
234 }
235
236 // apply pitch value to the pitch parameter sequence
237 for (uint i = pEvent->FragmentPos(); i < duration; i++) {
238 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i] = this->Pitch;
239 }
240
241 pEvent = pNextEvent;
242 }
243 }
244
245 /**
246 * Interpolates the input audio data (no loop).
247 *
248 * @param Samples - number of sample points to be rendered in this audio
249 * fragment cycle
250 * @param pSrc - pointer to input sample data
251 * @param Skip - number of sample points to skip in output buffer
252 */
253 void Voice::Interpolate(uint Samples, sample_t* pSrc, uint Skip) {
254 int i = Skip;
255
256 // FIXME: assuming either mono or stereo
257 if (this->pSample->Channels == 2) { // Stereo Sample
258 while (i < Samples) {
259 InterpolateOneStep_Stereo(pSrc, i,
260 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
261 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
262 }
263 }
264 else { // Mono Sample
265 while (i < Samples) {
266 InterpolateOneStep_Mono(pSrc, i,
267 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
268 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
269 }
270 }
271 }
272
273 /**
274 * Interpolates the input audio data, this method honors looping.
275 *
276 * @param Samples - number of sample points to be rendered in this audio
277 * fragment cycle
278 * @param pSrc - pointer to input sample data
279 * @param Skip - number of sample points to skip in output buffer
280 */
281 void Voice::InterpolateAndLoop(uint Samples, sample_t* pSrc, uint Skip) {
282 int i = Skip;
283
284 // FIXME: assuming either mono or stereo
285 if (pSample->Channels == 2) { // Stereo Sample
286 if (pSample->LoopPlayCount) {
287 // render loop (loop count limited)
288 while (i < Samples && LoopCyclesLeft) {
289 InterpolateOneStep_Stereo(pSrc, i,
290 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
291 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
292 if (Pos > pSample->LoopEnd) {
293 Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);;
294 LoopCyclesLeft--;
295 }
296 }
297 // render on without loop
298 while (i < Samples) {
299 InterpolateOneStep_Stereo(pSrc, i,
300 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
301 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
302 }
303 }
304 else { // render loop (endless loop)
305 while (i < Samples) {
306 InterpolateOneStep_Stereo(pSrc, i,
307 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
308 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
309 if (Pos > pSample->LoopEnd) {
310 Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);
311 }
312 }
313 }
314 }
315 else { // Mono Sample
316 if (pSample->LoopPlayCount) {
317 // render loop (loop count limited)
318 while (i < Samples && LoopCyclesLeft) {
319 InterpolateOneStep_Mono(pSrc, i,
320 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
321 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
322 if (Pos > pSample->LoopEnd) {
323 Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);;
324 LoopCyclesLeft--;
325 }
326 }
327 // render on without loop
328 while (i < Samples) {
329 InterpolateOneStep_Mono(pSrc, i,
330 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
331 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
332 }
333 }
334 else { // render loop (endless loop)
335 while (i < Samples) {
336 InterpolateOneStep_Mono(pSrc, i,
337 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
338 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
339 if (Pos > pSample->LoopEnd) {
340 Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);;
341 }
342 }
343 }
344 }
345 }
346
347 /**
348 * Immediately kill the voice.
349 */
350 void Voice::Kill() {
351 if (DiskVoice && DiskStreamRef.State != Stream::state_unused) {
352 pDiskThread->OrderDeletionOfStream(&DiskStreamRef);
353 }
354 DiskStreamRef.pStream = NULL;
355 DiskStreamRef.hStream = 0;
356 DiskStreamRef.State = Stream::state_unused;
357 DiskStreamRef.OrderID = 0;
358 Active = false;
359 }
360
361 /**
362 * Release the voice in an appropriate time range, the voice will go through
363 * it's release stage before it will be killed.
364 *
365 * @param Delay - number of sample points releasing should be delayed (for jitter correction)
366 */
367 void Voice::Release(uint Delay) {
368 if (!ReleaseSignalReceived) {
369 EG1.Release(Delay);
370 ReleaseSignalReceived = true;
371 }
372 }

  ViewVC Help
Powered by ViewVC