/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License * as published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307, USA */ #include "conv.h" #include "fluid.h" #include "sfont.h" #include "gen.h" #include "voice.h" namespace FluidS { #define FLUID_SAMPLESANITY_CHECK (1 << 0) #define FLUID_SAMPLESANITY_STARTUP (1 << 1) #define fluid_clip(_val, _min, _max) \ { (_val) = ((_val) < (_min))? (_min) : (((_val) > (_max))? (_max) : (_val)); } /* used for filter turn off optimization - if filter cutoff is above the specified value and filter q is below the other value, turn filter off */ #define FLUID_MAX_AUDIBLE_FILTER_FC 19000.0f #define FLUID_MIN_AUDIBLE_FILTER_Q 1.2f /* Smallest amplitude that can be perceived (full scale is +/- 0.5) * 16 bits => 96+4=100 dB dynamic range => 0.00001 * 0.00001 * 2 is approximately 0.00003 :) */ #define FLUID_NOISE_FLOOR 0.00003 /* these should be the absolute minimum that FluidSynth can deal with */ #define FLUID_MIN_LOOP_SIZE 2 #define FLUID_MIN_LOOP_PAD 0 /* min vol envelope release (to stop clicks) in SoundFont timecents */ #define FLUID_MIN_VOLENVRELEASE -7200.0f /* ~16ms */ //--------------------------------------------------------- // Voice //--------------------------------------------------------- Voice::Voice(Fluid* f) { _fluid = f; status = FLUID_VOICE_OFF; chan = NO_CHANNEL; key = 0; vel = 0; channel = 0; sample = 0; /* The 'sustain' and 'finished' segments of the volume / modulation * envelope are constant. They are never affected by any modulator * or generator. Therefore it is enough to initialize them once * during the lifetime of the synth. */ volenv_data[FLUID_VOICE_ENVSUSTAIN].count = 0xffffffff; volenv_data[FLUID_VOICE_ENVSUSTAIN].coeff = 1.0f; volenv_data[FLUID_VOICE_ENVSUSTAIN].incr = 0.0f; volenv_data[FLUID_VOICE_ENVSUSTAIN].min = -1.0f; volenv_data[FLUID_VOICE_ENVSUSTAIN].max = 2.0f; volenv_data[FLUID_VOICE_ENVFINISHED].count = 0xffffffff; volenv_data[FLUID_VOICE_ENVFINISHED].coeff = 0.0f; volenv_data[FLUID_VOICE_ENVFINISHED].incr = 0.0f; volenv_data[FLUID_VOICE_ENVFINISHED].min = -1.0f; volenv_data[FLUID_VOICE_ENVFINISHED].max = 1.0f; modenv_data[FLUID_VOICE_ENVSUSTAIN].count = 0xffffffff; modenv_data[FLUID_VOICE_ENVSUSTAIN].coeff = 1.0f; modenv_data[FLUID_VOICE_ENVSUSTAIN].incr = 0.0f; modenv_data[FLUID_VOICE_ENVSUSTAIN].min = -1.0f; modenv_data[FLUID_VOICE_ENVSUSTAIN].max = 2.0f; modenv_data[FLUID_VOICE_ENVFINISHED].count = 0xffffffff; modenv_data[FLUID_VOICE_ENVFINISHED].coeff = 0.0f; modenv_data[FLUID_VOICE_ENVFINISHED].incr = 0.0f; modenv_data[FLUID_VOICE_ENVFINISHED].min = -1.0f; modenv_data[FLUID_VOICE_ENVFINISHED].max = 1.0f; } //--------------------------------------------------------- // init // Initialize the synthesis process //--------------------------------------------------------- void Voice::init(Sample* _sample, Channel* _channel, int _key, int _vel, unsigned int _id, double tuning) { // Note: The voice parameters will be initialized later, when the // generators have been retrieved from the sound font. Here, only // the 'working memory' of the voice (position in envelopes, history // of IIR filters, position in sample etc) is initialized. id = _id; _noteTuning = tuning; chan = _channel->getNum(); key = _key; vel = _vel; channel = _channel; mod_count = 0; sample = _sample; ticks = 0; debug = 0; has_looped = false; // Will be set during voice_write when the 2nd loop point is reached last_fres = -1; // The filter coefficients have to be calculated later in the DSP loop. filter_startup = 1; // Set the filter immediately, don't fade between old and new settings interp_method = _channel->getInterpMethod(); // vol env initialization volenv_count = 0; volenv_section = 0; volenv_val = 0.0f; amp = 0.0f; // The last value of the volume envelope, used to // calculate the volume increment during // processing // mod env initialization modenv_count = 0; modenv_section = 0; modenv_val = 0.0f; /* mod lfo */ modlfo_val = 0.0; // Fixme: Retrieve from any other existing // voice on this channel to keep LFOs in // unison? /* vib lfo */ viblfo_val = 0.0f; // Fixme: See mod lfo /* Clear sample history in filter */ hist1 = 0; hist2 = 0; /* Set all the generators to their default value, according to SF * 2.01 section 8.1.3 (page 48). The value of NRPN messages are * copied from the channel to the voice's generators. The sound font * loader overwrites them. The generator values are later converted * into voice parameters in calculate_runtime_synthesis_parameters. */ fluid_gen_init(&gen[0], channel); /* For a looped sample, this value will be overwritten as soon as the * loop parameters are initialized (they may depend on modulators). * This value can be kept, it is a worst-case estimate. */ double g = _fluid->masterGain(); if (g == 0.0) g = 0.0000001; amplitude_that_reaches_noise_floor_nonloop = FLUID_NOISE_FLOOR / g; amplitude_that_reaches_noise_floor_loop = FLUID_NOISE_FLOOR / g; } //--------------------------------------------------------- // gen_set //--------------------------------------------------------- void Voice::gen_set(int i, float val) { gen[i].val = val; gen[i].flags = GEN_SET; } //--------------------------------------------------------- // gen_incr //--------------------------------------------------------- void Voice::gen_incr(int i, float val) { gen[i].val += val; gen[i].flags = GEN_SET; } //--------------------------------------------------------- // gen_get //--------------------------------------------------------- float Voice::gen_get(int g) { return gen[g].val; } //----------------------------------------------------------------------------- // write // // This is where it all happens. This function is called by the // synthesizer to generate the sound samples. The synthesizer passes // four audio buffers: left, right, reverb out, and chorus out. // // The biggest part of this function sets the correct values for all // the dsp parameters (all the control data boil down to only a few // dsp parameters). The dsp routine is #included in several places (fluid_dsp_core.c). //----------------------------------------------------------------------------- void Voice::write(unsigned n, float* left, float* right, float* reverb, float* chorus) { /* make sure we're playing and that we have sample data */ if (!PLAYING()) return; if (!sample) { printf("!sample\n"); off(); return; } float target_amp; /* target amplitude */ fluid_env_data_t* env_data; float x; float _fres; /* Range checking for sample- and loop-related parameters * Initial phase is calculated here*/ check_sample_sanity(); /******************* vol env **********************/ env_data = &volenv_data[volenv_section]; /* skip to the next section of the envelope if necessary */ while (volenv_count >= env_data->count) { // If we're switching envelope stages from decay to sustain, force the value to be the end value of the previous stage if (env_data && volenv_section == FLUID_VOICE_ENVDECAY) volenv_val = env_data->min * env_data->coeff; env_data = &volenv_data[++volenv_section]; volenv_count = 0; } /* calculate the envelope value and check for valid range */ x = env_data->coeff * volenv_val + env_data->incr * n; if (x < env_data->min) { x = env_data->min; volenv_section++; volenv_count = 0; } else if (x > env_data->max) { x = env_data->max; volenv_section++; volenv_count = 0; } volenv_val = x; volenv_count += n; if (volenv_section == FLUID_VOICE_ENVFINISHED) { off(); return; } fluid_check_fpe ("voice_write vol env"); /******************* mod env **********************/ env_data = &modenv_data[modenv_section]; /* skip to the next section of the envelope if necessary */ while (modenv_count >= env_data->count) { env_data = &modenv_data[++modenv_section]; modenv_count = 0; } /* calculate the envelope value and check for valid range */ x = env_data->coeff * modenv_val + env_data->incr * n; if (x < env_data->min) { x = env_data->min; modenv_section++; modenv_count = 0; } else if (x > env_data->max) { x = env_data->max; modenv_section++; modenv_count = 0; } modenv_val = x; modenv_count += n; fluid_check_fpe ("voice_write mod env"); /******************* mod lfo **********************/ if (ticks >= modlfo_delay) { modlfo_val += modlfo_incr * n; if (modlfo_val > 1.0) { modlfo_incr = -modlfo_incr; modlfo_val = (float) 2.0 - modlfo_val; } else if (modlfo_val < -1.0) { modlfo_incr = -modlfo_incr; modlfo_val = (float) -2.0 - modlfo_val; } } fluid_check_fpe ("voice_write mod LFO"); /******************* vib lfo **********************/ if (ticks >= viblfo_delay) { viblfo_val += viblfo_incr * n; if (viblfo_val > (float) 1.0) { viblfo_incr = -viblfo_incr; viblfo_val = (float) 2.0 - viblfo_val; } else if (viblfo_val < -1.0) { viblfo_incr = -viblfo_incr; viblfo_val = (float) -2.0 - viblfo_val; } } fluid_check_fpe ("voice_write Vib LFO"); /******************* amplitude **********************/ /* calculate final amplitude * - initial gain * - amplitude envelope */ if (volenv_section == FLUID_VOICE_ENVDELAY) { ticks += n; return; /* The volume amplitude is in hold phase. No sound is produced. */ } if (volenv_section == FLUID_VOICE_ENVATTACK) { /* the envelope is in the attack section: ramp linearly to max value. * A positive modlfo_to_vol should increase volume (negative attenuation). */ target_amp = fluid_atten2amp (attenuation) * fluid_cb2amp (modlfo_val * -modlfo_to_vol) * volenv_val; } else { float amplitude_that_reaches_noise_floor; float amp_max; target_amp = fluid_atten2amp (attenuation) * fluid_cb2amp (960.0f * (1.0f - volenv_val) + modlfo_val * -modlfo_to_vol); /* We turn off a voice, if the volume has dropped low enough. */ /* A voice can be turned off, when an estimate for the volume * (upper bound) falls below that volume, that will drop the * sample below the noise floor. */ /* If the loop amplitude is known, we can use it if the voice loop is within * the sample loop */ /* Is the playing pointer already in the loop? */ if (has_looped) amplitude_that_reaches_noise_floor = amplitude_that_reaches_noise_floor_loop; else amplitude_that_reaches_noise_floor = amplitude_that_reaches_noise_floor_nonloop; /* voice->attenuation_min is a lower boundary for the attenuation * now and in the future (possibly 0 in the worst case). Now the * amplitude of sample and volenv cannot exceed amp_max (since * volenv_val can only drop): */ amp_max = fluid_atten2amp (min_attenuation_cB) * volenv_val; /* And if amp_max is already smaller than the known amplitude, * which will attenuate the sample below the noise floor, then we * can safely turn off the voice. Duh. */ if (amp_max < amplitude_that_reaches_noise_floor) { off(); ticks += n; return; } } /* Volume increment to go from voice->amp to target_amp in FLUID_BUFSIZE steps */ amp_incr = (target_amp - amp) / n; fluid_check_fpe ("voice_write amplitude calculation"); /* no volume and not changing? - No need to process */ if ((amp == 0.0f) && (amp_incr == 0.0f)) { ticks += n; return; } /* Calculate the number of samples, that the DSP loop advances * through the original waveform with each step in the output * buffer. It is the ratio between the frequencies of original * waveform and output waveform.*/ { float cent = pitch + modlfo_val * modlfo_to_pitch + viblfo_val * viblfo_to_pitch + modenv_val * modenv_to_pitch; phase_incr = _fluid->ct2hz_real(cent) / root_pitch; } /* if phase_incr is not advancing, set it to the minimum fraction value (prevent stuckage) */ if (phase_incr == 0) phase_incr = 1; /*************** resonant filter ******************/ /* calculate the frequency of the resonant filter in Hz */ _fres = _fluid->ct2hz_real(fres + modlfo_val * modlfo_to_fc + modenv_val * modenv_to_fc); /* FIXME - Still potential for a click during turn on, can we interpolate between 20khz cutoff and 0 Q? */ /* I removed the optimization of turning the filter off when the * resonance frequence is above the maximum frequency. Instead, the * filter frequency is set to a maximum of 0.45 times the sampling * rate. For a 44100 kHz sampling rate, this amounts to 19845 * Hz. The reason is that there were problems with anti-aliasing when the * synthesizer was run at lower sampling rates. Thanks to Stephan * Tassart for pointing me to this bug. By turning the filter on and * clipping the maximum filter frequency at 0.45*srate, the filter * is used as an anti-aliasing filter. */ if (_fres > 0.45f * _fluid->sample_rate) _fres = 0.45f * _fluid->sample_rate; else if (_fres < 5) _fres = 5; /* if filter enabled and there is a significant frequency change.. */ if ((abs (_fres - last_fres) > 0.01)) { /* The filter coefficients have to be recalculated (filter * parameters have changed). Recalculation for various reasons is * forced by setting last_fres to -1. The flag filter_startup * indicates, that the DSP loop runs for the first time, in this * case, the filter is set directly, instead of smoothly fading * between old and new settings. * * Those equations from Robert Bristow-Johnson's `Cookbook * formulae for audio EQ biquad filter coefficients', obtained * from Harmony-central.com / Computer / Programming. They are * the result of the bilinear transform on an analogue filter * prototype. To quote, `BLT frequency warping has been taken * into account for both significant frequency relocation and for * bandwidth readjustment'. */ float omega = (float) (2.0 * M_PI * (_fres / ((float) _fluid->sample_rate))); float sin_coeff = (float) sin(omega); float cos_coeff = (float) cos(omega); float alpha_coeff = sin_coeff / (2.0f * q_lin); float a0_inv = 1.0f / (1.0f + alpha_coeff); /* Calculate the filter coefficients. All coefficients are * normalized by a0. Think of `a1' as `a1/a0'. * * Here a couple of multiplications are saved by reusing common expressions. * The original equations should be: * b0=(1.-cos_coeff)*a0_inv*0.5*voice->filter_gain; * b1=(1.-cos_coeff)*a0_inv*voice->filter_gain; * b2=(1.-cos_coeff)*a0_inv*0.5*voice->filter_gain; */ float a1_temp = -2.0f * cos_coeff * a0_inv; float a2_temp = (1.0f - alpha_coeff) * a0_inv; float b1_temp = (1.0f - cos_coeff) * a0_inv * filter_gain; /* both b0 -and- b2 */ float b02_temp = b1_temp * 0.5f; if (filter_startup) { /* The filter is calculated, because the voice was started up. * In this case set the filter coefficients without delay. */ a1 = a1_temp; a2 = a2_temp; b02 = b02_temp; b1 = b1_temp; filter_coeff_incr_count = 0; filter_startup = 0; // printf("Setting initial filter coefficients.\n"); } else { /* The filter frequency is changed. Calculate an increment * factor, so that the new setting is reached after one buffer * length. x_incr is added to the current value FLUID_BUFSIZE * times. The length is arbitrarily chosen. Longer than one * buffer will sacrifice some performance, though. Note: If * the filter is still too 'grainy', then increase this number * at will. */ #define FILTER_TRANSITION_SAMPLES 64 // (FLUID_BUFSIZE) a1_incr = (a1_temp - a1) / FILTER_TRANSITION_SAMPLES; a2_incr = (a2_temp - a2) / FILTER_TRANSITION_SAMPLES; b02_incr = (b02_temp - b02) / FILTER_TRANSITION_SAMPLES; b1_incr = (b1_temp - b1) / FILTER_TRANSITION_SAMPLES; /* Have to add the increments filter_coeff_incr_count times. */ filter_coeff_incr_count = FILTER_TRANSITION_SAMPLES; } last_fres = _fres; fluid_check_fpe ("voice_write filter calculation"); } fluid_check_fpe ("voice_write DSP coefficients"); /*********************** run the dsp chain ************************ * The sample is mixed with the output buffer. * The buffer has to be filled from 0 to FLUID_BUFSIZE-1. * Depending on the position in the loop and the loop size, this * may require several runs. */ float l_dsp_buf[n]; dsp_buf = l_dsp_buf; unsigned count; switch (interp_method) { case FLUID_INTERP_NONE: count = dsp_float_interpolate_none(n); break; case FLUID_INTERP_LINEAR: count = dsp_float_interpolate_linear(n); break; case FLUID_INTERP_4THORDER: default: count = dsp_float_interpolate_4th_order(n); break; case FLUID_INTERP_7THORDER: count = dsp_float_interpolate_7th_order(n); break; } if (count > 0) effects(count, left, right, reverb, chorus); /* turn off voice if short count (sample ended and not looping) */ if (count < n) off(); ticks += n; } //--------------------------------------------------------- // voice_start //--------------------------------------------------------- void Voice::voice_start() { /* The maximum volume of the loop is calculated and cached once for each * sample with its nominal loop settings. This happens, when the sample is used * for the first time.*/ /* * in this function we calculate the values of all the parameters. the * parameters are converted to their most useful unit for the DSP * algorithm, for example, number of samples instead of * timecents. Some parameters keep their "perceptual" unit and * conversion will be done in the DSP function. This is the case, for * example, for the pitch since it is modulated by the controllers in * cents. */ static const int list_of_generators_to_initialize[35] = { GEN_STARTADDROFS, // SF2.01 page 48 #0 GEN_ENDADDROFS, // #1 GEN_STARTLOOPADDROFS, // #2 GEN_ENDLOOPADDROFS, // #3 // GEN_STARTADDRCOARSEOFS see comment below [1] #4 GEN_MODLFOTOPITCH, // #5 GEN_VIBLFOTOPITCH, // #6 GEN_MODENVTOPITCH, // #7 GEN_FILTERFC, // #8 GEN_FILTERQ, // #9 GEN_MODLFOTOFILTERFC, // #10 GEN_MODENVTOFILTERFC, // #11 // GEN_ENDADDRCOARSEOFS [1] #12 GEN_MODLFOTOVOL, // #13 // not defined #14 GEN_CHORUSSEND, // #15 GEN_REVERBSEND, // #16 GEN_PAN, // #17 // not defined #18 // not defined #19 // not defined #20 GEN_MODLFODELAY, // #21 GEN_MODLFOFREQ, // #22 GEN_VIBLFODELAY, // #23 GEN_VIBLFOFREQ, // #24 GEN_MODENVDELAY, // #25 GEN_MODENVATTACK, // #26 GEN_MODENVHOLD, // #27 GEN_MODENVDECAY, // #28 // GEN_MODENVSUSTAIN [1] #29 GEN_MODENVRELEASE, // #30 // GEN_KEYTOMODENVHOLD [1] #31 // GEN_KEYTOMODENVDECAY [1] #32 GEN_VOLENVDELAY, // #33 GEN_VOLENVATTACK, // #34 GEN_VOLENVHOLD, // #35 GEN_VOLENVDECAY, // #36 // GEN_VOLENVSUSTAIN [1] #37 GEN_VOLENVRELEASE, // #38 // GEN_KEYTOVOLENVHOLD [1] #39 // GEN_KEYTOVOLENVDECAY [1] #40 // GEN_STARTLOOPADDRCOARSEOFS [1] #45 GEN_KEYNUM, // #46 GEN_VELOCITY, // #47 GEN_ATTENUATION, // #48 // GEN_ENDLOOPADDRCOARSEOFS [1] #50 // GEN_COARSETUNE [1] #51 // GEN_FINETUNE [1] #52 GEN_OVERRIDEROOTKEY, // #58 GEN_PITCH, // --- -1 // end-of-list marker }; /* When the voice is made ready for the synthesis process, a lot of * voice-internal parameters have to be calculated. * * At this point, the sound font has already set the -nominal- value * for all generators (excluding GEN_PITCH). Most generators can be * modulated - they include a nominal value and an offset (which * changes with velocity, note number, channel parameters like * aftertouch, mod wheel...) Now this offset will be calculated as * follows: * * - Process each modulator once. * - Calculate its output value. * - Find the target generator. * - Add the output value to the modulation value of the generator. * * Note: The generators have been initialized with * fluid_gen_set_default_values. */ for (int i = 0; i < mod_count; i++) { Mod* m = &mod[i]; float modval = m->get_value(channel, this); int dest_gen_index = m->dest; Generator* dest_gen = &gen[dest_gen_index]; dest_gen->mod += modval; } /* The GEN_PITCH is a hack to fit the pitch bend controller into the * modulator paradigm. Now the nominal pitch of the key is set. * Note about SCALETUNE: SF2.01 8.1.3 says, that this generator is a * non-realtime parameter. So we don't allow modulation (as opposed * to _GEN(voice, GEN_SCALETUNE) When the scale tuning is varied, * one key remains fixed. Here C3 (MIDI number 60) is used. */ gen[GEN_PITCH].val = _noteTuning + _fluid->getPitch(60) + (gen[GEN_SCALETUNE].val * .01 * (_fluid->getPitch(key) - _fluid->getPitch(60))); /* Now the generators are initialized, nominal and modulation value. * The voice parameters (which depend on generators) are calculated * with update_param. Processing the list of generator * changes will calculate each voice parameter once. * * Note [1]: Some voice parameters depend on several generators. For * example, the pitch depends on GEN_COARSETUNE, GEN_FINETUNE and * GEN_PITCH. voice->pitch. Unnecessary recalculation is avoided * by removing all but one generator from the list of voice * parameters. Same with GEN_XXX and GEN_XXXCOARSE: the * initialisation list contains only GEN_XXX. */ /* Calculate the voice parameter(s) dependent on each generator. */ for (int i = 0; list_of_generators_to_initialize[i] != -1; i++) update_param(list_of_generators_to_initialize[i]); /* Make an estimate on how loud this voice can get at any time (attenuation). */ min_attenuation_cB = get_lower_boundary_for_attenuation(); /* Force setting of the phase at the first DSP loop run * This cannot be done earlier, because it depends on modulators. */ check_sample_sanity_flag = FLUID_SAMPLESANITY_STARTUP; status = FLUID_VOICE_ON; } /* * calculate_hold_decay_frames */ int Voice::calculate_hold_decay_frames(int gen_base, int gen_key2base, int is_decay) { /* Purpose: * * Returns the number of DSP loops, that correspond to the hold * (is_decay=0) or decay (is_decay=1) time. * gen_base=GEN_VOLENVHOLD, GEN_VOLENVDECAY, GEN_MODENVHOLD, * GEN_MODENVDECAY gen_key2base=GEN_KEYTOVOLENVHOLD, * GEN_KEYTOVOLENVDECAY, GEN_KEYTOMODENVHOLD, GEN_KEYTOMODENVDECAY * * SF2.01 section 8.4.3 # 31, 32, 39, 40 * GEN_KEYTOxxxENVxxx uses key 60 as 'origin'. * The unit of the generator is timecents per key number. * If KEYTOxxxENVxxx is 100, a key one octave over key 60 (72) * will cause (60-72)*100=-1200 timecents of time variation. * The time is cut in half. */ float timecents = (GEN(gen_base) + GEN(gen_key2base) * (60.0 - key)); /* Range checking */ if (is_decay){ /* SF 2.01 section 8.1.3 # 28, 36 */ if (timecents > 8000.0) timecents = 8000.0; } else { /* SF 2.01 section 8.1.3 # 27, 35 */ if (timecents > 5000) timecents = 5000.0; /* SF 2.01 section 8.1.2 # 27, 35: * The most negative number indicates no hold time */ if (timecents <= -32768.) return 0; } /* SF 2.01 section 8.1.3 # 27, 28, 35, 36 */ if (timecents < -12000.0) timecents = -12000.0; float seconds = fluid_tc2sec(timecents); return (int)((float)_fluid->sample_rate * seconds); } /* * update_param * * Purpose: * * The value of a generator (gen) has changed. (The different * generators are listed in fluid.h, or in SF2.01 page 48-49) * Now the dependent 'voice' parameters are calculated. * * fluid_voice_update_param can be called during the setup of the * voice (to calculate the initial value for a voice parameter), or * during its operation (a generator has been changed due to * real-time parameter modifications like pitch-bend). * * Note: The generator holds three values: The base value .val, an * offset caused by modulators .mod, and an offset caused by the * NRPN system. _GEN(voice, generator_enumerator) returns the sum * of all three. */ void Voice::update_param(int _gen) { double q_dB; float x; float y; unsigned int count; double gain = _fluid->masterGain() / 32768.0f; switch (_gen) { case GEN_PAN: /* range checking is done in the fluid_pan function */ pan = GEN(GEN_PAN); amp_left = fluid_pan(pan, 1) * gain; amp_right = fluid_pan(pan, 0) * gain; break; case GEN_ATTENUATION: attenuation = GEN(GEN_ATTENUATION); /* Range: SF2.01 section 8.1.3 # 48 * Motivation for range checking: * OHPiano.SF2 sets initial attenuation to a whooping -96 dB */ attenuation = qBound(0.0f, attenuation, 1440.0f); break; /* The pitch is calculated from three different generators. * Read comment in fluid.h about GEN_PITCH. */ case GEN_PITCH: case GEN_COARSETUNE: case GEN_FINETUNE: /* The testing for allowed range is done in 'fluid_ct2hz' */ pitch = GEN(GEN_PITCH) + 100.0f * GEN(GEN_COARSETUNE) + GEN(GEN_FINETUNE); break; case GEN_REVERBSEND: /* The generator unit is 'tenths of a percent'. */ // reverb_send = GEN(GEN_REVERBSEND) / 1000.0f; reverb_send = float(channel->cc[EFFECTS_DEPTH1]) / 128.0; // fluid_clip(reverb_send, 0.0, 1.0); // amp_reverb = reverb_send * _fluid->masterGain(); amp_reverb = reverb_send * gain; break; case GEN_CHORUSSEND: /* The generator unit is 'tenths of a percent'. */ chorus_send = GEN(GEN_CHORUSSEND) / 1000.0f; fluid_clip(chorus_send, 0.0, 1.0); amp_chorus = chorus_send * gain; break; case GEN_OVERRIDEROOTKEY: /* This is a non-realtime parameter. Therefore the .mod part of the generator * can be neglected. * NOTE: origpitch sets MIDI root note while pitchadj is a fine tuning amount * which offsets the original rate. This means that the fine tuning is * inverted with respect to the root note (so subtract it, not add). */ if (gen[GEN_OVERRIDEROOTKEY].val > -1) { //FIXME: use flag instead of -1 root_pitch = gen[GEN_OVERRIDEROOTKEY].val * 100.0f - sample->pitchadj; } else { root_pitch = sample->origpitch * 100.0f - sample->pitchadj; } root_pitch = _fluid->ct2hz(root_pitch); if (sample != 0) root_pitch *= (float) _fluid->sample_rate / sample->samplerate; break; case GEN_FILTERFC: /* The resonance frequency is converted from absolute cents to * midicents .val and .mod are both used, this permits real-time * modulation. The allowed range is tested in the 'fluid_ct2hz' * function [PH,20021214] */ fres = GEN(GEN_FILTERFC); /* The synthesis loop will have to recalculate the filter * coefficients. */ last_fres = -1.0f; break; case GEN_FILTERQ: /* The generator contains 'centibels' (1/10 dB) => divide by 10 to * obtain dB */ q_dB = GEN(GEN_FILTERQ) / 10.0f; /* Range: SF2.01 section 8.1.3 # 8 (convert from cB to dB => /10) */ fluid_clip(q_dB, 0.0f, 96.0f); /* Short version: Modify the Q definition in a way, that a Q of 0 * dB leads to no resonance hump in the freq. response. * * Long version: From SF2.01, page 39, item 9 (initialFilterQ): * "The gain at the cutoff frequency may be less than zero when * zero is specified". Assume q_dB=0 / q_lin=1: If we would leave * q as it is, then this results in a 3 dB hump slightly below * fc. At fc, the gain is exactly the DC gain (0 dB). What is * (probably) meant here is that the filter does not show a * resonance hump for q_dB=0. In this case, the corresponding * q_lin is 1/sqrt(2)=0.707. The filter should have 3 dB of * attenuation at fc now. In this case Q_dB is the height of the * resonance peak not over the DC gain, but over the frequency * response of a non-resonant filter. This idea is implemented as * follows: */ q_dB -= 3.01f; /* The 'sound font' Q is defined in dB. The filter needs a linear q. Convert. */ q_lin = (float) (pow(10.0f, q_dB / 20.0f)); /* SF 2.01 page 59: * * The SoundFont specs ask for a gain reduction equal to half the * height of the resonance peak (Q). For example, for a 10 dB * resonance peak, the gain is reduced by 5 dB. This is done by * multiplying the total gain with sqrt(1/Q). `Sqrt' divides dB * by 2 (100 lin = 40 dB, 10 lin = 20 dB, 3.16 lin = 10 dB etc) * The gain is later factored into the 'b' coefficients * (numerator of the filter equation). This gain factor depends * only on Q, so this is the right place to calculate it. */ filter_gain = (float) (1.0 / sqrt(q_lin)); /* The synthesis loop will have to recalculate the filter coefficients. */ last_fres = -1.; break; case GEN_MODLFOTOPITCH: modlfo_to_pitch = GEN(GEN_MODLFOTOPITCH); fluid_clip(modlfo_to_pitch, -12000.0, 12000.0); break; case GEN_MODLFOTOVOL: modlfo_to_vol = GEN(GEN_MODLFOTOVOL); fluid_clip(modlfo_to_vol, -960.0, 960.0); break; case GEN_MODLFOTOFILTERFC: modlfo_to_fc = GEN(GEN_MODLFOTOFILTERFC); fluid_clip(modlfo_to_fc, -12000, 12000); break; case GEN_MODLFODELAY: x = GEN(GEN_MODLFODELAY); fluid_clip(x, -12000.0f, 5000.0f); modlfo_delay = (unsigned int) (_fluid->sample_rate * fluid_tc2sec_delay(x)); break; case GEN_MODLFOFREQ: /* - the frequency is converted into a delta value, per frame * - the delay into a sample delay */ x = GEN(GEN_MODLFOFREQ); fluid_clip(x, -16000.0f, 4500.0f); modlfo_incr = (4.0f * fluid_act2hz(x) / _fluid->sample_rate); break; case GEN_VIBLFOFREQ: /* vib lfo * * - the frequency is converted into a delta value per frame * - the delay into a sample delay */ x = GEN(GEN_VIBLFOFREQ); fluid_clip(x, -16000.0f, 4500.0f); viblfo_incr = (4.0f * fluid_act2hz(x) / _fluid->sample_rate); break; case GEN_VIBLFODELAY: x = GEN(GEN_VIBLFODELAY); fluid_clip(x, -12000.0f, 5000.0f); viblfo_delay = (unsigned int) (_fluid->sample_rate * fluid_tc2sec_delay(x)); break; case GEN_VIBLFOTOPITCH: viblfo_to_pitch = GEN(GEN_VIBLFOTOPITCH); fluid_clip(viblfo_to_pitch, -12000.0, 12000.0); break; case GEN_KEYNUM: /* GEN_KEYNUM: SF2.01 page 46, item 46 * * If this generator is active, it forces the key number to its * value. Non-realtime controller. * * There is a flag, which should indicate, whether a generator is * enabled or not. But here we rely on the default value of -1. */ x = GEN(GEN_KEYNUM); if (x >= 0) key = x; break; case GEN_VELOCITY: /* GEN_VELOCITY: SF2.01 page 46, item 47 * * If this generator is active, it forces the velocity to its * value. Non-realtime controller. * * There is a flag, which should indicate, whether a generator is * enabled or not. But here we rely on the default value of -1. */ x = GEN(GEN_VELOCITY); if (x > 0) vel = x; break; case GEN_MODENVTOPITCH: modenv_to_pitch = GEN(GEN_MODENVTOPITCH); fluid_clip(modenv_to_pitch, -12000.0, 12000.0); break; case GEN_MODENVTOFILTERFC: modenv_to_fc = GEN(GEN_MODENVTOFILTERFC); /* Range: SF2.01 section 8.1.3 # 1 * Motivation for range checking: * Filter is reported to make funny noises now and then */ fluid_clip(modenv_to_fc, -12000.0, 12000.0); break; /* sample start and ends points * * Range checking is initiated via the * check_sample_sanity flag, * because it is impossible to check here: * During the voice setup, all modulators are processed, while * the voice is inactive. Therefore, illegal settings may * occur during the setup (for example: First move the loop * end point ahead of the loop start point => invalid, then * move the loop start point forward => valid again. */ case GEN_STARTADDROFS: /* SF2.01 section 8.1.3 # 0 */ case GEN_STARTADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 4 */ if (sample != 0) { start = (sample->start + (int) GEN(GEN_STARTADDROFS) + 32768 * (int) GEN(GEN_STARTADDRCOARSEOFS)); check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK; } break; case GEN_ENDADDROFS: /* SF2.01 section 8.1.3 # 1 */ case GEN_ENDADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 12 */ if (sample != 0) { end = (sample->end + (int) GEN(GEN_ENDADDROFS) + 32768 * (int) GEN(GEN_ENDADDRCOARSEOFS)); check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK; } break; case GEN_STARTLOOPADDROFS: /* SF2.01 section 8.1.3 # 2 */ case GEN_STARTLOOPADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 45 */ if (sample != 0) { loopstart = (sample->loopstart + (int) GEN(GEN_STARTLOOPADDROFS) + 32768 * (int) GEN(GEN_STARTLOOPADDRCOARSEOFS)); check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK; } break; case GEN_ENDLOOPADDROFS: /* SF2.01 section 8.1.3 # 3 */ case GEN_ENDLOOPADDRCOARSEOFS: /* SF2.01 section 8.1.3 # 50 */ if (sample != 0) { loopend = (sample->loopend + (int) GEN(GEN_ENDLOOPADDROFS) + 32768 * (int) GEN(GEN_ENDLOOPADDRCOARSEOFS)); check_sample_sanity_flag = FLUID_SAMPLESANITY_CHECK; } break; /* Conversion functions differ in range limit */ #define NUM_FRAMES_DELAY(_v) (unsigned int) (_fluid->sample_rate * fluid_tc2sec_delay(_v)) #define NUM_FRAMES_ATTACK(_v) (unsigned int) (_fluid->sample_rate * fluid_tc2sec_attack(_v)) #define NUM_FRAMES_RELEASE(_v) (unsigned int) (_fluid->sample_rate * fluid_tc2sec_release(_v)) /* volume envelope * * - delay and hold times are converted to absolute number of samples * - sustain is converted to its absolute value * - attack, decay and release are converted to their increment per sample */ case GEN_VOLENVDELAY: /* SF2.01 section 8.1.3 # 33 */ x = GEN(GEN_VOLENVDELAY); fluid_clip(x, -12000.0f, 5000.0f); count = NUM_FRAMES_DELAY(x); volenv_data[FLUID_VOICE_ENVDELAY].count = count; volenv_data[FLUID_VOICE_ENVDELAY].coeff = 0.0f; volenv_data[FLUID_VOICE_ENVDELAY].incr = 0.0f; volenv_data[FLUID_VOICE_ENVDELAY].min = -1.0f; volenv_data[FLUID_VOICE_ENVDELAY].max = 1.0f; break; case GEN_VOLENVATTACK: /* SF2.01 section 8.1.3 # 34 */ x = GEN(GEN_VOLENVATTACK); fluid_clip(x, -12000.0f, 8000.0f); count = 1 + NUM_FRAMES_ATTACK(x); volenv_data[FLUID_VOICE_ENVATTACK].count = count; volenv_data[FLUID_VOICE_ENVATTACK].coeff = 1.0f; volenv_data[FLUID_VOICE_ENVATTACK].incr = count ? 1.0f / count : 0.0f; volenv_data[FLUID_VOICE_ENVATTACK].min = -1.0f; volenv_data[FLUID_VOICE_ENVATTACK].max = 1.0f; break; case GEN_VOLENVHOLD: /* SF2.01 section 8.1.3 # 35 */ case GEN_KEYTOVOLENVHOLD: /* SF2.01 section 8.1.3 # 39 */ count = calculate_hold_decay_frames(GEN_VOLENVHOLD, GEN_KEYTOVOLENVHOLD, 0); /* 0 means: hold */ volenv_data[FLUID_VOICE_ENVHOLD].count = count; volenv_data[FLUID_VOICE_ENVHOLD].coeff = 1.0f; volenv_data[FLUID_VOICE_ENVHOLD].incr = 0.0f; volenv_data[FLUID_VOICE_ENVHOLD].min = -1.0f; volenv_data[FLUID_VOICE_ENVHOLD].max = 2.0f; break; case GEN_VOLENVDECAY: /* SF2.01 section 8.1.3 # 36 */ case GEN_VOLENVSUSTAIN: /* SF2.01 section 8.1.3 # 37 */ case GEN_KEYTOVOLENVDECAY: /* SF2.01 section 8.1.3 # 40 */ y = 1.0f - 0.001f * GEN(GEN_VOLENVSUSTAIN); fluid_clip(y, 0.0f, 1.0f); count = calculate_hold_decay_frames(GEN_VOLENVDECAY, GEN_KEYTOVOLENVDECAY, 1); /* 1 for decay */ volenv_data[FLUID_VOICE_ENVDECAY].count = count; volenv_data[FLUID_VOICE_ENVDECAY].coeff = 1.0f; volenv_data[FLUID_VOICE_ENVDECAY].incr = count ? -1.0f / count : 0.0f; volenv_data[FLUID_VOICE_ENVDECAY].min = y; volenv_data[FLUID_VOICE_ENVDECAY].max = 2.0f; break; case GEN_VOLENVRELEASE: /* SF2.01 section 8.1.3 # 38 */ x = GEN(GEN_VOLENVRELEASE); fluid_clip(x, FLUID_MIN_VOLENVRELEASE, 8000.0f); count = 1 + NUM_FRAMES_RELEASE(x); volenv_data[FLUID_VOICE_ENVRELEASE].count = count; volenv_data[FLUID_VOICE_ENVRELEASE].coeff = 1.0f; volenv_data[FLUID_VOICE_ENVRELEASE].incr = count ? -1.0f / count : 0.0f; volenv_data[FLUID_VOICE_ENVRELEASE].min = 0.0f; volenv_data[FLUID_VOICE_ENVRELEASE].max = 1.0f; break; /* Modulation envelope */ case GEN_MODENVDELAY: /* SF2.01 section 8.1.3 # 25 */ x = GEN(GEN_MODENVDELAY); fluid_clip(x, -12000.0f, 5000.0f); modenv_data[FLUID_VOICE_ENVDELAY].count = NUM_FRAMES_DELAY(x); modenv_data[FLUID_VOICE_ENVDELAY].coeff = 0.0f; modenv_data[FLUID_VOICE_ENVDELAY].incr = 0.0f; modenv_data[FLUID_VOICE_ENVDELAY].min = -1.0f; modenv_data[FLUID_VOICE_ENVDELAY].max = 1.0f; break; case GEN_MODENVATTACK: /* SF2.01 section 8.1.3 # 26 */ x = GEN(GEN_MODENVATTACK); fluid_clip(x, -12000.0f, 8000.0f); count = 1 + NUM_FRAMES_ATTACK(x); modenv_data[FLUID_VOICE_ENVATTACK].count = count; modenv_data[FLUID_VOICE_ENVATTACK].coeff = 1.0f; modenv_data[FLUID_VOICE_ENVATTACK].incr = count ? 1.0f / count : 0.0f; modenv_data[FLUID_VOICE_ENVATTACK].min = -1.0f; modenv_data[FLUID_VOICE_ENVATTACK].max = 1.0f; break; case GEN_MODENVHOLD: /* SF2.01 section 8.1.3 # 27 */ case GEN_KEYTOMODENVHOLD: /* SF2.01 section 8.1.3 # 31 */ count = calculate_hold_decay_frames(GEN_MODENVHOLD, GEN_KEYTOMODENVHOLD, 0); /* 1 means: hold */ modenv_data[FLUID_VOICE_ENVHOLD].count = count; modenv_data[FLUID_VOICE_ENVHOLD].coeff = 1.0f; modenv_data[FLUID_VOICE_ENVHOLD].incr = 0.0f; modenv_data[FLUID_VOICE_ENVHOLD].min = -1.0f; modenv_data[FLUID_VOICE_ENVHOLD].max = 2.0f; break; case GEN_MODENVDECAY: /* SF 2.01 section 8.1.3 # 28 */ case GEN_MODENVSUSTAIN: /* SF 2.01 section 8.1.3 # 29 */ case GEN_KEYTOMODENVDECAY: /* SF 2.01 section 8.1.3 # 32 */ count = calculate_hold_decay_frames(GEN_MODENVDECAY, GEN_KEYTOMODENVDECAY, 1); /* 1 for decay */ y = 1.0f - 0.001f * GEN(GEN_MODENVSUSTAIN); fluid_clip(y, 0.0f, 1.0f); modenv_data[FLUID_VOICE_ENVDECAY].count = count; modenv_data[FLUID_VOICE_ENVDECAY].coeff = 1.0f; modenv_data[FLUID_VOICE_ENVDECAY].incr = count ? -1.0f / count : 0.0f; modenv_data[FLUID_VOICE_ENVDECAY].min = y; modenv_data[FLUID_VOICE_ENVDECAY].max = 2.0f; break; case GEN_MODENVRELEASE: /* SF 2.01 section 8.1.3 # 30 */ x = GEN(GEN_MODENVRELEASE); fluid_clip(x, -12000.0f, 8000.0f); count = 1 + NUM_FRAMES_RELEASE(x); modenv_data[FLUID_VOICE_ENVRELEASE].count = count; modenv_data[FLUID_VOICE_ENVRELEASE].coeff = 1.0f; modenv_data[FLUID_VOICE_ENVRELEASE].incr = count ? -1.0f / count : 0.0; modenv_data[FLUID_VOICE_ENVRELEASE].min = 0.0f; modenv_data[FLUID_VOICE_ENVRELEASE].max = 2.0f; break; } /* switch gen */ } /** * fluid_voice_modulate * * In this implementation, I want to make sure that all controllers * are event based: the parameter values of the DSP algorithm should * only be updates when a controller event arrived and not at every * iteration of the audio cycle (which would probably be feasible if * the synth was made in silicon). * * The update is done in three steps: * * - first, we look for all the modulators that have the changed * controller as a source. This will yield a list of generators that * will be changed because of the controller event. * * - For every changed generator, calculate its new value. This is the * sum of its original value plus the values of al the attached * modulators. * * - For every changed generator, convert its value to the correct * unit of the corresponding DSP parameter * */ void Voice::modulate(bool _cc, int _ctrl) { for (int i = 0; i < mod_count; i++) { Mod* m = &mod[i]; /* step 1: find all the modulators that have the changed controller * as input source. */ if (m->has_source(_cc, _ctrl)) { int g = m->get_dest(); float modval = 0.0; /* step 2: for every changed modulator, calculate the modulation * value of its associated generator */ for (int k = 0; k < mod_count; k++) { if (mod[k].has_dest(g)) { modval += mod[k].get_value(channel, this); } } gen[g].set_mod(modval); /* step 3: now that we have the new value of the generator, * recalculate the parameter values that are derived from the * generator */ update_param(g); } } } /** * fluid_voice_modulate_all * * Update all the modulators. This function is called after a * ALL_CTRL_OFF MIDI message has been received (CC 121). * */ void Voice::modulate_all() { /* Loop through all the modulators. FIXME: we should loop through the set of generators instead of the set of modulators. We risk to call 'fluid_voice_update_param' several times for the same generator if several modulators have that generator as destination. It's not an error, just a wast of energy (think polution, global warming, unhappy musicians, ...) */ for (int i = 0; i < mod_count; i++) { Mod* m = &mod[i]; int g = m->get_dest(); float modval = 0.0; /* Accumulate the modulation values of all the modulators with * destination generator 'gen' */ for (int k = 0; k < mod_count; k++) { if (mod[k].has_dest(g)) modval += mod[k].get_value(channel, this); } gen[g].set_mod(modval); /* Update the parameter values that are depend on the generator * 'gen' */ update_param(g); } } /* * fluid_voice_noteoff */ void Voice::noteoff() { if (channel && channel->sustained()) status = FLUID_VOICE_SUSTAINED; else { if (volenv_section == FLUID_VOICE_ENVATTACK) { /* A voice is turned off during the attack section of the volume * envelope. The attack section ramps up linearly with * amplitude. The other sections use logarithmic scaling. Calculate new * volenv_val to achieve equievalent amplitude during the release phase * for seamless volume transition. */ if (volenv_val > 0) { float lfo = modlfo_val * -modlfo_to_vol; float amp = volenv_val * pow (10.0, lfo / -200); float env_value = - ((-200 * log (amp) / log (10.0) - lfo) / 960.0 - 1); fluid_clip (env_value, 0.0, 1.0); volenv_val = env_value; } } volenv_section = FLUID_VOICE_ENVRELEASE; volenv_count = 0; modenv_section = FLUID_VOICE_ENVRELEASE; modenv_count = 0; } } /* * fluid_voice_kill_excl * * Percussion sounds can be mutually exclusive: for example, a 'closed * hihat' sound will terminate an 'open hihat' sound ringing at the * same time. This behaviour is modeled using 'exclusive classes', * turning on a voice with an exclusive class other than 0 will kill * all other voices having that exclusive class within the same preset * or channel. fluid_voice_kill_excl gets called, when 'voice' is to * be killed for that reason. */ void Voice::kill_excl() { if (!isPlaying()) return; /* Turn off the exclusive class information for this voice, so that it doesn't get killed twice */ gen_set(GEN_EXCLUSIVECLASS, 0); /* If the voice is not yet in release state, put it into release state */ if (volenv_section != FLUID_VOICE_ENVRELEASE) { volenv_section = FLUID_VOICE_ENVRELEASE; volenv_count = 0; modenv_section = FLUID_VOICE_ENVRELEASE; modenv_count = 0; } /* Speed up the volume envelope */ /* The value was found through listening tests with hi-hat samples. */ gen_set(GEN_VOLENVRELEASE, -200); update_param(GEN_VOLENVRELEASE); /* Speed up the modulation envelope */ gen_set(GEN_MODENVRELEASE, -200); update_param(GEN_MODENVRELEASE); } //--------------------------------------------------------- // off // Turns off a voice, meaning that it is not processed // anymore by the DSP loop. //--------------------------------------------------------- void Voice::off() { chan = NO_CHANNEL; volenv_section = FLUID_VOICE_ENVFINISHED; volenv_count = 0; modenv_section = FLUID_VOICE_ENVFINISHED; modenv_count = 0; status = FLUID_VOICE_OFF; _fluid->freeVoice(this); } /* * fluid_voice_add_mod * * Adds a modulator to the voice. "mode" indicates, what to do, if * an identical modulator exists already. * * mode == FLUID_VOICE_ADD: Identical modulators on preset level are added * mode == FLUID_VOICE_OVERWRITE: Identical modulators on instrument level are overwritten * mode == FLUID_VOICE_DEFAULT: This is a default modulator, there can be no identical modulator. * Don't check. */ void Voice::add_mod(const Mod* _mod, int mode) { /* * Some soundfonts come with a huge number of non-standard * controllers, because they have been designed for one particular * sound card. Discard them, maybe print a warning. */ if (((_mod->flags1 & FLUID_MOD_CC) == 0) && ((_mod->src1 != 0) /* SF2.01 section 8.2.1: Constant value */ && (_mod->src1 != 2) /* Note-on velocity */ && (_mod->src1 != 3) /* Note-on key number */ && (_mod->src1 != 10) /* Poly pressure */ && (_mod->src1 != 13) /* Channel pressure */ && (_mod->src1 != 14) /* Pitch wheel */ && (_mod->src1 != 16))) { /* Pitch wheel sensitivity */ _fluid->log("Ignoring invalid controller, using non-CC source %i.", _mod->src1); return; } if (mode == FLUID_VOICE_ADD) { /* if identical modulator exists, add them */ for (int i = 0; i < mod_count; i++) { if (test_identity(&mod[i], _mod)) { mod[i].amount += _mod->amount; return; } } } else if (mode == FLUID_VOICE_OVERWRITE) { /* if identical modulator exists, replace it (only the amount has to be changed) */ for (int i = 0; i < mod_count; i++) { if (test_identity(&mod[i], _mod)) { mod[i].amount = _mod->amount; return; } } } /* Add a new modulator (No existing modulator to add / overwrite). Also, default modulators (FLUID_VOICE_DEFAULT) are added without checking, if the same modulator already exists. */ if (mod_count < FLUID_NUM_MOD) mod[mod_count++] = *_mod; } /* * fluid_voice_get_lower_boundary_for_attenuation * * Purpose: * * A lower boundary for the attenuation (as in 'the minimum * attenuation of this voice, with volume pedals, modulators * etc. resulting in minimum attenuation, cannot fall below x cB) is * calculated. This has to be called during fluid_voice_init, after * all modulators have been run on the voice once. Also, * voice->attenuation has to be initialized. */ float Voice::get_lower_boundary_for_attenuation() { float possible_att_reduction_cB = 0; for (int i = 0; i < mod_count; i++) { Mod* m = &mod[i]; /* Modulator has attenuation as target and can change over time? */ if ((m->dest == GEN_ATTENUATION) && ((m->flags1 & FLUID_MOD_CC) || (m->flags2 & FLUID_MOD_CC))) { float current_val = m->get_value(channel, this); float v = fabs(m->amount); if ((m->src1 == FLUID_MOD_PITCHWHEEL) || (m->flags1 & FLUID_MOD_BIPOLAR) || (m->flags2 & FLUID_MOD_BIPOLAR) || (m->amount < 0)) { /* Can this modulator produce a negative contribution? */ v *= -1.0; } else { /* No negative value possible. But still, the minimum contribution is 0. */ v = 0; } /* For example: * - current_val=100 * - min_val=-4000 * - possible_att_reduction_cB += 4100 */ if (current_val > v) possible_att_reduction_cB += (current_val - v); } } float lower_bound = attenuation - possible_att_reduction_cB; /* SF2.01 specs do not allow negative attenuation */ if (lower_bound < 0) lower_bound = 0; return lower_bound; } /* Purpose: * * Make sure, that sample start / end point and loop points are in * proper order. When starting up, calculate the initial phase. */ void Voice::check_sample_sanity() { int min_index_nonloop=(int) sample->start; int max_index_nonloop=(int) sample->end; /* make sure we have enough samples surrounding the loop */ int min_index_loop=(int) sample->start + FLUID_MIN_LOOP_PAD; int max_index_loop=(int) sample->end - FLUID_MIN_LOOP_PAD; fluid_check_fpe("voice_check_sample_sanity start"); if (!check_sample_sanity_flag) return; #if 0 printf("Sample from %i to %i\n", sample->start, sample->end); printf("Sample loop from %i %i\n", sample->loopstart, sample->loopend); printf("Playback from %i to %i\n", start, end); printf("Playback loop from %i to %i\n", loopstart, loopend); #endif /* Keep the start point within the sample data */ if (start < min_index_nonloop) start = min_index_nonloop; else if (start > max_index_nonloop) start = max_index_nonloop; /* Keep the end point within the sample data */ if (end < min_index_nonloop) end = min_index_nonloop; else if (end > max_index_nonloop) end = max_index_nonloop; /* Keep start and end point in the right order */ if (start > end) { int temp = start; start = end; end = temp; } /* Zero length? */ if (start == end) { off(); return; } if ((SAMPLEMODE() == FLUID_LOOP_UNTIL_RELEASE) || (SAMPLEMODE() == FLUID_LOOP_DURING_RELEASE)) { /* Keep the loop start point within the sample data */ if (loopstart < min_index_loop) loopstart = min_index_loop; else if (loopstart > max_index_loop) loopstart = max_index_loop; /* Keep the loop end point within the sample data */ if (loopend < min_index_loop) loopend = min_index_loop; else if (loopend > max_index_loop) loopend = max_index_loop; /* Keep loop start and end point in the right order */ if (loopstart > loopend){ int temp = loopstart; loopstart = loopend; loopend = temp; } /* Loop too short? Then don't loop. */ if (loopend < loopstart + FLUID_MIN_LOOP_SIZE) gen[GEN_SAMPLEMODE].val = FLUID_UNLOOPED; /* The loop points may have changed. Obtain a new estimate for the loop volume. */ /* Is the voice loop within the sample loop? */ if ((int)loopstart >= (int)sample->loopstart && (int)loopend <= (int)sample->loopend){ /* Is there a valid peak amplitude available for the loop? */ if (sample->amplitude_that_reaches_noise_floor_is_valid) { double g = _fluid->masterGain(); if (g == 0.0) g = 0.0000001; amplitude_that_reaches_noise_floor_loop = sample->amplitude_that_reaches_noise_floor / g; } else /* Worst case */ amplitude_that_reaches_noise_floor_loop = amplitude_that_reaches_noise_floor_nonloop; } } /* if sample mode is looped */ /* Run startup specific code (only once, when the voice is started) */ if (check_sample_sanity_flag & FLUID_SAMPLESANITY_STARTUP) { if (max_index_loop - min_index_loop < FLUID_MIN_LOOP_SIZE){ if ((SAMPLEMODE() == FLUID_LOOP_UNTIL_RELEASE) || (SAMPLEMODE() == FLUID_LOOP_DURING_RELEASE)) gen[GEN_SAMPLEMODE].val = FLUID_UNLOOPED; } /* Set the initial phase of the voice (using the result from the start offset modulators). */ phase.setInt(start); } /* if startup */ /* Is this voice run in loop mode, or does it run straight to the end of the waveform data? */ if (((SAMPLEMODE() == FLUID_LOOP_UNTIL_RELEASE) && (volenv_section < FLUID_VOICE_ENVRELEASE)) || (SAMPLEMODE() == FLUID_LOOP_DURING_RELEASE)) { /* Yes, it will loop as soon as it reaches the loop point. In * this case we must prevent, that the playback pointer (phase) * happens to end up beyond the 2nd loop point, because the * point has moved. The DSP algorithm is unable to cope with * that situation. So if the phase is beyond the 2nd loop * point, set it to the start of the loop. No way to avoid some * noise here. Note: If the sample pointer ends up -before the * first loop point- instead, then the DSP loop will just play * the sample, enter the loop and proceed as expected => no * actions required. */ int index_in_sample = phase.index(); if (index_in_sample >= loopend) { /* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Phase after 2nd loop point!"); */ phase.setInt(loopstart); } } /* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Sample from %i to %i, loop from %i to %i", voice->start, voice->end, voice->loopstart, voice->loopend); */ /* Sample sanity has been assured. Don't check again, until some sample parameter is changed by modulation. */ check_sample_sanity_flag = 0; fluid_check_fpe("voice_check_sample_sanity"); } //--------------------------------------------------------- // set_param //--------------------------------------------------------- void Voice::set_param(int g, float nrpn_value, int abs) { gen[g].nrpn = nrpn_value; gen[g].flags = (abs)? GEN_ABS_NRPN : GEN_SET; update_param(g); } /** If the peak volume during the loop is known, then the voice can * be released earlier during the release phase. Otherwise, the * voice will operate (inaudibly), until the envelope is at the * nominal turnoff point. In many cases the loop volume is many dB * below the maximum volume. For example, the loop volume for a * typical acoustic piano is 20 dB below max. Taking that into * account in the turn-off algorithm we can save 20 dB / 100 dB => * 1/5 of the total release time. * So it's a good idea to call fluid_voice_optimize_sample * on each sample once. */ /* - Scan the loop * - determine the peak level * - Calculate, what factor will make the loop inaudible * - Store in sample */ void Sample::optimize() { Sample* s = this; signed short peak_max = 0; signed short peak_min = 0; signed short peak; float normalized_amplitude_during_loop; double result; int i; /* ignore ROM and other(?) invalid samples */ if (!s->valid()) return; if (!s->amplitude_that_reaches_noise_floor_is_valid) { /* Only once */ /* Scan the loop */ for (i = (int)s->loopstart; i < (int) s->loopend; i ++) { signed short val = s->data[i]; if (val > peak_max) peak_max = val; else if (val < peak_min) peak_min = val; } /* Determine the peak level */ if (peak_max >- peak_min) peak = peak_max; else peak =- peak_min; if (peak == 0) /* Avoid division by zero */ peak = 1; /* Calculate what factor will make the loop inaudible * For example: Take a peak of 3277 (10 % of 32768). The * normalized amplitude is 0.1 (10 % of 32768). An amplitude * factor of 0.0001 (as opposed to the default 0.00001) will * drop this sample to the noise floor. */ /* 16 bits => 96+4=100 dB dynamic range => 0.00001 */ normalized_amplitude_during_loop = ((float)peak)/32768.; result = FLUID_NOISE_FLOOR / normalized_amplitude_during_loop; /* Store in sample */ s->amplitude_that_reaches_noise_floor = (double)result; s->amplitude_that_reaches_noise_floor_is_valid = 1; } } /* Purpose: * * - filters (applies a lowpass filter with variable cutoff frequency and quality factor) * - mixes the processed sample to left and right output using the pan setting * - sends the processed sample to chorus and reverb * * A couple of variables are used internally, their results are discarded: * - dsp_phase_fractional: The fractional part of dsp_phase * - dsp_coeff: A table of four coefficients, depending on the fractional phase. * Used to interpolate between samples. * - dsp_process_buffer: Holds the processed signal between stages * - dsp_centernode: delay line for the IIR filter * - dsp_hist1: same * - dsp_hist2: same * */ void Voice::effects(int count, float* left, float* right, float* reverb, float* chorus) { /* filter (implement the voice filter according to SoundFont standard) */ /* Check for denormal number (too close to zero). */ if (fabs (hist1) < 1e-20) hist1 = 0.0f; /* FIXME JMG - Is this even needed? */ /* Two versions of the filter loop. One, while the filter is * changing towards its new setting. The other, if the filter * doesn't change. */ if (filter_coeff_incr_count > 0) { /* Increment is added to each filter coefficient filter_coeff_incr_count times. */ for (int i = 0; i < count; i++) { /* The filter is implemented in Direct-II form. */ float dsp_centernode = dsp_buf[i] - a1 * hist1 - a2 * hist2; dsp_buf[i] = b02 * (dsp_centernode + hist2) + b1 * hist1; hist2 = hist1; hist1 = dsp_centernode; if (filter_coeff_incr_count-- > 0) { a1 += a1_incr; a2 += a2_incr; b02 += b02_incr; b1 += b1_incr; } } } else { /* The filter parameters are constant. This is duplicated to save time. */ for (int i = 0; i < count; i++) { // The filter is implemented in Direct-II form. float dsp_centernode = dsp_buf[i] - a1 * hist1 - a2 * hist2; dsp_buf[i] = b02 * (dsp_centernode + hist2) + b1 * hist1; hist2 = hist1; hist1 = dsp_centernode; } } /* pan (Copy the signal to the left and right output buffer) The voice * panning generator has a range of -500 .. 500. If it is centered, * it's close to 0. amp_left and amp_right are then the * same, and we can save one multiplication per voice and sample. */ if ((-0.5 < pan) && (pan < 0.5)) { /* The voice is centered. Use amp_left twice. */ for (int i = 0; i < count; i++) { float v = amp_left * dsp_buf[i]; left[i] += v; right[i] += v; } } else { /* The voice is not centered. Stereo samples have one side zero. */ if (amp_left != 0.0) { for (int i = 0; i < count; i++) left[i] += amp_left * dsp_buf[i]; } if (amp_right != 0.0) { for (int i = 0; i < count; i++) right[i] += amp_right * dsp_buf[i]; } } for (int i = 0; i < count; i++) { float v = dsp_buf[i]; reverb[i] += amp_reverb * v; chorus[i] += amp_chorus * v; } } }