Path: blob/master/libs/fluidsynth/src/rvoice/fluid_rvoice.c
8848 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/*************** resonant filter ******************/438// Only "prepare" the filter here, the filter itself will be applied in the dsp_interpolation routines below.439// This is to satisfy SF2 Section 9.1.8, particularly, the filtered output must be gain-adjusted by the volEnv.440// Applying the filter after applying the gain from the volEnv might cause audible clicks for when turning off441// voices that are filtered by a high Q, see https://github.com/FluidSynth/fluidsynth/issues/1427442//443// Note that at this point we are using voice->dsp.output_rate which is set to the synth's output rate, because444// the filter will receive the interpolated waveform.445446fluid_iir_filter_calc(&voice->resonant_filter, voice->dsp.output_rate,447fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_fc +448modenv_val * voice->envlfo.modenv_to_fc);449450/* additional custom filter - only uses the fixed modulator, no lfos... */451fluid_iir_filter_calc(&voice->resonant_custom_filter, voice->dsp.output_rate, 0);452453/*********************** run the dsp chain ************************454* The sample is mixed with the output buffer.455* The buffer has to be filled from 0 to FLUID_BUFSIZE-1.456* Depending on the position in the loop and the loop size, this457* may require several runs. */458459if(count < 0)460{461// The voice is quite, i.e. either in delay phase or zero volume.462// We need to update the rvoice's dsp phase, as the delay phase shall not "postpone" the sound, rather463// it should be played silently, see https://github.com/FluidSynth/fluidsynth/issues/1312464//465// Currently, this does access the sample buffers, which is redundant and could be optimized away.466// On the other hand, entering this if-clause is not supposed to happen often.467return fluid_rvoice_dsp_interpolate_none(voice, dsp_buf, is_looping);468}469470switch(voice->dsp.interp_method)471{472case FLUID_INTERP_NONE:473count = fluid_rvoice_dsp_interpolate_none(voice, dsp_buf, is_looping);474break;475476case FLUID_INTERP_LINEAR:477count = fluid_rvoice_dsp_interpolate_linear(voice, dsp_buf, is_looping);478break;479480case FLUID_INTERP_4THORDER:481default:482count = fluid_rvoice_dsp_interpolate_4th_order(voice, dsp_buf, is_looping);483break;484485case FLUID_INTERP_7THORDER:486count = fluid_rvoice_dsp_interpolate_7th_order(voice, dsp_buf, is_looping);487break;488}489490fluid_check_fpe("voice_write interpolation");491492if(count == 0)493{494// voice has finished495return count;496}497498return count;499}500501/**502* Initialize buffers up to (and including) bufnum503*/504static int505fluid_rvoice_buffers_check_bufnum(fluid_rvoice_buffers_t *buffers, unsigned int bufnum)506{507unsigned int i;508509if(bufnum < buffers->count)510{511return FLUID_OK;512}513514if(bufnum >= FLUID_RVOICE_MAX_BUFS)515{516return FLUID_FAILED;517}518519for(i = buffers->count; i <= bufnum; i++)520{521buffers->bufs[i].target_amp = 0.0f;522buffers->bufs[i].current_amp = 0.0f;523}524525buffers->count = bufnum + 1;526return FLUID_OK;527}528529530DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_amp)531{532fluid_rvoice_buffers_t *buffers = obj;533unsigned int bufnum = param[0].i;534fluid_real_t value = param[1].real;535536if(fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK)537{538return;539}540541buffers->bufs[bufnum].target_amp = value;542}543544DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_buffers_set_mapping)545{546fluid_rvoice_buffers_t *buffers = obj;547unsigned int bufnum = param[0].i;548int mapping = param[1].i;549550if(fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK)551{552return;553}554555buffers->bufs[bufnum].mapping = mapping;556}557558559DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_reset)560{561fluid_rvoice_t *voice = obj;562563voice->dsp.has_looped = 0;564voice->envlfo.ticks = 0;565voice->envlfo.noteoff_ticks = 0;566voice->dsp.amp = 0.0f; /* The last value of the volume envelope, used to567calculate the volume increment during568processing */569570/* legato initialization */571voice->dsp.pitchoffset = 0.0; /* portamento initialization */572voice->dsp.pitchinc = 0.0;573574/* mod env initialization*/575fluid_adsr_env_reset(&voice->envlfo.modenv);576577/* vol env initialization */578fluid_adsr_env_reset(&voice->envlfo.volenv);579580/* Fixme: Retrieve from any other existing581voice on this channel to keep LFOs in582unison? */583fluid_lfo_reset(&voice->envlfo.viblfo);584fluid_lfo_reset(&voice->envlfo.modlfo);585586/* Clear sample history in filter */587fluid_iir_filter_reset(&voice->resonant_filter);588fluid_iir_filter_reset(&voice->resonant_custom_filter);589590/* Force setting of the phase at the first DSP loop run591* This cannot be done earlier, because it depends on modulators.592[DH] Is that comment really true? */593voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP;594}595596DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_noteoff)597{598fluid_rvoice_t *rvoice = obj;599unsigned int min_ticks = param[0].i;600601fluid_rvoice_noteoff_LOCAL(rvoice, min_ticks);602}603604static void605fluid_rvoice_noteoff_LOCAL(fluid_rvoice_t *voice, unsigned int min_ticks)606{607if(min_ticks > voice->envlfo.ticks)608{609/* Delay noteoff */610voice->envlfo.noteoff_ticks = min_ticks;611return;612}613614voice->envlfo.noteoff_ticks = 0;615616if(fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVATTACK)617{618/* A voice is turned off during the attack section of the volume619* envelope. The attack section ramps up linearly with620* amplitude. The other sections use logarithmic scaling. Calculate new621* volenv_val to achieve equivalent amplitude during the release phase622* for seamless volume transition.623*/624if(fluid_adsr_env_get_val(&voice->envlfo.volenv) > 0)625{626fluid_real_t lfo = fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol;627fluid_real_t amp = fluid_adsr_env_get_val(&voice->envlfo.volenv) * fluid_cb2amp(lfo);628fluid_real_t env_value = - (((-200.f / FLUID_M_LN10) * FLUID_LOGF(amp) - lfo) / FLUID_PEAK_ATTENUATION - 1);629fluid_clip(env_value, 0.0f, 1.0f);630fluid_adsr_env_set_val(&voice->envlfo.volenv, env_value);631}632}633634if(fluid_adsr_env_get_section(&voice->envlfo.modenv) == FLUID_VOICE_ENVATTACK)635{636/* A voice is turned off during the attack section of the modulation637* envelope. The attack section use convex scaling with pitch and filter638* frequency cutoff (see fluid_rvoice_write(): modenv_val = fluid_convex(127 * modenv.val)639* The other sections use linear scaling: modenv_val = modenv.val640*641* Calculate new modenv.val to achieve equivalent modenv_val during the release phase642* for seamless pitch and filter frequency cutoff transition.643*/644if(fluid_adsr_env_get_val(&voice->envlfo.modenv) > 0)645{646fluid_real_t env_value = fluid_convex(127 * fluid_adsr_env_get_val(&voice->envlfo.modenv));647fluid_clip(env_value, 0.0, 1.0);648fluid_adsr_env_set_val(&voice->envlfo.modenv, env_value);649}650}651652fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVRELEASE);653fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVRELEASE);654}655656/**657* skips to Attack section658*659* Updates vol and attack data660* Correction on volume val to achieve equivalent amplitude at noteOn legato661*662* @param voice the synthesis voice to be updated663*/664static FLUID_INLINE void fluid_rvoice_local_retrigger_attack(fluid_rvoice_t *voice)665{666/* skips to Attack section */667/* Once in Attack section, current count must be reset, to be sure668that the section will be not be prematurely finished. */669fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVATTACK);670{671/* Correction on volume val to achieve equivalent amplitude at noteOn legato */672fluid_env_data_t *env_data;673fluid_real_t peak = fluid_cb2amp(voice->dsp.attenuation);674fluid_real_t prev_peak = fluid_cb2amp(voice->dsp.prev_attenuation);675voice->envlfo.volenv.val = (voice->envlfo.volenv.val * prev_peak) / peak;676/* Correction on slope direction for Attack section */677env_data = &voice->envlfo.volenv.data[FLUID_VOICE_ENVATTACK];678679if(voice->envlfo.volenv.val <= 1.0f)680{681/* slope attack for legato note needs to be positive from val up to 1 */682env_data->increment = 1.0f / env_data->count;683env_data->min = -1.0f;684env_data->max = 1.0f;685}686else687{688/* slope attack for legato note needs to be negative: from val down to 1 */689env_data->increment = -voice->envlfo.volenv.val / env_data->count;690env_data->min = 1.0f;691env_data->max = voice->envlfo.volenv.val;692}693}694}695696/**697* Used by legato Mode : multi_retrigger698* see fluid_synth_noteon_mono_legato_multi_retrigger()699* @param voice the synthesis voice to be updated700*/701DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_multi_retrigger_attack)702{703fluid_rvoice_t *voice = obj;704int section; /* volume or modulation section */705706/*-------------------------------------------------------------------------707Section skip for volume envelope708--------------------------------------------------------------------------*/709section = fluid_adsr_env_get_section(&voice->envlfo.volenv);710if(section >= FLUID_VOICE_ENVHOLD)711{712/* DECAY, SUSTAIN,RELEASE section use logarithmic scaling. Calculates new713volenv_val to achieve equivalent amplitude during the attack phase714for seamless volume transition. */715fluid_real_t amp_cb, env_value;716amp_cb = FLUID_PEAK_ATTENUATION *717(1.0f - fluid_adsr_env_get_val(&voice->envlfo.volenv));718env_value = fluid_cb2amp(amp_cb); /* a bit of optimization */719fluid_clip(env_value, 0.0, 1.0);720fluid_adsr_env_set_val(&voice->envlfo.volenv, env_value);721/* next, skips to Attack section */722}723724/* skips to Attack section from any section */725/* Update vol and attack data */726fluid_rvoice_local_retrigger_attack(voice);727728/*-------------------------------------------------------------------------729Section skip for modulation envelope730--------------------------------------------------------------------------*/731section = fluid_adsr_env_get_section(&voice->envlfo.modenv);732if(section >= FLUID_VOICE_ENVHOLD)733{734/* DECAY, SUSTAIN,RELEASE section use linear scaling.735Since v 2.1 , as recommended by soundfont 2.01/2.4 spec, ATTACK section736uses convex shape (see fluid_rvoice_write() - fluid_convex()).737Calculate new modenv value (new_value) for seamless attack transition.738Here we need the inverse of fluid_convex() function defined as:739new_value = pow(10, (1 - current_val) . FLUID_PEAK_ATTENUATION / -200 . 2.0)740For performance reason we use fluid_cb2amp(Val) = pow(10, val/-200) with741val = (1 - current_val) . FLUID_PEAK_ATTENUATION / 2.0742*/743fluid_real_t new_value; /* new modenv value */744new_value = fluid_cb2amp((1.0f - fluid_adsr_env_get_val(&voice->envlfo.modenv))745* FLUID_PEAK_ATTENUATION / 2.0);746fluid_clip(new_value, 0.0, 1.0);747fluid_adsr_env_set_val(&voice->envlfo.modenv, new_value);748}749/* Skips from any section to ATTACK section */750fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVATTACK);751}752753/**754* sets the portamento dsp parameters: dsp.pitchoffset, dsp.pitchinc755* @param voice rvoice to set portamento.756* @param countinc increment count number.757* @param pitchoffset pitch offset to apply to voice dsp.pitch.758*759* Notes:760* 1) To get continuous portamento between consecutive noteOn (n1,n2,n3...),761* pitchoffset is accumulated in current dsp pitchoffset.762* 2) And to get constant portamento duration, dsp pitch increment is updated.763*/764DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_portamento)765{766fluid_rvoice_t *voice = obj;767unsigned int countinc = param[0].i;768fluid_real_t pitchoffset = param[1].real;769770if(countinc)771{772voice->dsp.pitchoffset += pitchoffset;773voice->dsp.pitchinc = - voice->dsp.pitchoffset / countinc;774}775776/* Then during the voice processing (in fluid_rvoice_write()),777dsp.pitchoffset will be incremented by dsp pitchinc. */778}779780781DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_output_rate)782{783fluid_rvoice_t *voice = obj;784fluid_real_t value = param[0].real;785786voice->dsp.output_rate = value;787}788789DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_interp_method)790{791fluid_rvoice_t *voice = obj;792int value = param[0].i;793794voice->dsp.interp_method = value;795}796797DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_root_pitch_hz)798{799fluid_rvoice_t *voice = obj;800fluid_real_t value = param[0].real;801802voice->dsp.root_pitch_hz = value;803}804805DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_pitch)806{807fluid_rvoice_t *voice = obj;808fluid_real_t value = param[0].real;809810voice->dsp.pitch = value;811}812813814DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_attenuation)815{816fluid_rvoice_t *voice = obj;817fluid_real_t value = param[0].real;818819voice->dsp.prev_attenuation = voice->dsp.attenuation;820voice->dsp.attenuation = value;821}822823DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_min_attenuation_cB)824{825fluid_rvoice_t *voice = obj;826fluid_real_t value = param[0].real;827828voice->dsp.min_attenuation_cB = value;829}830831DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_viblfo_to_pitch)832{833fluid_rvoice_t *voice = obj;834fluid_real_t value = param[0].real;835836voice->envlfo.viblfo_to_pitch = value;837}838839DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_pitch)840{841fluid_rvoice_t *voice = obj;842fluid_real_t value = param[0].real;843844voice->envlfo.modlfo_to_pitch = value;845}846847DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_vol)848{849fluid_rvoice_t *voice = obj;850fluid_real_t value = param[0].real;851852voice->envlfo.modlfo_to_vol = value;853}854855DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modlfo_to_fc)856{857fluid_rvoice_t *voice = obj;858fluid_real_t value = param[0].real;859860voice->envlfo.modlfo_to_fc = value;861}862863DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_fc)864{865fluid_rvoice_t *voice = obj;866fluid_real_t value = param[0].real;867868voice->envlfo.modenv_to_fc = value;869}870871DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_modenv_to_pitch)872{873fluid_rvoice_t *voice = obj;874fluid_real_t value = param[0].real;875876voice->envlfo.modenv_to_pitch = value;877}878879DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_synth_gain)880{881fluid_rvoice_t *voice = obj;882fluid_real_t value = param[0].real;883884voice->dsp.synth_gain = value;885886/* For a looped sample, this value will be overwritten as soon as the887* loop parameters are initialized (they may depend on modulators).888* This value can be kept, it is a worst-case estimate.889*/890voice->dsp.amplitude_that_reaches_noise_floor_nonloop = FLUID_NOISE_FLOOR / value;891voice->dsp.amplitude_that_reaches_noise_floor_loop = FLUID_NOISE_FLOOR / value;892voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;893}894895DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_start)896{897fluid_rvoice_t *voice = obj;898int value = param[0].i;899900voice->dsp.start = value;901voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;902}903904DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_end)905{906fluid_rvoice_t *voice = obj;907int value = param[0].i;908909voice->dsp.end = value;910voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;911}912913DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopstart)914{915fluid_rvoice_t *voice = obj;916int value = param[0].i;917918voice->dsp.loopstart = value;919voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;920}921922DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_loopend)923{924fluid_rvoice_t *voice = obj;925int value = param[0].i;926927voice->dsp.loopend = value;928voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;929}930931DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_samplemode)932{933fluid_rvoice_t *voice = obj;934enum fluid_loop value = param[0].i;935936voice->dsp.samplemode = value;937voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;938}939940941DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_set_sample)942{943fluid_rvoice_t *voice = obj;944fluid_sample_t *value = param[0].ptr;945946voice->dsp.sample = value;947948if(value)949{950voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP;951}952}953954DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_voiceoff)955{956fluid_rvoice_t *voice = obj;957958fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVFINISHED);959fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVFINISHED);960}961962963