Path: blob/master/libs/fluidsynth/src/rvoice/fluid_rvoice.c
4396 views
/* FluidSynth - A Software Synthesizer1*2* Copyright (C) 2003 Peter Hanappe and others.3*4* This library is free software; you can redistribute it and/or5* modify it under the terms of the GNU Lesser General Public License6* as published by the Free Software Foundation; either version 2.1 of7* the License, or (at your option) any later version.8*9* This library is distributed in the hope that it will be useful, but10* WITHOUT ANY WARRANTY; without even the implied warranty of11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU12* Lesser General Public License for more details.13*14* You should have received a copy of the GNU Lesser General Public15* License along with this library; if not, write to the Free16* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA17* 02110-1301, USA18*/1920#include "fluid_rvoice.h"21#include "fluid_conv.h"22#include "fluid_sys.h"232425static void fluid_rvoice_noteoff_LOCAL(fluid_rvoice_t *voice, unsigned int min_ticks);2627/**28* @return -1 if voice is quiet, 0 if voice has finished, 1 otherwise29*/30static FLUID_INLINE int31fluid_rvoice_calc_amp(fluid_rvoice_t *voice)32{33fluid_real_t target_amp; /* target amplitude */3435if(fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVDELAY)36{37return -1; /* The volume amplitude is in hold phase. No sound is produced. */38}3940if(fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVATTACK)41{42/* the envelope is in the attack section: ramp linearly to max value.43* A positive modlfo_to_vol should increase volume (negative attenuation).44*/45target_amp = fluid_cb2amp(voice->dsp.attenuation)46* fluid_cb2amp(fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol)47* fluid_adsr_env_get_val(&voice->envlfo.volenv);48}49else50{51fluid_real_t amplitude_that_reaches_noise_floor;52fluid_real_t amp_max;5354target_amp = fluid_cb2amp(voice->dsp.attenuation)55* fluid_cb2amp(FLUID_PEAK_ATTENUATION * (1.0f - fluid_adsr_env_get_val(&voice->envlfo.volenv))56+ fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol);5758/* We turn off a voice, if the volume has dropped low enough. */5960/* A voice can be turned off, when an estimate for the volume61* (upper bound) falls below that volume, that will drop the62* sample below the noise floor.63*/6465/* If the loop amplitude is known, we can use it if the voice loop is within66* the sample loop67*/6869/* Is the playing pointer already in the loop? */70if(voice->dsp.has_looped)71{72amplitude_that_reaches_noise_floor = voice->dsp.amplitude_that_reaches_noise_floor_loop;73}74else75{76amplitude_that_reaches_noise_floor = voice->dsp.amplitude_that_reaches_noise_floor_nonloop;77}7879/* voice->attenuation_min is a lower boundary for the attenuation80* now and in the future (possibly 0 in the worst case). Now the81* amplitude of sample and volenv cannot exceed amp_max (since82* volenv_val can only drop):83*/8485amp_max = fluid_cb2amp(voice->dsp.min_attenuation_cB) *86fluid_adsr_env_get_val(&voice->envlfo.volenv);8788/* And if amp_max is already smaller than the known amplitude,89* which will attenuate the sample below the noise floor, then we90* can safely turn off the voice. Duh. */91if(amp_max < amplitude_that_reaches_noise_floor)92{93return 0;94}95}9697/* Volume increment to go from voice->amp to target_amp in FLUID_BUFSIZE steps */98voice->dsp.amp_incr = (target_amp - voice->dsp.amp) / FLUID_BUFSIZE;99100fluid_check_fpe("voice_write amplitude calculation");101102/* no volume and not changing? - No need to process */103if((voice->dsp.amp == 0.0f) && (voice->dsp.amp_incr == 0.0f))104{105return -1;106}107108return 1;109}110111112/* these should be the absolute minimum that FluidSynth can deal with */113#define FLUID_MIN_LOOP_SIZE 2114#define FLUID_MIN_LOOP_PAD 0115116#define FLUID_SAMPLESANITY_CHECK (1 << 0)117#define FLUID_SAMPLESANITY_STARTUP (1 << 1)118119/* Purpose:120*121* Make sure, that sample start / end point and loop points are in122* proper order. When starting up, calculate the initial phase.123* TODO: Investigate whether this can be moved from rvoice to voice.124*/125static void126fluid_rvoice_check_sample_sanity(fluid_rvoice_t *voice)127{128int min_index_nonloop = (int) voice->dsp.sample->start;129int max_index_nonloop = (int) voice->dsp.sample->end;130131/* make sure we have enough samples surrounding the loop */132int min_index_loop = (int) voice->dsp.sample->start + FLUID_MIN_LOOP_PAD;133int max_index_loop = (int) voice->dsp.sample->end - FLUID_MIN_LOOP_PAD + 1; /* 'end' is last valid sample, loopend can be + 1 */134fluid_check_fpe("voice_check_sample_sanity start");135136#if 0137printf("Sample from %i to %i\n", voice->dsp.sample->start, voice->dsp.sample->end);138printf("Sample loop from %i %i\n", voice->dsp.sample->loopstart, voice->dsp.sample->loopend);139printf("Playback from %i to %i\n", voice->dsp.start, voice->dsp.end);140printf("Playback loop from %i to %i\n", voice->dsp.loopstart, voice->dsp.loopend);141#endif142143/* Keep the start point within the sample data */144if(voice->dsp.start < min_index_nonloop)145{146voice->dsp.start = min_index_nonloop;147}148else if(voice->dsp.start > max_index_nonloop)149{150voice->dsp.start = max_index_nonloop;151}152153/* Keep the end point within the sample data */154if(voice->dsp.end < min_index_nonloop)155{156voice->dsp.end = min_index_nonloop;157}158else if(voice->dsp.end > max_index_nonloop)159{160voice->dsp.end = max_index_nonloop;161}162163/* Keep start and end point in the right order */164if(voice->dsp.start > voice->dsp.end)165{166int temp = voice->dsp.start;167voice->dsp.start = voice->dsp.end;168voice->dsp.end = temp;169/*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of start / end points!"); */170}171172/* Zero length? */173if(voice->dsp.start == voice->dsp.end)174{175fluid_rvoice_voiceoff(voice, NULL);176return;177}178179if((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE)180|| (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE))181{182/* Keep the loop start point within the sample data */183if(voice->dsp.loopstart < min_index_loop)184{185voice->dsp.loopstart = min_index_loop;186}187else if(voice->dsp.loopstart > max_index_loop)188{189voice->dsp.loopstart = max_index_loop;190}191192/* Keep the loop end point within the sample data */193if(voice->dsp.loopend < min_index_loop)194{195voice->dsp.loopend = min_index_loop;196}197else if(voice->dsp.loopend > max_index_loop)198{199voice->dsp.loopend = max_index_loop;200}201202/* Keep loop start and end point in the right order */203if(voice->dsp.loopstart > voice->dsp.loopend)204{205int temp = voice->dsp.loopstart;206voice->dsp.loopstart = voice->dsp.loopend;207voice->dsp.loopend = temp;208/*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of loop points!"); */209}210211/* Loop too short? Then don't loop. */212if(voice->dsp.loopend < voice->dsp.loopstart + FLUID_MIN_LOOP_SIZE)213{214voice->dsp.samplemode = FLUID_UNLOOPED;215}216217/* The loop points may have changed. Obtain a new estimate for the loop volume. */218/* Is the voice loop within the sample loop? */219if((int)voice->dsp.loopstart >= (int)voice->dsp.sample->loopstart220&& (int)voice->dsp.loopend <= (int)voice->dsp.sample->loopend)221{222/* Is there a valid peak amplitude available for the loop, and can we use it? */223if(voice->dsp.sample->amplitude_that_reaches_noise_floor_is_valid && voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)224{225voice->dsp.amplitude_that_reaches_noise_floor_loop = voice->dsp.sample->amplitude_that_reaches_noise_floor / voice->dsp.synth_gain;226}227else228{229/* Worst case */230voice->dsp.amplitude_that_reaches_noise_floor_loop = voice->dsp.amplitude_that_reaches_noise_floor_nonloop;231};232};233234} /* if sample mode is looped */235236/* Run startup specific code (only once, when the voice is started) */237if(voice->dsp.check_sample_sanity_flag & FLUID_SAMPLESANITY_STARTUP)238{239if(max_index_loop - min_index_loop < FLUID_MIN_LOOP_SIZE)240{241if((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE)242|| (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE))243{244voice->dsp.samplemode = FLUID_UNLOOPED;245}246}247248/* Set the initial phase of the voice (using the result from the249start offset modulators). */250fluid_phase_set_int(voice->dsp.phase, voice->dsp.start);251} /* if startup */252253/* Is this voice run in loop mode, or does it run straight to the254end of the waveform data? */255if(((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE) &&256(fluid_adsr_env_get_section(&voice->envlfo.volenv) < FLUID_VOICE_ENVRELEASE))257|| (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE))258{259/* Yes, it will loop as soon as it reaches the loop point. In260* this case we must prevent, that the playback pointer (phase)261* happens to end up beyond the 2nd loop point, because the262* point has moved. The DSP algorithm is unable to cope with263* that situation. So if the phase is beyond the 2nd loop264* point, set it to the start of the loop. No way to avoid some265* noise here. Note: If the sample pointer ends up -before the266* first loop point- instead, then the DSP loop will just play267* the sample, enter the loop and proceed as expected => no268* actions required.269*/270int index_in_sample = fluid_phase_index(voice->dsp.phase);271272if(index_in_sample >= voice->dsp.loopend)273{274/* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Phase after 2nd loop point!"); */275fluid_phase_set_int(voice->dsp.phase, voice->dsp.loopstart);276}277}278279/* 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); */280281/* Sample sanity has been assured. Don't check again, until some282sample parameter is changed by modulation. */283voice->dsp.check_sample_sanity_flag = 0;284#if 0285printf("Sane? playback loop from %i to %i\n", voice->dsp.loopstart, voice->dsp.loopend);286#endif287fluid_check_fpe("voice_check_sample_sanity");288}289290291/**292* Synthesize a voice to a buffer.293*294* @param voice rvoice to synthesize295* @param dsp_buf Audio buffer to synthesize to (#FLUID_BUFSIZE in length)296* @return Count of samples written to dsp_buf. (-1 means voice is currently297* quiet, 0 .. #FLUID_BUFSIZE-1 means voice finished.)298*299* Panning, reverb and chorus are processed separately. The dsp interpolation300* routine is in (fluid_rvoice_dsp.c).301*/302int303fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf)304{305int ticks = voice->envlfo.ticks;306int count, is_looping;307fluid_real_t modenv_val;308309/******************* sample sanity check **********/310311if(!voice->dsp.sample)312{313return 0;314}315316if(voice->dsp.check_sample_sanity_flag)317{318fluid_rvoice_check_sample_sanity(voice);319}320321/******************* noteoff check ****************/322323if(voice->envlfo.noteoff_ticks != 0 &&324voice->envlfo.ticks >= voice->envlfo.noteoff_ticks)325{326fluid_rvoice_noteoff_LOCAL(voice, 0);327}328329voice->envlfo.ticks += FLUID_BUFSIZE;330331/******************* vol env **********************/332333fluid_adsr_env_calc(&voice->envlfo.volenv);334fluid_check_fpe("voice_write vol env");335336if(fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVFINISHED)337{338return 0;339}340341/******************* mod env **********************/342343fluid_adsr_env_calc(&voice->envlfo.modenv);344fluid_check_fpe("voice_write mod env");345346/******************* lfo **********************/347348fluid_lfo_calc(&voice->envlfo.modlfo, ticks);349fluid_check_fpe("voice_write mod LFO");350fluid_lfo_calc(&voice->envlfo.viblfo, ticks);351fluid_check_fpe("voice_write vib LFO");352353/******************* amplitude **********************/354355count = fluid_rvoice_calc_amp(voice);356if(count == 0)357{358// Voice has finished, remove from dsp loop359return 0;360}361// else if count is negative, still process the voice362363364/******************* phase **********************/365366/* SF2.04 section 8.1.2 #26:367* attack of modEnv is convex ?!?368*/369modenv_val = (fluid_adsr_env_get_section(&voice->envlfo.modenv) == FLUID_VOICE_ENVATTACK)370? fluid_convex(127 * fluid_adsr_env_get_val(&voice->envlfo.modenv))371: fluid_adsr_env_get_val(&voice->envlfo.modenv);372/* Calculate the number of samples, that the DSP loop advances373* through the original waveform with each step in the output374* buffer. It is the ratio between the frequencies of original375* waveform and output waveform.*/376voice->dsp.phase_incr = fluid_ct2hz_real(voice->dsp.pitch +377voice->dsp.pitchoffset +378fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_pitch379+ fluid_lfo_get_val(&voice->envlfo.viblfo) * voice->envlfo.viblfo_to_pitch380+ modenv_val * voice->envlfo.modenv_to_pitch)381/ voice->dsp.root_pitch_hz;382383/******************* portamento ****************/384/* pitchoffset is updated if enabled.385Pitchoffset will be added to dsp pitch at next phase calculation time */386387/* In most cases portamento will be disabled. Thus first verify that portamento is388* enabled before updating pitchoffset and before disabling portamento when necessary,389* in order to keep the performance loss at minimum.390* If the algorithm would first update pitchoffset and then verify if portamento391* needs to be disabled, there would be a significant performance drop on a x87 FPU392*/393if(voice->dsp.pitchinc > 0.0f)394{395/* portamento is enabled, so update pitchoffset */396voice->dsp.pitchoffset += voice->dsp.pitchinc;397398/* when pitchoffset reaches 0.0f, portamento is disabled */399if(voice->dsp.pitchoffset > 0.0f)400{401voice->dsp.pitchoffset = voice->dsp.pitchinc = 0.0f;402}403}404else if(voice->dsp.pitchinc < 0.0f)405{406/* portamento is enabled, so update pitchoffset */407voice->dsp.pitchoffset += voice->dsp.pitchinc;408409/* when pitchoffset reaches 0.0f, portamento is disabled */410if(voice->dsp.pitchoffset < 0.0f)411{412voice->dsp.pitchoffset = voice->dsp.pitchinc = 0.0f;413}414}415416fluid_check_fpe("voice_write phase calculation");417418/* if phase_incr is not advancing, set it to the minimum fraction value (prevent stuckage) */419if(voice->dsp.phase_incr == 0)420{421voice->dsp.phase_incr = 1;422}423424/* loop mode release? if not in release, the voice is silent425* note: this intentionally processes the volenv before returning silence,426* since that's what polyphone does (PR #1400) */427if(voice->dsp.samplemode == FLUID_START_ON_RELEASE && fluid_adsr_env_get_section(&voice->envlfo.volenv) < FLUID_VOICE_ENVRELEASE)428{429return -1;430}431432/* voice is currently looping? */433is_looping = voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE434|| (voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE435&& fluid_adsr_env_get_section(&voice->envlfo.volenv) < FLUID_VOICE_ENVRELEASE);436437/*********************** run the dsp chain ************************438* The sample is mixed with the output buffer.439* The buffer has to be filled from 0 to FLUID_BUFSIZE-1.440* Depending on the position in the loop and the loop size, this441* may require several runs. */442443if(count < 0)444{445// The voice is quite, i.e. either in delay phase or zero volume.446// We need to update the rvoice's dsp phase, as the delay phase shall not "postpone" the sound, rather447// it should be played silently, see https://github.com/FluidSynth/fluidsynth/issues/1312448//449// Currently, this does access the sample buffers, which is redundant and could be optimized away.450// On the other hand, entering this if-clause is not supposed to happen often.451//452// Also note, that we're returning directly without running the IIR filter below.453return fluid_rvoice_dsp_interpolate_none(&voice->dsp, dsp_buf, is_looping);454}455456switch(voice->dsp.interp_method)457{458case FLUID_INTERP_NONE:459count = fluid_rvoice_dsp_interpolate_none(&voice->dsp, dsp_buf, is_looping);460break;461462case FLUID_INTERP_LINEAR:463count = fluid_rvoice_dsp_interpolate_linear(&voice->dsp, dsp_buf, is_looping);464break;465466case FLUID_INTERP_4THORDER:467default:468count = fluid_rvoice_dsp_interpolate_4th_order(&voice->dsp, dsp_buf, is_looping);469break;470471case FLUID_INTERP_7THORDER:472count = fluid_rvoice_dsp_interpolate_7th_order(&voice->dsp, dsp_buf, is_looping);473break;474}475476fluid_check_fpe("voice_write interpolation");477478if(count == 0)479{480// voice has finished481return count;482}483484/*************** resonant filter ******************/485486fluid_iir_filter_calc(&voice->resonant_filter, voice->dsp.output_rate,487fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_fc +488modenv_val * voice->envlfo.modenv_to_fc);489490fluid_iir_filter_apply(&voice->resonant_filter, dsp_buf, count);491492/* additional custom filter - only uses the fixed modulator, no lfos... */493fluid_iir_filter_calc(&voice->resonant_custom_filter, voice->dsp.output_rate, 0);494fluid_iir_filter_apply(&voice->resonant_custom_filter, dsp_buf, count);495496return count;497}498499/**500* Initialize buffers up to (and including) bufnum501*/502static int503fluid_rvoice_buffers_check_bufnum(fluid_rvoice_buffers_t *buffers, unsigned int bufnum)504{505unsigned int i;506507if(bufnum < buffers->count)508{509return FLUID_OK;510}511512if(bufnum >= FLUID_RVOICE_MAX_BUFS)513{514return FLUID_FAILED;515}516517for(i = buffers->count; i <= bufnum; i++)518{519buffers->bufs[i].target_amp = 0.0f;520buffers->bufs[i].current_amp = 0.0f;521}522523buffers->count = bufnum + 1;524return FLUID_OK;525}526527528DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_amp)529{530fluid_rvoice_buffers_t *buffers = obj;531unsigned int bufnum = param[0].i;532fluid_real_t value = param[1].real;533534if(fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK)535{536return;537}538539buffers->bufs[bufnum].target_amp = value;540}541542DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_mapping)543{544fluid_rvoice_buffers_t *buffers = obj;545unsigned int bufnum = param[0].i;546int mapping = param[1].i;547548if(fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK)549{550return;551}552553buffers->bufs[bufnum].mapping = mapping;554}555556557DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_reset)558{559fluid_rvoice_t *voice = obj;560561voice->dsp.has_looped = 0;562voice->envlfo.ticks = 0;563voice->envlfo.noteoff_ticks = 0;564voice->dsp.amp = 0.0f; /* The last value of the volume envelope, used to565calculate the volume increment during566processing */567568/* legato initialization */569voice->dsp.pitchoffset = 0.0; /* portamento initialization */570voice->dsp.pitchinc = 0.0;571572/* mod env initialization*/573fluid_adsr_env_reset(&voice->envlfo.modenv);574575/* vol env initialization */576fluid_adsr_env_reset(&voice->envlfo.volenv);577578/* Fixme: Retrieve from any other existing579voice on this channel to keep LFOs in580unison? */581fluid_lfo_reset(&voice->envlfo.viblfo);582fluid_lfo_reset(&voice->envlfo.modlfo);583584/* Clear sample history in filter */585fluid_iir_filter_reset(&voice->resonant_filter);586fluid_iir_filter_reset(&voice->resonant_custom_filter);587588/* Force setting of the phase at the first DSP loop run589* This cannot be done earlier, because it depends on modulators.590[DH] Is that comment really true? */591voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP;592}593594DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_noteoff)595{596fluid_rvoice_t *rvoice = obj;597unsigned int min_ticks = param[0].i;598599fluid_rvoice_noteoff_LOCAL(rvoice, min_ticks);600}601602static void603fluid_rvoice_noteoff_LOCAL(fluid_rvoice_t *voice, unsigned int min_ticks)604{605if(min_ticks > voice->envlfo.ticks)606{607/* Delay noteoff */608voice->envlfo.noteoff_ticks = min_ticks;609return;610}611612voice->envlfo.noteoff_ticks = 0;613614if(fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVATTACK)615{616/* A voice is turned off during the attack section of the volume617* envelope. The attack section ramps up linearly with618* amplitude. The other sections use logarithmic scaling. Calculate new619* volenv_val to achieve equivalent amplitude during the release phase620* for seamless volume transition.621*/622if(fluid_adsr_env_get_val(&voice->envlfo.volenv) > 0)623{624fluid_real_t lfo = fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol;625fluid_real_t amp = fluid_adsr_env_get_val(&voice->envlfo.volenv) * fluid_cb2amp(lfo);626fluid_real_t env_value = - (((-200.f / FLUID_M_LN10) * FLUID_LOGF(amp) - lfo) / FLUID_PEAK_ATTENUATION - 1);627fluid_clip(env_value, 0.0f, 1.0f);628fluid_adsr_env_set_val(&voice->envlfo.volenv, env_value);629}630}631632if(fluid_adsr_env_get_section(&voice->envlfo.modenv) == FLUID_VOICE_ENVATTACK)633{634/* A voice is turned off during the attack section of the modulation635* envelope. The attack section use convex scaling with pitch and filter636* frequency cutoff (see fluid_rvoice_write(): modenv_val = fluid_convex(127 * modenv.val)637* The other sections use linear scaling: modenv_val = modenv.val638*639* Calculate new modenv.val to achieve equivalent modenv_val during the release phase640* for seamless pitch and filter frequency cutoff transition.641*/642if(fluid_adsr_env_get_val(&voice->envlfo.modenv) > 0)643{644fluid_real_t env_value = fluid_convex(127 * fluid_adsr_env_get_val(&voice->envlfo.modenv));645fluid_clip(env_value, 0.0, 1.0);646fluid_adsr_env_set_val(&voice->envlfo.modenv, env_value);647}648}649650fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVRELEASE);651fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVRELEASE);652}653654/**655* skips to Attack section656*657* Updates vol and attack data658* Correction on volume val to achieve equivalent amplitude at noteOn legato659*660* @param voice the synthesis voice to be updated661*/662static FLUID_INLINE void fluid_rvoice_local_retrigger_attack(fluid_rvoice_t *voice)663{664/* skips to Attack section */665/* Once in Attack section, current count must be reset, to be sure666that the section will be not be prematurely finished. */667fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVATTACK);668{669/* Correction on volume val to achieve equivalent amplitude at noteOn legato */670fluid_env_data_t *env_data;671fluid_real_t peak = fluid_cb2amp(voice->dsp.attenuation);672fluid_real_t prev_peak = fluid_cb2amp(voice->dsp.prev_attenuation);673voice->envlfo.volenv.val = (voice->envlfo.volenv.val * prev_peak) / peak;674/* Correction on slope direction for Attack section */675env_data = &voice->envlfo.volenv.data[FLUID_VOICE_ENVATTACK];676677if(voice->envlfo.volenv.val <= 1.0f)678{679/* slope attack for legato note needs to be positive from val up to 1 */680env_data->increment = 1.0f / env_data->count;681env_data->min = -1.0f;682env_data->max = 1.0f;683}684else685{686/* slope attack for legato note needs to be negative: from val down to 1 */687env_data->increment = -voice->envlfo.volenv.val / env_data->count;688env_data->min = 1.0f;689env_data->max = voice->envlfo.volenv.val;690}691}692}693694/**695* Used by legato Mode : multi_retrigger696* see fluid_synth_noteon_mono_legato_multi_retrigger()697* @param voice the synthesis voice to be updated698*/699DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_multi_retrigger_attack)700{701fluid_rvoice_t *voice = obj;702int section; /* volume or modulation section */703704/*-------------------------------------------------------------------------705Section skip for volume envelope706--------------------------------------------------------------------------*/707section = fluid_adsr_env_get_section(&voice->envlfo.volenv);708if(section >= FLUID_VOICE_ENVHOLD)709{710/* DECAY, SUSTAIN,RELEASE section use logarithmic scaling. Calculates new711volenv_val to achieve equivalent amplitude during the attack phase712for seamless volume transition. */713fluid_real_t amp_cb, env_value;714amp_cb = FLUID_PEAK_ATTENUATION *715(1.0f - fluid_adsr_env_get_val(&voice->envlfo.volenv));716env_value = fluid_cb2amp(amp_cb); /* a bit of optimization */717fluid_clip(env_value, 0.0, 1.0);718fluid_adsr_env_set_val(&voice->envlfo.volenv, env_value);719/* next, skips to Attack section */720}721722/* skips to Attack section from any section */723/* Update vol and attack data */724fluid_rvoice_local_retrigger_attack(voice);725726/*-------------------------------------------------------------------------727Section skip for modulation envelope728--------------------------------------------------------------------------*/729section = fluid_adsr_env_get_section(&voice->envlfo.modenv);730if(section >= FLUID_VOICE_ENVHOLD)731{732/* DECAY, SUSTAIN,RELEASE section use linear scaling.733Since v 2.1 , as recommended by soundfont 2.01/2.4 spec, ATTACK section734uses convex shape (see fluid_rvoice_write() - fluid_convex()).735Calculate new modenv value (new_value) for seamless attack transition.736Here we need the inverse of fluid_convex() function defined as:737new_value = pow(10, (1 - current_val) . FLUID_PEAK_ATTENUATION / -200 . 2.0)738For performance reason we use fluid_cb2amp(Val) = pow(10, val/-200) with739val = (1 - current_val) . FLUID_PEAK_ATTENUATION / 2.0740*/741fluid_real_t new_value; /* new modenv value */742new_value = fluid_cb2amp((1.0f - fluid_adsr_env_get_val(&voice->envlfo.modenv))743* FLUID_PEAK_ATTENUATION / 2.0);744fluid_clip(new_value, 0.0, 1.0);745fluid_adsr_env_set_val(&voice->envlfo.modenv, new_value);746}747/* Skips from any section to ATTACK section */748fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVATTACK);749}750751/**752* sets the portamento dsp parameters: dsp.pitchoffset, dsp.pitchinc753* @param voice rvoice to set portamento.754* @param countinc increment count number.755* @param pitchoffset pitch offset to apply to voice dsp.pitch.756*757* Notes:758* 1) To get continuous portamento between consecutive noteOn (n1,n2,n3...),759* pitchoffset is accumulated in current dsp pitchoffset.760* 2) And to get constant portamento duration, dsp pitch increment is updated.761*/762DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_portamento)763{764fluid_rvoice_t *voice = obj;765unsigned int countinc = param[0].i;766fluid_real_t pitchoffset = param[1].real;767768if(countinc)769{770voice->dsp.pitchoffset += pitchoffset;771voice->dsp.pitchinc = - voice->dsp.pitchoffset / countinc;772}773774/* Then during the voice processing (in fluid_rvoice_write()),775dsp.pitchoffset will be incremented by dsp pitchinc. */776}777778779DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_output_rate)780{781fluid_rvoice_t *voice = obj;782fluid_real_t value = param[0].real;783784voice->dsp.output_rate = value;785}786787DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_interp_method)788{789fluid_rvoice_t *voice = obj;790int value = param[0].i;791792voice->dsp.interp_method = value;793}794795DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_root_pitch_hz)796{797fluid_rvoice_t *voice = obj;798fluid_real_t value = param[0].real;799800voice->dsp.root_pitch_hz = value;801}802803DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_pitch)804{805fluid_rvoice_t *voice = obj;806fluid_real_t value = param[0].real;807808voice->dsp.pitch = value;809}810811812DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_attenuation)813{814fluid_rvoice_t *voice = obj;815fluid_real_t value = param[0].real;816817voice->dsp.prev_attenuation = voice->dsp.attenuation;818voice->dsp.attenuation = value;819}820821DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_min_attenuation_cB)822{823fluid_rvoice_t *voice = obj;824fluid_real_t value = param[0].real;825826voice->dsp.min_attenuation_cB = value;827}828829DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_viblfo_to_pitch)830{831fluid_rvoice_t *voice = obj;832fluid_real_t value = param[0].real;833834voice->envlfo.viblfo_to_pitch = value;835}836837DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_pitch)838{839fluid_rvoice_t *voice = obj;840fluid_real_t value = param[0].real;841842voice->envlfo.modlfo_to_pitch = value;843}844845DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_vol)846{847fluid_rvoice_t *voice = obj;848fluid_real_t value = param[0].real;849850voice->envlfo.modlfo_to_vol = value;851}852853DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_fc)854{855fluid_rvoice_t *voice = obj;856fluid_real_t value = param[0].real;857858voice->envlfo.modlfo_to_fc = value;859}860861DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_fc)862{863fluid_rvoice_t *voice = obj;864fluid_real_t value = param[0].real;865866voice->envlfo.modenv_to_fc = value;867}868869DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_pitch)870{871fluid_rvoice_t *voice = obj;872fluid_real_t value = param[0].real;873874voice->envlfo.modenv_to_pitch = value;875}876877DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_synth_gain)878{879fluid_rvoice_t *voice = obj;880fluid_real_t value = param[0].real;881882voice->dsp.synth_gain = value;883884/* For a looped sample, this value will be overwritten as soon as the885* loop parameters are initialized (they may depend on modulators).886* This value can be kept, it is a worst-case estimate.887*/888voice->dsp.amplitude_that_reaches_noise_floor_nonloop = FLUID_NOISE_FLOOR / value;889voice->dsp.amplitude_that_reaches_noise_floor_loop = FLUID_NOISE_FLOOR / value;890voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;891}892893DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_start)894{895fluid_rvoice_t *voice = obj;896int value = param[0].i;897898voice->dsp.start = value;899voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;900}901902DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_end)903{904fluid_rvoice_t *voice = obj;905int value = param[0].i;906907voice->dsp.end = value;908voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;909}910911DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopstart)912{913fluid_rvoice_t *voice = obj;914int value = param[0].i;915916voice->dsp.loopstart = value;917voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;918}919920DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopend)921{922fluid_rvoice_t *voice = obj;923int value = param[0].i;924925voice->dsp.loopend = value;926voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;927}928929DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_samplemode)930{931fluid_rvoice_t *voice = obj;932enum fluid_loop value = param[0].i;933934voice->dsp.samplemode = value;935voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;936}937938939DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_sample)940{941fluid_rvoice_t *voice = obj;942fluid_sample_t *value = param[0].ptr;943944voice->dsp.sample = value;945946if(value)947{948voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP;949}950}951952DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_voiceoff)953{954fluid_rvoice_t *voice = obj;955956fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVFINISHED);957fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVFINISHED);958}959960961