1 /* FluidSynth - A Software Synthesizer
3 * Copyright (C) 2003 Peter Hanappe and others.
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.
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.
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
21 #include "fluid_rvoice.h"
22 #include "fluid_conv.h"
23 #include "fluid_sys.h"
26 * @return -1 if voice has finished, 0 if it's currently quiet, 1 otherwise
29 fluid_rvoice_calc_amp(fluid_rvoice_t* voice)
31 fluid_real_t target_amp; /* target amplitude */
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. */
36 if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVATTACK)
38 /* the envelope is in the attack section: ramp linearly to max value.
39 * A positive modlfo_to_vol should increase volume (negative attenuation).
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);
47 fluid_real_t amplitude_that_reaches_noise_floor;
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);
54 /* We turn off a voice, if the volume has dropped low enough. */
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.
61 /* If the loop amplitude is known, we can use it if the voice loop is within
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;
69 amplitude_that_reaches_noise_floor = voice->dsp.amplitude_that_reaches_noise_floor_nonloop;
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):
77 amp_max = fluid_atten2amp (voice->dsp.min_attenuation_cB) *
78 fluid_adsr_env_get_val(&voice->envlfo.volenv);
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)
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;
92 fluid_check_fpe ("voice_write amplitude calculation");
94 /* no volume and not changing? - No need to process */
95 if ((voice->dsp.amp == 0.0f) && (voice->dsp.amp_incr == 0.0f))
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
106 #define FLUID_SAMPLESANITY_CHECK (1 << 0)
107 #define FLUID_SAMPLESANITY_STARTUP (1 << 1)
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.
116 fluid_rvoice_check_sample_sanity(fluid_rvoice_t* voice)
118 int min_index_nonloop=(int) voice->dsp.sample->start;
119 int max_index_nonloop=(int) voice->dsp.sample->end;
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");
126 if (!voice->dsp.check_sample_sanity_flag){
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);
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;
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;
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!"); */
160 if (voice->dsp.start == voice->dsp.end){
161 fluid_rvoice_voiceoff(voice);
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;
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;
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!"); */
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;
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;
203 voice->dsp.amplitude_that_reaches_noise_floor_loop=voice->dsp.amplitude_that_reaches_noise_floor_nonloop;
207 } /* if sample mode is looped */
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;
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);
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
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);
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); */
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;
251 printf("Sane? playback loop from %i to %i\n", voice->dsp.loopstart, voice->dsp.loopend);
253 fluid_check_fpe("voice_check_sample_sanity");
258 * Synthesize a voice to a buffer.
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.)
265 * Panning, reverb and chorus are processed separately. The dsp interpolation
266 * routine is in (fluid_dsp_float.c).
269 fluid_rvoice_write (fluid_rvoice_t* voice, fluid_real_t *dsp_buf)
271 int ticks = voice->envlfo.ticks;
274 /******************* sample sanity check **********/
276 if (!voice->dsp.sample)
278 if (voice->dsp.check_sample_sanity_flag)
279 fluid_rvoice_check_sample_sanity(voice);
281 /******************* noteoff check ****************/
283 if (voice->envlfo.noteoff_ticks != 0 &&
284 voice->envlfo.ticks >= voice->envlfo.noteoff_ticks) {
285 fluid_rvoice_noteoff(voice, 0);
288 voice->envlfo.ticks += FLUID_BUFSIZE;
290 /******************* vol env **********************/
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)
297 /******************* mod env **********************/
299 fluid_adsr_env_calc(&voice->envlfo.modenv, 0);
300 fluid_check_fpe ("voice_write mod env");
302 /******************* lfo **********************/
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");
309 /******************* amplitude **********************/
311 count = fluid_rvoice_calc_amp(voice);
315 /******************* phase **********************/
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;
327 fluid_check_fpe ("voice_write phase calculation");
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;
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);
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;
343 switch (voice->dsp.interp_method)
345 case FLUID_INTERP_NONE:
346 count = fluid_rvoice_dsp_interpolate_none (&voice->dsp);
348 case FLUID_INTERP_LINEAR:
349 count = fluid_rvoice_dsp_interpolate_linear (&voice->dsp);
351 case FLUID_INTERP_4THORDER:
353 count = fluid_rvoice_dsp_interpolate_4th_order (&voice->dsp);
355 case FLUID_INTERP_7THORDER:
356 count = fluid_rvoice_dsp_interpolate_7th_order (&voice->dsp);
359 fluid_check_fpe ("voice_write interpolation");
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);
368 fluid_iir_filter_apply(&voice->resonant_filter, dsp_buf, count);
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)
378 int j = buffers->bufs[index].mapping;
379 if (j >= dest_bufcount || j < 0) return NULL;
384 * Mix data down to buffers
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
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)
397 int bufcount = buffers->count;
399 if (!samplecount || !bufcount || !dest_bufcount)
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)
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];
416 next_buf[dsp_i] += samp;
421 for (dsp_i = 0; dsp_i < samplecount; dsp_i++)
422 buf[dsp_i] += amp * dsp_buf[dsp_i];
428 * Initialize buffers up to (and including) bufnum
431 fluid_rvoice_buffers_check_bufnum(fluid_rvoice_buffers_t* buffers, unsigned int bufnum)
435 if (bufnum < buffers->count) return FLUID_OK;
436 if (bufnum >= FLUID_RVOICE_MAX_BUFS) return FLUID_FAILED;
438 for (i = buffers->count; i <= bufnum; i++) {
439 buffers->bufs[bufnum].amp = 0.0f;
440 buffers->bufs[bufnum].mapping = i;
442 buffers->count = bufnum+1;
448 fluid_rvoice_buffers_set_amp(fluid_rvoice_buffers_t* buffers,
449 unsigned int bufnum, fluid_real_t value)
451 if (fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK)
453 buffers->bufs[bufnum].amp = value;
457 fluid_rvoice_buffers_set_mapping(fluid_rvoice_buffers_t* buffers,
458 unsigned int bufnum, int mapping)
460 if (fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK)
462 buffers->bufs[bufnum].mapping = mapping;
467 fluid_rvoice_reset(fluid_rvoice_t* voice)
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
476 /* mod env initialization*/
477 fluid_adsr_env_reset(&voice->envlfo.modenv);
479 /* vol env initialization */
480 fluid_adsr_env_reset(&voice->envlfo.volenv);
482 /* Fixme: Retrieve from any other existing
483 voice on this channel to keep LFOs in
485 fluid_lfo_reset(&voice->envlfo.viblfo);
486 fluid_lfo_reset(&voice->envlfo.modlfo);
488 /* Clear sample history in filter */
489 fluid_iir_filter_reset(&voice->resonant_filter);
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;
499 fluid_rvoice_noteoff(fluid_rvoice_t* voice, unsigned int min_ticks)
501 if (min_ticks > voice->envlfo.ticks) {
503 voice->envlfo.noteoff_ticks = min_ticks;
506 voice->envlfo.noteoff_ticks = 0;
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.
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);
523 fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVRELEASE);
524 fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVRELEASE);
529 fluid_rvoice_set_output_rate(fluid_rvoice_t* voice, fluid_real_t value)
531 voice->dsp.output_rate = value;
535 fluid_rvoice_set_interp_method(fluid_rvoice_t* voice, int value)
537 voice->dsp.interp_method = value;
541 fluid_rvoice_set_root_pitch_hz(fluid_rvoice_t* voice, fluid_real_t value)
543 voice->dsp.root_pitch_hz = value;
547 fluid_rvoice_set_pitch(fluid_rvoice_t* voice, fluid_real_t value)
549 voice->dsp.pitch = value;
554 fluid_rvoice_set_attenuation(fluid_rvoice_t* voice, fluid_real_t value)
556 voice->dsp.attenuation = value;
560 fluid_rvoice_set_min_attenuation_cB(fluid_rvoice_t* voice, fluid_real_t value)
562 voice->dsp.min_attenuation_cB = value;
566 fluid_rvoice_set_viblfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value)
568 voice->envlfo.viblfo_to_pitch = value;
571 void fluid_rvoice_set_modlfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value)
573 voice->envlfo.modlfo_to_pitch = value;
577 fluid_rvoice_set_modlfo_to_vol(fluid_rvoice_t* voice, fluid_real_t value)
579 voice->envlfo.modlfo_to_vol = value;
583 fluid_rvoice_set_modlfo_to_fc(fluid_rvoice_t* voice, fluid_real_t value)
585 voice->envlfo.modlfo_to_fc = value;
589 fluid_rvoice_set_modenv_to_fc(fluid_rvoice_t* voice, fluid_real_t value)
591 voice->envlfo.modenv_to_fc = value;
595 fluid_rvoice_set_modenv_to_pitch(fluid_rvoice_t* voice, fluid_real_t value)
597 voice->envlfo.modenv_to_pitch = value;
601 fluid_rvoice_set_synth_gain(fluid_rvoice_t* voice, fluid_real_t value)
603 voice->dsp.synth_gain = value;
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.
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;
615 fluid_rvoice_set_start(fluid_rvoice_t* voice, int value)
617 voice->dsp.start = value;
618 voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
622 fluid_rvoice_set_end(fluid_rvoice_t* voice, int value)
624 voice->dsp.end = value;
625 voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
629 fluid_rvoice_set_loopstart(fluid_rvoice_t* voice, int value)
631 voice->dsp.loopstart = value;
632 voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
635 void fluid_rvoice_set_loopend(fluid_rvoice_t* voice, int value)
637 voice->dsp.loopend = value;
638 voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
641 void fluid_rvoice_set_samplemode(fluid_rvoice_t* voice, enum fluid_loop value)
643 voice->dsp.samplemode = value;
644 voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
649 fluid_rvoice_set_sample(fluid_rvoice_t* voice, fluid_sample_t* value)
651 voice->dsp.sample = value;
653 voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP;
658 fluid_rvoice_voiceoff(fluid_rvoice_t* voice)
660 fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVFINISHED);
661 fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVFINISHED);