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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 35 - (show annotations) (download)
Fri Mar 5 13:46:15 2004 UTC (20 years, 1 month ago) by schoenebeck
File size: 16824 byte(s)
* implemented parser for the LinuxSampler control protocol (LSCP) by using
  flex / bison (where src/network/lscp.l is the input file for lex / flex
  and src/network/lscp.y is the input file for yacc / bison), parser and
  scanner can be regenerated by 'make parser'
* implemented LSCP network server (only single threaded so far), LSCP
  server will be launched if LinuxSampler was started with "--server" flag,
  implemented the following LSCP commands so far: "LOAD INSTRUMENT", "GET
  CHANNEL VOICE_COUNT", "GET CHANNEL STREAM_COUNT", "GET CHANNEL
  BUFFER_FILL", "SET CHANNEL VOLUME" and "RESET CHANNEL"
* disk thread now started within the engine

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 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
129 // ************************************************
130
131 return 0; // success
132 }
133
134 /**
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) {
161
162 case playback_state_ram: {
163 if (RAMLoop) InterpolateAndLoop(Samples, (sample_t*) pSample->GetCache().pStart, Delay);
164 else Interpolate(Samples, (sample_t*) pSample->GetCache().pStart, Delay);
165 if (DiskVoice) {
166 // check if we reached the allowed limit of the sample RAM cache
167 if (Pos > MaxRAMPos) {
168 dmsg(5,("Voice: switching to disk playback (Pos=%f)\n", Pos));
169 this->PlaybackState = playback_state_disk;
170 }
171 }
172 else if (Pos >= pSample->GetCache().Size / pSample->FrameSize) {
173 this->PlaybackState = playback_state_end;
174 }
175 }
176 break;
177
178 case playback_state_disk: {
179 if (!DiskStreamRef.pStream) {
180 // check if the disk thread created our ordered disk stream in the meantime
181 DiskStreamRef.pStream = pDiskThread->AskForCreatedStream(DiskStreamRef.OrderID);
182 if (!DiskStreamRef.pStream) {
183 std::cout << stderr << "Disk stream not available in time!" << std::endl << std::flush;
184 Kill();
185 return;
186 }
187 DiskStreamRef.pStream->IncrementReadPos(pSample->Channels * (double_to_int(Pos) - MaxRAMPos));
188 Pos -= double_to_int(Pos);
189 }
190
191 // 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() < (MaxSamplesPerCycle << MAX_PITCH) / pSample->Channels) {
193 DiskStreamRef.pStream->WriteSilence((MaxSamplesPerCycle << MAX_PITCH) / pSample->Channels);
194 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
198 Interpolate(Samples, ptr, Delay);
199 DiskStreamRef.pStream->IncrementReadPos(double_to_int(Pos) * pSample->Channels);
200 Pos -= double_to_int(Pos);
201 }
202 break;
203
204 case playback_state_end:
205 Kill(); // free voice
206 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 /**
220 * Resets voice variables. Should only be called if rendering process is
221 * 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 * 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
277 if (this->pSample->Channels == 2) { // Stereo Sample
278 while (i < Samples) {
279 InterpolateOneStep_Stereo(pSrc, i,
280 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
281 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
282 }
283 }
284 else { // Mono Sample
285 while (i < Samples) {
286 InterpolateOneStep_Mono(pSrc, i,
287 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vca][i],
288 ModulationSystem::pDestinationParameter[ModulationSystem::destination_vco][i]);
289 }
290 }
291 }
292
293 /**
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() {
371 if (DiskVoice && DiskStreamRef.State != Stream::state_unused) {
372 pDiskThread->OrderDeletionOfStream(&DiskStreamRef);
373 }
374 Reset();
375 }

  ViewVC Help
Powered by ViewVC