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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 832 - (show annotations) (download)
Sun Feb 5 10:24:05 2006 UTC (18 years, 1 month ago) by persson
File size: 47901 byte(s)
* added smoothing of volume changes caused by control change messages
* fine tuning of the crossfade volume curve

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005, 2006 Christian Schoenebeck *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the Free Software *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21 * MA 02111-1307 USA *
22 ***************************************************************************/
23
24 #include "../../common/Features.h"
25 #include "Synthesizer.h"
26 #include "Profiler.h"
27
28 #include "Voice.h"
29
30 namespace LinuxSampler { namespace gig {
31
32 const float Voice::FILTER_CUTOFF_COEFF(CalculateFilterCutoffCoeff());
33
34 float Voice::CalculateFilterCutoffCoeff() {
35 return log(CONFIG_FILTER_CUTOFF_MAX / CONFIG_FILTER_CUTOFF_MIN);
36 }
37
38 Voice::Voice() {
39 pEngine = NULL;
40 pDiskThread = NULL;
41 PlaybackState = playback_state_end;
42 pLFO1 = new LFOUnsigned(1.0f); // amplitude EG (0..1 range)
43 pLFO2 = new LFOUnsigned(1.0f); // filter EG (0..1 range)
44 pLFO3 = new LFOSigned(1200.0f); // pitch EG (-1200..+1200 range)
45 KeyGroup = 0;
46 SynthesisMode = 0; // set all mode bits to 0 first
47 // select synthesis implementation (currently either pure C++ or MMX+SSE(1))
48 #if CONFIG_ASM && ARCH_X86
49 SYNTHESIS_MODE_SET_IMPLEMENTATION(SynthesisMode, Features::supportsMMX() && Features::supportsSSE());
50 #else
51 SYNTHESIS_MODE_SET_IMPLEMENTATION(SynthesisMode, false);
52 #endif
53 SYNTHESIS_MODE_SET_PROFILING(SynthesisMode, Profiler::isEnabled());
54
55 finalSynthesisParameters.filterLeft.Reset();
56 finalSynthesisParameters.filterRight.Reset();
57 }
58
59 Voice::~Voice() {
60 if (pLFO1) delete pLFO1;
61 if (pLFO2) delete pLFO2;
62 if (pLFO3) delete pLFO3;
63 }
64
65 void Voice::SetEngine(Engine* pEngine) {
66 this->pEngine = pEngine;
67 this->pDiskThread = pEngine->pDiskThread;
68 dmsg(6,("Voice::SetEngine()\n"));
69 }
70
71 /**
72 * Initializes and triggers the voice, a disk stream will be launched if
73 * needed.
74 *
75 * @param pEngineChannel - engine channel on which this voice was ordered
76 * @param itNoteOnEvent - event that caused triggering of this voice
77 * @param PitchBend - MIDI detune factor (-8192 ... +8191)
78 * @param pDimRgn - points to the dimension region which provides sample wave(s) and articulation data
79 * @param VoiceType - type of this voice
80 * @param iKeyGroup - a value > 0 defines a key group in which this voice is member of
81 * @returns 0 on success, a value < 0 if the voice wasn't triggered
82 * (either due to an error or e.g. because no region is
83 * defined for the given key)
84 */
85 int Voice::Trigger(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itNoteOnEvent, int PitchBend, ::gig::DimensionRegion* pDimRgn, type_t VoiceType, int iKeyGroup) {
86 this->pEngineChannel = pEngineChannel;
87 this->pDimRgn = pDimRgn;
88
89 #if CONFIG_DEVMODE
90 if (itNoteOnEvent->FragmentPos() > pEngine->MaxSamplesPerCycle) { // just a sanity check for debugging
91 dmsg(1,("Voice::Trigger(): ERROR, TriggerDelay > Totalsamples\n"));
92 }
93 #endif // CONFIG_DEVMODE
94
95 Type = VoiceType;
96 MIDIKey = itNoteOnEvent->Param.Note.Key;
97 PlaybackState = playback_state_init; // mark voice as triggered, but no audio rendered yet
98 Delay = itNoteOnEvent->FragmentPos();
99 itTriggerEvent = itNoteOnEvent;
100 itKillEvent = Pool<Event>::Iterator();
101 KeyGroup = iKeyGroup;
102 pSample = pDimRgn->pSample; // sample won't change until the voice is finished
103
104 // calculate volume
105 const double velocityAttenuation = pDimRgn->GetVelocityAttenuation(itNoteOnEvent->Param.Note.Velocity);
106
107 float volume = velocityAttenuation / 32768.0f; // we downscale by 32768 to convert from int16 value range to DSP value range (which is -1.0..1.0)
108
109 volume *= pDimRgn->SampleAttenuation;
110
111 // the volume of release triggered samples depends on note length
112 if (Type == type_release_trigger) {
113 float noteLength = float(pEngine->FrameTime + Delay -
114 pEngineChannel->pMIDIKeyInfo[MIDIKey].NoteOnTime) / pEngine->SampleRate;
115 float attenuation = 1 - 0.01053 * (256 >> pDimRgn->ReleaseTriggerDecay) * noteLength;
116 if (attenuation <= 0) return -1;
117 volume *= attenuation;
118 }
119
120 // select channel mode (mono or stereo)
121 SYNTHESIS_MODE_SET_CHANNELS(SynthesisMode, pSample->Channels == 2);
122
123 // get starting crossfade volume level
124 float crossfadeVolume;
125 switch (pDimRgn->AttenuationController.type) {
126 case ::gig::attenuation_ctrl_t::type_channelaftertouch:
127 crossfadeVolume = 1.0f; //TODO: aftertouch not supported yet
128 break;
129 case ::gig::attenuation_ctrl_t::type_velocity:
130 crossfadeVolume = Engine::CrossfadeCurve[CrossfadeAttenuation(itNoteOnEvent->Param.Note.Velocity)];
131 break;
132 case ::gig::attenuation_ctrl_t::type_controlchange: //FIXME: currently not sample accurate
133 crossfadeVolume = Engine::CrossfadeCurve[CrossfadeAttenuation(pEngineChannel->ControllerTable[pDimRgn->AttenuationController.controller_number])];
134 break;
135 case ::gig::attenuation_ctrl_t::type_none: // no crossfade defined
136 default:
137 crossfadeVolume = 1.0f;
138 }
139
140 VolumeLeft = volume * Engine::PanCurve[64 - pDimRgn->Pan];
141 VolumeRight = volume * Engine::PanCurve[64 + pDimRgn->Pan];
142
143 float subfragmentRate = pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE;
144 CrossfadeSmoother.trigger(crossfadeVolume, subfragmentRate);
145 VolumeSmoother.trigger(pEngineChannel->GlobalVolume, subfragmentRate);
146 PanLeftSmoother.trigger(pEngineChannel->GlobalPanLeft, subfragmentRate);
147 PanRightSmoother.trigger(pEngineChannel->GlobalPanRight, subfragmentRate);
148
149 finalSynthesisParameters.dPos = pDimRgn->SampleStartOffset; // offset where we should start playback of sample (0 - 2000 sample points)
150 Pos = pDimRgn->SampleStartOffset;
151
152 // Check if the sample needs disk streaming or is too short for that
153 long cachedsamples = pSample->GetCache().Size / pSample->FrameSize;
154 DiskVoice = cachedsamples < pSample->SamplesTotal;
155
156 if (DiskVoice) { // voice to be streamed from disk
157 MaxRAMPos = cachedsamples - (pEngine->MaxSamplesPerCycle << CONFIG_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)
158
159 // check if there's a loop defined which completely fits into the cached (RAM) part of the sample
160 RAMLoop = (pSample->Loops && pSample->LoopEnd <= MaxRAMPos);
161
162 if (pDiskThread->OrderNewStream(&DiskStreamRef, pSample, MaxRAMPos, !RAMLoop) < 0) {
163 dmsg(1,("Disk stream order failed!\n"));
164 KillImmediately();
165 return -1;
166 }
167 dmsg(4,("Disk voice launched (cached samples: %d, total Samples: %d, MaxRAMPos: %d, RAMLooping: %s)\n", cachedsamples, pSample->SamplesTotal, MaxRAMPos, (RAMLoop) ? "yes" : "no"));
168 }
169 else { // RAM only voice
170 MaxRAMPos = cachedsamples;
171 RAMLoop = (pSample->Loops != 0);
172 dmsg(4,("RAM only voice launched (Looping: %s)\n", (RAMLoop) ? "yes" : "no"));
173 }
174 if (RAMLoop) {
175 loop.uiTotalCycles = pSample->LoopPlayCount;
176 loop.uiCyclesLeft = pSample->LoopPlayCount;
177 loop.uiStart = pSample->LoopStart;
178 loop.uiEnd = pSample->LoopEnd;
179 loop.uiSize = pSample->LoopSize;
180 }
181
182 // calculate initial pitch value
183 {
184 double pitchbasecents = pDimRgn->FineTune + (int) pEngine->ScaleTuning[MIDIKey % 12];
185 if (pDimRgn->PitchTrack) pitchbasecents += (MIDIKey - (int) pDimRgn->UnityNote) * 100;
186 this->PitchBase = RTMath::CentsToFreqRatio(pitchbasecents) * (double(pSample->SamplesPerSecond) / double(pEngine->SampleRate));
187 this->PitchBend = RTMath::CentsToFreqRatio(((double) PitchBend / 8192.0) * 200.0); // pitchbend wheel +-2 semitones = 200 cents
188 }
189
190 // the length of the decay and release curves are dependent on the velocity
191 const double velrelease = 1 / pDimRgn->GetVelocityRelease(itNoteOnEvent->Param.Note.Velocity);
192
193 // setup EG 1 (VCA EG)
194 {
195 // get current value of EG1 controller
196 double eg1controllervalue;
197 switch (pDimRgn->EG1Controller.type) {
198 case ::gig::eg1_ctrl_t::type_none: // no controller defined
199 eg1controllervalue = 0;
200 break;
201 case ::gig::eg1_ctrl_t::type_channelaftertouch:
202 eg1controllervalue = 0; // TODO: aftertouch not yet supported
203 break;
204 case ::gig::eg1_ctrl_t::type_velocity:
205 eg1controllervalue = itNoteOnEvent->Param.Note.Velocity;
206 break;
207 case ::gig::eg1_ctrl_t::type_controlchange: // MIDI control change controller
208 eg1controllervalue = pEngineChannel->ControllerTable[pDimRgn->EG1Controller.controller_number];
209 break;
210 }
211 if (pDimRgn->EG1ControllerInvert) eg1controllervalue = 127 - eg1controllervalue;
212
213 // calculate influence of EG1 controller on EG1's parameters
214 // (eg1attack is different from the others)
215 double eg1attack = (pDimRgn->EG1ControllerAttackInfluence) ?
216 1 + 0.031 * (double) (pDimRgn->EG1ControllerAttackInfluence == 1 ?
217 1 : 1 << pDimRgn->EG1ControllerAttackInfluence) * eg1controllervalue : 1.0;
218 double eg1decay = (pDimRgn->EG1ControllerDecayInfluence) ? 1 + 0.00775 * (double) (1 << pDimRgn->EG1ControllerDecayInfluence) * eg1controllervalue : 1.0;
219 double eg1release = (pDimRgn->EG1ControllerReleaseInfluence) ? 1 + 0.00775 * (double) (1 << pDimRgn->EG1ControllerReleaseInfluence) * eg1controllervalue : 1.0;
220
221 EG1.trigger(pDimRgn->EG1PreAttack,
222 pDimRgn->EG1Attack * eg1attack,
223 pDimRgn->EG1Hold,
224 pDimRgn->EG1Decay1 * eg1decay * velrelease,
225 pDimRgn->EG1Decay2 * eg1decay * velrelease,
226 pDimRgn->EG1InfiniteSustain,
227 pDimRgn->EG1Sustain,
228 pDimRgn->EG1Release * eg1release * velrelease,
229 velocityAttenuation,
230 pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
231 }
232
233 #ifdef CONFIG_INTERPOLATE_VOLUME
234 // setup initial volume in synthesis parameters
235 #ifdef CONFIG_PROCESS_MUTED_CHANNELS
236 if (pEngineChannel->GetMute()) {
237 finalSynthesisParameters.fFinalVolumeLeft = 0;
238 finalSynthesisParameters.fFinalVolumeRight = 0;
239 }
240 else
241 #else
242 {
243 float finalVolume = pEngineChannel->GlobalVolume * crossfadeVolume * EG1.getLevel();
244
245 finalSynthesisParameters.fFinalVolumeLeft = finalVolume * VolumeLeft * pEngineChannel->GlobalPanLeft;
246 finalSynthesisParameters.fFinalVolumeRight = finalVolume * VolumeRight * pEngineChannel->GlobalPanRight;
247 }
248 #endif
249 #endif
250
251 // setup EG 2 (VCF Cutoff EG)
252 {
253 // get current value of EG2 controller
254 double eg2controllervalue;
255 switch (pDimRgn->EG2Controller.type) {
256 case ::gig::eg2_ctrl_t::type_none: // no controller defined
257 eg2controllervalue = 0;
258 break;
259 case ::gig::eg2_ctrl_t::type_channelaftertouch:
260 eg2controllervalue = 0; // TODO: aftertouch not yet supported
261 break;
262 case ::gig::eg2_ctrl_t::type_velocity:
263 eg2controllervalue = itNoteOnEvent->Param.Note.Velocity;
264 break;
265 case ::gig::eg2_ctrl_t::type_controlchange: // MIDI control change controller
266 eg2controllervalue = pEngineChannel->ControllerTable[pDimRgn->EG2Controller.controller_number];
267 break;
268 }
269 if (pDimRgn->EG2ControllerInvert) eg2controllervalue = 127 - eg2controllervalue;
270
271 // calculate influence of EG2 controller on EG2's parameters
272 double eg2attack = (pDimRgn->EG2ControllerAttackInfluence) ? 1 + 0.00775 * (double) (1 << pDimRgn->EG2ControllerAttackInfluence) * eg2controllervalue : 1.0;
273 double eg2decay = (pDimRgn->EG2ControllerDecayInfluence) ? 1 + 0.00775 * (double) (1 << pDimRgn->EG2ControllerDecayInfluence) * eg2controllervalue : 1.0;
274 double eg2release = (pDimRgn->EG2ControllerReleaseInfluence) ? 1 + 0.00775 * (double) (1 << pDimRgn->EG2ControllerReleaseInfluence) * eg2controllervalue : 1.0;
275
276 EG2.trigger(pDimRgn->EG2PreAttack,
277 pDimRgn->EG2Attack * eg2attack,
278 false,
279 pDimRgn->EG2Decay1 * eg2decay * velrelease,
280 pDimRgn->EG2Decay2 * eg2decay * velrelease,
281 pDimRgn->EG2InfiniteSustain,
282 pDimRgn->EG2Sustain,
283 pDimRgn->EG2Release * eg2release * velrelease,
284 velocityAttenuation,
285 pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
286 }
287
288
289 // setup EG 3 (VCO EG)
290 {
291 // if portamento mode is on, we dedicate EG3 purely for portamento, otherwise if portamento is off we do as told by the patch
292 bool bPortamento = pEngineChannel->PortamentoMode && pEngineChannel->PortamentoPos >= 0.0f;
293 float eg3depth = (bPortamento)
294 ? RTMath::CentsToFreqRatio((pEngineChannel->PortamentoPos - (float) MIDIKey) * 100)
295 : RTMath::CentsToFreqRatio(pDimRgn->EG3Depth);
296 float eg3time = (bPortamento)
297 ? pEngineChannel->PortamentoTime
298 : pDimRgn->EG3Attack;
299 EG3.trigger(eg3depth, eg3time, pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
300 dmsg(5,("PortamentoPos=%f, depth=%f, time=%f\n", pEngineChannel->PortamentoPos, eg3depth, eg3time));
301 }
302
303
304 // setup LFO 1 (VCA LFO)
305 {
306 uint16_t lfo1_internal_depth;
307 switch (pDimRgn->LFO1Controller) {
308 case ::gig::lfo1_ctrl_internal:
309 lfo1_internal_depth = pDimRgn->LFO1InternalDepth;
310 pLFO1->ExtController = 0; // no external controller
311 bLFO1Enabled = (lfo1_internal_depth > 0);
312 break;
313 case ::gig::lfo1_ctrl_modwheel:
314 lfo1_internal_depth = 0;
315 pLFO1->ExtController = 1; // MIDI controller 1
316 bLFO1Enabled = (pDimRgn->LFO1ControlDepth > 0);
317 break;
318 case ::gig::lfo1_ctrl_breath:
319 lfo1_internal_depth = 0;
320 pLFO1->ExtController = 2; // MIDI controller 2
321 bLFO1Enabled = (pDimRgn->LFO1ControlDepth > 0);
322 break;
323 case ::gig::lfo1_ctrl_internal_modwheel:
324 lfo1_internal_depth = pDimRgn->LFO1InternalDepth;
325 pLFO1->ExtController = 1; // MIDI controller 1
326 bLFO1Enabled = (lfo1_internal_depth > 0 || pDimRgn->LFO1ControlDepth > 0);
327 break;
328 case ::gig::lfo1_ctrl_internal_breath:
329 lfo1_internal_depth = pDimRgn->LFO1InternalDepth;
330 pLFO1->ExtController = 2; // MIDI controller 2
331 bLFO1Enabled = (lfo1_internal_depth > 0 || pDimRgn->LFO1ControlDepth > 0);
332 break;
333 default:
334 lfo1_internal_depth = 0;
335 pLFO1->ExtController = 0; // no external controller
336 bLFO1Enabled = false;
337 }
338 if (bLFO1Enabled) pLFO1->trigger(pDimRgn->LFO1Frequency,
339 start_level_max,
340 lfo1_internal_depth,
341 pDimRgn->LFO1ControlDepth,
342 pDimRgn->LFO1FlipPhase,
343 pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
344 }
345
346
347 // setup LFO 2 (VCF Cutoff LFO)
348 {
349 uint16_t lfo2_internal_depth;
350 switch (pDimRgn->LFO2Controller) {
351 case ::gig::lfo2_ctrl_internal:
352 lfo2_internal_depth = pDimRgn->LFO2InternalDepth;
353 pLFO2->ExtController = 0; // no external controller
354 bLFO2Enabled = (lfo2_internal_depth > 0);
355 break;
356 case ::gig::lfo2_ctrl_modwheel:
357 lfo2_internal_depth = 0;
358 pLFO2->ExtController = 1; // MIDI controller 1
359 bLFO2Enabled = (pDimRgn->LFO2ControlDepth > 0);
360 break;
361 case ::gig::lfo2_ctrl_foot:
362 lfo2_internal_depth = 0;
363 pLFO2->ExtController = 4; // MIDI controller 4
364 bLFO2Enabled = (pDimRgn->LFO2ControlDepth > 0);
365 break;
366 case ::gig::lfo2_ctrl_internal_modwheel:
367 lfo2_internal_depth = pDimRgn->LFO2InternalDepth;
368 pLFO2->ExtController = 1; // MIDI controller 1
369 bLFO2Enabled = (lfo2_internal_depth > 0 || pDimRgn->LFO2ControlDepth > 0);
370 break;
371 case ::gig::lfo2_ctrl_internal_foot:
372 lfo2_internal_depth = pDimRgn->LFO2InternalDepth;
373 pLFO2->ExtController = 4; // MIDI controller 4
374 bLFO2Enabled = (lfo2_internal_depth > 0 || pDimRgn->LFO2ControlDepth > 0);
375 break;
376 default:
377 lfo2_internal_depth = 0;
378 pLFO2->ExtController = 0; // no external controller
379 bLFO2Enabled = false;
380 }
381 if (bLFO2Enabled) pLFO2->trigger(pDimRgn->LFO2Frequency,
382 start_level_max,
383 lfo2_internal_depth,
384 pDimRgn->LFO2ControlDepth,
385 pDimRgn->LFO2FlipPhase,
386 pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
387 }
388
389
390 // setup LFO 3 (VCO LFO)
391 {
392 uint16_t lfo3_internal_depth;
393 switch (pDimRgn->LFO3Controller) {
394 case ::gig::lfo3_ctrl_internal:
395 lfo3_internal_depth = pDimRgn->LFO3InternalDepth;
396 pLFO3->ExtController = 0; // no external controller
397 bLFO3Enabled = (lfo3_internal_depth > 0);
398 break;
399 case ::gig::lfo3_ctrl_modwheel:
400 lfo3_internal_depth = 0;
401 pLFO3->ExtController = 1; // MIDI controller 1
402 bLFO3Enabled = (pDimRgn->LFO3ControlDepth > 0);
403 break;
404 case ::gig::lfo3_ctrl_aftertouch:
405 lfo3_internal_depth = 0;
406 pLFO3->ExtController = 0; // TODO: aftertouch not implemented yet
407 bLFO3Enabled = false; // see TODO comment in line above
408 break;
409 case ::gig::lfo3_ctrl_internal_modwheel:
410 lfo3_internal_depth = pDimRgn->LFO3InternalDepth;
411 pLFO3->ExtController = 1; // MIDI controller 1
412 bLFO3Enabled = (lfo3_internal_depth > 0 || pDimRgn->LFO3ControlDepth > 0);
413 break;
414 case ::gig::lfo3_ctrl_internal_aftertouch:
415 lfo3_internal_depth = pDimRgn->LFO3InternalDepth;
416 pLFO1->ExtController = 0; // TODO: aftertouch not implemented yet
417 bLFO3Enabled = (lfo3_internal_depth > 0 /*|| pDimRgn->LFO3ControlDepth > 0*/); // see TODO comment in line above
418 break;
419 default:
420 lfo3_internal_depth = 0;
421 pLFO3->ExtController = 0; // no external controller
422 bLFO3Enabled = false;
423 }
424 if (bLFO3Enabled) pLFO3->trigger(pDimRgn->LFO3Frequency,
425 start_level_mid,
426 lfo3_internal_depth,
427 pDimRgn->LFO3ControlDepth,
428 false,
429 pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
430 }
431
432
433 #if CONFIG_FORCE_FILTER
434 const bool bUseFilter = true;
435 #else // use filter only if instrument file told so
436 const bool bUseFilter = pDimRgn->VCFEnabled;
437 #endif // CONFIG_FORCE_FILTER
438 SYNTHESIS_MODE_SET_FILTER(SynthesisMode, bUseFilter);
439 if (bUseFilter) {
440 #ifdef CONFIG_OVERRIDE_CUTOFF_CTRL
441 VCFCutoffCtrl.controller = CONFIG_OVERRIDE_CUTOFF_CTRL;
442 #else // use the one defined in the instrument file
443 switch (pDimRgn->VCFCutoffController) {
444 case ::gig::vcf_cutoff_ctrl_modwheel:
445 VCFCutoffCtrl.controller = 1;
446 break;
447 case ::gig::vcf_cutoff_ctrl_effect1:
448 VCFCutoffCtrl.controller = 12;
449 break;
450 case ::gig::vcf_cutoff_ctrl_effect2:
451 VCFCutoffCtrl.controller = 13;
452 break;
453 case ::gig::vcf_cutoff_ctrl_breath:
454 VCFCutoffCtrl.controller = 2;
455 break;
456 case ::gig::vcf_cutoff_ctrl_foot:
457 VCFCutoffCtrl.controller = 4;
458 break;
459 case ::gig::vcf_cutoff_ctrl_sustainpedal:
460 VCFCutoffCtrl.controller = 64;
461 break;
462 case ::gig::vcf_cutoff_ctrl_softpedal:
463 VCFCutoffCtrl.controller = 67;
464 break;
465 case ::gig::vcf_cutoff_ctrl_genpurpose7:
466 VCFCutoffCtrl.controller = 82;
467 break;
468 case ::gig::vcf_cutoff_ctrl_genpurpose8:
469 VCFCutoffCtrl.controller = 83;
470 break;
471 case ::gig::vcf_cutoff_ctrl_aftertouch: //TODO: not implemented yet
472 case ::gig::vcf_cutoff_ctrl_none:
473 default:
474 VCFCutoffCtrl.controller = 0;
475 break;
476 }
477 #endif // CONFIG_OVERRIDE_CUTOFF_CTRL
478
479 #ifdef CONFIG_OVERRIDE_RESONANCE_CTRL
480 VCFResonanceCtrl.controller = CONFIG_OVERRIDE_RESONANCE_CTRL;
481 #else // use the one defined in the instrument file
482 switch (pDimRgn->VCFResonanceController) {
483 case ::gig::vcf_res_ctrl_genpurpose3:
484 VCFResonanceCtrl.controller = 18;
485 break;
486 case ::gig::vcf_res_ctrl_genpurpose4:
487 VCFResonanceCtrl.controller = 19;
488 break;
489 case ::gig::vcf_res_ctrl_genpurpose5:
490 VCFResonanceCtrl.controller = 80;
491 break;
492 case ::gig::vcf_res_ctrl_genpurpose6:
493 VCFResonanceCtrl.controller = 81;
494 break;
495 case ::gig::vcf_res_ctrl_none:
496 default:
497 VCFResonanceCtrl.controller = 0;
498 }
499 #endif // CONFIG_OVERRIDE_RESONANCE_CTRL
500
501 #ifndef CONFIG_OVERRIDE_FILTER_TYPE
502 finalSynthesisParameters.filterLeft.SetType(pDimRgn->VCFType);
503 finalSynthesisParameters.filterRight.SetType(pDimRgn->VCFType);
504 #else // override filter type
505 FilterLeft.SetType(CONFIG_OVERRIDE_FILTER_TYPE);
506 FilterRight.SetType(CONFIG_OVERRIDE_FILTER_TYPE);
507 #endif // CONFIG_OVERRIDE_FILTER_TYPE
508
509 VCFCutoffCtrl.value = pEngineChannel->ControllerTable[VCFCutoffCtrl.controller];
510 VCFResonanceCtrl.value = pEngineChannel->ControllerTable[VCFResonanceCtrl.controller];
511
512 // calculate cutoff frequency
513 float cutoff = pDimRgn->GetVelocityCutoff(itNoteOnEvent->Param.Note.Velocity);
514 if (pDimRgn->VCFKeyboardTracking) {
515 cutoff *= exp((itNoteOnEvent->Param.Note.Key - pDimRgn->VCFKeyboardTrackingBreakpoint) * 0.057762265f); // (ln(2) / 12)
516 }
517 CutoffBase = cutoff;
518
519 int cvalue;
520 if (VCFCutoffCtrl.controller) {
521 cvalue = pEngineChannel->ControllerTable[VCFCutoffCtrl.controller];
522 if (pDimRgn->VCFCutoffControllerInvert) cvalue = 127 - cvalue;
523 // VCFVelocityScale in this case means Minimum cutoff
524 if (cvalue < pDimRgn->VCFVelocityScale) cvalue = pDimRgn->VCFVelocityScale;
525 }
526 else {
527 cvalue = pDimRgn->VCFCutoff;
528 }
529 cutoff *= float(cvalue) * 0.00787402f; // (1 / 127)
530 if (cutoff > 1.0) cutoff = 1.0;
531 cutoff = (cutoff < 0.5 ? cutoff * 4826 - 1 : cutoff * 5715 - 449);
532 if (cutoff < 1.0) cutoff = 1.0;
533
534 // calculate resonance
535 float resonance = (float) (VCFResonanceCtrl.controller ? VCFResonanceCtrl.value : pDimRgn->VCFResonance) * 0.00787f; // 0.0..1.0
536
537 VCFCutoffCtrl.fvalue = cutoff - 1.0;
538 VCFResonanceCtrl.fvalue = resonance;
539 }
540 else {
541 VCFCutoffCtrl.controller = 0;
542 VCFResonanceCtrl.controller = 0;
543 }
544
545 return 0; // success
546 }
547
548 /**
549 * Renders the audio data for this voice for the current audio fragment.
550 * The sample input data can either come from RAM (cached sample or sample
551 * part) or directly from disk. The output signal will be rendered by
552 * resampling / interpolation. If this voice is a disk streaming voice and
553 * the voice completely played back the cached RAM part of the sample, it
554 * will automatically switch to disk playback for the next RenderAudio()
555 * call.
556 *
557 * @param Samples - number of samples to be rendered in this audio fragment cycle
558 */
559 void Voice::Render(uint Samples) {
560
561 // select default values for synthesis mode bits
562 SYNTHESIS_MODE_SET_LOOP(SynthesisMode, false);
563
564 switch (this->PlaybackState) {
565
566 case playback_state_init:
567 this->PlaybackState = playback_state_ram; // we always start playback from RAM cache and switch then to disk if needed
568 // no break - continue with playback_state_ram
569
570 case playback_state_ram: {
571 if (RAMLoop) SYNTHESIS_MODE_SET_LOOP(SynthesisMode, true); // enable looping
572
573 // render current fragment
574 Synthesize(Samples, (sample_t*) pSample->GetCache().pStart, Delay);
575
576 if (DiskVoice) {
577 // check if we reached the allowed limit of the sample RAM cache
578 if (finalSynthesisParameters.dPos > MaxRAMPos) {
579 dmsg(5,("Voice: switching to disk playback (Pos=%f)\n", finalSynthesisParameters.dPos));
580 this->PlaybackState = playback_state_disk;
581 }
582 } else if (finalSynthesisParameters.dPos >= pSample->GetCache().Size / pSample->FrameSize) {
583 this->PlaybackState = playback_state_end;
584 }
585 }
586 break;
587
588 case playback_state_disk: {
589 if (!DiskStreamRef.pStream) {
590 // check if the disk thread created our ordered disk stream in the meantime
591 DiskStreamRef.pStream = pDiskThread->AskForCreatedStream(DiskStreamRef.OrderID);
592 if (!DiskStreamRef.pStream) {
593 std::cout << stderr << "Disk stream not available in time!" << std::endl << std::flush;
594 KillImmediately();
595 return;
596 }
597 DiskStreamRef.pStream->IncrementReadPos(pSample->Channels * (int(finalSynthesisParameters.dPos) - MaxRAMPos));
598 finalSynthesisParameters.dPos -= int(finalSynthesisParameters.dPos);
599 RealSampleWordsLeftToRead = -1; // -1 means no silence has been added yet
600 }
601
602 const int sampleWordsLeftToRead = DiskStreamRef.pStream->GetReadSpace();
603
604 // add silence sample at the end if we reached the end of the stream (for the interpolator)
605 if (DiskStreamRef.State == Stream::state_end) {
606 const int maxSampleWordsPerCycle = (pEngine->MaxSamplesPerCycle << CONFIG_MAX_PITCH) * pSample->Channels + 6; // +6 for the interpolator algorithm
607 if (sampleWordsLeftToRead <= maxSampleWordsPerCycle) {
608 // remember how many sample words there are before any silence has been added
609 if (RealSampleWordsLeftToRead < 0) RealSampleWordsLeftToRead = sampleWordsLeftToRead;
610 DiskStreamRef.pStream->WriteSilence(maxSampleWordsPerCycle - sampleWordsLeftToRead);
611 }
612 }
613
614 sample_t* ptr = DiskStreamRef.pStream->GetReadPtr(); // get the current read_ptr within the ringbuffer where we read the samples from
615
616 // render current audio fragment
617 Synthesize(Samples, ptr, Delay);
618
619 const int iPos = (int) finalSynthesisParameters.dPos;
620 const int readSampleWords = iPos * pSample->Channels; // amount of sample words actually been read
621 DiskStreamRef.pStream->IncrementReadPos(readSampleWords);
622 finalSynthesisParameters.dPos -= iPos; // just keep fractional part of playback position
623
624 // change state of voice to 'end' if we really reached the end of the sample data
625 if (RealSampleWordsLeftToRead >= 0) {
626 RealSampleWordsLeftToRead -= readSampleWords;
627 if (RealSampleWordsLeftToRead <= 0) this->PlaybackState = playback_state_end;
628 }
629 }
630 break;
631
632 case playback_state_end:
633 std::cerr << "gig::Voice::Render(): entered with playback_state_end, this is a bug!\n" << std::flush;
634 break;
635 }
636
637 // Reset delay
638 Delay = 0;
639
640 itTriggerEvent = Pool<Event>::Iterator();
641
642 // If sample stream or release stage finished, kill the voice
643 if (PlaybackState == playback_state_end || EG1.getSegmentType() == EGADSR::segment_end) KillImmediately();
644 }
645
646 /**
647 * Resets voice variables. Should only be called if rendering process is
648 * suspended / not running.
649 */
650 void Voice::Reset() {
651 finalSynthesisParameters.filterLeft.Reset();
652 finalSynthesisParameters.filterRight.Reset();
653 DiskStreamRef.pStream = NULL;
654 DiskStreamRef.hStream = 0;
655 DiskStreamRef.State = Stream::state_unused;
656 DiskStreamRef.OrderID = 0;
657 PlaybackState = playback_state_end;
658 itTriggerEvent = Pool<Event>::Iterator();
659 itKillEvent = Pool<Event>::Iterator();
660 }
661
662 /**
663 * Process given list of MIDI note on, note off and sustain pedal events
664 * for the given time.
665 *
666 * @param itEvent - iterator pointing to the next event to be processed
667 * @param End - youngest time stamp where processing should be stopped
668 */
669 void Voice::processTransitionEvents(RTList<Event>::Iterator& itEvent, uint End) {
670 for (; itEvent && itEvent->FragmentPos() <= End; ++itEvent) {
671 if (itEvent->Type == Event::type_release) {
672 EG1.update(EGADSR::event_release, pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
673 EG2.update(EGADSR::event_release, pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
674 } else if (itEvent->Type == Event::type_cancel_release) {
675 EG1.update(EGADSR::event_cancel_release, pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
676 EG2.update(EGADSR::event_cancel_release, pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
677 }
678 }
679 }
680
681 /**
682 * Process given list of MIDI control change and pitch bend events for
683 * the given time.
684 *
685 * @param itEvent - iterator pointing to the next event to be processed
686 * @param End - youngest time stamp where processing should be stopped
687 */
688 void Voice::processCCEvents(RTList<Event>::Iterator& itEvent, uint End) {
689 for (; itEvent && itEvent->FragmentPos() <= End; ++itEvent) {
690 if (itEvent->Type == Event::type_control_change &&
691 itEvent->Param.CC.Controller) { // if (valid) MIDI control change event
692 if (itEvent->Param.CC.Controller == VCFCutoffCtrl.controller) {
693 processCutoffEvent(itEvent);
694 }
695 if (itEvent->Param.CC.Controller == VCFResonanceCtrl.controller) {
696 processResonanceEvent(itEvent);
697 }
698 if (itEvent->Param.CC.Controller == pLFO1->ExtController) {
699 pLFO1->update(itEvent->Param.CC.Value);
700 }
701 if (itEvent->Param.CC.Controller == pLFO2->ExtController) {
702 pLFO2->update(itEvent->Param.CC.Value);
703 }
704 if (itEvent->Param.CC.Controller == pLFO3->ExtController) {
705 pLFO3->update(itEvent->Param.CC.Value);
706 }
707 if (pDimRgn->AttenuationController.type == ::gig::attenuation_ctrl_t::type_controlchange &&
708 itEvent->Param.CC.Controller == pDimRgn->AttenuationController.controller_number) {
709 CrossfadeSmoother.update(Engine::CrossfadeCurve[CrossfadeAttenuation(itEvent->Param.CC.Value)]);
710 }
711 if (itEvent->Param.CC.Controller == 7) { // volume
712 VolumeSmoother.update(Engine::VolumeCurve[itEvent->Param.CC.Value] * CONFIG_GLOBAL_ATTENUATION);
713 } else if (itEvent->Param.CC.Controller == 10) { // panpot
714 PanLeftSmoother.update(Engine::PanCurve[128 - itEvent->Param.CC.Value]);
715 PanRightSmoother.update(Engine::PanCurve[itEvent->Param.CC.Value]);
716 }
717 } else if (itEvent->Type == Event::type_pitchbend) { // if pitch bend event
718 processPitchEvent(itEvent);
719 }
720 }
721 }
722
723 void Voice::processPitchEvent(RTList<Event>::Iterator& itEvent) {
724 const float pitch = RTMath::CentsToFreqRatio(((double) itEvent->Param.Pitch.Pitch / 8192.0) * 200.0); // +-two semitones = +-200 cents
725 finalSynthesisParameters.fFinalPitch *= pitch;
726 PitchBend = pitch;
727 }
728
729 void Voice::processCutoffEvent(RTList<Event>::Iterator& itEvent) {
730 int ccvalue = itEvent->Param.CC.Value;
731 if (VCFCutoffCtrl.value == ccvalue) return;
732 VCFCutoffCtrl.value == ccvalue;
733 if (pDimRgn->VCFCutoffControllerInvert) ccvalue = 127 - ccvalue;
734 if (ccvalue < pDimRgn->VCFVelocityScale) ccvalue = pDimRgn->VCFVelocityScale;
735 float cutoff = CutoffBase * float(ccvalue) * 0.00787402f; // (1 / 127)
736 if (cutoff > 1.0) cutoff = 1.0;
737 cutoff = (cutoff < 0.5 ? cutoff * 4826 - 1 : cutoff * 5715 - 449);
738 if (cutoff < 1.0) cutoff = 1.0;
739
740 VCFCutoffCtrl.fvalue = cutoff - 1.0; // needed for initialization of fFinalCutoff next time
741 fFinalCutoff = cutoff;
742 }
743
744 void Voice::processResonanceEvent(RTList<Event>::Iterator& itEvent) {
745 // convert absolute controller value to differential
746 const int ctrldelta = itEvent->Param.CC.Value - VCFResonanceCtrl.value;
747 VCFResonanceCtrl.value = itEvent->Param.CC.Value;
748 const float resonancedelta = (float) ctrldelta * 0.00787f; // 0.0..1.0
749 fFinalResonance += resonancedelta;
750 // needed for initialization of parameter
751 VCFResonanceCtrl.fvalue = itEvent->Param.CC.Value * 0.00787f;
752 }
753
754 /**
755 * Synthesizes the current audio fragment for this voice.
756 *
757 * @param Samples - number of sample points to be rendered in this audio
758 * fragment cycle
759 * @param pSrc - pointer to input sample data
760 * @param Skip - number of sample points to skip in output buffer
761 */
762 void Voice::Synthesize(uint Samples, sample_t* pSrc, uint Skip) {
763 finalSynthesisParameters.pOutLeft = &pEngineChannel->pOutputLeft[Skip];
764 finalSynthesisParameters.pOutRight = &pEngineChannel->pOutputRight[Skip];
765 finalSynthesisParameters.pSrc = pSrc;
766
767 RTList<Event>::Iterator itCCEvent = pEngineChannel->pEvents->first();
768 RTList<Event>::Iterator itNoteEvent = pEngineChannel->pMIDIKeyInfo[MIDIKey].pEvents->first();
769
770 if (Skip) { // skip events that happened before this voice was triggered
771 while (itCCEvent && itCCEvent->FragmentPos() <= Skip) ++itCCEvent;
772 while (itNoteEvent && itNoteEvent->FragmentPos() <= Skip) ++itNoteEvent;
773 }
774
775 uint killPos;
776 if (itKillEvent) killPos = RTMath::Min(itKillEvent->FragmentPos(), pEngine->MaxFadeOutPos);
777
778 uint i = Skip;
779 while (i < Samples) {
780 int iSubFragmentEnd = RTMath::Min(i + CONFIG_DEFAULT_SUBFRAGMENT_SIZE, Samples);
781
782 // initialize all final synthesis parameters
783 finalSynthesisParameters.fFinalPitch = PitchBase * PitchBend;
784 fFinalCutoff = VCFCutoffCtrl.fvalue;
785 fFinalResonance = VCFResonanceCtrl.fvalue;
786
787 // process MIDI control change and pitchbend events for this subfragment
788 processCCEvents(itCCEvent, iSubFragmentEnd);
789
790 float fFinalVolume = VolumeSmoother.render() * CrossfadeSmoother.render();
791 #ifdef CONFIG_PROCESS_MUTED_CHANNELS
792 if (pEngineChannel->GetMute()) fFinalVolume = 0;
793 #endif
794
795 // process transition events (note on, note off & sustain pedal)
796 processTransitionEvents(itNoteEvent, iSubFragmentEnd);
797
798 // if the voice was killed in this subfragment switch EG1 to fade out stage
799 if (itKillEvent && killPos <= iSubFragmentEnd) {
800 EG1.enterFadeOutStage();
801 itKillEvent = Pool<Event>::Iterator();
802 }
803
804 // process envelope generators
805 switch (EG1.getSegmentType()) {
806 case EGADSR::segment_lin:
807 fFinalVolume *= EG1.processLin();
808 break;
809 case EGADSR::segment_exp:
810 fFinalVolume *= EG1.processExp();
811 break;
812 case EGADSR::segment_end:
813 fFinalVolume *= EG1.getLevel();
814 break; // noop
815 }
816 switch (EG2.getSegmentType()) {
817 case EGADSR::segment_lin:
818 fFinalCutoff *= EG2.processLin();
819 break;
820 case EGADSR::segment_exp:
821 fFinalCutoff *= EG2.processExp();
822 break;
823 case EGADSR::segment_end:
824 fFinalCutoff *= EG2.getLevel();
825 break; // noop
826 }
827 if (EG3.active()) finalSynthesisParameters.fFinalPitch *= EG3.render();
828
829 // process low frequency oscillators
830 if (bLFO1Enabled) fFinalVolume *= pLFO1->render();
831 if (bLFO2Enabled) fFinalCutoff *= pLFO2->render();
832 if (bLFO3Enabled) finalSynthesisParameters.fFinalPitch *= RTMath::CentsToFreqRatio(pLFO3->render());
833
834 // if filter enabled then update filter coefficients
835 if (SYNTHESIS_MODE_GET_FILTER(SynthesisMode)) {
836 finalSynthesisParameters.filterLeft.SetParameters(fFinalCutoff + 1.0, fFinalResonance, pEngine->SampleRate);
837 finalSynthesisParameters.filterRight.SetParameters(fFinalCutoff + 1.0, fFinalResonance, pEngine->SampleRate);
838 }
839
840 // do we need resampling?
841 const float __PLUS_ONE_CENT = 1.000577789506554859250142541782224725466f;
842 const float __MINUS_ONE_CENT = 0.9994225441413807496009516495583113737666f;
843 const bool bResamplingRequired = !(finalSynthesisParameters.fFinalPitch <= __PLUS_ONE_CENT &&
844 finalSynthesisParameters.fFinalPitch >= __MINUS_ONE_CENT);
845 SYNTHESIS_MODE_SET_INTERPOLATE(SynthesisMode, bResamplingRequired);
846
847 // prepare final synthesis parameters structure
848 finalSynthesisParameters.uiToGo = iSubFragmentEnd - i;
849 #ifdef CONFIG_INTERPOLATE_VOLUME
850 finalSynthesisParameters.fFinalVolumeDeltaLeft =
851 (fFinalVolume * VolumeLeft * PanLeftSmoother.render() -
852 finalSynthesisParameters.fFinalVolumeLeft) / finalSynthesisParameters.uiToGo;
853 finalSynthesisParameters.fFinalVolumeDeltaRight =
854 (fFinalVolume * VolumeRight * PanRightSmoother.render() -
855 finalSynthesisParameters.fFinalVolumeRight) / finalSynthesisParameters.uiToGo;
856 #else
857 finalSynthesisParameters.fFinalVolumeLeft =
858 fFinalVolume * VolumeLeft * PanLeftSmoother.render();
859 finalSynthesisParameters.fFinalVolumeRight =
860 fFinalVolume * VolumeRight * PanRightSmoother.render();
861 #endif
862 // render audio for one subfragment
863 RunSynthesisFunction(SynthesisMode, &finalSynthesisParameters, &loop);
864
865 // stop the rendering if volume EG is finished
866 if (EG1.getSegmentType() == EGADSR::segment_end) break;
867
868 const double newPos = Pos + (iSubFragmentEnd - i) * finalSynthesisParameters.fFinalPitch;
869
870 // increment envelopes' positions
871 if (EG1.active()) {
872
873 // if sample has a loop and loop start has been reached in this subfragment, send a special event to EG1 to let it finish the attack hold stage
874 if (pSample->Loops && Pos <= pSample->LoopStart && pSample->LoopStart < newPos) {
875 EG1.update(EGADSR::event_hold_end, pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
876 }
877
878 EG1.increment(1);
879 if (!EG1.toStageEndLeft()) EG1.update(EGADSR::event_stage_end, pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
880 }
881 if (EG2.active()) {
882 EG2.increment(1);
883 if (!EG2.toStageEndLeft()) EG2.update(EGADSR::event_stage_end, pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
884 }
885 EG3.increment(1);
886 if (!EG3.toEndLeft()) EG3.update(); // neutralize envelope coefficient if end reached
887
888 Pos = newPos;
889 i = iSubFragmentEnd;
890 }
891 }
892
893 /** @brief Update current portamento position.
894 *
895 * Will be called when portamento mode is enabled to get the final
896 * portamento position of this active voice from where the next voice(s)
897 * might continue to slide on.
898 *
899 * @param itNoteOffEvent - event which causes this voice to die soon
900 */
901 void Voice::UpdatePortamentoPos(Pool<Event>::Iterator& itNoteOffEvent) {
902 const float fFinalEG3Level = EG3.level(itNoteOffEvent->FragmentPos());
903 pEngineChannel->PortamentoPos = (float) MIDIKey + RTMath::FreqRatioToCents(fFinalEG3Level) * 0.01f;
904 }
905
906 /**
907 * Immediately kill the voice. This method should not be used to kill
908 * a normal, active voice, because it doesn't take care of things like
909 * fading down the volume level to avoid clicks and regular processing
910 * until the kill event actually occured!
911 *
912 * @see Kill()
913 */
914 void Voice::KillImmediately() {
915 if (DiskVoice && DiskStreamRef.State != Stream::state_unused) {
916 pDiskThread->OrderDeletionOfStream(&DiskStreamRef);
917 }
918 Reset();
919 }
920
921 /**
922 * Kill the voice in regular sense. Let the voice render audio until
923 * the kill event actually occured and then fade down the volume level
924 * very quickly and let the voice die finally. Unlike a normal release
925 * of a voice, a kill process cannot be cancalled and is therefore
926 * usually used for voice stealing and key group conflicts.
927 *
928 * @param itKillEvent - event which caused the voice to be killed
929 */
930 void Voice::Kill(Pool<Event>::Iterator& itKillEvent) {
931 #if CONFIG_DEVMODE
932 if (!itKillEvent) dmsg(1,("gig::Voice::Kill(): ERROR, !itKillEvent !!!\n"));
933 if (itKillEvent && !itKillEvent.isValid()) dmsg(1,("gig::Voice::Kill(): ERROR, itKillEvent invalid !!!\n"));
934 #endif // CONFIG_DEVMODE
935
936 if (itTriggerEvent && itKillEvent->FragmentPos() <= itTriggerEvent->FragmentPos()) return;
937 this->itKillEvent = itKillEvent;
938 }
939
940 }} // namespace LinuxSampler::gig

  ViewVC Help
Powered by ViewVC