ba8da9833398f05dd989e2eedec3fea0605a788d
[ardour.git] / libs / fluidsynth / src / fluid_rvoice.c
1 /* FluidSynth - A Software Synthesizer
2  *
3  * Copyright (C) 2003  Peter Hanappe and others.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public License
7  * as published by the Free Software Foundation; either version 2 of
8  * the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the Free
17  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18  * 02110-1301, USA
19  */
20
21 #include "fluid_rvoice.h"
22 #include "fluid_conv.h"
23 #include "fluid_sys.h"
24
25 /**
26  * @return -1 if voice has finished, 0 if it's currently quiet, 1 otherwise
27  */
28 static inline int
29 fluid_rvoice_calc_amp(fluid_rvoice_t* voice)
30 {
31   fluid_real_t target_amp;      /* target amplitude */
32
33   if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVDELAY)
34     return -1;  /* The volume amplitude is in hold phase. No sound is produced. */
35
36   if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVATTACK)
37   {
38     /* the envelope is in the attack section: ramp linearly to max value.
39      * A positive modlfo_to_vol should increase volume (negative attenuation).
40      */
41     target_amp = fluid_atten2amp (voice->dsp.attenuation)
42       * fluid_cb2amp (fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol)
43       * fluid_adsr_env_get_val(&voice->envlfo.volenv);
44   }
45   else
46   {
47     fluid_real_t amplitude_that_reaches_noise_floor;
48     fluid_real_t amp_max;
49
50     target_amp = fluid_atten2amp (voice->dsp.attenuation)
51       * fluid_cb2amp (960.0f * (1.0f - fluid_adsr_env_get_val(&voice->envlfo.volenv))
52                       + fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol);
53
54     /* We turn off a voice, if the volume has dropped low enough. */
55
56     /* A voice can be turned off, when an estimate for the volume
57      * (upper bound) falls below that volume, that will drop the
58      * sample below the noise floor.
59      */
60
61     /* If the loop amplitude is known, we can use it if the voice loop is within
62      * the sample loop
63      */
64
65     /* Is the playing pointer already in the loop? */
66     if (voice->dsp.has_looped)
67       amplitude_that_reaches_noise_floor = voice->dsp.amplitude_that_reaches_noise_floor_loop;
68     else
69       amplitude_that_reaches_noise_floor = voice->dsp.amplitude_that_reaches_noise_floor_nonloop;
70
71     /* voice->attenuation_min is a lower boundary for the attenuation
72      * now and in the future (possibly 0 in the worst case).  Now the
73      * amplitude of sample and volenv cannot exceed amp_max (since
74      * volenv_val can only drop):
75      */
76
77     amp_max = fluid_atten2amp (voice->dsp.min_attenuation_cB) * 
78               fluid_adsr_env_get_val(&voice->envlfo.volenv);
79
80     /* And if amp_max is already smaller than the known amplitude,
81      * which will attenuate the sample below the noise floor, then we
82      * can safely turn off the voice. Duh. */
83     if (amp_max < amplitude_that_reaches_noise_floor)
84     {
85       return 0;
86     }
87   }
88
89   /* Volume increment to go from voice->amp to target_amp in FLUID_BUFSIZE steps */
90   voice->dsp.amp_incr = (target_amp - voice->dsp.amp) / FLUID_BUFSIZE;
91
92   fluid_check_fpe ("voice_write amplitude calculation");
93
94   /* no volume and not changing? - No need to process */
95   if ((voice->dsp.amp == 0.0f) && (voice->dsp.amp_incr == 0.0f))
96     return -1;
97
98   return 1;
99 }
100
101
102 /* these should be the absolute minimum that FluidSynth can deal with */
103 #define FLUID_MIN_LOOP_SIZE 2
104 #define FLUID_MIN_LOOP_PAD 0
105
106 #define FLUID_SAMPLESANITY_CHECK (1 << 0)
107 #define FLUID_SAMPLESANITY_STARTUP (1 << 1)
108
109 /* Purpose:
110  *
111  * Make sure, that sample start / end point and loop points are in
112  * proper order. When starting up, calculate the initial phase.
113  * TODO: Investigate whether this can be moved from rvoice to voice.
114  */
115 static void
116 fluid_rvoice_check_sample_sanity(fluid_rvoice_t* voice)
117 {
118     int min_index_nonloop=(int) voice->dsp.sample->start;
119     int max_index_nonloop=(int) voice->dsp.sample->end;
120
121     /* make sure we have enough samples surrounding the loop */
122     int min_index_loop=(int) voice->dsp.sample->start + FLUID_MIN_LOOP_PAD;
123     int max_index_loop=(int) voice->dsp.sample->end - FLUID_MIN_LOOP_PAD + 1;   /* 'end' is last valid sample, loopend can be + 1 */
124     fluid_check_fpe("voice_check_sample_sanity start");
125
126     if (!voice->dsp.check_sample_sanity_flag){
127         return;
128     }
129
130 #if 0
131     printf("Sample from %i to %i\n",voice->dsp.sample->start, voice->dsp.sample->end);
132     printf("Sample loop from %i %i\n",voice->dsp.sample->loopstart, voice->dsp.sample->loopend);
133     printf("Playback from %i to %i\n", voice->dsp.start, voice->dsp.end);
134     printf("Playback loop from %i to %i\n",voice->dsp.loopstart, voice->dsp.loopend);
135 #endif
136
137     /* Keep the start point within the sample data */
138     if (voice->dsp.start < min_index_nonloop){
139         voice->dsp.start = min_index_nonloop;
140     } else if (voice->dsp.start > max_index_nonloop){
141         voice->dsp.start = max_index_nonloop;
142     }
143
144     /* Keep the end point within the sample data */
145     if (voice->dsp.end < min_index_nonloop){
146       voice->dsp.end = min_index_nonloop;
147     } else if (voice->dsp.end > max_index_nonloop){
148       voice->dsp.end = max_index_nonloop;
149     }
150
151     /* Keep start and end point in the right order */
152     if (voice->dsp.start > voice->dsp.end){
153         int temp = voice->dsp.start;
154         voice->dsp.start = voice->dsp.end;
155         voice->dsp.end = temp;
156         /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of start / end points!"); */
157     }
158
159     /* Zero length? */
160     if (voice->dsp.start == voice->dsp.end){
161         fluid_rvoice_voiceoff(voice);
162         return;
163     }
164
165     if ((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE)
166         || (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)) {
167         /* Keep the loop start point within the sample data */
168         if (voice->dsp.loopstart < min_index_loop){
169             voice->dsp.loopstart = min_index_loop;
170       } else if (voice->dsp.loopstart > max_index_loop){
171         voice->dsp.loopstart = max_index_loop;
172       }
173
174       /* Keep the loop end point within the sample data */
175       if (voice->dsp.loopend < min_index_loop){
176         voice->dsp.loopend = min_index_loop;
177       } else if (voice->dsp.loopend > max_index_loop){
178         voice->dsp.loopend = max_index_loop;
179       }
180
181       /* Keep loop start and end point in the right order */
182       if (voice->dsp.loopstart > voice->dsp.loopend){
183         int temp = voice->dsp.loopstart;
184         voice->dsp.loopstart = voice->dsp.loopend;
185         voice->dsp.loopend = temp;
186         /*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of loop points!"); */
187       }
188
189       /* Loop too short? Then don't loop. */
190       if (voice->dsp.loopend < voice->dsp.loopstart + FLUID_MIN_LOOP_SIZE){
191           voice->dsp.samplemode = FLUID_UNLOOPED;
192       }
193
194       /* The loop points may have changed. Obtain a new estimate for the loop volume. */
195       /* Is the voice loop within the sample loop? */
196       if ((int)voice->dsp.loopstart >= (int)voice->dsp.sample->loopstart
197           && (int)voice->dsp.loopend <= (int)voice->dsp.sample->loopend){
198         /* Is there a valid peak amplitude available for the loop, and can we use it? */
199         if (voice->dsp.sample->amplitude_that_reaches_noise_floor_is_valid && voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE){
200           voice->dsp.amplitude_that_reaches_noise_floor_loop=voice->dsp.sample->amplitude_that_reaches_noise_floor / voice->dsp.synth_gain;
201         } else {
202           /* Worst case */
203           voice->dsp.amplitude_that_reaches_noise_floor_loop=voice->dsp.amplitude_that_reaches_noise_floor_nonloop;
204         };
205       };
206
207     } /* if sample mode is looped */
208
209     /* Run startup specific code (only once, when the voice is started) */
210     if (voice->dsp.check_sample_sanity_flag & FLUID_SAMPLESANITY_STARTUP){
211       if (max_index_loop - min_index_loop < FLUID_MIN_LOOP_SIZE){
212         if ((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE)
213             || (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)){
214           voice->dsp.samplemode = FLUID_UNLOOPED;
215         }
216       }
217
218       /* Set the initial phase of the voice (using the result from the
219          start offset modulators). */
220       fluid_phase_set_int(voice->dsp.phase, voice->dsp.start);
221     } /* if startup */
222
223     /* Is this voice run in loop mode, or does it run straight to the
224        end of the waveform data? */
225     if (((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE) && 
226         (fluid_adsr_env_get_section(&voice->envlfo.volenv) < FLUID_VOICE_ENVRELEASE))
227         || (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)) {
228       /* Yes, it will loop as soon as it reaches the loop point.  In
229        * this case we must prevent, that the playback pointer (phase)
230        * happens to end up beyond the 2nd loop point, because the
231        * point has moved.  The DSP algorithm is unable to cope with
232        * that situation.  So if the phase is beyond the 2nd loop
233        * point, set it to the start of the loop. No way to avoid some
234        * noise here.  Note: If the sample pointer ends up -before the
235        * first loop point- instead, then the DSP loop will just play
236        * the sample, enter the loop and proceed as expected => no
237        * actions required.
238        */
239       int index_in_sample = fluid_phase_index(voice->dsp.phase);
240       if (index_in_sample >= voice->dsp.loopend){
241         /* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Phase after 2nd loop point!"); */
242         fluid_phase_set_int(voice->dsp.phase, voice->dsp.loopstart);
243       }
244     }
245 /*    FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Sample from %i to %i, loop from %i to %i", voice->dsp.start, voice->dsp.end, voice->dsp.loopstart, voice->dsp.loopend); */
246
247     /* Sample sanity has been assured. Don't check again, until some
248        sample parameter is changed by modulation. */
249     voice->dsp.check_sample_sanity_flag=0;
250 #if 0
251     printf("Sane? playback loop from %i to %i\n", voice->dsp.loopstart, voice->dsp.loopend);
252 #endif
253     fluid_check_fpe("voice_check_sample_sanity");
254 }
255
256
257 /**
258  * Synthesize a voice to a buffer.
259  *
260  * @param voice rvoice to synthesize
261  * @param dsp_buf Audio buffer to synthesize to (#FLUID_BUFSIZE in length)
262  * @return Count of samples written to dsp_buf. (-1 means voice is currently 
263  * quiet, 0 .. #FLUID_BUFSIZE-1 means voice finished.)
264  *
265  * Panning, reverb and chorus are processed separately. The dsp interpolation
266  * routine is in (fluid_dsp_float.c).
267  */
268 int
269 fluid_rvoice_write (fluid_rvoice_t* voice, fluid_real_t *dsp_buf)
270 {
271   int ticks = voice->envlfo.ticks;
272   int count;
273
274   /******************* sample sanity check **********/
275
276   if (!voice->dsp.sample)
277     return 0;
278   if (voice->dsp.check_sample_sanity_flag)
279     fluid_rvoice_check_sample_sanity(voice);
280
281   /******************* noteoff check ****************/
282
283   if (voice->envlfo.noteoff_ticks != 0 && 
284       voice->envlfo.ticks >= voice->envlfo.noteoff_ticks) {
285     fluid_rvoice_noteoff(voice, 0);
286   }
287
288   voice->envlfo.ticks += FLUID_BUFSIZE;
289
290   /******************* vol env **********************/
291
292   fluid_adsr_env_calc(&voice->envlfo.volenv, 1);
293   fluid_check_fpe ("voice_write vol env");
294   if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVFINISHED)
295     return 0;
296
297   /******************* mod env **********************/
298
299   fluid_adsr_env_calc(&voice->envlfo.modenv, 0);
300   fluid_check_fpe ("voice_write mod env");
301
302   /******************* lfo **********************/
303
304   fluid_lfo_calc(&voice->envlfo.modlfo, ticks);
305   fluid_check_fpe ("voice_write mod LFO");
306   fluid_lfo_calc(&voice->envlfo.viblfo, ticks);
307   fluid_check_fpe ("voice_write vib LFO");
308
309   /******************* amplitude **********************/
310
311   count = fluid_rvoice_calc_amp(voice);
312   if (count <= 0) 
313     return count;
314
315   /******************* phase **********************/
316
317   /* Calculate the number of samples, that the DSP loop advances
318    * through the original waveform with each step in the output
319    * buffer. It is the ratio between the frequencies of original
320    * waveform and output waveform.*/
321   voice->dsp.phase_incr = fluid_ct2hz_real(voice->dsp.pitch + 
322      fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_pitch
323      + fluid_lfo_get_val(&voice->envlfo.viblfo) * voice->envlfo.viblfo_to_pitch
324      + fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_pitch) 
325      / voice->dsp.root_pitch_hz;
326
327   fluid_check_fpe ("voice_write phase calculation");
328
329   /* if phase_incr is not advancing, set it to the minimum fraction value (prevent stuckage) */
330   if (voice->dsp.phase_incr == 0) voice->dsp.phase_incr = 1;
331
332   voice->dsp.is_looping = voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE
333     || (voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE
334         && fluid_adsr_env_get_section(&voice->envlfo.volenv) < FLUID_VOICE_ENVRELEASE);
335
336   /*********************** run the dsp chain ************************
337    * The sample is mixed with the output buffer.
338    * The buffer has to be filled from 0 to FLUID_BUFSIZE-1.
339    * Depending on the position in the loop and the loop size, this
340    * may require several runs. */
341   voice->dsp.dsp_buf = dsp_buf; 
342
343   switch (voice->dsp.interp_method)
344   {
345     case FLUID_INTERP_NONE:
346       count = fluid_rvoice_dsp_interpolate_none (&voice->dsp);
347       break;
348     case FLUID_INTERP_LINEAR:
349       count = fluid_rvoice_dsp_interpolate_linear (&voice->dsp);
350       break;
351     case FLUID_INTERP_4THORDER:
352     default:
353       count = fluid_rvoice_dsp_interpolate_4th_order (&voice->dsp);
354       break;
355     case FLUID_INTERP_7THORDER:
356       count = fluid_rvoice_dsp_interpolate_7th_order (&voice->dsp);
357       break;
358   }
359   fluid_check_fpe ("voice_write interpolation");
360   if (count == 0)
361     return count;
362
363   /*************** resonant filter ******************/
364   fluid_iir_filter_calc(&voice->resonant_filter, voice->dsp.output_rate,
365                         fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_fc +
366                         fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_fc);
367
368   fluid_iir_filter_apply(&voice->resonant_filter, dsp_buf, count);
369
370   return count;
371 }
372
373
374 static inline fluid_real_t* 
375 get_dest_buf(fluid_rvoice_buffers_t* buffers, int index,
376              fluid_real_t** dest_bufs, int dest_bufcount)
377 {
378   int j = buffers->bufs[index].mapping;
379   if (j >= dest_bufcount || j < 0) return NULL;
380   return dest_bufs[j];
381 }
382
383 /**
384  * Mix data down to buffers
385  *
386  * @param buffers Destination buffer(s)
387  * @param dsp_buf Mono sample source
388  * @param samplecount Number of samples to process (no FLUID_BUFSIZE restriction)
389  * @param dest_bufs Array of buffers to mixdown to
390  * @param dest_bufcount Length of dest_bufs
391  */
392 void 
393 fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t* buffers, 
394                          fluid_real_t* dsp_buf, int samplecount, 
395                          fluid_real_t** dest_bufs, int dest_bufcount)
396 {
397   int bufcount = buffers->count;
398   int i, dsp_i;
399   if (!samplecount || !bufcount || !dest_bufcount) 
400     return;
401
402   for (i=0; i < bufcount; i++) {
403     fluid_real_t* buf = get_dest_buf(buffers, i, dest_bufs, dest_bufcount);
404     fluid_real_t* next_buf;
405     fluid_real_t amp = buffers->bufs[i].amp;
406     if (buf == NULL || amp == 0.0f)
407       continue;
408
409     /* Optimization for centered stereo samples - we can save one 
410        multiplication per sample */
411     next_buf = (i+1 >= bufcount ? NULL : get_dest_buf(buffers, i+1, dest_bufs, dest_bufcount));
412     if (next_buf && buffers->bufs[i+1].amp == amp) {
413       for (dsp_i = 0; dsp_i < samplecount; dsp_i++) {
414         fluid_real_t samp = amp * dsp_buf[dsp_i]; 
415         buf[dsp_i] += samp;
416         next_buf[dsp_i] += samp;
417       }
418       i++;
419     }
420     else {
421       for (dsp_i = 0; dsp_i < samplecount; dsp_i++)
422         buf[dsp_i] += amp * dsp_buf[dsp_i];
423     }
424   }
425 }
426
427 /**
428  * Initialize buffers up to (and including) bufnum
429  */
430 static int
431 fluid_rvoice_buffers_check_bufnum(fluid_rvoice_buffers_t* buffers, unsigned int bufnum)
432 {
433   unsigned int i; 
434
435   if (bufnum < buffers->count) return FLUID_OK;
436   if (bufnum >= FLUID_RVOICE_MAX_BUFS) return FLUID_FAILED;
437
438   for (i = buffers->count; i <= bufnum; i++) {
439     buffers->bufs[bufnum].amp = 0.0f;  
440     buffers->bufs[bufnum].mapping = i;  
441   }
442   buffers->count = bufnum+1;
443   return FLUID_OK;
444 }
445
446
447 void 
448 fluid_rvoice_buffers_set_amp(fluid_rvoice_buffers_t* buffers, 
449                              unsigned int bufnum, fluid_real_t value)
450 {
451   if (fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK)
452     return;
453   buffers->bufs[bufnum].amp = value;
454 }
455
456 void 
457 fluid_rvoice_buffers_set_mapping(fluid_rvoice_buffers_t* buffers, 
458                                  unsigned int bufnum, int mapping)
459 {
460   if (fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK)
461     return;
462   buffers->bufs[bufnum].mapping = mapping;
463 }
464
465
466 void
467 fluid_rvoice_reset(fluid_rvoice_t* voice)
468 {
469   voice->dsp.has_looped = 0;
470   voice->envlfo.ticks = 0;
471   voice->envlfo.noteoff_ticks = 0;
472   voice->dsp.amp = 0.0f; /* The last value of the volume envelope, used to
473                             calculate the volume increment during
474                             processing */
475
476   /* mod env initialization*/
477   fluid_adsr_env_reset(&voice->envlfo.modenv);
478
479   /* vol env initialization */
480   fluid_adsr_env_reset(&voice->envlfo.volenv);
481
482   /* Fixme: Retrieve from any other existing
483      voice on this channel to keep LFOs in
484      unison? */
485   fluid_lfo_reset(&voice->envlfo.viblfo);
486   fluid_lfo_reset(&voice->envlfo.modlfo);
487
488   /* Clear sample history in filter */
489   fluid_iir_filter_reset(&voice->resonant_filter);
490
491   /* Force setting of the phase at the first DSP loop run
492    * This cannot be done earlier, because it depends on modulators. 
493      [DH] Is that comment really true? */
494   voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP;
495 }
496
497
498 void 
499 fluid_rvoice_noteoff(fluid_rvoice_t* voice, unsigned int min_ticks)
500 {
501   if (min_ticks > voice->envlfo.ticks) {
502     /* Delay noteoff */
503     voice->envlfo.noteoff_ticks = min_ticks;
504     return;
505   }
506   voice->envlfo.noteoff_ticks = 0;
507
508   if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVATTACK) {
509     /* A voice is turned off during the attack section of the volume
510      * envelope.  The attack section ramps up linearly with
511      * amplitude. The other sections use logarithmic scaling. Calculate new
512      * volenv_val to achieve equievalent amplitude during the release phase
513      * for seamless volume transition.
514      */
515     if (fluid_adsr_env_get_val(&voice->envlfo.volenv) > 0){
516       fluid_real_t lfo = fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol;
517       fluid_real_t amp = fluid_adsr_env_get_val(&voice->envlfo.volenv) * pow (10.0, lfo / -200);
518       fluid_real_t env_value = - ((-200 * log (amp) / log (10.0) - lfo) / 960.0 - 1);
519       fluid_clip (env_value, 0.0, 1.0);
520       fluid_adsr_env_set_val(&voice->envlfo.volenv, env_value);
521     }
522   }
523   fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVRELEASE);
524   fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVRELEASE);
525 }
526
527
528 void 
529 fluid_rvoice_set_output_rate(fluid_rvoice_t* voice, fluid_real_t value)
530 {
531   voice->dsp.output_rate = value;
532 }
533
534 void 
535 fluid_rvoice_set_interp_method(fluid_rvoice_t* voice, int value)
536 {
537   voice->dsp.interp_method = value;
538 }
539
540 void 
541 fluid_rvoice_set_root_pitch_hz(fluid_rvoice_t* voice, fluid_real_t value)
542 {
543   voice->dsp.root_pitch_hz = value;
544 }
545
546 void 
547 fluid_rvoice_set_pitch(fluid_rvoice_t* voice, fluid_real_t value)
548 {
549   voice->dsp.pitch = value;
550 }
551
552
553 void 
554 fluid_rvoice_set_attenuation(fluid_rvoice_t* voice, fluid_real_t value)
555 {
556   voice->dsp.attenuation = value;
557 }
558
559 void 
560 fluid_rvoice_set_min_attenuation_cB(fluid_rvoice_t* voice, fluid_real_t value)
561 {
562   voice->dsp.min_attenuation_cB = value;
563 }
564
565 void 
566 fluid_rvoice_set_viblfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value)
567 {
568   voice->envlfo.viblfo_to_pitch = value;
569 }
570
571 void fluid_rvoice_set_modlfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value)
572 {
573   voice->envlfo.modlfo_to_pitch = value;
574 }
575
576 void 
577 fluid_rvoice_set_modlfo_to_vol(fluid_rvoice_t* voice, fluid_real_t value)
578 {
579   voice->envlfo.modlfo_to_vol = value;
580 }
581
582 void 
583 fluid_rvoice_set_modlfo_to_fc(fluid_rvoice_t* voice, fluid_real_t value)
584 {
585   voice->envlfo.modlfo_to_fc = value;
586 }
587
588 void 
589 fluid_rvoice_set_modenv_to_fc(fluid_rvoice_t* voice, fluid_real_t value)
590 {
591   voice->envlfo.modenv_to_fc = value;
592 }
593
594 void 
595 fluid_rvoice_set_modenv_to_pitch(fluid_rvoice_t* voice, fluid_real_t value)
596 {
597   voice->envlfo.modenv_to_pitch = value;
598 }
599
600 void 
601 fluid_rvoice_set_synth_gain(fluid_rvoice_t* voice, fluid_real_t value)
602 {
603   voice->dsp.synth_gain = value;
604
605   /* For a looped sample, this value will be overwritten as soon as the
606    * loop parameters are initialized (they may depend on modulators).
607    * This value can be kept, it is a worst-case estimate.
608    */
609   voice->dsp.amplitude_that_reaches_noise_floor_nonloop = FLUID_NOISE_FLOOR / value;
610   voice->dsp.amplitude_that_reaches_noise_floor_loop = FLUID_NOISE_FLOOR / value;
611   voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
612 }
613
614 void 
615 fluid_rvoice_set_start(fluid_rvoice_t* voice, int value)
616 {
617   voice->dsp.start = value;
618   voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
619 }
620
621 void 
622 fluid_rvoice_set_end(fluid_rvoice_t* voice, int value)
623 {
624   voice->dsp.end = value;
625   voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
626 }
627
628 void 
629 fluid_rvoice_set_loopstart(fluid_rvoice_t* voice, int value)
630 {
631   voice->dsp.loopstart = value;
632   voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
633 }
634
635 void fluid_rvoice_set_loopend(fluid_rvoice_t* voice, int value)
636 {
637   voice->dsp.loopend = value;
638   voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
639 }
640
641 void fluid_rvoice_set_samplemode(fluid_rvoice_t* voice, enum fluid_loop value)
642 {
643   voice->dsp.samplemode = value;
644   voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
645 }
646
647
648 void 
649 fluid_rvoice_set_sample(fluid_rvoice_t* voice, fluid_sample_t* value)
650 {
651   voice->dsp.sample = value;
652   if (value) {
653     voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP;
654   }
655 }
656
657 void 
658 fluid_rvoice_voiceoff(fluid_rvoice_t* voice)
659 {
660   fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVFINISHED);
661   fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVFINISHED);
662 }
663
664