Split voice into soft and hard real-time parts

This commit is contained in:
David Henningsson 2010-06-26 15:02:58 +00:00
parent 335f21b697
commit e3fd4984dd
8 changed files with 1695 additions and 599 deletions

View file

@ -114,7 +114,6 @@ set ( libfluidsynth_SOURCES
fluid_conv.h
fluid_defsfont.c
fluid_defsfont.h
fluid_dsp_float.c
fluid_event.c
fluid_event_priv.h
fluid_event_queue.c
@ -127,6 +126,9 @@ set ( libfluidsynth_SOURCES
rvoice/fluid_iir_filter.h
rvoice/fluid_lfo.h
rvoice/fluid_adsr_env.h
rvoice/fluid_rvoice.h
rvoice/fluid_rvoice.c
rvoice/fluid_rvoice_dsp.c
fluid_list.c
fluid_list.h
fluid_mdriver.c

View file

@ -249,7 +249,7 @@ fluid_synth_init(void)
fluid_conversion_config();
fluid_dsp_float_config();
fluid_rvoice_dsp_config();
fluid_sys_config();
@ -1324,11 +1324,9 @@ fluid_synth_noteoff_LOCAL(fluid_synth_t* synth, int chan, int key)
used_voices++;
}
}
FLUID_LOG(FLUID_INFO, "noteoff\t%d\t%d\t%d\t%05d\t%.3f\t%.3f\t%.3f\t%d",
FLUID_LOG(FLUID_INFO, "noteoff\t%d\t%d\t%d\t%05d\t%.3f\t%d",
voice->chan, voice->key, 0, voice->id,
(float) (voice->start_time + voice->ticks) / 44100.0f,
(fluid_curtime() - synth->start) / 1000.0f,
(float) voice->ticks / 44100.0f,
used_voices);
} /* if verbose */
@ -2977,7 +2975,8 @@ static int
fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out)
{
fluid_real_t local_voice_buf[FLUID_BUFSIZE];
int i, auchan, start_index, voice_index;
int i, auchan;
// int start_index, voice_index;
fluid_voice_t* voice;
fluid_real_t* left_buf;
fluid_real_t* right_buf;
@ -3024,6 +3023,7 @@ fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out)
fluid_profile(FLUID_PROF_ONE_BLOCK_CLEAR, prof_ref);
#if 0
if (synth->cores > 1)
{
/* Look for first active voice to process */
@ -3111,6 +3111,7 @@ got_voice: /* We got a voice to process */
}
else /* synth->cores < 1 - Not multi-core enabled */
{
#endif
/* call all playing synthesis processes */
for (i = 0; i < synth->polyphony; i++) {
fluid_profile_ref_var (prof_ref_voice);
@ -3135,12 +3136,16 @@ got_voice: /* We got a voice to process */
left_buf = synth->left_buf[auchan];
right_buf = synth->right_buf[auchan];
fluid_voice_write (voice, local_voice_buf);
fluid_voice_mix (voice, left_buf, right_buf, reverb_buf, chorus_buf);
count = fluid_voice_write (voice, local_voice_buf);
if (count > 0)
fluid_voice_mix (voice, count, local_voice_buf, left_buf, right_buf,
reverb_buf, chorus_buf);
fluid_profile (FLUID_PROF_ONE_BLOCK_VOICE, prof_ref_voice);
}
#if 0
}
#endif
fluid_check_fpe("Synthesis processes");
@ -3424,7 +3429,8 @@ fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t* synth)
this_voice_prio -= (synth->noteid - fluid_voice_get_id(voice));
/* take a rough estimate of loudness into account. Louder voices are more important. */
this_voice_prio += fluid_voice_get_loudness(voice) * 1000.;
// FIXME
// this_voice_prio += fluid_voice_get_loudness(voice) * 1000.;
/* check if this voice has less priority than the previous candidate. */
if (this_voice_prio < best_prio)

View file

