MuseScore/fluid/dsp.cpp
2012-05-26 14:49:10 +02:00

645 lines
26 KiB
C++

/* 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.h"
#include "voice.h"
#include "sfont.h"
namespace FluidS {
/* 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) */
float Voice::interp_coeff_linear[FLUID_INTERP_MAX][2];
/* 4th order (cubic) interpolation table (4 coefficients centered on 2nd) */
float Voice::interp_coeff[FLUID_INTERP_MAX][4];
/* 7th order interpolation (7 coefficients centered on 3rd) */
float Voice::sinc_table7[FLUID_INTERP_MAX][7];
#define SINC_INTERP_ORDER 7 /* 7th order constant */
//---------------------------------------------------------
// dsp_float_config
// Initializes interpolation tables
//---------------------------------------------------------
void Voice::dsp_float_config()
{
/* 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 (int i = 0; i < FLUID_INTERP_MAX; i++) {
double x = (double) i / (double) FLUID_INTERP_MAX;
interp_coeff[i][0] = (float)(x * (-0.5 + x * (1 - 0.5 * x)));
interp_coeff[i][1] = (float)(1.0 + x * x * (1.5 * x - 2.5));
interp_coeff[i][2] = (float)(x * (0.5 + x * (2.0 - 1.5 * x)));
interp_coeff[i][3] = (float)(0.5 * x * x * (x - 1.0));
interp_coeff_linear[i][0] = (float)(1.0 - x);
interp_coeff_linear[i][1] = (float)x;
}
/* i: Offset in terms of whole samples */
for (int i = 0; i < SINC_INTERP_ORDER; i++) {
/* i2: Offset in terms of fractional samples ('subsamples') */
for (int i2 = 0; i2 < FLUID_INTERP_MAX; i2++) {
/* center on middle of table */
double 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) */
double v;
if (fabs (i_shifted) > 0.000001) {
v = (float)sin (i_shifted * M_PI) / (M_PI * i_shifted);
/* Hamming window */
v *= (float)0.5 * (1.0 + cos (2.0 * M_PI * i_shifted / (float)SINC_INTERP_ORDER));
}
else
v = 1.0;
sinc_table7[FLUID_INTERP_MAX - i2 - 1][i] = v;
}
}
fluid_check_fpe("interpolation table calculation");
}
//-------------------------------------------------------------------
// fluid_dsp_float_interpolate_none
// No interpolation. Just take the sample, which is closest to
// the playback pointer. Questionable quality, but very
// efficient.
//-------------------------------------------------------------------
int Voice::dsp_float_interpolate_none(unsigned n)
{
Voice* voice = this;
Phase dsp_phase = voice->phase;
Phase dsp_phase_incr; // end_phase;
short int *dsp_data = voice->sample->data;
float *dsp_buf = voice->dsp_buf;
float dsp_amp = voice->amp;
float 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 */
dsp_phase_incr.setFloat(voice->phase_incr);
/* voice is currently looping? */
looping = SAMPLEMODE() == FLUID_LOOP_DURING_RELEASE
|| (SAMPLEMODE() == FLUID_LOOP_UNTIL_RELEASE
&& voice->volenv_section < FLUID_VOICE_ENVRELEASE);
end_index = looping ? voice->loopend - 1 : voice->end;
while(1) {
dsp_phase_index = dsp_phase.index_round(); // round to nearest point
/* interpolate sequence of sample points */
for ( ; dsp_i < n && dsp_phase_index <= end_index; dsp_i++) {
dsp_buf[dsp_i] = dsp_amp * dsp_data[dsp_phase_index];
/* increment phase and amplitude */
dsp_phase += dsp_phase_incr;
dsp_phase_index = dsp_phase.index_round(); /* 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) {
dsp_phase -= (voice->loopend - voice->loopstart);
voice->has_looped = true;
}
/* break out if filled buffer */
if (dsp_i >= n)
break;
}
voice->phase = dsp_phase;
voice->amp = dsp_amp;
return dsp_i;
}
//---------------------------------------------------------
// dsp_float_interpolate_linear
// Straight line interpolation.
// Returns number of samples processed (usually FLUID_BUFSIZE but could be
// smaller if end of sample occurs).
//---------------------------------------------------------
int Voice::dsp_float_interpolate_linear(unsigned n)
{
Voice* voice = this;
Phase dsp_phase = voice->phase;
Phase dsp_phase_incr; // end_phase;
short int *dsp_data = voice->sample->data;
float *dsp_buf = voice->dsp_buf;
float dsp_amp = voice->amp;
float dsp_amp_incr = voice->amp_incr;
unsigned int dsp_i = 0;
unsigned int dsp_phase_index;
unsigned int end_index;
short int point;
float *coeffs;
int looping;
/* Convert playback "speed" floating point value to phase index/fract */
dsp_phase_incr.setFloat(voice->phase_incr);
/* voice is currently looping? */
looping = SAMPLEMODE() == FLUID_LOOP_DURING_RELEASE
|| (SAMPLEMODE() == FLUID_LOOP_UNTIL_RELEASE
&& voice->volenv_section < FLUID_VOICE_ENVRELEASE);
/* 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 = dsp_phase.index();
/* interpolate the sequence of sample points */
for ( ; dsp_i < n && 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 */
dsp_phase += dsp_phase_incr;
dsp_phase_index = dsp_phase.index();
dsp_amp += dsp_amp_incr;
}
/* break out if buffer filled */
if (dsp_i >= n)
break;
end_index++; /* we're now interpolating the last point */
/* interpolate within last point */
for (; dsp_phase_index <= end_index && dsp_i < n; 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 */
dsp_phase += dsp_phase_incr;
dsp_phase_index = dsp_phase.index();
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) {
dsp_phase -= (voice->loopend - voice->loopstart);
voice->has_looped = true;
}
/* break out if filled buffer */
if (dsp_i >= n)
break;
end_index--; /* set end back to second to last sample point */
}
voice->phase = dsp_phase;
voice->amp = dsp_amp;
return dsp_i;
}
//-----------------------------------------------------------------------------
// dsp_float_interpolate_4th_order
// 4th order (cubic) interpolation.
// Returns number of samples processed (usually FLUID_BUFSIZE but could be
// smaller if end of sample occurs).
//-----------------------------------------------------------------------------
int Voice::dsp_float_interpolate_4th_order(unsigned n)
{
Phase dsp_phase_incr; // end_phase;
short int* dsp_data = sample->data;
float dsp_amp_incr = amp_incr;
unsigned int dsp_i = 0;
unsigned int dsp_phase_index;
unsigned int start_index;
short int start_point, end_point1, end_point2;
float *coeffs;
/* Convert playback "speed" floating point value to phase index/fract */
dsp_phase_incr.setFloat(phase_incr);
/* voice is currently looping? */
int looping = SAMPLEMODE() == FLUID_LOOP_DURING_RELEASE
|| (SAMPLEMODE() == FLUID_LOOP_UNTIL_RELEASE
&& volenv_section < FLUID_VOICE_ENVRELEASE);
/* last index before 4th interpolation point must be specially handled */
unsigned int end_index = (looping ? loopend - 1 : end) - 2;
if (has_looped) { /* set start_index and start point if looped or not */
start_index = loopstart;
start_point = dsp_data[loopend - 1]; /* last point in loop (wrap around) */
}
else {
start_index = start;
start_point = dsp_data[start]; /* just duplicate the point */
}
/* get points off the end (loop start if looping, duplicate point if end) */
if (looping) {
end_point1 = dsp_data[loopstart];
end_point2 = dsp_data[loopstart + 1];
}
else {
end_point1 = dsp_data[end];
end_point2 = end_point1;
}
while (1) {
dsp_phase_index = phase.index();
/* interpolate first sample point (start or loop start) if needed */
for ( ; dsp_phase_index == start_index && dsp_i < n; dsp_i++) {
coeffs = interp_coeff[fluid_phase_fract_to_tablerow (phase)];
dsp_buf[dsp_i] = 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 */
phase += dsp_phase_incr;
dsp_phase_index = phase.index();
amp += dsp_amp_incr;
}
/* interpolate the sequence of sample points */
for ( ; dsp_i < n && dsp_phase_index <= end_index; dsp_i++) {
coeffs = interp_coeff[fluid_phase_fract_to_tablerow (phase)];
dsp_buf[dsp_i] = 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 */
phase += dsp_phase_incr;
dsp_phase_index = phase.index();
amp += dsp_amp_incr;
}
/* break out if buffer filled */
if (dsp_i >= n)
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 < n; dsp_i++) {
coeffs = interp_coeff[fluid_phase_fract_to_tablerow (phase)];
dsp_buf[dsp_i] = 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 */
phase += dsp_phase_incr;
dsp_phase_index = phase.index();
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 < n; dsp_i++) {
coeffs = interp_coeff[fluid_phase_fract_to_tablerow (phase)];
dsp_buf[dsp_i] = 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 */
phase += dsp_phase_incr;
dsp_phase_index = phase.index();
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) {
phase -= (loopend - loopstart);
if (!has_looped) {
has_looped = true;
start_index = loopstart;
start_point = dsp_data[loopend - 1];
}
}
/* break out if filled buffer */
if (dsp_i >= n)
break;
end_index -= 2; /* set end back to third to last sample point */
}
return dsp_i;
}
//-----------------------------------------------------------------------------
// dsp_float_interpolate_7th_order
// 7th order interpolation.
// Returns number of samples processed (usually FLUID_BUFSIZE but could be
// smaller if end of sample occurs).
//-----------------------------------------------------------------------------
int Voice::dsp_float_interpolate_7th_order(unsigned n)
{
Voice* voice = this;
Phase dsp_phase = voice->phase;
Phase dsp_phase_incr; // end_phase;
short int *dsp_data = voice->sample->data;
float *dsp_buf = voice->dsp_buf;
float dsp_amp = voice->amp;
float 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];
float *coeffs;
int looping;
/* Convert playback "speed" floating point value to phase index/fract */
dsp_phase_incr.setFloat(voice->phase_incr);
/* add 1/2 sample to dsp_phase since 7th order interpolation is centered on
* the 4th sample point */
dsp_phase += (Phase)0x80000000;
/* voice is currently looping? */
looping = SAMPLEMODE() == FLUID_LOOP_DURING_RELEASE
|| (SAMPLEMODE() == FLUID_LOOP_UNTIL_RELEASE
&& voice->volenv_section < FLUID_VOICE_ENVRELEASE);
/* 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 = dsp_phase.index();
/* interpolate first sample point (start or loop start) if needed */
for ( ; dsp_phase_index == start_index && dsp_i < n; dsp_i++) {
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * (float)start_points[2]
+ coeffs[1] * (float)start_points[1]
+ coeffs[2] * (float)start_points[0]
+ coeffs[3] * (float)dsp_data[dsp_phase_index]
+ coeffs[4] * (float)dsp_data[dsp_phase_index+1]
+ coeffs[5] * (float)dsp_data[dsp_phase_index+2]
+ coeffs[6] * (float)dsp_data[dsp_phase_index+3]);
/* increment phase and amplitude */
dsp_phase += dsp_phase_incr;
dsp_phase_index = dsp_phase.index();
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 < n; dsp_i++) {
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * (float)start_points[1]
+ coeffs[1] * (float)start_points[0]
+ coeffs[2] * (float)dsp_data[dsp_phase_index-1]
+ coeffs[3] * (float)dsp_data[dsp_phase_index]
+ coeffs[4] * (float)dsp_data[dsp_phase_index+1]
+ coeffs[5] * (float)dsp_data[dsp_phase_index+2]
+ coeffs[6] * (float)dsp_data[dsp_phase_index+3]);
/* increment phase and amplitude */
dsp_phase += dsp_phase_incr;
dsp_phase_index = dsp_phase.index();
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 < n; dsp_i++) {
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * (float)start_points[0]
+ coeffs[1] * (float)dsp_data[dsp_phase_index-2]
+ coeffs[2] * (float)dsp_data[dsp_phase_index-1]
+ coeffs[3] * (float)dsp_data[dsp_phase_index]
+ coeffs[4] * (float)dsp_data[dsp_phase_index+1]
+ coeffs[5] * (float)dsp_data[dsp_phase_index+2]
+ coeffs[6] * (float)dsp_data[dsp_phase_index+3]);
/* increment phase and amplitude */
dsp_phase += dsp_phase_incr;
dsp_phase_index = dsp_phase.index();
dsp_amp += dsp_amp_incr;
}
start_index -= 2; /* set back to original start index */
/* interpolate the sequence of sample points */
for ( ; dsp_i < n && 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] * (float)dsp_data[dsp_phase_index-3]
+ coeffs[1] * (float)dsp_data[dsp_phase_index-2]
+ coeffs[2] * (float)dsp_data[dsp_phase_index-1]
+ coeffs[3] * (float)dsp_data[dsp_phase_index]
+ coeffs[4] * (float)dsp_data[dsp_phase_index+1]
+ coeffs[5] * (float)dsp_data[dsp_phase_index+2]
+ coeffs[6] * (float)dsp_data[dsp_phase_index+3]);
/* increment phase and amplitude */
dsp_phase += dsp_phase_incr;
dsp_phase_index = dsp_phase.index();
dsp_amp += dsp_amp_incr;
}
/* break out if buffer filled */
if (dsp_i >= n)
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 < n; dsp_i++) {
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * (float)dsp_data[dsp_phase_index-3]
+ coeffs[1] * (float)dsp_data[dsp_phase_index-2]
+ coeffs[2] * (float)dsp_data[dsp_phase_index-1]
+ coeffs[3] * (float)dsp_data[dsp_phase_index]
+ coeffs[4] * (float)dsp_data[dsp_phase_index+1]
+ coeffs[5] * (float)dsp_data[dsp_phase_index+2]
+ coeffs[6] * (float)end_points[0]);
/* increment phase and amplitude */
dsp_phase += dsp_phase_incr;
dsp_phase_index = dsp_phase.index();
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 < n; dsp_i++) {
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * (float)dsp_data[dsp_phase_index-3]
+ coeffs[1] * (float)dsp_data[dsp_phase_index-2]
+ coeffs[2] * (float)dsp_data[dsp_phase_index-1]
+ coeffs[3] * (float)dsp_data[dsp_phase_index]
+ coeffs[4] * (float)dsp_data[dsp_phase_index+1]
+ coeffs[5] * (float)end_points[0]
+ coeffs[6] * (float)end_points[1]);
/* increment phase and amplitude */
dsp_phase += dsp_phase_incr;
dsp_phase_index = dsp_phase.index();
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 < n; dsp_i++) {
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * (float)dsp_data[dsp_phase_index-3]
+ coeffs[1] * (float)dsp_data[dsp_phase_index-2]
+ coeffs[2] * (float)dsp_data[dsp_phase_index-1]
+ coeffs[3] * (float)dsp_data[dsp_phase_index]
+ coeffs[4] * (float)end_points[0]
+ coeffs[5] * (float)end_points[1]
+ coeffs[6] * (float)end_points[2]);
/* increment phase and amplitude */
dsp_phase += dsp_phase_incr;
dsp_phase_index = dsp_phase.index();
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) {
dsp_phase -= (voice->loopend - voice->loopstart);
if (!voice->has_looped) {
voice->has_looped = true;
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 >= n)
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) */
dsp_phase -= (Phase)0x80000000;
voice->phase = dsp_phase;
voice->amp = dsp_amp;
return dsp_i;
}
}