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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 233 - (hide annotations) (download)
Tue Sep 7 09:32:21 2004 UTC (19 years, 7 months ago) by schoenebeck
File size: 48490 byte(s)
* added support for layers
* fixed initial pitch calculation which did not honor the sample's own
  sample rate

1 schoenebeck 53 /***************************************************************************
2     * *
3     * LinuxSampler - modular, streaming capable sampler *
4     * *
5 schoenebeck 56 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 schoenebeck 53 * *
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 "EGADSR.h"
24     #include "Manipulator.h"
25    
26     #include "Voice.h"
27    
28     namespace LinuxSampler { namespace gig {
29    
30 schoenebeck 233 // TODO: no support for crossfades yet
31 schoenebeck 53
32     const float Voice::FILTER_CUTOFF_COEFF(CalculateFilterCutoffCoeff());
33    
34 schoenebeck 80 const int Voice::FILTER_UPDATE_MASK(CalculateFilterUpdateMask());
35    
36 schoenebeck 53 float Voice::CalculateFilterCutoffCoeff() {
37     return log(FILTER_CUTOFF_MIN / FILTER_CUTOFF_MAX);
38     }
39    
40 schoenebeck 80 int Voice::CalculateFilterUpdateMask() {
41     if (FILTER_UPDATE_PERIOD <= 0) return 0;
42     int power_of_two;
43     for (power_of_two = 0; 1<<power_of_two < FILTER_UPDATE_PERIOD; power_of_two++);
44     return (1 << power_of_two) - 1;
45     }
46    
47 schoenebeck 53 Voice::Voice() {
48     pEngine = NULL;
49     pDiskThread = NULL;
50     Active = false;
51     pEG1 = NULL;
52     pEG2 = NULL;
53     pEG3 = NULL;
54     pVCAManipulator = NULL;
55     pVCFCManipulator = NULL;
56     pVCOManipulator = NULL;
57     pLFO1 = NULL;
58     pLFO2 = NULL;
59     pLFO3 = NULL;
60     }
61    
62     Voice::~Voice() {
63     if (pEG1) delete pEG1;
64     if (pEG2) delete pEG2;
65     if (pEG3) delete pEG3;
66     if (pLFO1) delete pLFO1;
67     if (pLFO2) delete pLFO2;
68     if (pLFO3) delete pLFO3;
69     if (pVCAManipulator) delete pVCAManipulator;
70     if (pVCFCManipulator) delete pVCFCManipulator;
71     if (pVCOManipulator) delete pVCOManipulator;
72     }
73    
74     void Voice::SetEngine(Engine* pEngine) {
75     this->pEngine = pEngine;
76    
77     // delete old objects
78     if (pEG1) delete pEG1;
79     if (pEG2) delete pEG2;
80     if (pEG3) delete pEG3;
81     if (pVCAManipulator) delete pVCAManipulator;
82     if (pVCFCManipulator) delete pVCFCManipulator;
83     if (pVCOManipulator) delete pVCOManipulator;
84     if (pLFO1) delete pLFO1;
85     if (pLFO2) delete pLFO2;
86     if (pLFO3) delete pLFO3;
87    
88     // create new ones
89     pEG1 = new EGADSR(pEngine, Event::destination_vca);
90     pEG2 = new EGADSR(pEngine, Event::destination_vcfc);
91     pEG3 = new EGDecay(pEngine, Event::destination_vco);
92     pVCAManipulator = new VCAManipulator(pEngine);
93     pVCFCManipulator = new VCFCManipulator(pEngine);
94     pVCOManipulator = new VCOManipulator(pEngine);
95     pLFO1 = new LFO<gig::VCAManipulator>(0.0f, 1.0f, LFO<VCAManipulator>::propagation_top_down, pVCAManipulator, pEngine->pEventPool);
96     pLFO2 = new LFO<gig::VCFCManipulator>(0.0f, 1.0f, LFO<VCFCManipulator>::propagation_top_down, pVCFCManipulator, pEngine->pEventPool);
97     pLFO3 = new LFO<gig::VCOManipulator>(-1200.0f, 1200.0f, LFO<VCOManipulator>::propagation_middle_balanced, pVCOManipulator, pEngine->pEventPool); // +-1 octave (+-1200 cents) max.
98    
99     this->pDiskThread = pEngine->pDiskThread;
100 schoenebeck 64 dmsg(6,("Voice::SetEngine()\n"));
101 schoenebeck 53 }
102    
103     /**
104     * Initializes and triggers the voice, a disk stream will be launched if
105     * needed.
106     *
107     * @param pNoteOnEvent - event that caused triggering of this voice
108     * @param PitchBend - MIDI detune factor (-8192 ... +8191)
109     * @param pInstrument - points to the loaded instrument which provides sample wave(s) and articulation data
110 schoenebeck 233 * @param iLayer - layer number this voice refers to (only if this is a layered sound of course)
111 schoenebeck 53 * @returns 0 on success, a value < 0 if something failed
112     */
113 schoenebeck 233 int Voice::Trigger(Event* pNoteOnEvent, int PitchBend, ::gig::Instrument* pInstrument, int iLayer) {
114 schoenebeck 53 if (!pInstrument) {
115     dmsg(1,("voice::trigger: !pInstrument\n"));
116     exit(EXIT_FAILURE);
117     }
118    
119     Active = true;
120     MIDIKey = pNoteOnEvent->Key;
121     pRegion = pInstrument->GetRegion(MIDIKey);
122     PlaybackState = playback_state_ram; // we always start playback from RAM cache and switch then to disk if needed
123     Pos = 0;
124     Delay = pNoteOnEvent->FragmentPos();
125     pTriggerEvent = pNoteOnEvent;
126    
127     if (!pRegion) {
128 schoenebeck 230 std::cerr << "gig::Voice: No Region defined for MIDI key " << MIDIKey << std::endl << std::flush;
129 schoenebeck 53 Kill();
130     return -1;
131     }
132    
133 schoenebeck 230 // get current dimension values to select the right dimension region
134     //FIXME: controller values for selecting the dimension region here are currently not sample accurate
135     uint DimValues[5] = {0,0,0,0,0};
136     for (int i = pRegion->Dimensions - 1; i >= 0; i--) {
137     switch (pRegion->pDimensionDefinitions[i].dimension) {
138     case ::gig::dimension_samplechannel:
139     DimValues[i] = 0; //TODO: we currently ignore this dimension
140     break;
141     case ::gig::dimension_layer:
142 schoenebeck 233 DimValues[i] = iLayer;
143     // if this is the 1st layer then spawn further voices for all the other layers
144     if (iLayer == 0)
145     for (int iNewLayer = 1; iNewLayer < pRegion->pDimensionDefinitions[i].zones; iNewLayer++)
146     pEngine->LaunchVoice(pNoteOnEvent, iNewLayer);
147 schoenebeck 230 break;
148     case ::gig::dimension_velocity:
149 schoenebeck 53 DimValues[i] = pNoteOnEvent->Velocity;
150 schoenebeck 230 break;
151     case ::gig::dimension_channelaftertouch:
152     DimValues[i] = 0; //TODO: we currently ignore this dimension
153     break;
154     case ::gig::dimension_releasetrigger:
155     DimValues[i] = 0; //TODO: we currently ignore this dimension
156     break;
157     case ::gig::dimension_keyboard:
158     DimValues[i] = (uint) pNoteOnEvent->Key;
159     break;
160     case ::gig::dimension_modwheel:
161     DimValues[i] = pEngine->ControllerTable[1];
162     break;
163     case ::gig::dimension_breath:
164     DimValues[i] = pEngine->ControllerTable[2];
165     break;
166     case ::gig::dimension_foot:
167     DimValues[i] = pEngine->ControllerTable[4];
168     break;
169     case ::gig::dimension_portamentotime:
170     DimValues[i] = pEngine->ControllerTable[5];
171     break;
172     case ::gig::dimension_effect1:
173     DimValues[i] = pEngine->ControllerTable[12];
174     break;
175     case ::gig::dimension_effect2:
176     DimValues[i] = pEngine->ControllerTable[13];
177     break;
178     case ::gig::dimension_genpurpose1:
179     DimValues[i] = pEngine->ControllerTable[16];
180     break;
181     case ::gig::dimension_genpurpose2:
182     DimValues[i] = pEngine->ControllerTable[17];
183     break;
184     case ::gig::dimension_genpurpose3:
185     DimValues[i] = pEngine->ControllerTable[18];
186     break;
187     case ::gig::dimension_genpurpose4:
188     DimValues[i] = pEngine->ControllerTable[19];
189     break;
190     case ::gig::dimension_sustainpedal:
191     DimValues[i] = pEngine->ControllerTable[64];
192     break;
193     case ::gig::dimension_portamento:
194     DimValues[i] = pEngine->ControllerTable[65];
195     break;
196     case ::gig::dimension_sostenutopedal:
197     DimValues[i] = pEngine->ControllerTable[66];
198     break;
199     case ::gig::dimension_softpedal:
200     DimValues[i] = pEngine->ControllerTable[67];
201     break;
202     case ::gig::dimension_genpurpose5:
203     DimValues[i] = pEngine->ControllerTable[80];
204     break;
205     case ::gig::dimension_genpurpose6:
206     DimValues[i] = pEngine->ControllerTable[81];
207     break;
208     case ::gig::dimension_genpurpose7:
209     DimValues[i] = pEngine->ControllerTable[82];
210     break;
211     case ::gig::dimension_genpurpose8:
212     DimValues[i] = pEngine->ControllerTable[83];
213     break;
214     case ::gig::dimension_effect1depth:
215     DimValues[i] = pEngine->ControllerTable[91];
216     break;
217     case ::gig::dimension_effect2depth:
218     DimValues[i] = pEngine->ControllerTable[92];
219     break;
220     case ::gig::dimension_effect3depth:
221     DimValues[i] = pEngine->ControllerTable[93];
222     break;
223     case ::gig::dimension_effect4depth:
224     DimValues[i] = pEngine->ControllerTable[94];
225     break;
226     case ::gig::dimension_effect5depth:
227     DimValues[i] = pEngine->ControllerTable[95];
228     break;
229     case ::gig::dimension_none:
230     std::cerr << "gig::Voice::Trigger() Error: dimension=none\n" << std::flush;
231     break;
232     default:
233     std::cerr << "gig::Voice::Trigger() Error: Unknown dimension\n" << std::flush;
234 schoenebeck 53 }
235     }
236 schoenebeck 230 ::gig::DimensionRegion* pDimRgn = pRegion->GetDimensionRegionByValue(DimValues[4],DimValues[3],DimValues[2],DimValues[1],DimValues[0]);
237 schoenebeck 53
238     pSample = pDimRgn->pSample; // sample won't change until the voice is finished
239    
240     // Check if the sample needs disk streaming or is too short for that
241     long cachedsamples = pSample->GetCache().Size / pSample->FrameSize;
242     DiskVoice = cachedsamples < pSample->SamplesTotal;
243    
244     if (DiskVoice) { // voice to be streamed from disk
245 schoenebeck 225 MaxRAMPos = cachedsamples - (pEngine->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)
246 schoenebeck 53
247     // check if there's a loop defined which completely fits into the cached (RAM) part of the sample
248     if (pSample->Loops && pSample->LoopEnd <= MaxRAMPos) {
249     RAMLoop = true;
250     LoopCyclesLeft = pSample->LoopPlayCount;
251     }
252     else RAMLoop = false;
253    
254     if (pDiskThread->OrderNewStream(&DiskStreamRef, pSample, MaxRAMPos, !RAMLoop) < 0) {
255     dmsg(1,("Disk stream order failed!\n"));
256     Kill();
257     return -1;
258     }
259     dmsg(4,("Disk voice launched (cached samples: %d, total Samples: %d, MaxRAMPos: %d, RAMLooping: %s)\n", cachedsamples, pSample->SamplesTotal, MaxRAMPos, (RAMLoop) ? "yes" : "no"));
260     }
261     else { // RAM only voice
262     MaxRAMPos = cachedsamples;
263     if (pSample->Loops) {
264     RAMLoop = true;
265     LoopCyclesLeft = pSample->LoopPlayCount;
266     }
267     else RAMLoop = false;
268     dmsg(4,("RAM only voice launched (Looping: %s)\n", (RAMLoop) ? "yes" : "no"));
269     }
270    
271    
272     // calculate initial pitch value
273     {
274     double pitchbasecents = pDimRgn->FineTune * 10;
275     if (pDimRgn->PitchTrack) pitchbasecents += (MIDIKey - (int) pDimRgn->UnityNote) * 100;
276 schoenebeck 233 this->PitchBase = RTMath::CentsToFreqRatio(pitchbasecents) * (double(pSample->SamplesPerSecond) / double(pEngine->pAudioOutputDevice->SampleRate()));
277 schoenebeck 53 this->PitchBend = RTMath::CentsToFreqRatio(((double) PitchBend / 8192.0) * 200.0); // pitchbend wheel +-2 semitones = 200 cents
278     }
279    
280    
281     Volume = pDimRgn->GetVelocityAttenuation(pNoteOnEvent->Velocity) / 32768.0f; // we downscale by 32768 to convert from int16 value range to DSP value range (which is -1.0..1.0)
282    
283    
284     // setup EG 1 (VCA EG)
285     {
286     // get current value of EG1 controller
287     double eg1controllervalue;
288     switch (pDimRgn->EG1Controller.type) {
289     case ::gig::eg1_ctrl_t::type_none: // no controller defined
290     eg1controllervalue = 0;
291     break;
292     case ::gig::eg1_ctrl_t::type_channelaftertouch:
293     eg1controllervalue = 0; // TODO: aftertouch not yet supported
294     break;
295     case ::gig::eg1_ctrl_t::type_velocity:
296     eg1controllervalue = pNoteOnEvent->Velocity;
297     break;
298     case ::gig::eg1_ctrl_t::type_controlchange: // MIDI control change controller
299     eg1controllervalue = pEngine->ControllerTable[pDimRgn->EG1Controller.controller_number];
300     break;
301     }
302     if (pDimRgn->EG1ControllerInvert) eg1controllervalue = 127 - eg1controllervalue;
303    
304     // calculate influence of EG1 controller on EG1's parameters (TODO: needs to be fine tuned)
305     double eg1attack = (pDimRgn->EG1ControllerAttackInfluence) ? 0.0001 * (double) (1 << pDimRgn->EG1ControllerAttackInfluence) * eg1controllervalue : 0.0;
306     double eg1decay = (pDimRgn->EG1ControllerDecayInfluence) ? 0.0001 * (double) (1 << pDimRgn->EG1ControllerDecayInfluence) * eg1controllervalue : 0.0;
307     double eg1release = (pDimRgn->EG1ControllerReleaseInfluence) ? 0.0001 * (double) (1 << pDimRgn->EG1ControllerReleaseInfluence) * eg1controllervalue : 0.0;
308    
309     pEG1->Trigger(pDimRgn->EG1PreAttack,
310     pDimRgn->EG1Attack + eg1attack,
311     pDimRgn->EG1Hold,
312     pSample->LoopStart,
313     pDimRgn->EG1Decay1 + eg1decay,
314     pDimRgn->EG1Decay2 + eg1decay,
315     pDimRgn->EG1InfiniteSustain,
316     pDimRgn->EG1Sustain,
317     pDimRgn->EG1Release + eg1release,
318     Delay);
319     }
320    
321    
322     #if ENABLE_FILTER
323     // setup EG 2 (VCF Cutoff EG)
324     {
325     // get current value of EG2 controller
326     double eg2controllervalue;
327     switch (pDimRgn->EG2Controller.type) {
328     case ::gig::eg2_ctrl_t::type_none: // no controller defined
329     eg2controllervalue = 0;
330     break;
331     case ::gig::eg2_ctrl_t::type_channelaftertouch:
332     eg2controllervalue = 0; // TODO: aftertouch not yet supported
333     break;
334     case ::gig::eg2_ctrl_t::type_velocity:
335     eg2controllervalue = pNoteOnEvent->Velocity;
336     break;
337     case ::gig::eg2_ctrl_t::type_controlchange: // MIDI control change controller
338     eg2controllervalue = pEngine->ControllerTable[pDimRgn->EG2Controller.controller_number];
339     break;
340     }
341     if (pDimRgn->EG2ControllerInvert) eg2controllervalue = 127 - eg2controllervalue;
342    
343     // calculate influence of EG2 controller on EG2's parameters (TODO: needs to be fine tuned)
344     double eg2attack = (pDimRgn->EG2ControllerAttackInfluence) ? 0.0001 * (double) (1 << pDimRgn->EG2ControllerAttackInfluence) * eg2controllervalue : 0.0;
345     double eg2decay = (pDimRgn->EG2ControllerDecayInfluence) ? 0.0001 * (double) (1 << pDimRgn->EG2ControllerDecayInfluence) * eg2controllervalue : 0.0;
346     double eg2release = (pDimRgn->EG2ControllerReleaseInfluence) ? 0.0001 * (double) (1 << pDimRgn->EG2ControllerReleaseInfluence) * eg2controllervalue : 0.0;
347    
348     pEG2->Trigger(pDimRgn->EG2PreAttack,
349     pDimRgn->EG2Attack + eg2attack,
350     false,
351     pSample->LoopStart,
352     pDimRgn->EG2Decay1 + eg2decay,
353     pDimRgn->EG2Decay2 + eg2decay,
354     pDimRgn->EG2InfiniteSustain,
355     pDimRgn->EG2Sustain,
356     pDimRgn->EG2Release + eg2release,
357     Delay);
358     }
359     #endif // ENABLE_FILTER
360    
361    
362     // setup EG 3 (VCO EG)
363     {
364     double eg3depth = RTMath::CentsToFreqRatio(pDimRgn->EG3Depth);
365     pEG3->Trigger(eg3depth, pDimRgn->EG3Attack, Delay);
366     }
367    
368    
369     // setup LFO 1 (VCA LFO)
370     {
371     uint16_t lfo1_internal_depth;
372     switch (pDimRgn->LFO1Controller) {
373     case ::gig::lfo1_ctrl_internal:
374     lfo1_internal_depth = pDimRgn->LFO1InternalDepth;
375     pLFO1->ExtController = 0; // no external controller
376     break;
377     case ::gig::lfo1_ctrl_modwheel:
378     lfo1_internal_depth = 0;
379     pLFO1->ExtController = 1; // MIDI controller 1
380     break;
381     case ::gig::lfo1_ctrl_breath:
382     lfo1_internal_depth = 0;
383     pLFO1->ExtController = 2; // MIDI controller 2
384     break;
385     case ::gig::lfo1_ctrl_internal_modwheel:
386     lfo1_internal_depth = pDimRgn->LFO1InternalDepth;
387     pLFO1->ExtController = 1; // MIDI controller 1
388     break;
389     case ::gig::lfo1_ctrl_internal_breath:
390     lfo1_internal_depth = pDimRgn->LFO1InternalDepth;
391     pLFO1->ExtController = 2; // MIDI controller 2
392     break;
393     default:
394     lfo1_internal_depth = 0;
395     pLFO1->ExtController = 0; // no external controller
396     }
397     pLFO1->Trigger(pDimRgn->LFO1Frequency,
398     lfo1_internal_depth,
399     pDimRgn->LFO1ControlDepth,
400     pEngine->ControllerTable[pLFO1->ExtController],
401     pDimRgn->LFO1FlipPhase,
402 schoenebeck 225 pEngine->SampleRate,
403 schoenebeck 53 Delay);
404     }
405    
406     #if ENABLE_FILTER
407     // setup LFO 2 (VCF Cutoff LFO)
408     {
409     uint16_t lfo2_internal_depth;
410     switch (pDimRgn->LFO2Controller) {
411     case ::gig::lfo2_ctrl_internal:
412     lfo2_internal_depth = pDimRgn->LFO2InternalDepth;
413     pLFO2->ExtController = 0; // no external controller
414     break;
415     case ::gig::lfo2_ctrl_modwheel:
416     lfo2_internal_depth = 0;
417     pLFO2->ExtController = 1; // MIDI controller 1
418     break;
419     case ::gig::lfo2_ctrl_foot:
420     lfo2_internal_depth = 0;
421     pLFO2->ExtController = 4; // MIDI controller 4
422     break;
423     case ::gig::lfo2_ctrl_internal_modwheel:
424     lfo2_internal_depth = pDimRgn->LFO2InternalDepth;
425     pLFO2->ExtController = 1; // MIDI controller 1
426     break;
427     case ::gig::lfo2_ctrl_internal_foot:
428     lfo2_internal_depth = pDimRgn->LFO2InternalDepth;
429     pLFO2->ExtController = 4; // MIDI controller 4
430     break;
431     default:
432     lfo2_internal_depth = 0;
433     pLFO2->ExtController = 0; // no external controller
434     }
435     pLFO2->Trigger(pDimRgn->LFO2Frequency,
436     lfo2_internal_depth,
437     pDimRgn->LFO2ControlDepth,
438     pEngine->ControllerTable[pLFO2->ExtController],
439     pDimRgn->LFO2FlipPhase,
440 schoenebeck 225 pEngine->SampleRate,
441 schoenebeck 53 Delay);
442     }
443     #endif // ENABLE_FILTER
444    
445     // setup LFO 3 (VCO LFO)
446     {
447     uint16_t lfo3_internal_depth;
448     switch (pDimRgn->LFO3Controller) {
449     case ::gig::lfo3_ctrl_internal:
450     lfo3_internal_depth = pDimRgn->LFO3InternalDepth;
451     pLFO3->ExtController = 0; // no external controller
452     break;
453     case ::gig::lfo3_ctrl_modwheel:
454     lfo3_internal_depth = 0;
455     pLFO3->ExtController = 1; // MIDI controller 1
456     break;
457     case ::gig::lfo3_ctrl_aftertouch:
458     lfo3_internal_depth = 0;
459     pLFO3->ExtController = 0; // TODO: aftertouch not implemented yet
460     break;
461     case ::gig::lfo3_ctrl_internal_modwheel:
462     lfo3_internal_depth = pDimRgn->LFO3InternalDepth;
463     pLFO3->ExtController = 1; // MIDI controller 1
464     break;
465     case ::gig::lfo3_ctrl_internal_aftertouch:
466     lfo3_internal_depth = pDimRgn->LFO3InternalDepth;
467     pLFO1->ExtController = 0; // TODO: aftertouch not implemented yet
468     break;
469     default:
470     lfo3_internal_depth = 0;
471     pLFO3->ExtController = 0; // no external controller
472     }
473     pLFO3->Trigger(pDimRgn->LFO3Frequency,
474     lfo3_internal_depth,
475     pDimRgn->LFO3ControlDepth,
476     pEngine->ControllerTable[pLFO3->ExtController],
477     false,
478 schoenebeck 225 pEngine->SampleRate,
479 schoenebeck 53 Delay);
480     }
481    
482     #if ENABLE_FILTER
483     #if FORCE_FILTER_USAGE
484     FilterLeft.Enabled = FilterRight.Enabled = true;
485     #else // use filter only if instrument file told so
486     FilterLeft.Enabled = FilterRight.Enabled = pDimRgn->VCFEnabled;
487     #endif // FORCE_FILTER_USAGE
488     if (pDimRgn->VCFEnabled) {
489     #ifdef OVERRIDE_FILTER_CUTOFF_CTRL
490     VCFCutoffCtrl.controller = OVERRIDE_FILTER_CUTOFF_CTRL;
491     #else // use the one defined in the instrument file
492     switch (pDimRgn->VCFCutoffController) {
493     case ::gig::vcf_cutoff_ctrl_modwheel:
494     VCFCutoffCtrl.controller = 1;
495     break;
496     case ::gig::vcf_cutoff_ctrl_effect1:
497     VCFCutoffCtrl.controller = 12;
498     break;
499     case ::gig::vcf_cutoff_ctrl_effect2:
500     VCFCutoffCtrl.controller = 13;
501     break;
502     case ::gig::vcf_cutoff_ctrl_breath:
503     VCFCutoffCtrl.controller = 2;
504     break;
505     case ::gig::vcf_cutoff_ctrl_foot:
506     VCFCutoffCtrl.controller = 4;
507     break;
508     case ::gig::vcf_cutoff_ctrl_sustainpedal:
509     VCFCutoffCtrl.controller = 64;
510     break;
511     case ::gig::vcf_cutoff_ctrl_softpedal:
512     VCFCutoffCtrl.controller = 67;
513     break;
514     case ::gig::vcf_cutoff_ctrl_genpurpose7:
515     VCFCutoffCtrl.controller = 82;
516     break;
517     case ::gig::vcf_cutoff_ctrl_genpurpose8:
518     VCFCutoffCtrl.controller = 83;
519     break;
520     case ::gig::vcf_cutoff_ctrl_aftertouch: //TODO: not implemented yet
521     case ::gig::vcf_cutoff_ctrl_none:
522     default:
523     VCFCutoffCtrl.controller = 0;
524     break;
525     }
526     #endif // OVERRIDE_FILTER_CUTOFF_CTRL
527    
528     #ifdef OVERRIDE_FILTER_RES_CTRL
529     VCFResonanceCtrl.controller = OVERRIDE_FILTER_RES_CTRL;
530     #else // use the one defined in the instrument file
531     switch (pDimRgn->VCFResonanceController) {
532     case ::gig::vcf_res_ctrl_genpurpose3:
533     VCFResonanceCtrl.controller = 18;
534     break;
535     case ::gig::vcf_res_ctrl_genpurpose4:
536     VCFResonanceCtrl.controller = 19;
537     break;
538     case ::gig::vcf_res_ctrl_genpurpose5:
539     VCFResonanceCtrl.controller = 80;
540     break;
541     case ::gig::vcf_res_ctrl_genpurpose6:
542     VCFResonanceCtrl.controller = 81;
543     break;
544     case ::gig::vcf_res_ctrl_none:
545     default:
546     VCFResonanceCtrl.controller = 0;
547     }
548     #endif // OVERRIDE_FILTER_RES_CTRL
549    
550     #ifndef OVERRIDE_FILTER_TYPE
551     FilterLeft.SetType(pDimRgn->VCFType);
552     FilterRight.SetType(pDimRgn->VCFType);
553     #else // override filter type
554     FilterLeft.SetType(OVERRIDE_FILTER_TYPE);
555     FilterRight.SetType(OVERRIDE_FILTER_TYPE);
556     #endif // OVERRIDE_FILTER_TYPE
557    
558     VCFCutoffCtrl.value = pEngine->ControllerTable[VCFCutoffCtrl.controller];
559     VCFResonanceCtrl.value = pEngine->ControllerTable[VCFResonanceCtrl.controller];
560    
561     // calculate cutoff frequency
562     float cutoff = (!VCFCutoffCtrl.controller)
563     ? exp((float) (127 - pNoteOnEvent->Velocity) * (float) pDimRgn->VCFVelocityScale * 6.2E-5f * FILTER_CUTOFF_COEFF) * FILTER_CUTOFF_MAX
564     : exp((float) VCFCutoffCtrl.value * 0.00787402f * FILTER_CUTOFF_COEFF) * FILTER_CUTOFF_MAX;
565    
566     // calculate resonance
567     float resonance = (float) VCFResonanceCtrl.value * 0.00787f; // 0.0..1.0
568     if (pDimRgn->VCFKeyboardTracking) {
569     resonance += (float) (pNoteOnEvent->Key - pDimRgn->VCFKeyboardTrackingBreakpoint) * 0.00787f;
570     }
571     Constrain(resonance, 0.0, 1.0); // correct resonance if outside allowed value range (0.0..1.0)
572    
573     VCFCutoffCtrl.fvalue = cutoff - FILTER_CUTOFF_MIN;
574     VCFResonanceCtrl.fvalue = resonance;
575    
576 schoenebeck 225 FilterLeft.SetParameters(cutoff, resonance, pEngine->SampleRate);
577     FilterRight.SetParameters(cutoff, resonance, pEngine->SampleRate);
578 schoenebeck 53
579     FilterUpdateCounter = -1;
580     }
581     else {
582     VCFCutoffCtrl.controller = 0;
583     VCFResonanceCtrl.controller = 0;
584     }
585     #endif // ENABLE_FILTER
586    
587     // ************************************************
588     // TODO: ARTICULATION DATA HANDLING IS MISSING HERE
589     // ************************************************
590    
591     return 0; // success
592     }
593    
594     /**
595     * Renders the audio data for this voice for the current audio fragment.
596     * The sample input data can either come from RAM (cached sample or sample
597     * part) or directly from disk. The output signal will be rendered by
598     * resampling / interpolation. If this voice is a disk streaming voice and
599     * the voice completely played back the cached RAM part of the sample, it
600     * will automatically switch to disk playback for the next RenderAudio()
601     * call.
602     *
603     * @param Samples - number of samples to be rendered in this audio fragment cycle
604     */
605     void Voice::Render(uint Samples) {
606    
607     // Reset the synthesis parameter matrix
608 schoenebeck 111 pEngine->ResetSynthesisParameters(Event::destination_vca, this->Volume * pEngine->GlobalVolume);
609 schoenebeck 53 pEngine->ResetSynthesisParameters(Event::destination_vco, this->PitchBase);
610     #if ENABLE_FILTER
611     pEngine->ResetSynthesisParameters(Event::destination_vcfc, VCFCutoffCtrl.fvalue);
612     pEngine->ResetSynthesisParameters(Event::destination_vcfr, VCFResonanceCtrl.fvalue);
613     #endif // ENABLE_FILTER
614    
615    
616     // Apply events to the synthesis parameter matrix
617     ProcessEvents(Samples);
618    
619    
620     // Let all modulators write their parameter changes to the synthesis parameter matrix for the current audio fragment
621     pEG1->Process(Samples, pEngine->pMIDIKeyInfo[MIDIKey].pEvents, pTriggerEvent, this->Pos, this->PitchBase * this->PitchBend);
622     #if ENABLE_FILTER
623     pEG2->Process(Samples, pEngine->pMIDIKeyInfo[MIDIKey].pEvents, pTriggerEvent, this->Pos, this->PitchBase * this->PitchBend);
624     #endif // ENABLE_FILTER
625     pEG3->Process(Samples);
626     pLFO1->Process(Samples);
627     #if ENABLE_FILTER
628     pLFO2->Process(Samples);
629     #endif // ENABLE_FILTER
630     pLFO3->Process(Samples);
631    
632    
633 schoenebeck 80 #if ENABLE_FILTER
634     CalculateBiquadParameters(Samples); // calculate the final biquad filter parameters
635     #endif // ENABLE_FILTER
636    
637    
638 schoenebeck 53 switch (this->PlaybackState) {
639    
640     case playback_state_ram: {
641     if (RAMLoop) InterpolateAndLoop(Samples, (sample_t*) pSample->GetCache().pStart, Delay);
642     else Interpolate(Samples, (sample_t*) pSample->GetCache().pStart, Delay);
643     if (DiskVoice) {
644     // check if we reached the allowed limit of the sample RAM cache
645     if (Pos > MaxRAMPos) {
646     dmsg(5,("Voice: switching to disk playback (Pos=%f)\n", Pos));
647     this->PlaybackState = playback_state_disk;
648     }
649     }
650     else if (Pos >= pSample->GetCache().Size / pSample->FrameSize) {
651     this->PlaybackState = playback_state_end;
652     }
653     }
654     break;
655    
656     case playback_state_disk: {
657     if (!DiskStreamRef.pStream) {
658     // check if the disk thread created our ordered disk stream in the meantime
659     DiskStreamRef.pStream = pDiskThread->AskForCreatedStream(DiskStreamRef.OrderID);
660     if (!DiskStreamRef.pStream) {
661     std::cout << stderr << "Disk stream not available in time!" << std::endl << std::flush;
662     Kill();
663     return;
664     }
665     DiskStreamRef.pStream->IncrementReadPos(pSample->Channels * (RTMath::DoubleToInt(Pos) - MaxRAMPos));
666     Pos -= RTMath::DoubleToInt(Pos);
667     }
668    
669     // add silence sample at the end if we reached the end of the stream (for the interpolator)
670 schoenebeck 225 if (DiskStreamRef.State == Stream::state_end && DiskStreamRef.pStream->GetReadSpace() < (pEngine->MaxSamplesPerCycle << MAX_PITCH) / pSample->Channels) {
671     DiskStreamRef.pStream->WriteSilence((pEngine->MaxSamplesPerCycle << MAX_PITCH) / pSample->Channels);
672 schoenebeck 53 this->PlaybackState = playback_state_end;
673     }
674    
675     sample_t* ptr = DiskStreamRef.pStream->GetReadPtr(); // get the current read_ptr within the ringbuffer where we read the samples from
676     Interpolate(Samples, ptr, Delay);
677     DiskStreamRef.pStream->IncrementReadPos(RTMath::DoubleToInt(Pos) * pSample->Channels);
678     Pos -= RTMath::DoubleToInt(Pos);
679     }
680     break;
681    
682     case playback_state_end:
683     Kill(); // free voice
684     break;
685     }
686    
687    
688     #if ENABLE_FILTER
689     // Reset synthesis event lists (except VCO, as VCO events apply channel wide currently)
690     pEngine->pSynthesisEvents[Event::destination_vcfc]->clear();
691     pEngine->pSynthesisEvents[Event::destination_vcfr]->clear();
692     #endif // ENABLE_FILTER
693    
694     // Reset delay
695     Delay = 0;
696    
697     pTriggerEvent = NULL;
698    
699     // If release stage finished, let the voice be killed
700     if (pEG1->GetStage() == EGADSR::stage_end) this->PlaybackState = playback_state_end;
701     }
702    
703     /**
704     * Resets voice variables. Should only be called if rendering process is
705     * suspended / not running.
706     */
707     void Voice::Reset() {
708     pLFO1->Reset();
709     pLFO2->Reset();
710     pLFO3->Reset();
711     DiskStreamRef.pStream = NULL;
712     DiskStreamRef.hStream = 0;
713     DiskStreamRef.State = Stream::state_unused;
714     DiskStreamRef.OrderID = 0;
715     Active = false;
716     }
717    
718     /**
719     * Process the control change event lists of the engine for the current
720     * audio fragment. Event values will be applied to the synthesis parameter
721     * matrix.
722     *
723     * @param Samples - number of samples to be rendered in this audio fragment cycle
724     */
725     void Voice::ProcessEvents(uint Samples) {
726    
727     // dispatch control change events
728     Event* pCCEvent = pEngine->pCCEvents->first();
729     if (Delay) { // skip events that happened before this voice was triggered
730     while (pCCEvent && pCCEvent->FragmentPos() <= Delay) pCCEvent = pEngine->pCCEvents->next();
731     }
732     while (pCCEvent) {
733     if (pCCEvent->Controller) { // if valid MIDI controller
734     #if ENABLE_FILTER
735     if (pCCEvent->Controller == VCFCutoffCtrl.controller) {
736     pEngine->pSynthesisEvents[Event::destination_vcfc]->alloc_assign(*pCCEvent);
737     }
738     if (pCCEvent->Controller == VCFResonanceCtrl.controller) {
739     pEngine->pSynthesisEvents[Event::destination_vcfr]->alloc_assign(*pCCEvent);
740     }
741     #endif // ENABLE_FILTER
742     if (pCCEvent->Controller == pLFO1->ExtController) {
743     pLFO1->SendEvent(pCCEvent);
744     }
745     #if ENABLE_FILTER
746     if (pCCEvent->Controller == pLFO2->ExtController) {
747     pLFO2->SendEvent(pCCEvent);
748     }
749     #endif // ENABLE_FILTER
750     if (pCCEvent->Controller == pLFO3->ExtController) {
751     pLFO3->SendEvent(pCCEvent);
752     }
753     }
754    
755     pCCEvent = pEngine->pCCEvents->next();
756     }
757    
758    
759     // process pitch events
760     {
761     RTEList<Event>* pVCOEventList = pEngine->pSynthesisEvents[Event::destination_vco];
762     Event* pVCOEvent = pVCOEventList->first();
763     if (Delay) { // skip events that happened before this voice was triggered
764     while (pVCOEvent && pVCOEvent->FragmentPos() <= Delay) pVCOEvent = pVCOEventList->next();
765     }
766     // apply old pitchbend value until first pitch event occurs
767     if (this->PitchBend != 1.0) {
768     uint end = (pVCOEvent) ? pVCOEvent->FragmentPos() : Samples;
769     for (uint i = Delay; i < end; i++) {
770     pEngine->pSynthesisParameters[Event::destination_vco][i] *= this->PitchBend;
771     }
772     }
773     float pitch;
774     while (pVCOEvent) {
775     Event* pNextVCOEvent = pVCOEventList->next();
776    
777     // calculate the influence length of this event (in sample points)
778     uint end = (pNextVCOEvent) ? pNextVCOEvent->FragmentPos() : Samples;
779    
780     pitch = RTMath::CentsToFreqRatio(((double) pVCOEvent->Pitch / 8192.0) * 200.0); // +-two semitones = +-200 cents
781    
782     // apply pitch value to the pitch parameter sequence
783     for (uint i = pVCOEvent->FragmentPos(); i < end; i++) {
784     pEngine->pSynthesisParameters[Event::destination_vco][i] *= pitch;
785     }
786    
787     pVCOEvent = pNextVCOEvent;
788     }
789     if (pVCOEventList->last()) this->PitchBend = pitch;
790     }
791    
792    
793     #if ENABLE_FILTER
794     // process filter cutoff events
795     {
796     RTEList<Event>* pCutoffEventList = pEngine->pSynthesisEvents[Event::destination_vcfc];
797     Event* pCutoffEvent = pCutoffEventList->first();
798     if (Delay) { // skip events that happened before this voice was triggered
799     while (pCutoffEvent && pCutoffEvent->FragmentPos() <= Delay) pCutoffEvent = pCutoffEventList->next();
800     }
801     float cutoff;
802     while (pCutoffEvent) {
803     Event* pNextCutoffEvent = pCutoffEventList->next();
804    
805     // calculate the influence length of this event (in sample points)
806     uint end = (pNextCutoffEvent) ? pNextCutoffEvent->FragmentPos() : Samples;
807    
808     cutoff = exp((float) pCutoffEvent->Value * 0.00787402f * FILTER_CUTOFF_COEFF) * FILTER_CUTOFF_MAX - FILTER_CUTOFF_MIN;
809    
810     // apply cutoff frequency to the cutoff parameter sequence
811     for (uint i = pCutoffEvent->FragmentPos(); i < end; i++) {
812     pEngine->pSynthesisParameters[Event::destination_vcfc][i] = cutoff;
813     }
814    
815     pCutoffEvent = pNextCutoffEvent;
816     }
817     if (pCutoffEventList->last()) VCFCutoffCtrl.fvalue = cutoff; // needed for initialization of parameter matrix next time
818     }
819    
820     // process filter resonance events
821     {
822     RTEList<Event>* pResonanceEventList = pEngine->pSynthesisEvents[Event::destination_vcfr];
823     Event* pResonanceEvent = pResonanceEventList->first();
824     if (Delay) { // skip events that happened before this voice was triggered
825     while (pResonanceEvent && pResonanceEvent->FragmentPos() <= Delay) pResonanceEvent = pResonanceEventList->next();
826     }
827     while (pResonanceEvent) {
828     Event* pNextResonanceEvent = pResonanceEventList->next();
829    
830     // calculate the influence length of this event (in sample points)
831     uint end = (pNextResonanceEvent) ? pNextResonanceEvent->FragmentPos() : Samples;
832    
833     // convert absolute controller value to differential
834     int ctrldelta = pResonanceEvent->Value - VCFResonanceCtrl.value;
835     VCFResonanceCtrl.value = pResonanceEvent->Value;
836    
837     float resonancedelta = (float) ctrldelta * 0.00787f; // 0.0..1.0
838    
839     // apply cutoff frequency to the cutoff parameter sequence
840     for (uint i = pResonanceEvent->FragmentPos(); i < end; i++) {
841     pEngine->pSynthesisParameters[Event::destination_vcfr][i] += resonancedelta;
842     }
843    
844     pResonanceEvent = pNextResonanceEvent;
845     }
846     if (pResonanceEventList->last()) VCFResonanceCtrl.fvalue = pResonanceEventList->last()->Value * 0.00787f; // needed for initialization of parameter matrix next time
847     }
848     #endif // ENABLE_FILTER
849     }
850    
851 schoenebeck 80 #if ENABLE_FILTER
852 schoenebeck 53 /**
853 schoenebeck 80 * Calculate all necessary, final biquad filter parameters.
854     *
855     * @param Samples - number of samples to be rendered in this audio fragment cycle
856     */
857     void Voice::CalculateBiquadParameters(uint Samples) {
858     if (!FilterLeft.Enabled) return;
859    
860     biquad_param_t bqbase;
861     biquad_param_t bqmain;
862     float prev_cutoff = pEngine->pSynthesisParameters[Event::destination_vcfc][0];
863     float prev_res = pEngine->pSynthesisParameters[Event::destination_vcfr][0];
864 schoenebeck 225 FilterLeft.SetParameters(&bqbase, &bqmain, prev_cutoff, prev_res, pEngine->SampleRate);
865 schoenebeck 80 pEngine->pBasicFilterParameters[0] = bqbase;
866     pEngine->pMainFilterParameters[0] = bqmain;
867    
868     float* bq;
869     for (int i = 1; i < Samples; i++) {
870     // recalculate biquad parameters if cutoff or resonance differ from previous sample point
871     if (!(i & FILTER_UPDATE_MASK)) if (pEngine->pSynthesisParameters[Event::destination_vcfr][i] != prev_res ||
872     pEngine->pSynthesisParameters[Event::destination_vcfc][i] != prev_cutoff) {
873     prev_cutoff = pEngine->pSynthesisParameters[Event::destination_vcfc][i];
874     prev_res = pEngine->pSynthesisParameters[Event::destination_vcfr][i];
875 schoenebeck 225 FilterLeft.SetParameters(&bqbase, &bqmain, prev_cutoff, prev_res, pEngine->SampleRate);
876 schoenebeck 80 }
877    
878     //same as 'pEngine->pBasicFilterParameters[i] = bqbase;'
879     bq = (float*) &pEngine->pBasicFilterParameters[i];
880     bq[0] = bqbase.a1;
881     bq[1] = bqbase.a2;
882     bq[2] = bqbase.b0;
883     bq[3] = bqbase.b1;
884     bq[4] = bqbase.b2;
885    
886     // same as 'pEngine->pMainFilterParameters[i] = bqmain;'
887     bq = (float*) &pEngine->pMainFilterParameters[i];
888     bq[0] = bqmain.a1;
889     bq[1] = bqmain.a2;
890     bq[2] = bqmain.b0;
891     bq[3] = bqmain.b1;
892     bq[4] = bqmain.b2;
893     }
894     }
895     #endif // ENABLE_FILTER
896    
897     /**
898 schoenebeck 53 * Interpolates the input audio data (no loop).
899     *
900     * @param Samples - number of sample points to be rendered in this audio
901     * fragment cycle
902     * @param pSrc - pointer to input sample data
903     * @param Skip - number of sample points to skip in output buffer
904     */
905     void Voice::Interpolate(uint Samples, sample_t* pSrc, uint Skip) {
906     int i = Skip;
907    
908     // FIXME: assuming either mono or stereo
909     if (this->pSample->Channels == 2) { // Stereo Sample
910     while (i < Samples) {
911     InterpolateOneStep_Stereo(pSrc, i,
912     pEngine->pSynthesisParameters[Event::destination_vca][i],
913     pEngine->pSynthesisParameters[Event::destination_vco][i],
914 schoenebeck 80 pEngine->pBasicFilterParameters[i],
915     pEngine->pMainFilterParameters[i]);
916 schoenebeck 53 }
917     }
918     else { // Mono Sample
919     while (i < Samples) {
920     InterpolateOneStep_Mono(pSrc, i,
921     pEngine->pSynthesisParameters[Event::destination_vca][i],
922     pEngine->pSynthesisParameters[Event::destination_vco][i],
923 schoenebeck 80 pEngine->pBasicFilterParameters[i],
924     pEngine->pMainFilterParameters[i]);
925 schoenebeck 53 }
926     }
927     }
928    
929     /**
930     * Interpolates the input audio data, this method honors looping.
931     *
932     * @param Samples - number of sample points to be rendered in this audio
933     * fragment cycle
934     * @param pSrc - pointer to input sample data
935     * @param Skip - number of sample points to skip in output buffer
936     */
937     void Voice::InterpolateAndLoop(uint Samples, sample_t* pSrc, uint Skip) {
938     int i = Skip;
939    
940     // FIXME: assuming either mono or stereo
941     if (pSample->Channels == 2) { // Stereo Sample
942     if (pSample->LoopPlayCount) {
943     // render loop (loop count limited)
944     while (i < Samples && LoopCyclesLeft) {
945     InterpolateOneStep_Stereo(pSrc, i,
946     pEngine->pSynthesisParameters[Event::destination_vca][i],
947     pEngine->pSynthesisParameters[Event::destination_vco][i],
948 schoenebeck 80 pEngine->pBasicFilterParameters[i],
949     pEngine->pMainFilterParameters[i]);
950 schoenebeck 53 if (Pos > pSample->LoopEnd) {
951     Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);;
952     LoopCyclesLeft--;
953     }
954     }
955     // render on without loop
956     while (i < Samples) {
957     InterpolateOneStep_Stereo(pSrc, i,
958     pEngine->pSynthesisParameters[Event::destination_vca][i],
959     pEngine->pSynthesisParameters[Event::destination_vco][i],
960 schoenebeck 80 pEngine->pBasicFilterParameters[i],
961     pEngine->pMainFilterParameters[i]);
962 schoenebeck 53 }
963     }
964     else { // render loop (endless loop)
965     while (i < Samples) {
966     InterpolateOneStep_Stereo(pSrc, i,
967     pEngine->pSynthesisParameters[Event::destination_vca][i],
968     pEngine->pSynthesisParameters[Event::destination_vco][i],
969 schoenebeck 80 pEngine->pBasicFilterParameters[i],
970     pEngine->pMainFilterParameters[i]);
971 schoenebeck 53 if (Pos > pSample->LoopEnd) {
972     Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);
973     }
974     }
975     }
976     }
977     else { // Mono Sample
978     if (pSample->LoopPlayCount) {
979     // render loop (loop count limited)
980     while (i < Samples && LoopCyclesLeft) {
981     InterpolateOneStep_Mono(pSrc, i,
982     pEngine->pSynthesisParameters[Event::destination_vca][i],
983     pEngine->pSynthesisParameters[Event::destination_vco][i],
984 schoenebeck 80 pEngine->pBasicFilterParameters[i],
985     pEngine->pMainFilterParameters[i]);
986 schoenebeck 53 if (Pos > pSample->LoopEnd) {
987     Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);;
988     LoopCyclesLeft--;
989     }
990     }
991     // render on without loop
992     while (i < Samples) {
993     InterpolateOneStep_Mono(pSrc, i,
994     pEngine->pSynthesisParameters[Event::destination_vca][i],
995     pEngine->pSynthesisParameters[Event::destination_vco][i],
996 schoenebeck 80 pEngine->pBasicFilterParameters[i],
997     pEngine->pMainFilterParameters[i]);
998 schoenebeck 53 }
999     }
1000     else { // render loop (endless loop)
1001     while (i < Samples) {
1002     InterpolateOneStep_Mono(pSrc, i,
1003     pEngine->pSynthesisParameters[Event::destination_vca][i],
1004     pEngine->pSynthesisParameters[Event::destination_vco][i],
1005 schoenebeck 80 pEngine->pBasicFilterParameters[i],
1006     pEngine->pMainFilterParameters[i]);
1007 schoenebeck 53 if (Pos > pSample->LoopEnd) {
1008     Pos = pSample->LoopStart + fmod(Pos - pSample->LoopEnd, pSample->LoopSize);;
1009     }
1010     }
1011     }
1012     }
1013     }
1014    
1015     /**
1016     * Immediately kill the voice.
1017     */
1018     void Voice::Kill() {
1019     if (DiskVoice && DiskStreamRef.State != Stream::state_unused) {
1020     pDiskThread->OrderDeletionOfStream(&DiskStreamRef);
1021     }
1022     Reset();
1023     }
1024    
1025     }} // namespace LinuxSampler::gig

  ViewVC Help
Powered by ViewVC