@ -61,12 +61,12 @@
*
* ENUM
*/
enum fluid_loop {
/*enum fluid_loop {
FLUID_UNLOOPED = 0,
FLUID_LOOP_DURING_RELEASE = 1,
FLUID_NOTUSED = 2,
FLUID_LOOP_UNTIL_RELEASE = 3
};
};*/
enum fluid_synth_status
{

File diff suppressed because it is too large Load diff

View file

@ -28,6 +28,7 @@
#include "fluid_iir_filter.h"
#include "fluid_adsr_env.h"
#include "fluid_lfo.h"
#include "fluid_rvoice.h"
#define NO_CHANNEL 0xff
@ -49,97 +50,29 @@ struct _fluid_voice_t
it's used for noteoff's */
unsigned char status;
unsigned char chan; /* the channel number, quick access for channel messages */
unsigned char key; /* the key, quick acces for noteoff */
unsigned char key; /* the key, quick access for noteoff */
unsigned char vel; /* the velocity */
fluid_channel_t* channel;
fluid_gen_t gen[GEN_LAST];
fluid_mod_t mod[FLUID_NUM_MOD];
int mod_count;
int has_looped; /* Flag that is set as soon as the first loop is completed. */
fluid_sample_t* sample;
int check_sample_sanity_flag; /* Flag that initiates, that sample-related parameters
have to be checked. */
#if 0
/* Instead of keeping a pointer to a fluid_sample_t structure,
* I think it would be better to copy the sample data in the
* voice structure. SoundFont loader then do not have to
* allocate and maintain the fluid_sample_t structure. [PH]
*
* The notify callback may be used also for streaming samples.
*/
short* sample_data; /* pointer to the sample data */
int sample_data_offset; /* the offset of data[0] in the whole sample */
int sample_data_length; /* the length of the data array */
unsigned int sample_start;
unsigned int sample_end;
unsigned int sample_loopstart;
unsigned int sample_loopend;
unsigned int sample_rate;
int sample_origpitch;
int sample_pitchadj;
int sample_type;
int (*sample_notify)(fluid_voice_t* voice, int reason);
void* sample_userdata;
#endif
fluid_sample_t* sample; /* Pointer to sample (dupe in rvoice) */
int has_noteoff; /* Flag set when noteoff has been sent */
/* basic parameters */
fluid_real_t output_rate; /* the sample rate of the synthesizer */
fluid_real_t output_rate; /* the sample rate of the synthesizer (dupe in rvoice) */
unsigned int start_time;
unsigned int ticks;
unsigned int noteoff_ticks; /* Delay note-off until this tick */
fluid_real_t amp; /* current linear amplitude */
fluid_phase_t phase; /* the phase of the sample wave */
/* Temporary variables used in fluid_voice_write() */
fluid_real_t phase_incr; /* the phase increment for the next 64 samples */
fluid_real_t amp_incr; /* amplitude increment value */
fluid_real_t *dsp_buf; /* buffer to store interpolated sample data to */
int dsp_buf_count; /* Number of audio samples in dsp_buf */
/* End temporary variables */
/* basic parameters */
fluid_real_t pitch; /* the pitch in midicents */
fluid_real_t attenuation; /* the attenuation in centibels */
fluid_real_t min_attenuation_cB; /* Estimate on the smallest possible attenuation
* during the lifetime of the voice */
fluid_real_t root_pitch, root_pitch_hz;
fluid_real_t pitch; /* the pitch in midicents (dupe in rvoice) */
fluid_real_t attenuation; /* the attenuation in centibels (dupe in rvoice) */
fluid_real_t root_pitch;
/* sample and loop start and end points (offset in sample memory). */
int start;
int end;
int loopstart;
int loopend; /* Note: first point following the loop (superimposed on loopstart) */
/* master gain */
/* master gain (dupe in rvoice) */
fluid_real_t synth_gain;
/* vol env */
fluid_adsr_env_t volenv;
fluid_real_t amplitude_that_reaches_noise_floor_nonloop;
fluid_real_t amplitude_that_reaches_noise_floor_loop;
/* mod env */
fluid_adsr_env_t modenv;
fluid_real_t modenv_to_fc;
fluid_real_t modenv_to_pitch;
/* mod lfo */
fluid_lfo_t modlfo;
fluid_real_t modlfo_to_fc;
fluid_real_t modlfo_to_pitch;
fluid_real_t modlfo_to_vol;
/* vib lfo */
fluid_lfo_t viblfo;
fluid_real_t viblfo_to_pitch;
fluid_iir_filter_t resonant_filter; /* IIR resonance dsp filter */
/* pan */
fluid_real_t pan;
fluid_real_t amp_left;
@ -153,8 +86,9 @@ struct _fluid_voice_t
fluid_real_t chorus_send;
fluid_real_t amp_chorus;
/* interpolation method, as in fluid_interp in fluidsynth.h */
int interp_method;
/* rvoice control */
fluid_rvoice_t* rvoice;
int can_access_rvoice; /* False if rvoice is being rendered in separate thread */
/* for debugging */
int debug;
@ -193,9 +127,10 @@ void fluid_voice_update_param(fluid_voice_t* voice, int gen);
int fluid_voice_noteoff(fluid_voice_t* voice);
int fluid_voice_off(fluid_voice_t* voice);
void fluid_voice_mix (fluid_voice_t *voice,
fluid_real_t* left_buf, fluid_real_t* right_buf,
fluid_real_t* reverb_buf, fluid_real_t* chorus_buf);
void fluid_voice_mix (fluid_voice_t *voice, int count, fluid_real_t* dsp_buf,
fluid_real_t* left_buf, fluid_real_t* right_buf,
fluid_real_t* reverb_buf, fluid_real_t* chorus_buf);
int fluid_voice_kill_excl(fluid_voice_t* voice);
#define fluid_voice_get_channel(voice) ((voice)->channel)
@ -210,7 +145,7 @@ int fluid_voice_kill_excl(fluid_voice_t* voice);
/* A voice is 'ON', if it has not yet received a noteoff
* event. Sending a noteoff event will advance the envelopes to
* section 5 (release). */
#define _ON(voice) ((voice)->status == FLUID_VOICE_ON && fluid_adsr_env_get_section(&voice->volenv) < FLUID_VOICE_ENVRELEASE)
#define _ON(voice) ((voice)->status == FLUID_VOICE_ON && !voice->has_noteoff)
#define _SUSTAINED(voice) ((voice)->status == FLUID_VOICE_SUSTAINED)
#define _AVAILABLE(voice) (((voice)->status == FLUID_VOICE_CLEAN) || ((voice)->status == FLUID_VOICE_OFF))
#define _RELEASED(voice) ((voice)->chan == NO_CHANNEL)
@ -227,10 +162,6 @@ fluid_real_t fluid_voice_gen_value(fluid_voice_t* voice, int num);
+ (fluid_real_t)(_voice)->gen[_n].mod \
+ (fluid_real_t)(_voice)->gen[_n].nrpn)
#define FLUID_SAMPLESANITY_CHECK (1 << 0)
#define FLUID_SAMPLESANITY_STARTUP (1 << 1)
/* defined in fluid_dsp_float.c */
void fluid_dsp_float_config (void);

View file

@ -0,0 +1,644 @@
/* 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 "fluid_rvoice.h"
#include "fluid_conv.h"
#include "fluid_sys.h"
/**
* @return -1 if voice has finished, 0 if it's currently quiet, 1 otherwise
*/
static inline int
fluid_rvoice_calc_amp(fluid_rvoice_t* voice)
{
fluid_real_t target_amp; /* target amplitude */
if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVDELAY)
return -1; /* The volume amplitude is in hold phase. No sound is produced. */
if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == 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 (voice->dsp.attenuation)
* fluid_cb2amp (fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol)
* fluid_adsr_env_get_val(&voice->envlfo.volenv);
}
else
{
fluid_real_t amplitude_that_reaches_noise_floor;
fluid_real_t amp_max;
target_amp = fluid_atten2amp (voice->dsp.attenuation)
* fluid_cb2amp (960.0f * (1.0f - fluid_adsr_env_get_val(&voice->envlfo.volenv))
+ fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.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 (voice->dsp.has_looped)
amplitude_that_reaches_noise_floor = voice->dsp.amplitude_that_reaches_noise_floor_loop;
else
amplitude_that_reaches_noise_floor = voice->dsp.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 (voice->dsp.min_attenuation_cB) *
fluid_adsr_env_get_val(&voice->envlfo.volenv);
/* 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)
{
return 0;
}
}
/* Volume increment to go from voice->amp to target_amp in FLUID_BUFSIZE steps */
voice->dsp.amp_incr = (target_amp - voice->dsp.amp) / FLUID_BUFSIZE;
fluid_check_fpe ("voice_write amplitude calculation");
/* no volume and not changing? - No need to process */
if ((voice->dsp.amp == 0.0f) && (voice->dsp.amp_incr == 0.0f))
return -1;
return 1;
}
/* these should be the absolute minimum that FluidSynth can deal with */
#define FLUID_MIN_LOOP_SIZE 2
#define FLUID_MIN_LOOP_PAD 0
#define FLUID_SAMPLESANITY_CHECK (1 << 0)
#define FLUID_SAMPLESANITY_STARTUP (1 << 1)
/* Purpose:
*
* Make sure, that sample start / end point and loop points are in
* proper order. When starting up, calculate the initial phase.
* TODO: Investigate whether this can be moved from rvoice to voice.
*/
static void
fluid_rvoice_check_sample_sanity(fluid_rvoice_t* voice)
{
int min_index_nonloop=(int) voice->dsp.sample->start;
int max_index_nonloop=(int) voice->dsp.sample->end;
/* make sure we have enough samples surrounding the loop */
int min_index_loop=(int) voice->dsp.sample->start + FLUID_MIN_LOOP_PAD;
int max_index_loop=(int) voice->dsp.sample->end - FLUID_MIN_LOOP_PAD + 1; /* 'end' is last valid sample, loopend can be + 1 */
fluid_check_fpe("voice_check_sample_sanity start");
if (!voice->dsp.check_sample_sanity_flag){
return;
}
#if 0
printf("Sample from %i to %i\n",voice->dsp.sample->start, voice->dsp.sample->end);
printf("Sample loop from %i %i\n",voice->dsp.sample->loopstart, voice->dsp.sample->loopend);
printf("Playback from %i to %i\n", voice->dsp.start, voice->dsp.end);
printf("Playback loop from %i to %i\n",voice->dsp.loopstart, voice->dsp.loopend);
#endif
/* Keep the start point within the sample data */
if (voice->dsp.start < min_index_nonloop){
voice->dsp.start = min_index_nonloop;
} else if (voice->dsp.start > max_index_nonloop){
voice->dsp.start = max_index_nonloop;
}
/* Keep the end point within the sample data */
if (voice->dsp.end < min_index_nonloop){
voice->dsp.end = min_index_nonloop;
} else if (voice->dsp.end > max_index_nonloop){
voice->dsp.end = max_index_nonloop;
}
/* Keep start and end point in the right order */
if (voice->dsp.start > voice->dsp.end){
int temp = voice->dsp.start;
voice->dsp.start = voice->dsp.end;
voice->dsp.end = temp;
/*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of start / end points!"); */
}
/* Zero length? */
if (voice->dsp.start == voice->dsp.end){
fluid_rvoice_voiceoff(voice);
return;
}
if ((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE)
|| (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)) {
/* Keep the loop start point within the sample data */
if (voice->dsp.loopstart < min_index_loop){
voice->dsp.loopstart = min_index_loop;
} else if (voice->dsp.loopstart > max_index_loop){
voice->dsp.loopstart = max_index_loop;
}
/* Keep the loop end point within the sample data */
if (voice->dsp.loopend < min_index_loop){
voice->dsp.loopend = min_index_loop;
} else if (voice->dsp.loopend > max_index_loop){
voice->dsp.loopend = max_index_loop;
}
/* Keep loop start and end point in the right order */
if (voice->dsp.loopstart > voice->dsp.loopend){
int temp = voice->dsp.loopstart;
voice->dsp.loopstart = voice->dsp.loopend;
voice->dsp.loopend = temp;
/*FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Changing order of loop points!"); */
}
/* Loop too short? Then don't loop. */
if (voice->dsp.loopend < voice->dsp.loopstart + FLUID_MIN_LOOP_SIZE){
voice->dsp.samplemode = 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)voice->dsp.loopstart >= (int)voice->dsp.sample->loopstart
&& (int)voice->dsp.loopend <= (int)voice->dsp.sample->loopend){
/* Is there a valid peak amplitude available for the loop? */
if (voice->dsp.sample->amplitude_that_reaches_noise_floor_is_valid){
voice->dsp.amplitude_that_reaches_noise_floor_loop=voice->dsp.sample->amplitude_that_reaches_noise_floor / voice->dsp.synth_gain;
} else {
/* Worst case */
voice->dsp.amplitude_that_reaches_noise_floor_loop=voice->dsp.amplitude_that_reaches_noise_floor_nonloop;
};
};
} /* if sample mode is looped */
/* Run startup specific code (only once, when the voice is started) */
if (voice->dsp.check_sample_sanity_flag & FLUID_SAMPLESANITY_STARTUP){
if (max_index_loop - min_index_loop < FLUID_MIN_LOOP_SIZE){
if ((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE)
|| (voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE)){
voice->dsp.samplemode = FLUID_UNLOOPED;
}
}
/* Set the initial phase of the voice (using the result from the
start offset modulators). */
fluid_phase_set_int(voice->dsp.phase, voice->dsp.start);
} /* if startup */
/* Is this voice run in loop mode, or does it run straight to the
end of the waveform data? */
if (((voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE) &&
(fluid_adsr_env_get_section(&voice->envlfo.volenv) < FLUID_VOICE_ENVRELEASE))
|| (voice->dsp.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 = fluid_phase_index(voice->dsp.phase);
if (index_in_sample >= voice->dsp.loopend){
/* FLUID_LOG(FLUID_DBG, "Loop / sample sanity check: Phase after 2nd loop point!"); */
fluid_phase_set_int(voice->dsp.phase, voice->dsp.loopstart);
}
}
/* 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); */
/* Sample sanity has been assured. Don't check again, until some
sample parameter is changed by modulation. */
voice->dsp.check_sample_sanity_flag=0;
#if 0
printf("Sane? playback loop from %i to %i\n", voice->dsp.loopstart, voice->dsp.loopend);
#endif
fluid_check_fpe("voice_check_sample_sanity");
}
/**
* Synthesize a voice to a buffer.
*
* @param voice rvoice to synthesize
* @param dsp_buf Audio buffer to synthesize to (#FLUID_BUFSIZE in length)
* @return Count of samples written to dsp_buf. (-1 means voice is currently
* quiet, 0 .. #FLUID_BUFSIZE-1 means voice finished.)
*
* Panning, reverb and chorus are processed separately. The dsp interpolation
* routine is in (fluid_dsp_float.c).
*/
int
fluid_rvoice_write (fluid_rvoice_t* voice, fluid_real_t *dsp_buf)
{
int ticks = voice->envlfo.ticks;
int count;
/******************* sample sanity check **********/
if (voice->dsp.check_sample_sanity_flag)
fluid_rvoice_check_sample_sanity(voice);
/******************* noteoff check ****************/
if (voice->envlfo.noteoff_ticks != 0 &&
voice->envlfo.ticks >= voice->envlfo.noteoff_ticks) {
fluid_rvoice_noteoff(voice, 0);
}
voice->envlfo.ticks += FLUID_BUFSIZE;
/******************* vol env **********************/
fluid_adsr_env_calc(&voice->envlfo.volenv, 1);
fluid_check_fpe ("voice_write vol env");
if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == FLUID_VOICE_ENVFINISHED)
return 0;
/******************* mod env **********************/
fluid_adsr_env_calc(&voice->envlfo.modenv, 0);
fluid_check_fpe ("voice_write mod env");
/******************* lfo **********************/
fluid_lfo_calc(&voice->envlfo.modlfo, ticks);
fluid_check_fpe ("voice_write mod LFO");
fluid_lfo_calc(&voice->envlfo.viblfo, ticks);
fluid_check_fpe ("voice_write vib LFO");
/******************* amplitude **********************/
count = fluid_rvoice_calc_amp(voice);
if (count <= 0)
return count;
/******************* phase **********************/
/* 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.*/
voice->dsp.phase_incr = fluid_ct2hz_real(voice->dsp.pitch +
fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_pitch
+ fluid_lfo_get_val(&voice->envlfo.viblfo) * voice->envlfo.viblfo_to_pitch
+ fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_pitch)
/ voice->dsp.root_pitch_hz;
fluid_check_fpe ("voice_write phase calculation");
/* if phase_incr is not advancing, set it to the minimum fraction value (prevent stuckage) */
if (voice->dsp.phase_incr == 0) voice->dsp.phase_incr = 1;
voice->dsp.is_looping = voice->dsp.samplemode == FLUID_LOOP_DURING_RELEASE
|| (voice->dsp.samplemode == FLUID_LOOP_UNTIL_RELEASE
&& fluid_adsr_env_get_section(&voice->envlfo.volenv) < FLUID_VOICE_ENVRELEASE);
/*********************** 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. */
voice->dsp.dsp_buf = dsp_buf;
switch (voice->dsp.interp_method)
{
case FLUID_INTERP_NONE:
count = fluid_rvoice_dsp_interpolate_none (&voice->dsp);
break;
case FLUID_INTERP_LINEAR:
count = fluid_rvoice_dsp_interpolate_linear (&voice->dsp);
break;
case FLUID_INTERP_4THORDER:
default:
count = fluid_rvoice_dsp_interpolate_4th_order (&voice->dsp);
break;
case FLUID_INTERP_7THORDER:
count = fluid_rvoice_dsp_interpolate_7th_order (&voice->dsp);
break;
}
fluid_check_fpe ("voice_write interpolation");
if (count == 0)
return count;
/*************** resonant filter ******************/
fluid_iir_filter_calc(&voice->resonant_filter, voice->dsp.output_rate,
fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_fc +
fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_fc);
fluid_iir_filter_apply(&voice->resonant_filter, dsp_buf, count);
return count;
}
/**
* Mix data down to buffers
*
* @param buffers Destination buffer(s)
* @param dsp_buf Mono sample source
* @param count Number of samples to process
*/
void
fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t* buffers,
fluid_real_t* dsp_buf, int count)
{
int bufcount = buffers->count;
int i, dsp_i;
for (i=0; i < bufcount; i++) {
fluid_real_t amp = buffers->bufs[i].amp;
fluid_real_t* buf = buffers->bufs[i].buf;
if (buf == NULL || amp == 0.0f)
continue;
/* Optimization for centered stereo samples - we can save one
multiplication per sample */
if (i < bufcount-1 && buffers->bufs[i+1].buf != NULL &&
buffers->bufs[i+1].amp == amp) {
fluid_real_t* next_buf = buffers->bufs[i+1].buf;
for (dsp_i = 0; dsp_i < count; dsp_i++) {
fluid_real_t samp = amp * dsp_buf[dsp_i];
buf[dsp_i] += samp;
next_buf[dsp_i] += samp;
}
i++;
}
else {
for (dsp_i = 0; dsp_i < count; dsp_i++)
buf[dsp_i] += amp * dsp_buf[dsp_i];
}
}
}
/**
* Synthesize a voice to several buffers.
*
* @param voice rvoice to synthesize
* @return Count of samples written to the buffers. (-1 means voice is currently
* quiet, 0 .. #FLUID_BUFSIZE-1 means voice finished.)
*/
int
fluid_rvoice_render(fluid_rvoice_t* voice)
{
fluid_real_t dsp_buf[FLUID_BUFSIZE];
int result = fluid_rvoice_write(voice, dsp_buf);
if (result > 0)
fluid_rvoice_buffers_mix(&voice->buffers, dsp_buf, result);
return result;
}
void
fluid_rvoice_reset(fluid_rvoice_t* voice)
{
voice->dsp.has_looped = 0;
voice->envlfo.ticks = 0;
voice->envlfo.noteoff_ticks = 0;
voice->dsp.amp = 0.0f; /* The last value of the volume envelope, used to
calculate the volume increment during
processing */
/* mod env initialization*/
fluid_adsr_env_reset(&voice->envlfo.modenv);
/* vol env initialization */
fluid_adsr_env_reset(&voice->envlfo.volenv);
/* Fixme: Retrieve from any other existing
voice on this channel to keep LFOs in
unison? */
fluid_lfo_reset(&voice->envlfo.viblfo);
fluid_lfo_reset(&voice->envlfo.modlfo);
/* Clear sample history in filter */
fluid_iir_filter_reset(&voice->resonant_filter);
/* Force setting of the phase at the first DSP loop run
* This cannot be done earlier, because it depends on modulators.
[DH] Is that comment really true? */
voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP;
}
void
fluid_rvoice_noteoff(fluid_rvoice_t* voice, unsigned int min_ticks)
{
if (min_ticks > voice->envlfo.ticks) {
/* Delay noteoff */
voice->envlfo.noteoff_ticks = min_ticks;
return;
}
voice->envlfo.noteoff_ticks = 0;
if (fluid_adsr_env_get_section(&voice->envlfo.volenv) == 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 (fluid_adsr_env_get_val(&voice->envlfo.volenv) > 0){
fluid_real_t lfo = fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol;
fluid_real_t amp = fluid_adsr_env_get_val(&voice->envlfo.volenv) * pow (10.0, lfo / -200);
fluid_real_t env_value = - ((-200 * log (amp) / log (10.0) - lfo) / 960.0 - 1);
fluid_clip (env_value, 0.0, 1.0);
fluid_adsr_env_set_val(&voice->envlfo.volenv, env_value);
}
}
fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVRELEASE);
fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVRELEASE);
}
void
fluid_rvoice_set_output_rate(fluid_rvoice_t* voice, fluid_real_t value)
{
voice->dsp.output_rate = value;
}
void
fluid_rvoice_set_interp_method(fluid_rvoice_t* voice, int value)
{
voice->dsp.interp_method = value;
}
void
fluid_rvoice_set_root_pitch_hz(fluid_rvoice_t* voice, fluid_real_t value)
{
voice->dsp.root_pitch_hz = value;
}
void
fluid_rvoice_set_pitch(fluid_rvoice_t* voice, fluid_real_t value)
{
voice->dsp.pitch = value;
}
void
fluid_rvoice_set_attenuation(fluid_rvoice_t* voice, fluid_real_t value)
{
voice->dsp.attenuation = value;
}
void
fluid_rvoice_set_min_attenuation_cB(fluid_rvoice_t* voice, fluid_real_t value)
{
voice->dsp.min_attenuation_cB = value;
}
void
fluid_rvoice_set_buf_amp(fluid_rvoice_t* voice, unsigned int bufnum, fluid_real_t value)
{
if (bufnum >= FLUID_RVOICE_MAX_BUFS) return; /* Safety net */
if (voice->buffers.count <= bufnum)
voice->buffers.count = bufnum+1;
voice->buffers.bufs[bufnum].amp = value;
}
void
fluid_rvoice_set_viblfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value)
{
voice->envlfo.viblfo_to_pitch = value;
}
void fluid_rvoice_set_modlfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value)
{
voice->envlfo.modlfo_to_pitch = value;
}
void
fluid_rvoice_set_modlfo_to_vol(fluid_rvoice_t* voice, fluid_real_t value)
{
voice->envlfo.modlfo_to_vol = value;
}
void
fluid_rvoice_set_modlfo_to_fc(fluid_rvoice_t* voice, fluid_real_t value)
{
voice->envlfo.modlfo_to_fc = value;
}
void
fluid_rvoice_set_modenv_to_fc(fluid_rvoice_t* voice, fluid_real_t value)
{
voice->envlfo.modenv_to_fc = value;
}
void
fluid_rvoice_set_modenv_to_pitch(fluid_rvoice_t* voice, fluid_real_t value)
{
voice->envlfo.modenv_to_pitch = value;
}
void
fluid_rvoice_set_synth_gain(fluid_rvoice_t* voice, fluid_real_t value)
{
voice->dsp.synth_gain = value;
/* 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.
*/
voice->dsp.amplitude_that_reaches_noise_floor_nonloop = FLUID_NOISE_FLOOR / value;
voice->dsp.amplitude_that_reaches_noise_floor_loop = FLUID_NOISE_FLOOR / value;
voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
}
void
fluid_rvoice_set_start(fluid_rvoice_t* voice, int value)
{
voice->dsp.start = value;
voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
}
void
fluid_rvoice_set_end(fluid_rvoice_t* voice, int value)
{
voice->dsp.end = value;
voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
}
void
fluid_rvoice_set_loopstart(fluid_rvoice_t* voice, int value)
{
voice->dsp.loopstart = value;
voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
}
void fluid_rvoice_set_loopend(fluid_rvoice_t* voice, int value)
{
voice->dsp.loopend = value;
voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
}
void fluid_rvoice_set_samplemode(fluid_rvoice_t* voice, enum fluid_loop value)
{
voice->dsp.samplemode = value;
voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_CHECK;
}
void
fluid_rvoice_set_sample(fluid_rvoice_t* voice, fluid_sample_t* value)
{
if (voice->dsp.sample) {
fluid_sample_decr_ref(voice->dsp.sample);
voice->dsp.sample = NULL;
}
if (value) {
voice->dsp.sample = value;
fluid_sample_incr_ref(voice->dsp.sample);
voice->dsp.check_sample_sanity_flag |= FLUID_SAMPLESANITY_STARTUP;
}
}
void
fluid_rvoice_voiceoff(fluid_rvoice_t* voice)
{
fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVFINISHED);
fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVFINISHED);
if (voice->dsp.sample) {
fluid_sample_decr_ref(voice->dsp.sample);
voice->dsp.sample = NULL;
}
}

