mirror of
https://github.com/ZDoom/fluidsynth.git
synced 2024-11-27 14:32:12 +00:00
I've split up the dsp core file in three
files: fluid_dsp_simple.c, fluid_dsp_float.c, and fluid_dsp_sse.c. This improves the readability.
This commit is contained in:
parent
dd092ac0ad
commit
654ec72119
4 changed files with 794 additions and 0 deletions
|
@ -1,3 +1,9 @@
|
|||
2004-03-30 Peter Hanappe <peter@hanappe.com>
|
||||
|
||||
* src/fluid_dsp_core.c: I've split up the dsp core file in three
|
||||
files: fluid_dsp_simple.c, fluid_dsp_float.c, and
|
||||
fluid_dsp_sse.c. This improves the readability.
|
||||
|
||||
2004-03-29 Peter Hanappe <peter@hanappe.com>
|
||||
|
||||
* src/fluid_jack.c (new_fluid_jack_audio_driver2): Testing the
|
||||
|
|
281
fluidsynth/src/fluid_dsp_float.c
Normal file
281
fluidsynth/src/fluid_dsp_float.c
Normal file
|
@ -0,0 +1,281 @@
|
|||
/* 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
|
||||
*/
|
||||
|
||||
|
||||
/* Purpose:
|
||||
* Low-level voice processing:
|
||||
*
|
||||
* - interpolates (obtains values between the samples of the original waveform data)
|
||||
* - filters (applies a lowpass filter with variable cutoff frequency and quality factor)
|
||||
* - mixes the processed sample to left and right output using the pan setting
|
||||
* - sends the processed sample to chorus and reverb
|
||||
*
|
||||
*
|
||||
* This file does -not- generate an object file.
|
||||
* Instead, it is #included in several places in fluid_voice.c.
|
||||
* The motivation for this is
|
||||
* - Calling it as a subroutine may be time consuming, especially with optimization off
|
||||
* - The previous implementation as a macro was clumsy to handle
|
||||
*
|
||||
*
|
||||
* Fluid_voice.c sets a couple of variables before #including this:
|
||||
* - dsp_data: Pointer to the original waveform data
|
||||
* - dsp_left_buf: The generated signal goes here, left channel
|
||||
* - dsp_right_buf: right channel
|
||||
* - dsp_reverb_buf: Send to reverb unit
|
||||
* - dsp_chorus_buf: Send to chorus unit
|
||||
* - dsp_start: Start processing at this output buffer index
|
||||
* - dsp_end: End processing just before this output buffer index
|
||||
* - dsp_a1: Coefficient for the filter
|
||||
* - dsp_a2: same
|
||||
* - dsp_b0: same
|
||||
* - dsp_b1: same
|
||||
* - dsp_b2: same
|
||||
* - dsp_filter_flag: Set, the filter is needed (many sound fonts don't use
|
||||
* the filter at all. If it is left at its default setting
|
||||
* of roughly 20 kHz, there is no need to apply filterling.)
|
||||
* - dsp_interp_method: Which interpolation method to use.
|
||||
* - voice holds the voice structure
|
||||
*
|
||||
* Some variables are set and modified:
|
||||
* - 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_phase_fractional: The fractional part of dsp_phase
|
||||
* - dsp_coeff: A table of four coefficients, depending on the fractional phase.
|
||||
* Used to interpolate between samples.
|
||||
* - dsp_process_buffer: Holds the processed signal between stages
|
||||
* - dsp_centernode: delay line for the IIR filter
|
||||
* - dsp_hist1: same
|
||||
* - dsp_hist2: same
|
||||
*
|
||||
*/
|
||||
|
||||
/* Purpose:
|
||||
* zap_almost_zero will return a number, as long as its
|
||||
* absolute value is over a certain threshold. Otherwise 0. See
|
||||
* fluid_rev.c for documentation (denormal numbers)
|
||||
*/
|
||||
|
||||
# if defined(WITH_FLOAT)
|
||||
# define zap_almost_zero(_sample) \
|
||||
((((*(unsigned int*)&(_sample))&0x7f800000) < 0x08000000)? 0.0f : (_sample))
|
||||
# else
|
||||
/* 1e-20 was chosen as an arbitrary (small) threshold. */
|
||||
#define zap_almost_zero(_sample) ((abs(_sample) < 1e-20)? 0.0f : (_sample))
|
||||
#endif
|
||||
|
||||
/* Interpolation (find a value between two samples of the original waveform) */
|
||||
|
||||
if ((fluid_phase_fract(dsp_phase) == 0)
|
||||
&& (fluid_phase_fract(dsp_phase_incr) == 0)
|
||||
&& (fluid_phase_index(dsp_phase_incr) == 1)) {
|
||||
|
||||
/* Check for a special case: The current phase falls directly on an
|
||||
* original sample. Also, the stepsize per output sample is exactly
|
||||
* one sample, no fractional part. In other words: The sample is
|
||||
* played back at normal phase and root pitch. => No interpolation
|
||||
* needed.
|
||||
*/
|
||||
for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) {
|
||||
/* Mix to the buffer and advance the phase by one sample */
|
||||
dsp_buf[dsp_i] = dsp_amp * dsp_data[fluid_phase_index_plusplus(dsp_phase)];
|
||||
dsp_amp += dsp_amp_incr;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* wave table interpolation: Choose the interpolation method */
|
||||
|
||||
switch(dsp_interp_method){
|
||||
case FLUID_INTERP_NONE:
|
||||
/* No interpolation. Just take the sample, which is closest to
|
||||
* the playback pointer. Questionable quality, but very
|
||||
* efficient. */
|
||||
|
||||
for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) {
|
||||
dsp_phase_index = fluid_phase_index(dsp_phase);
|
||||
dsp_buf[dsp_i] = dsp_amp * dsp_data[dsp_phase_index];
|
||||
|
||||
/* increment phase and amplitude */
|
||||
fluid_phase_incr(dsp_phase, dsp_phase_incr);
|
||||
dsp_amp += dsp_amp_incr;
|
||||
};
|
||||
break;
|
||||
|
||||
case FLUID_INTERP_LINEAR:
|
||||
/* Straight line interpolation. */
|
||||
for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) {
|
||||
dsp_coeff = &interp_coeff_linear[fluid_phase_fract_to_tablerow(dsp_phase)];
|
||||
dsp_phase_index = fluid_phase_index(dsp_phase);
|
||||
dsp_buf[dsp_i] = (dsp_amp *
|
||||
(dsp_coeff->a0 * dsp_data[dsp_phase_index]
|
||||
+ dsp_coeff->a1 * dsp_data[dsp_phase_index+1]));
|
||||
|
||||
/* increment phase and amplitude */
|
||||
fluid_phase_incr(dsp_phase, dsp_phase_incr);
|
||||
dsp_amp += dsp_amp_incr;
|
||||
};
|
||||
break;
|
||||
|
||||
case FLUID_INTERP_4THORDER:
|
||||
default:
|
||||
/* Default interpolation loop using floats */
|
||||
|
||||
for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) {
|
||||
dsp_coeff = &interp_coeff[fluid_phase_fract_to_tablerow(dsp_phase)];
|
||||
|
||||
dsp_phase_index = fluid_phase_index(dsp_phase);
|
||||
dsp_buf[dsp_i] = (dsp_amp *
|
||||
(dsp_coeff->a0 * dsp_data[dsp_phase_index]
|
||||
+ dsp_coeff->a1 * dsp_data[dsp_phase_index+1]
|
||||
+ dsp_coeff->a2 * dsp_data[dsp_phase_index+2]
|
||||
+ dsp_coeff->a3 * dsp_data[dsp_phase_index+3]));
|
||||
|
||||
/* increment phase and amplitude */
|
||||
fluid_phase_incr(dsp_phase, dsp_phase_incr);
|
||||
dsp_amp += dsp_amp_incr;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case FLUID_INTERP_7THORDER:
|
||||
for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) {
|
||||
int fract = fluid_phase_fract_to_tablerow(dsp_phase);
|
||||
dsp_phase_index = fluid_phase_index(dsp_phase);
|
||||
dsp_buf[dsp_i] = (dsp_amp *
|
||||
(sinc_table7[0][fract] * (fluid_real_t) dsp_data[dsp_phase_index]
|
||||
+ sinc_table7[1][fract] * (fluid_real_t) dsp_data[dsp_phase_index+1]
|
||||
+ sinc_table7[2][fract] * (fluid_real_t) dsp_data[dsp_phase_index+2]
|
||||
+ sinc_table7[3][fract] * (fluid_real_t) dsp_data[dsp_phase_index+3]
|
||||
+ sinc_table7[4][fract] * (fluid_real_t) dsp_data[dsp_phase_index+4]
|
||||
+ sinc_table7[5][fract] * (fluid_real_t) dsp_data[dsp_phase_index+5]
|
||||
+ sinc_table7[6][fract] * (fluid_real_t) dsp_data[dsp_phase_index+6]));
|
||||
|
||||
/* increment phase and amplitude */
|
||||
fluid_phase_incr(dsp_phase, dsp_phase_incr);
|
||||
dsp_amp += dsp_amp_incr;
|
||||
}
|
||||
break;
|
||||
|
||||
} /* switch interpolation method */
|
||||
} /* If interpolation is needed */
|
||||
|
||||
/* filter (implement the voice filter according to Soundfont standard) */
|
||||
if (dsp_use_filter_flag) {
|
||||
|
||||
/* Check for denormal number (too close to zero) once in a
|
||||
* while. This is not a big concern here - why would someone play a
|
||||
* sample with an empty tail? */
|
||||
dsp_hist1 = zap_almost_zero(dsp_hist1);
|
||||
|
||||
/* Two versions of the filter loop. One, while the filter is
|
||||
* changing towards its new setting. The other, if the filter
|
||||
* doesn't change.
|
||||
*/
|
||||
|
||||
if (dsp_filter_coeff_incr_count > 0) {
|
||||
/* The increment is added to each filter coefficient
|
||||
filter_coeff_incr_count times. */
|
||||
|
||||
for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) {
|
||||
/* The filter is implemented in Direct-II form. */
|
||||
dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2;
|
||||
dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1;
|
||||
dsp_hist2 = dsp_hist1;
|
||||
dsp_hist1 = dsp_centernode;
|
||||
|
||||
if (dsp_filter_coeff_incr_count-- > 0){
|
||||
dsp_a1 += dsp_a1_incr;
|
||||
dsp_a2 += dsp_a2_incr;
|
||||
dsp_b02 += dsp_b02_incr;
|
||||
dsp_b1 += dsp_b1_incr;
|
||||
}
|
||||
} /* for dsp_i */
|
||||
|
||||
} else {
|
||||
|
||||
/* The filter parameters are constant. This is duplicated to save
|
||||
* time. */
|
||||
|
||||
for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) {
|
||||
/* The filter is implemented in Direct-II form. */
|
||||
dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2;
|
||||
dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1;
|
||||
dsp_hist2 = dsp_hist1;
|
||||
dsp_hist1 = dsp_centernode;
|
||||
}
|
||||
} /* if filter is fixed */
|
||||
} /* if filter is enabled */
|
||||
|
||||
|
||||
|
||||
/* pan (Copy the signal to the left and right output buffer) The voice
|
||||
* panning generator has a range of -500 .. 500. If it is centered,
|
||||
* it's close to 0. voice->amp_left and voice->amp_right are then the
|
||||
* same, and we can save one multiplication per voice and sample.
|
||||
*/
|
||||
if ((-0.5 < voice->pan) && (voice->pan < 0.5)) {
|
||||
|
||||
/* The voice is centered. Use voice->amp_left twice. */
|
||||
for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) {
|
||||
float v = voice->amp_left * dsp_buf[dsp_i];
|
||||
dsp_left_buf[dsp_i] += v;
|
||||
dsp_right_buf[dsp_i] += v;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* The voice is not centered. For stereo samples, one of the
|
||||
* amplitudes will be zero. */
|
||||
if (voice->amp_left != 0.0){
|
||||
for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) {
|
||||
dsp_left_buf[dsp_i] += voice->amp_left * dsp_buf[dsp_i];
|
||||
}
|
||||
}
|
||||
if (voice->amp_right != 0.0){
|
||||
for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) {
|
||||
dsp_right_buf[dsp_i] += voice->amp_right * dsp_buf[dsp_i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* reverb send. Buffer may be NULL. */
|
||||
if ((dsp_reverb_buf != NULL) && (voice->amp_reverb != 0.0)) {
|
||||
for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) {
|
||||
dsp_reverb_buf[dsp_i] += voice->amp_reverb * dsp_buf[dsp_i];
|
||||
}
|
||||
}
|
||||
|
||||
/* chorus send. Buffer may be NULL. */
|
||||
if ((dsp_chorus_buf != NULL) && (voice->amp_chorus != 0)) {
|
||||
for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) {
|
||||
dsp_chorus_buf[dsp_i] += voice->amp_chorus * dsp_buf[dsp_i];
|
||||
}
|
||||
}
|
120
fluidsynth/src/fluid_dsp_simple.c
Normal file
120
fluidsynth/src/fluid_dsp_simple.c
Normal file
|
@ -0,0 +1,120 @@
|
|||
/* 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
|
||||
*/
|
||||
|
||||
|
||||
/* Purpose:
|
||||
* Low-level voice processing:
|
||||
*
|
||||
* - interpolates (obtains values between the samples of the original waveform data)
|
||||
* - filters (applies a lowpass filter with variable cutoff frequency and quality factor)
|
||||
* - mixes the processed sample to left and right output using the pan setting
|
||||
* - sends the processed sample to chorus and reverb
|
||||
*
|
||||
*
|
||||
* This file does -not- generate an object file.
|
||||
* Instead, it is #included in several places in fluid_voice.c.
|
||||
* The motivation for this is
|
||||
* - Calling it as a subroutine may be time consuming, especially with optimization off
|
||||
* - The previous implementation as a macro was clumsy to handle
|
||||
*
|
||||
*
|
||||
* Fluid_voice.c sets a couple of variables before #including this:
|
||||
* - dsp_data: Pointer to the original waveform data
|
||||
* - dsp_left_buf: The generated signal goes here, left channel
|
||||
* - dsp_right_buf: right channel
|
||||
* - dsp_reverb_buf: Send to reverb unit
|
||||
* - dsp_chorus_buf: Send to chorus unit
|
||||
* - dsp_start: Start processing at this output buffer index
|
||||
* - dsp_end: End processing just before this output buffer index
|
||||
* - dsp_a1: Coefficient for the filter
|
||||
* - dsp_a2: same
|
||||
* - dsp_b0: same
|
||||
* - dsp_b1: same
|
||||
* - dsp_b2: same
|
||||
* - dsp_filter_flag: Set, the filter is needed (many sound fonts don't use
|
||||
* the filter at all. If it is left at its default setting
|
||||
* of roughly 20 kHz, there is no need to apply filterling.)
|
||||
* - dsp_interp_method: Which interpolation method to use.
|
||||
* - voice holds the voice structure
|
||||
*
|
||||
* Some variables are set and modified:
|
||||
* - 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_phase_fractional: The fractional part of dsp_phase
|
||||
* - dsp_coeff: A table of four coefficients, depending on the fractional phase.
|
||||
* Used to interpolate between samples.
|
||||
* - dsp_process_buffer: Holds the processed signal between stages
|
||||
* - dsp_centernode: delay line for the IIR filter
|
||||
* - dsp_hist1: same
|
||||
* - dsp_hist2: same
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/* Nonoptimized DSP loop */
|
||||
#warning "This code is meant for experiments only.";
|
||||
|
||||
/* wave table interpolation */
|
||||
for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) {
|
||||
|
||||
dsp_coeff = &interp_coeff[fluid_phase_fract_to_tablerow(dsp_phase)];
|
||||
dsp_phase_index = fluid_phase_index(dsp_phase);
|
||||
dsp_sample = (dsp_amp *
|
||||
(dsp_coeff->a0 * dsp_data[dsp_phase_index]
|
||||
+ dsp_coeff->a1 * dsp_data[dsp_phase_index+1]
|
||||
+ dsp_coeff->a2 * dsp_data[dsp_phase_index+2]
|
||||
+ dsp_coeff->a3 * dsp_data[dsp_phase_index+3]));
|
||||
|
||||
/* increment phase and amplitude */
|
||||
fluid_phase_incr(dsp_phase, dsp_phase_incr);
|
||||
dsp_amp += dsp_amp_incr;
|
||||
|
||||
/* filter */
|
||||
/* The filter is implemented in Direct-II form. */
|
||||
dsp_centernode = dsp_sample - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2;
|
||||
dsp_sample = dsp_b0 * dsp_centernode + dsp_b1 * dsp_hist1 + dsp_b2 * dsp_hist2;
|
||||
dsp_hist2 = dsp_hist1;
|
||||
dsp_hist1 = dsp_centernode;
|
||||
|
||||
/* pan */
|
||||
dsp_left_buf[dsp_i] += voice->amp_left * dsp_sample;
|
||||
dsp_right_buf[dsp_i] += voice->amp_right * dsp_sample;
|
||||
|
||||
/* reverb */
|
||||
if (dsp_reverb_buf){
|
||||
dsp_reverb_buf[dsp_i] += voice->amp_reverb * dsp_sample;
|
||||
}
|
||||
|
||||
/* chorus */
|
||||
if (dsp_chorus_buf){
|
||||
dsp_chorus_buf[dsp_i] += voice->amp_chorus * dsp_sample;
|
||||
}
|
||||
}
|
||||
|
387
fluidsynth/src/fluid_dsp_sse.c
Normal file
387
fluidsynth/src/fluid_dsp_sse.c
Normal file
|
@ -0,0 +1,387 @@
|
|||
/* 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
|
||||
*/
|
||||
|
||||
|
||||
/* Purpose:
|
||||
* Low-level voice processing:
|
||||
*
|
||||
* - interpolates (obtains values between the samples of the original waveform data)
|
||||
* - filters (applies a lowpass filter with variable cutoff frequency and quality factor)
|
||||
* - mixes the processed sample to left and right output using the pan setting
|
||||
* - sends the processed sample to chorus and reverb
|
||||
*
|
||||
*
|
||||
* This file does -not- generate an object file.
|
||||
* Instead, it is #included in several places in fluid_voice.c.
|
||||
* The motivation for this is
|
||||
* - Calling it as a subroutine may be time consuming, especially with optimization off
|
||||
* - The previous implementation as a macro was clumsy to handle
|
||||
*
|
||||
*
|
||||
* Fluid_voice.c sets a couple of variables before #including this:
|
||||
* - dsp_data: Pointer to the original waveform data
|
||||
* - dsp_left_buf: The generated signal goes here, left channel
|
||||
* - dsp_right_buf: right channel
|
||||
* - dsp_reverb_buf: Send to reverb unit
|
||||
* - dsp_chorus_buf: Send to chorus unit
|
||||
* - dsp_start: Start processing at this output buffer index
|
||||
* - dsp_end: End processing just before this output buffer index
|
||||
* - dsp_a1: Coefficient for the filter
|
||||
* - dsp_a2: same
|
||||
* - dsp_b0: same
|
||||
* - dsp_b1: same
|
||||
* - dsp_b2: same
|
||||
* - dsp_filter_flag: Set, the filter is needed (many sound fonts don't use
|
||||
* the filter at all. If it is left at its default setting
|
||||
* of roughly 20 kHz, there is no need to apply filterling.)
|
||||
* - dsp_interp_method: Which interpolation method to use.
|
||||
* - voice holds the voice structure
|
||||
*
|
||||
* Some variables are set and modified:
|
||||
* - 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_phase_fractional: The fractional part of dsp_phase
|
||||
* - dsp_coeff: A table of four coefficients, depending on the fractional phase.
|
||||
* Used to interpolate between samples.
|
||||
* - dsp_process_buffer: Holds the processed signal between stages
|
||||
* - dsp_centernode: delay line for the IIR filter
|
||||
* - dsp_hist1: same
|
||||
* - dsp_hist2: same
|
||||
*
|
||||
*/
|
||||
|
||||
/* Purpose:
|
||||
* zap_almost_zero will return a number, as long as its
|
||||
* absolute value is over a certain threshold. Otherwise 0. See
|
||||
* fluid_rev.c for documentation (denormal numbers)
|
||||
*/
|
||||
|
||||
# if defined(WITH_FLOAT)
|
||||
# define zap_almost_zero(_sample) \
|
||||
((((*(unsigned int*)&(_sample))&0x7f800000) < 0x08000000)? 0.0f : (_sample))
|
||||
# else
|
||||
/* 1e-20 was chosen as an arbitrary (small) threshold. */
|
||||
#define zap_almost_zero(_sample) ((abs(_sample) < 1e-20)? 0.0f : (_sample))
|
||||
#endif
|
||||
|
||||
/* Interpolation (find a value between two samples of the original waveform) */
|
||||
|
||||
if ((fluid_phase_fract(dsp_phase) == 0)
|
||||
&& (fluid_phase_fract(dsp_phase_incr) == 0)
|
||||
&& (fluid_phase_index(dsp_phase_incr) == 1)) {
|
||||
|
||||
/* Check for a special case: The current phase falls directly on an
|
||||
* original sample. Also, the stepsize per output sample is exactly
|
||||
* one sample, no fractional part. In other words: The sample is
|
||||
* played back at normal phase and root pitch. => No interpolation
|
||||
* needed.
|
||||
*/
|
||||
for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) {
|
||||
/* Mix to the buffer and advance the phase by one sample */
|
||||
dsp_buf[dsp_i] = dsp_amp * dsp_data[fluid_phase_index_plusplus(dsp_phase)];
|
||||
dsp_amp += dsp_amp_incr;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* wave table interpolation: Choose the interpolation method */
|
||||
/* !!! SSE interpolation is less efficient that normal interpolation. */
|
||||
|
||||
|
||||
/* Initialize amplitude increase */
|
||||
sse_b->sf[0] = sse_b->sf[1] = sse_b->sf[2] = sse_b->sf[3] = 4.*dsp_amp_incr;
|
||||
|
||||
/* Initialize amplitude => xmm7
|
||||
* The amplitude is kept in xmm7 throughout the whole process
|
||||
*/
|
||||
|
||||
sse_a->sf[0]=sse_a->sf[1]=sse_a->sf[2]=sse_a->sf[3]=dsp_amp;
|
||||
sse_a->sf[1] += dsp_amp_incr;
|
||||
sse_a->sf[2] += 2.* dsp_amp_incr;
|
||||
sse_a->sf[3] += 3.* dsp_amp_incr;
|
||||
movaps_m2r(*sse_a,xmm7);
|
||||
|
||||
/* Where to store the result */
|
||||
sse_dest=(sse_t*)&dsp_buf[0];
|
||||
for (dsp_i = 0; dsp_i < FLUID_BUFSIZE; dsp_i += 4) {
|
||||
/* Note / fixme: The coefficients are first copied to
|
||||
* sse_c, then to the xmm register. Can't get it through
|
||||
* the compiler differently... */
|
||||
|
||||
/* Load the four source samples for the 1st output sample */
|
||||
dsp_phase_index = fluid_phase_index(dsp_phase);
|
||||
sse_a->sf[0]=(fluid_real_t)dsp_data[dsp_phase_index];
|
||||
sse_a->sf[1]=(fluid_real_t)dsp_data[dsp_phase_index+1];
|
||||
sse_a->sf[2]=(fluid_real_t)dsp_data[dsp_phase_index+2];
|
||||
sse_a->sf[3]=(fluid_real_t)dsp_data[dsp_phase_index+3];
|
||||
movaps_m2r(*sse_a,xmm0);
|
||||
*sse_c=interp_coeff_sse[fluid_phase_fract_to_tablerow(dsp_phase)];
|
||||
mulps_m2r(*sse_c,xmm0);
|
||||
|
||||
fluid_phase_incr(dsp_phase, dsp_phase_incr);
|
||||
|
||||
/* Load the four source samples for the 2nd output sample */
|
||||
dsp_phase_index = fluid_phase_index(dsp_phase);
|
||||
sse_a->sf[0]=(fluid_real_t)dsp_data[dsp_phase_index];
|
||||
sse_a->sf[1]=(fluid_real_t)dsp_data[dsp_phase_index+1];
|
||||
sse_a->sf[2]=(fluid_real_t)dsp_data[dsp_phase_index+2];
|
||||
sse_a->sf[3]=(fluid_real_t)dsp_data[dsp_phase_index+3];
|
||||
movaps_m2r(*sse_a,xmm1);
|
||||
*sse_c=interp_coeff_sse[fluid_phase_fract_to_tablerow(dsp_phase)];
|
||||
mulps_m2r(*sse_c,xmm1);
|
||||
|
||||
fluid_phase_incr(dsp_phase, dsp_phase_incr);
|
||||
|
||||
/* Load the four source samples for the 3rd output sample */
|
||||
dsp_phase_index = fluid_phase_index(dsp_phase);
|
||||
sse_a->sf[0]=(fluid_real_t)dsp_data[dsp_phase_index];
|
||||
sse_a->sf[1]=(fluid_real_t)dsp_data[dsp_phase_index+1];
|
||||
sse_a->sf[2]=(fluid_real_t)dsp_data[dsp_phase_index+2];
|
||||
sse_a->sf[3]=(fluid_real_t)dsp_data[dsp_phase_index+3];
|
||||
movaps_m2r(*sse_a,xmm2);
|
||||
*sse_c=interp_coeff_sse[fluid_phase_fract_to_tablerow(dsp_phase)];
|
||||
mulps_m2r(*sse_c,xmm2);
|
||||
|
||||
fluid_phase_incr(dsp_phase, dsp_phase_incr);
|
||||
|
||||
/* Load the four source samples for the 4th output sample */
|
||||
dsp_phase_index = fluid_phase_index(dsp_phase);
|
||||
sse_a->sf[0]=(fluid_real_t)dsp_data[dsp_phase_index];
|
||||
sse_a->sf[1]=(fluid_real_t)dsp_data[dsp_phase_index+1];
|
||||
sse_a->sf[2]=(fluid_real_t)dsp_data[dsp_phase_index+2];
|
||||
sse_a->sf[3]=(fluid_real_t)dsp_data[dsp_phase_index+3];
|
||||
movaps_m2r(*sse_a,xmm3);
|
||||
*sse_c=interp_coeff_sse[fluid_phase_fract_to_tablerow(dsp_phase)];
|
||||
mulps_m2r(*sse_c,xmm3);
|
||||
|
||||
fluid_phase_incr(dsp_phase, dsp_phase_incr);
|
||||
|
||||
#if 0
|
||||
/*Testcase for horizontal add */
|
||||
sse_a->sf[0]=0.1;sse_a->sf[1]=0.01;sse_a->sf[2]=0.001;sse_a->sf[3]=0.0001;
|
||||
movaps_m2r(*sse_a,xmm0);
|
||||
sse_a->sf[0]=0.2;sse_a->sf[1]=0.02;sse_a->sf[2]=0.002;sse_a->sf[3]=0.0002;
|
||||
movaps_m2r(*sse_a,xmm1);
|
||||
sse_a->sf[0]=0.3;sse_a->sf[1]=0.03;sse_a->sf[2]=0.003;sse_a->sf[3]=0.0003;
|
||||
movaps_m2r(*sse_a,xmm2);
|
||||
sse_a->sf[0]=0.4;sse_a->sf[1]=0.04;sse_a->sf[2]=0.004;sse_a->sf[3]=0.0004;
|
||||
movaps_m2r(*sse_a,xmm3);
|
||||
#endif /* #if 0 */
|
||||
|
||||
#if 1
|
||||
/* Horizontal add
|
||||
* xmm4[0]:=xmm0[0]+xmm1[0]+xmm2[0]+xmm3[0]
|
||||
* xmm4[1]:=xmm0[1]+xmm1[1]+xmm2[1]+xmm3[1]
|
||||
* etc.
|
||||
* The only register, which is unused, is xmm7.
|
||||
*/
|
||||
movaps_r2r(xmm0,xmm5);
|
||||
movaps_r2r(xmm2,xmm6);
|
||||
movlhps_r2r(xmm1,xmm5);
|
||||
movlhps_r2r(xmm3,xmm6);
|
||||
movhlps_r2r(xmm0,xmm1);
|
||||
movhlps_r2r(xmm2,xmm3);
|
||||
addps_r2r(xmm1,xmm5);
|
||||
addps_r2r(xmm3,xmm6);
|
||||
movaps_r2r(xmm5,xmm4);
|
||||
shufps_r2r(xmm6,xmm5,0xDD);
|
||||
shufps_r2r(xmm6,xmm4,0x88);
|
||||
addps_r2r(xmm5,xmm4);
|
||||
|
||||
/* movaps_r2m(xmm4,*sse_a); */
|
||||
/* printf("xmm4 (Result): %f %f %f %f\n", */
|
||||
/* sse_a->sf[0], sse_a->sf[1], */
|
||||
/* sse_a->sf[2], sse_a->sf[3]); */
|
||||
#else
|
||||
/* Add using normal FPU */
|
||||
movaps_r2m(xmm0,*sse_a);
|
||||
sse_c->sf[0]=sse_a->sf[0]+sse_a->sf[1]+sse_a->sf[2]+sse_a->sf[3];
|
||||
movaps_r2m(xmm1,*sse_a);
|
||||
sse_c->sf[1]=sse_a->sf[0]+sse_a->sf[1]+sse_a->sf[2]+sse_a->sf[3];
|
||||
movaps_r2m(xmm2,*sse_a);
|
||||
sse_c->sf[2]=sse_a->sf[0]+sse_a->sf[1]+sse_a->sf[2]+sse_a->sf[3];
|
||||
movaps_r2m(xmm3,*sse_a);
|
||||
sse_c->sf[3]=sse_a->sf[0]+sse_a->sf[1]+sse_a->sf[2]+sse_a->sf[3];
|
||||
movaps_m2r(*sse_c,xmm4);
|
||||
#endif /* #if 1 */
|
||||
/* end horizontal add. Result in xmm6. */
|
||||
|
||||
|
||||
/* Multiply xmm4 with amplitude */
|
||||
mulps_r2r(xmm7,xmm4);
|
||||
|
||||
/* Store the result */
|
||||
movaps_r2m(xmm4,*sse_dest); // ++
|
||||
|
||||
/* Advance the position in the output buffer */
|
||||
sse_dest++;
|
||||
|
||||
/* Change the amplitude */
|
||||
addps_m2r(*sse_b,xmm7);
|
||||
|
||||
} /* for dsp_i in steps of four */
|
||||
|
||||
movaps_r2m(xmm7,*sse_a);
|
||||
|
||||
/* Retrieve the last amplitude value. */
|
||||
dsp_amp=sse_a->sf[3];
|
||||
|
||||
|
||||
} /* If interpolation is needed */
|
||||
|
||||
/* filter (implement the voice filter according to Soundfont standard) */
|
||||
if (dsp_use_filter_flag) {
|
||||
|
||||
/* Check for denormal number (too close to zero) once in a
|
||||
* while. This is not a big concern here - why would someone play a
|
||||
* sample with an empty tail? */
|
||||
dsp_hist1 = zap_almost_zero(dsp_hist1);
|
||||
|
||||
/* Two versions of the filter loop. One, while the filter is
|
||||
* changing towards its new setting. The other, if the filter
|
||||
* doesn't change.
|
||||
*/
|
||||
|
||||
if (dsp_filter_coeff_incr_count > 0) {
|
||||
/* The increment is added to each filter coefficient
|
||||
filter_coeff_incr_count times. */
|
||||
|
||||
for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) {
|
||||
/* The filter is implemented in Direct-II form. */
|
||||
dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2;
|
||||
dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1;
|
||||
dsp_hist2 = dsp_hist1;
|
||||
dsp_hist1 = dsp_centernode;
|
||||
|
||||
if (dsp_filter_coeff_incr_count-- > 0){
|
||||
dsp_a1 += dsp_a1_incr;
|
||||
dsp_a2 += dsp_a2_incr;
|
||||
dsp_b02 += dsp_b02_incr;
|
||||
dsp_b1 += dsp_b1_incr;
|
||||
}
|
||||
} /* for dsp_i */
|
||||
|
||||
} else {
|
||||
|
||||
/* The filter parameters are constant. This is duplicated to save
|
||||
* time. */
|
||||
|
||||
for (dsp_i = dsp_start; dsp_i < dsp_end; dsp_i++) {
|
||||
/* The filter is implemented in Direct-II form. */
|
||||
dsp_centernode = dsp_buf[dsp_i] - dsp_a1 * dsp_hist1 - dsp_a2 * dsp_hist2;
|
||||
dsp_buf[dsp_i] = dsp_b02 * (dsp_centernode + dsp_hist2) + dsp_b1 * dsp_hist1;
|
||||
dsp_hist2 = dsp_hist1;
|
||||
dsp_hist1 = dsp_centernode;
|
||||
}
|
||||
} /* if filter is fixed */
|
||||
} /* if filter is enabled */
|
||||
|
||||
|
||||
/* The following optimization will process a whole buffer using the
|
||||
* SSE extension of the Pentium processor.
|
||||
*/
|
||||
|
||||
if (voice->amp_left != 0.0) {
|
||||
sse_a->sf[0]=voice->amp_left;
|
||||
sse_a->sf[1]=voice->amp_left;
|
||||
sse_a->sf[2]=voice->amp_left;
|
||||
sse_a->sf[3]=voice->amp_left;
|
||||
movaps_m2r(*sse_a,xmm0);
|
||||
|
||||
sse_src=(sse_t*)dsp_buf;
|
||||
sse_dest=(sse_t*)&dsp_left_buf[0];
|
||||
for (dsp_i = 0; dsp_i < FLUID_BUFSIZE; dsp_i+=4) {
|
||||
movaps_m2r(*sse_src,xmm4); /* Load original sample */
|
||||
mulps_r2r(xmm0,xmm4); /* Gain */
|
||||
sse_src++;
|
||||
addps_m2r(*sse_dest,xmm4); /* Mix with buf */
|
||||
movaps_r2m(xmm4,*sse_dest); /* Store in buf */
|
||||
sse_dest++;
|
||||
}
|
||||
}
|
||||
|
||||
if (voice->amp_right != 0.0){
|
||||
sse_a->sf[0]=voice->amp_right;
|
||||
sse_a->sf[1]=voice->amp_right;
|
||||
sse_a->sf[2]=voice->amp_right;
|
||||
sse_a->sf[3]=voice->amp_right;
|
||||
movaps_m2r(*sse_a,xmm0);
|
||||
|
||||
sse_src=(sse_t*)dsp_buf;
|
||||
sse_dest=(sse_t*)&dsp_right_buf[0];
|
||||
for (dsp_i = 0; dsp_i < FLUID_BUFSIZE; dsp_i+=4) {
|
||||
movaps_m2r(*sse_src,xmm4); /* Load original sample */
|
||||
sse_src++;
|
||||
mulps_r2r(xmm0,xmm4); /* Gain */
|
||||
addps_m2r(*sse_dest,xmm4); /* Mix with buf */
|
||||
movaps_r2m(xmm4,*sse_dest); /* Store in buf */
|
||||
sse_dest++;
|
||||
}
|
||||
}
|
||||
|
||||
/* reverb send. Buffer may be NULL. */
|
||||
if (dsp_reverb_buf && voice->amp_reverb != 0.0){
|
||||
sse_a->sf[0]=voice->amp_reverb;
|
||||
sse_a->sf[1]=voice->amp_reverb;
|
||||
sse_a->sf[2]=voice->amp_reverb;
|
||||
sse_a->sf[3]=voice->amp_reverb;
|
||||
movaps_m2r(*sse_a,xmm0);
|
||||
|
||||
sse_src=(sse_t*)dsp_buf;
|
||||
sse_dest=(sse_t*)&dsp_reverb_buf[0];
|
||||
for (dsp_i = 0; dsp_i < FLUID_BUFSIZE; dsp_i+=4) {
|
||||
movaps_m2r(*sse_src,xmm4); /* Load original sample */
|
||||
sse_src++;
|
||||
mulps_r2r(xmm0,xmm4); /* Gain */
|
||||
addps_m2r(*sse_dest,xmm4); /* Mix with buf */
|
||||
movaps_r2m(xmm4,*sse_dest); /* Store in buf */
|
||||
sse_dest++;
|
||||
}
|
||||
}
|
||||
|
||||
/* chorus send. Buffer may be NULL. */
|
||||
if (dsp_chorus_buf && voice->amp_chorus != 0){
|
||||
sse_a->sf[0]=voice->amp_chorus;
|
||||
sse_a->sf[1]=voice->amp_chorus;
|
||||
sse_a->sf[2]=voice->amp_chorus;
|
||||
sse_a->sf[3]=voice->amp_chorus;
|
||||
movaps_m2r(*sse_a,xmm0);
|
||||
|
||||
sse_src=(sse_t*)dsp_buf;
|
||||
sse_dest=(sse_t*)&dsp_chorus_buf[0];
|
||||
for (dsp_i = 0; dsp_i < FLUID_BUFSIZE; dsp_i+=4) {
|
||||
movaps_m2r(*sse_src,xmm4); /* Load original sample */
|
||||
sse_src++;
|
||||
mulps_r2r(xmm0,xmm4); /* Gain */
|
||||
addps_m2r(*sse_dest,xmm4); /* Mix with buf */
|
||||
movaps_r2m(xmm4,*sse_dest); /* Store in buf */
|
||||
sse_dest++;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue