mirror of
https://github.com/ZDoom/fluidsynth.git
synced 2025-02-22 11:51:56 +00:00
Split voice into soft and hard real-time parts
This commit is contained in:
parent
335f21b697
commit
e3fd4984dd
8 changed files with 1695 additions and 599 deletions
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
|
|
644
fluidsynth/src/rvoice/fluid_rvoice.c
Normal file
644
fluidsynth/src/rvoice/fluid_rvoice.c
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
196
fluidsynth/src/rvoice/fluid_rvoice.h
Normal file
196
fluidsynth/src/rvoice/fluid_rvoice.h
Normal 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
|
675
fluidsynth/src/rvoice/fluid_rvoice_dsp.c
Normal file
675
fluidsynth/src/rvoice/fluid_rvoice_dsp.c
Normal 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);
|
||||
}
|
Loading…
Reference in a new issue