View file

@ -0,0 +1,196 @@
/* 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
*/
#ifndef _FLUID_RVOICE_H
#define _FLUID_RVOICE_H
#include "fluidsynth_priv.h"
#include "fluid_iir_filter.h"
#include "fluid_adsr_env.h"
#include "fluid_lfo.h"
#include "fluid_phase.h"
#include "fluid_sfont.h"
typedef struct _fluid_rvoice_envlfo_t fluid_rvoice_envlfo_t;
typedef struct _fluid_rvoice_dsp_t fluid_rvoice_dsp_t;
typedef struct _fluid_rvoice_buffers_t fluid_rvoice_buffers_t;
typedef struct _fluid_rvoice_t fluid_rvoice_t;
/* 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
enum fluid_loop {
FLUID_UNLOOPED = 0,
FLUID_LOOP_DURING_RELEASE = 1,
FLUID_NOTUSED = 2,
FLUID_LOOP_UNTIL_RELEASE = 3
};
/**
* rvoice ticks-based parameters
* These parameters must be updated even if the voice is currently quiet.
*/
struct _fluid_rvoice_envlfo_t
{
/* Note-off minimum length */
unsigned int ticks;
unsigned int noteoff_ticks;
/* vol env */
fluid_adsr_env_t volenv;
/* mod env */
fluid_adsr_env_t modenv;
fluid_real_t modenv_to_fc;
fluid_real_t modenv_to_pitch;
/* mod lfo */
fluid_lfo_t modlfo;
fluid_real_t modlfo_to_fc;
fluid_real_t modlfo_to_pitch;
fluid_real_t modlfo_to_vol;
/* vib lfo */
fluid_lfo_t viblfo;
fluid_real_t viblfo_to_pitch;
};
/**
* rvoice parameters needed for dsp interpolation
*/
struct _fluid_rvoice_dsp_t
{
/* interpolation method, as in fluid_interp in fluidsynth.h */
int interp_method;
fluid_sample_t* sample;
int check_sample_sanity_flag; /* Flag that initiates, that sample-related parameters
have to be checked. */
/* sample and loop start and end points (offset in sample memory). */
int start;
int end;
int loopstart;
int loopend; /* Note: first point following the loop (superimposed on loopstart) */
enum fluid_loop samplemode;
/* Stuff needed for phase calculations */
fluid_real_t pitch; /* the pitch in midicents */
fluid_real_t root_pitch_hz;
fluid_real_t output_rate;
/* Stuff needed for amplitude calculations */
int has_looped; /* Flag that is set as soon as the first loop is completed. */
fluid_real_t attenuation; /* the attenuation in centibels */
fluid_real_t min_attenuation_cB; /* Estimate on the smallest possible attenuation
* during the lifetime of the voice */
fluid_real_t amplitude_that_reaches_noise_floor_nonloop;
fluid_real_t amplitude_that_reaches_noise_floor_loop;
fluid_real_t synth_gain; /* master gain */
/* Dynamic input to the interpolator below */
fluid_real_t *dsp_buf; /* buffer to store interpolated sample data to */
fluid_real_t amp; /* current linear amplitude */
fluid_real_t amp_incr; /* amplitude increment value for the next FLUID_BUFSIZE samples */
fluid_phase_t phase; /* the phase (current sample offset) of the sample wave */
fluid_real_t phase_incr; /* the phase increment for the next FLUID_BUFSIZE samples */
int is_looping;
};
/* Currently left, right, reverb, chorus. To be changed if we
ever add surround positioning, or stereo reverb/chorus */
#define FLUID_RVOICE_MAX_BUFS (4)
/**
* rvoice mixer-related parameters
*/
struct _fluid_rvoice_buffers_t
{
unsigned int count; /* Number of buffers */
struct {
fluid_real_t amp;
fluid_real_t* buf;
} bufs[FLUID_RVOICE_MAX_BUFS];
};
/**
* Parameters needed to synthesize a voice
*/
struct _fluid_rvoice_t
{
fluid_rvoice_envlfo_t envlfo;
fluid_rvoice_dsp_t dsp;
fluid_iir_filter_t resonant_filter; /* IIR resonant dsp filter */
fluid_rvoice_buffers_t buffers;
};
int fluid_rvoice_write (fluid_rvoice_t* voice, fluid_real_t *dsp_buf);
void fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t* buffers,
fluid_real_t* dsp_buf, int count);
int fluid_rvoice_render(fluid_rvoice_t* voice);
/* Dynamic update functions */
void fluid_rvoice_noteoff(fluid_rvoice_t* voice, unsigned int min_ticks);
void fluid_rvoice_voiceoff(fluid_rvoice_t* voice);
void fluid_rvoice_reset(fluid_rvoice_t* voice);
void fluid_rvoice_set_output_rate(fluid_rvoice_t* voice, fluid_real_t output_rate);
void fluid_rvoice_set_interp_method(fluid_rvoice_t* voice, int interp_method);
void fluid_rvoice_set_root_pitch_hz(fluid_rvoice_t* voice, fluid_real_t root_pitch_hz);
void fluid_rvoice_set_pitch(fluid_rvoice_t* voice, fluid_real_t value);
void fluid_rvoice_set_synth_gain(fluid_rvoice_t* voice, fluid_real_t value);
void fluid_rvoice_set_attenuation(fluid_rvoice_t* voice, fluid_real_t value);
void fluid_rvoice_set_min_attenuation_cB(fluid_rvoice_t* voice, fluid_real_t value);
void fluid_rvoice_set_buf_amp(fluid_rvoice_t* voice, unsigned int bufnum, fluid_real_t value);
void fluid_rvoice_set_viblfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value);
void fluid_rvoice_set_modlfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value);
void fluid_rvoice_set_modlfo_to_vol(fluid_rvoice_t* voice, fluid_real_t value);
void fluid_rvoice_set_modlfo_to_fc(fluid_rvoice_t* voice, fluid_real_t value);
void fluid_rvoice_set_modenv_to_fc(fluid_rvoice_t* voice, fluid_real_t value);
void fluid_rvoice_set_modenv_to_pitch(fluid_rvoice_t* voice, fluid_real_t value);
void fluid_rvoice_set_start(fluid_rvoice_t* voice, int value);
void fluid_rvoice_set_end(fluid_rvoice_t* voice, int value);
void fluid_rvoice_set_loopstart(fluid_rvoice_t* voice, int value);
void fluid_rvoice_set_loopend(fluid_rvoice_t* voice, int value);
void fluid_rvoice_set_sample(fluid_rvoice_t* voice, fluid_sample_t* value);
void fluid_rvoice_set_samplemode(fluid_rvoice_t* voice, enum fluid_loop value);
/* defined in fluid_rvoice_dsp.c */
void fluid_rvoice_dsp_config (void);
int fluid_rvoice_dsp_interpolate_none (fluid_rvoice_dsp_t *voice);
int fluid_rvoice_dsp_interpolate_linear (fluid_rvoice_dsp_t *voice);
int fluid_rvoice_dsp_interpolate_4th_order (fluid_rvoice_dsp_t *voice);
int fluid_rvoice_dsp_interpolate_7th_order (fluid_rvoice_dsp_t *voice);
#endif

View file

@ -0,0 +1,675 @@
/* 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 "fluidsynth_priv.h"
#include "fluid_phase.h"
#include "fluid_rvoice.h"
#include "fluid_sys.h"
/* Purpose:
*
* Interpolates audio data (obtains values between the samples of the original
* waveform data).
*
* Variables loaded from the voice structure (assigned in fluid_voice_write()):
* - dsp_data: Pointer to the original waveform data
* - dsp_phase: The position in the original waveform data.
* This has an integer and a fractional part (between samples).
* - dsp_phase_incr: For each output sample, the position in the original
* waveform advances by dsp_phase_incr. This also has an integer
* part and a fractional part.
* If a sample is played at root pitch (no pitch change),
* dsp_phase_incr is integer=1 and fractional=0.
* - dsp_amp: The current amplitude envelope value.
* - dsp_amp_incr: The changing rate of the amplitude envelope.
*
* A couple of variables are used internally, their results are discarded:
* - dsp_i: Index through the output buffer
* - dsp_buf: Output buffer of floating point values (FLUID_BUFSIZE in length)
*/
/* Interpolation (find a value between two samples of the original waveform) */
/* Linear interpolation table (2 coefficients centered on 1st) */
static fluid_real_t interp_coeff_linear[FLUID_INTERP_MAX][2];
/* 4th order (cubic) interpolation table (4 coefficients centered on 2nd) */
static fluid_real_t interp_coeff[FLUID_INTERP_MAX][4];
/* 7th order interpolation (7 coefficients centered on 3rd) */
static fluid_real_t sinc_table7[FLUID_INTERP_MAX][7];
#define SINC_INTERP_ORDER 7 /* 7th order constant */
/* Initializes interpolation tables */
void fluid_rvoice_dsp_config (void)
{
int i, i2;
double x, v;
double i_shifted;
/* Initialize the coefficients for the interpolation. The math comes
* from a mail, posted by Olli Niemitalo to the music-dsp mailing
* list (I found it in the music-dsp archives
* http://www.smartelectronix.com/musicdsp/). */
for (i = 0; i < FLUID_INTERP_MAX; i++)
{
x = (double) i / (double) FLUID_INTERP_MAX;
interp_coeff[i][0] = (fluid_real_t)(x * (-0.5 + x * (1 - 0.5 * x)));
interp_coeff[i][1] = (fluid_real_t)(1.0 + x * x * (1.5 * x - 2.5));
interp_coeff[i][2] = (fluid_real_t)(x * (0.5 + x * (2.0 - 1.5 * x)));
interp_coeff[i][3] = (fluid_real_t)(0.5 * x * x * (x - 1.0));
interp_coeff_linear[i][0] = (fluid_real_t)(1.0 - x);
interp_coeff_linear[i][1] = (fluid_real_t)x;
}
/* i: Offset in terms of whole samples */
for (i = 0; i < SINC_INTERP_ORDER; i++)
{ /* i2: Offset in terms of fractional samples ('subsamples') */
for (i2 = 0; i2 < FLUID_INTERP_MAX; i2++)
{
/* center on middle of table */
i_shifted = (double)i - ((double)SINC_INTERP_ORDER / 2.0)
+ (double)i2 / (double)FLUID_INTERP_MAX;
/* sinc(0) cannot be calculated straightforward (limit needed for 0/0) */
if (fabs (i_shifted) > 0.000001)
{
v = (fluid_real_t)sin (i_shifted * M_PI) / (M_PI * i_shifted);
/* Hamming window */
v *= (fluid_real_t)0.5 * (1.0 + cos (2.0 * M_PI * i_shifted / (fluid_real_t)SINC_INTERP_ORDER));
}
else v = 1.0;
sinc_table7[FLUID_INTERP_MAX - i2 - 1][i] = v;
}
}
#if 0
for (i = 0; i < FLUID_INTERP_MAX; i++)
{
printf ("%d %0.3f %0.3f %0.3f %0.3f %0.3f %0.3f %0.3f\n",
i, sinc_table7[0][i], sinc_table7[1][i], sinc_table7[2][i],
sinc_table7[3][i], sinc_table7[4][i], sinc_table7[5][i], sinc_table7[6][i]);
}
#endif
fluid_check_fpe("interpolation table calculation");
}
/* No interpolation. Just take the sample, which is closest to
* the playback pointer. Questionable quality, but very
* efficient. */
int
fluid_rvoice_dsp_interpolate_none (fluid_rvoice_dsp_t *voice)
{
fluid_phase_t dsp_phase = voice->phase;
fluid_phase_t dsp_phase_incr;
short int *dsp_data = voice->sample->data;
fluid_real_t *dsp_buf = voice->dsp_buf;
fluid_real_t dsp_amp = voice->amp;
fluid_real_t dsp_amp_incr = voice->amp_incr;
unsigned int dsp_i = 0;
unsigned int dsp_phase_index;
unsigned int end_index;
int looping;
/* Convert playback "speed" floating point value to phase index/fract */
fluid_phase_set_float (dsp_phase_incr, voice->phase_incr);
/* voice is currently looping? */
looping = voice->is_looping;
end_index = looping ? voice->loopend - 1 : voice->end;
while (1)
{
dsp_phase_index = fluid_phase_index_round (dsp_phase); /* round to nearest point */
/* interpolate sequence of sample points */
for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++)
{
dsp_buf[dsp_i] = dsp_amp * dsp_data[dsp_phase_index];
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index_round (dsp_phase); /* round to nearest point */
dsp_amp += dsp_amp_incr;
}
/* break out if not looping (buffer may not be full) */
if (!looping) break;
/* go back to loop start */
if (dsp_phase_index > end_index)
{
fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart);
voice->has_looped = 1;
}
/* break out if filled buffer */
if (dsp_i >= FLUID_BUFSIZE) break;
}
voice->phase = dsp_phase;
voice->amp = dsp_amp;
return (dsp_i);
}
/* Straight line interpolation.
* Returns number of samples processed (usually FLUID_BUFSIZE but could be
* smaller if end of sample occurs).
*/
int
fluid_rvoice_dsp_interpolate_linear (fluid_rvoice_dsp_t *voice)
{
fluid_phase_t dsp_phase = voice->phase;
fluid_phase_t dsp_phase_incr;
short int *dsp_data = voice->sample->data;
fluid_real_t *dsp_buf = voice->dsp_buf;
fluid_real_t dsp_amp = voice->amp;
fluid_real_t dsp_amp_incr = voice->amp_incr;
unsigned int dsp_i = 0;
unsigned int dsp_phase_index;
unsigned int end_index;
short int point;
fluid_real_t *coeffs;
int looping;
/* Convert playback "speed" floating point value to phase index/fract */
fluid_phase_set_float (dsp_phase_incr, voice->phase_incr);
/* voice is currently looping? */
looping = voice->is_looping;
/* last index before 2nd interpolation point must be specially handled */
end_index = (looping ? voice->loopend - 1 : voice->end) - 1;
/* 2nd interpolation point to use at end of loop or sample */
if (looping) point = dsp_data[voice->loopstart]; /* loop start */
else point = dsp_data[voice->end]; /* duplicate end for samples no longer looping */
while (1)
{
dsp_phase_index = fluid_phase_index (dsp_phase);
/* interpolate the sequence of sample points */
for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++)
{
coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index]
+ coeffs[1] * dsp_data[dsp_phase_index+1]);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
/* break out if buffer filled */
if (dsp_i >= FLUID_BUFSIZE) break;
end_index++; /* we're now interpolating the last point */
/* interpolate within last point */
for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
{
coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index]
+ coeffs[1] * point);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr; /* increment amplitude */
}
if (!looping) break; /* break out if not looping (end of sample) */
/* go back to loop start (if past */
if (dsp_phase_index > end_index)
{
fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart);
voice->has_looped = 1;
}
/* break out if filled buffer */
if (dsp_i >= FLUID_BUFSIZE) break;
end_index--; /* set end back to second to last sample point */
}
voice->phase = dsp_phase;
voice->amp = dsp_amp;
return (dsp_i);
}
/* 4th order (cubic) interpolation.
* Returns number of samples processed (usually FLUID_BUFSIZE but could be
* smaller if end of sample occurs).
*/
int
fluid_rvoice_dsp_interpolate_4th_order (fluid_rvoice_dsp_t *voice)
{
fluid_phase_t dsp_phase = voice->phase;
fluid_phase_t dsp_phase_incr;
short int *dsp_data = voice->sample->data;
fluid_real_t *dsp_buf = voice->dsp_buf;
fluid_real_t dsp_amp = voice->amp;
fluid_real_t dsp_amp_incr = voice->amp_incr;
unsigned int dsp_i = 0;
unsigned int dsp_phase_index;
unsigned int start_index, end_index;
short int start_point, end_point1, end_point2;
fluid_real_t *coeffs;
int looping;
/* Convert playback "speed" floating point value to phase index/fract */
fluid_phase_set_float (dsp_phase_incr, voice->phase_incr);
/* voice is currently looping? */
looping = voice->is_looping;
/* last index before 4th interpolation point must be specially handled */
end_index = (looping ? voice->loopend - 1 : voice->end) - 2;
if (voice->has_looped) /* set start_index and start point if looped or not */
{
start_index = voice->loopstart;
start_point = dsp_data[voice->loopend - 1]; /* last point in loop (wrap around) */
}
else
{
start_index = voice->start;
start_point = dsp_data[voice->start]; /* just duplicate the point */
}
/* get points off the end (loop start if looping, duplicate point if end) */
if (looping)
{
end_point1 = dsp_data[voice->loopstart];
end_point2 = dsp_data[voice->loopstart + 1];
}
else
{
end_point1 = dsp_data[voice->end];
end_point2 = end_point1;
}
while (1)
{
dsp_phase_index = fluid_phase_index (dsp_phase);
/* interpolate first sample point (start or loop start) if needed */
for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
{
coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * start_point
+ coeffs[1] * dsp_data[dsp_phase_index]
+ coeffs[2] * dsp_data[dsp_phase_index+1]
+ coeffs[3] * dsp_data[dsp_phase_index+2]);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
/* interpolate the sequence of sample points */
for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++)
{
coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1]
+ coeffs[1] * dsp_data[dsp_phase_index]
+ coeffs[2] * dsp_data[dsp_phase_index+1]
+ coeffs[3] * dsp_data[dsp_phase_index+2]);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
/* break out if buffer filled */
if (dsp_i >= FLUID_BUFSIZE) break;
end_index++; /* we're now interpolating the 2nd to last point */
/* interpolate within 2nd to last point */
for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
{
coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1]
+ coeffs[1] * dsp_data[dsp_phase_index]
+ coeffs[2] * dsp_data[dsp_phase_index+1]
+ coeffs[3] * end_point1);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
end_index++; /* we're now interpolating the last point */
/* interpolate within the last point */
for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
{
coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * dsp_data[dsp_phase_index-1]
+ coeffs[1] * dsp_data[dsp_phase_index]
+ coeffs[2] * end_point1
+ coeffs[3] * end_point2);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
if (!looping) break; /* break out if not looping (end of sample) */
/* go back to loop start */
if (dsp_phase_index > end_index)
{
fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart);
if (!voice->has_looped)
{
voice->has_looped = 1;
start_index = voice->loopstart;
start_point = dsp_data[voice->loopend - 1];
}
}
/* break out if filled buffer */
if (dsp_i >= FLUID_BUFSIZE) break;
end_index -= 2; /* set end back to third to last sample point */
}
voice->phase = dsp_phase;
voice->amp = dsp_amp;
return (dsp_i);
}
/* 7th order interpolation.
* Returns number of samples processed (usually FLUID_BUFSIZE but could be
* smaller if end of sample occurs).
*/
int
fluid_rvoice_dsp_interpolate_7th_order (fluid_rvoice_dsp_t *voice)
{
fluid_phase_t dsp_phase = voice->phase;
fluid_phase_t dsp_phase_incr;
short int *dsp_data = voice->sample->data;
fluid_real_t *dsp_buf = voice->dsp_buf;
fluid_real_t dsp_amp = voice->amp;
fluid_real_t dsp_amp_incr = voice->amp_incr;
unsigned int dsp_i = 0;
unsigned int dsp_phase_index;
unsigned int start_index, end_index;
short int start_points[3];
short int end_points[3];
fluid_real_t *coeffs;
int looping;
/* Convert playback "speed" floating point value to phase index/fract */
fluid_phase_set_float (dsp_phase_incr, voice->phase_incr);
/* add 1/2 sample to dsp_phase since 7th order interpolation is centered on
* the 4th sample point */
fluid_phase_incr (dsp_phase, (fluid_phase_t)0x80000000);
/* voice is currently looping? */
looping = voice->is_looping;
/* last index before 7th interpolation point must be specially handled */
end_index = (looping ? voice->loopend - 1 : voice->end) - 3;
if (voice->has_looped) /* set start_index and start point if looped or not */
{
start_index = voice->loopstart;
start_points[0] = dsp_data[voice->loopend - 1];
start_points[1] = dsp_data[voice->loopend - 2];
start_points[2] = dsp_data[voice->loopend - 3];
}
else
{
start_index = voice->start;
start_points[0] = dsp_data[voice->start]; /* just duplicate the start point */
start_points[1] = start_points[0];
start_points[2] = start_points[0];
}
/* get the 3 points off the end (loop start if looping, duplicate point if end) */
if (looping)
{
end_points[0] = dsp_data[voice->loopstart];
end_points[1] = dsp_data[voice->loopstart + 1];
end_points[2] = dsp_data[voice->loopstart + 2];
}
else
{
end_points[0] = dsp_data[voice->end];
end_points[1] = end_points[0];
end_points[2] = end_points[0];
}
while (1)
{
dsp_phase_index = fluid_phase_index (dsp_phase);
/* interpolate first sample point (start or loop start) if needed */
for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
{
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp
* (coeffs[0] * (fluid_real_t)start_points[2]
+ coeffs[1] * (fluid_real_t)start_points[1]
+ coeffs[2] * (fluid_real_t)start_points[0]
+ coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2]
+ coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
start_index++;
/* interpolate 2nd to first sample point (start or loop start) if needed */
for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
{
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp
* (coeffs[0] * (fluid_real_t)start_points[1]
+ coeffs[1] * (fluid_real_t)start_points[0]
+ coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2]
+ coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
start_index++;
/* interpolate 3rd to first sample point (start or loop start) if needed */
for ( ; dsp_phase_index == start_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
{
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp
* (coeffs[0] * (fluid_real_t)start_points[0]
+ coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2]
+ coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2]
+ coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
start_index -= 2; /* set back to original start index */
/* interpolate the sequence of sample points */
for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++)
{
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp
* (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3]
+ coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2]
+ coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2]
+ coeffs[6] * (fluid_real_t)dsp_data[dsp_phase_index+3]);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
/* break out if buffer filled */
if (dsp_i >= FLUID_BUFSIZE) break;
end_index++; /* we're now interpolating the 3rd to last point */
/* interpolate within 3rd to last point */
for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
{
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp
* (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3]
+ coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2]
+ coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ coeffs[5] * (fluid_real_t)dsp_data[dsp_phase_index+2]
+ coeffs[6] * (fluid_real_t)end_points[0]);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
end_index++; /* we're now interpolating the 2nd to last point */
/* interpolate within 2nd to last point */
for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
{
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp
* (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3]
+ coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2]
+ coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ coeffs[4] * (fluid_real_t)dsp_data[dsp_phase_index+1]
+ coeffs[5] * (fluid_real_t)end_points[0]
+ coeffs[6] * (fluid_real_t)end_points[1]);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
end_index++; /* we're now interpolating the last point */
/* interpolate within last point */
for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
{
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp
* (coeffs[0] * (fluid_real_t)dsp_data[dsp_phase_index-3]
+ coeffs[1] * (fluid_real_t)dsp_data[dsp_phase_index-2]
+ coeffs[2] * (fluid_real_t)dsp_data[dsp_phase_index-1]
+ coeffs[3] * (fluid_real_t)dsp_data[dsp_phase_index]
+ coeffs[4] * (fluid_real_t)end_points[0]
+ coeffs[5] * (fluid_real_t)end_points[1]
+ coeffs[6] * (fluid_real_t)end_points[2]);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
dsp_phase_index = fluid_phase_index (dsp_phase);
dsp_amp += dsp_amp_incr;
}
if (!looping) break; /* break out if not looping (end of sample) */
/* go back to loop start */
if (dsp_phase_index > end_index)
{
fluid_phase_sub_int (dsp_phase, voice->loopend - voice->loopstart);
if (!voice->has_looped)
{
voice->has_looped = 1;
start_index = voice->loopstart;
start_points[0] = dsp_data[voice->loopend - 1];
start_points[1] = dsp_data[voice->loopend - 2];
start_points[2] = dsp_data[voice->loopend - 3];
}
}
/* break out if filled buffer */
if (dsp_i >= FLUID_BUFSIZE) break;
end_index -= 3; /* set end back to 4th to last sample point */
}
/* sub 1/2 sample from dsp_phase since 7th order interpolation is centered on
* the 4th sample point (correct back to real value) */
fluid_phase_decr (dsp_phase, (fluid_phase_t)0x80000000);
voice->phase = dsp_phase;
voice->amp = dsp_amp;
return (dsp_i);
}