Compile time constant lookup tables with cmake (#438)

Autogenerate lookup tables with a C helper tool, allowing them to be compile time constant and reduce memory requirements esp. on embedded systems.
This commit is contained in:
Tom M 2018-11-03 14:38:54 +01:00 committed by GitHub
parent 16d81d50ea
commit 0160543cdd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 358 additions and 187 deletions

View File

@ -360,3 +360,13 @@ else ( MACOSX_FRAMEWORK )
install ( FILES ${public_main_HEADER} DESTINATION ${INCLUDE_INSTALL_DIR} )
endif ( MACOSX_FRAMEWORK )
# ******* Auto Generated Lookup Tables ******
include(ExternalProject)
ExternalProject_Add(gentables
DOWNLOAD_COMMAND ""
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/gentables
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/gentables
INSTALL_COMMAND ${CMAKE_CURRENT_BINARY_DIR}/gentables/make_tables${CMAKE_EXECUTABLE_SUFFIX} "${CMAKE_BINARY_DIR}/"
)
add_dependencies(libfluidsynth-OBJ gentables)

View File

@ -0,0 +1,27 @@
cmake_minimum_required(VERSION 3.1)
# remove $CC from the current environment and by that force cmake to look for a (working) C compiler,
# which hopefully will be the host compiler
unset(ENV{CC})
project (gentables C)
set ( CMAKE_BUILD_TYPE Debug )
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR})
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../)
# Add the executable that generates the table
add_executable( make_tables
make_tables.c
gen_conv.c
gen_rvoice_dsp.c)
if ( WIN32 )
add_definitions ( -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS )
else ( WIN32 )
target_link_libraries (make_tables "m")
endif ()

81
src/gentables/gen_conv.c Normal file
View File

@ -0,0 +1,81 @@
#include "utils/fluid_conv_tables.h"
#include "make_tables.h"
/* conversion tables */
static double fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE];
static double fluid_cb2amp_tab[FLUID_CB_AMP_SIZE];
static double fluid_concave_tab[FLUID_VEL_CB_SIZE];
static double fluid_convex_tab[FLUID_VEL_CB_SIZE];
static double fluid_pan_tab[FLUID_PAN_SIZE];
/*
* void fluid_synth_init
*
* Does all the initialization for this module.
*/
static void fluid_conversion_config(void)
{
int i;
double x;
for(i = 0; i < FLUID_CENTS_HZ_SIZE; i++)
{
fluid_ct2hz_tab[i] = pow(2.0, (double) i / 1200.0);
}
/* centibels to amplitude conversion
* Note: SF2.01 section 8.1.3: Initial attenuation range is
* between 0 and 144 dB. Therefore a negative attenuation is
* not allowed.
*/
for(i = 0; i < FLUID_CB_AMP_SIZE; i++)
{
fluid_cb2amp_tab[i] = pow(10.0, (double) i / -200.0);
}
/* initialize the conversion tables (see fluid_mod.c
fluid_mod_get_value cases 4 and 8) */
/* concave unipolar positive transform curve */
fluid_concave_tab[0] = 0.0;
fluid_concave_tab[FLUID_VEL_CB_SIZE - 1] = 1.0;
/* convex unipolar positive transform curve */
fluid_convex_tab[0] = 0;
fluid_convex_tab[FLUID_VEL_CB_SIZE - 1] = 1.0;
/* There seems to be an error in the specs. The equations are
implemented according to the pictures on SF2.01 page 73. */
for(i = 1; i < FLUID_VEL_CB_SIZE - 1; i++)
{
x = (-200.0 / FLUID_PEAK_ATTENUATION) * log((double)(i * i) / ((FLUID_VEL_CB_SIZE - 1) * (FLUID_VEL_CB_SIZE - 1))) / M_LN10;
fluid_convex_tab[i] = (1.0 - x);
fluid_concave_tab[(FLUID_VEL_CB_SIZE - 1) - i] = x;
}
/* initialize the pan conversion table */
x = M_PI / 2.0 / (FLUID_PAN_SIZE - 1.0);
for(i = 0; i < FLUID_PAN_SIZE; i++)
{
fluid_pan_tab[i] = sin(i * x);
}
}
void gen_conv_table(FILE *fp)
{
/* Calculate the values */
fluid_conversion_config();
/* fluid_ct2hz_tab */
EMIT_ARRAY(fp, fluid_ct2hz_tab);
EMIT_ARRAY(fp, fluid_cb2amp_tab);
EMIT_ARRAY(fp, fluid_concave_tab);
EMIT_ARRAY(fp, fluid_convex_tab);
EMIT_ARRAY(fp, fluid_pan_tab);
}

View File

@ -0,0 +1,81 @@
#include "rvoice/fluid_rvoice_dsp_tables.h"
#include "make_tables.h"
/* Linear interpolation table (2 coefficients centered on 1st) */
static double interp_coeff_linear[FLUID_INTERP_MAX][2];
/* 4th order (cubic) interpolation table (4 coefficients centered on 2nd) */
static double interp_coeff[FLUID_INTERP_MAX][4];
/* 7th order interpolation (7 coefficients centered on 3rd) */
static double sinc_table7[FLUID_INTERP_MAX][SINC_INTERP_ORDER];
static double cb_interp_coeff_linear(int y, int x) { return interp_coeff_linear[y][x]; }
static double cb_interp_coeff (int y, int x) { return interp_coeff[y][x]; }
static double cb_sinc_table7 (int y, int x) { return sinc_table7[y][x]; }
/* 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] = (x * (-0.5 + x * (1 - 0.5 * x)));
interp_coeff[i][1] = (1.0 + x * x * (1.5 * x - 2.5));
interp_coeff[i][2] = (x * (0.5 + x * (2.0 - 1.5 * x)));
interp_coeff[i][3] = (0.5 * x * x * (x - 1.0));
interp_coeff_linear[i][0] = (1.0 - x);
interp_coeff_linear[i][1] = 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)
{
double arg = M_PI * i_shifted;
v = sin(arg) / (arg);
/* Hanning window */
v *= 0.5 * (1.0 + cos(2.0 * arg / (double)SINC_INTERP_ORDER));
}
else
{
v = 1.0;
}
sinc_table7[FLUID_INTERP_MAX - i2 - 1][i] = v;
}
}
}
void gen_rvoice_table_dsp (FILE *fp)
{
/* Calculate the values */
fluid_rvoice_dsp_config();
/* Emit the matrices */
emit_matrix(fp, "interp_coeff_linear", cb_interp_coeff_linear, FLUID_INTERP_MAX, 2);
emit_matrix(fp, "interp_coeff", cb_interp_coeff, FLUID_INTERP_MAX, 4);
emit_matrix(fp, "sinc_table7", cb_sinc_table7, FLUID_INTERP_MAX, 7);
}

View File

@ -0,0 +1,84 @@
#include "make_tables.h"
static void write_value(FILE *fp, double val, int i)
{
fprintf(fp, " %.15e%c /* %d */\n",
val,
',',
i
);
}
/* Emit an array of real numbers */
void emit_array(FILE *fp, const char *tblname, const double *tbl, int size)
{
int i;
fprintf(fp, "static const fluid_real_t %s[%d] = {\n", tblname, size);
for (i = 0; i < size; i++)
{
write_value(fp, tbl[i], i);
}
fprintf(fp, "};\n\n");
}
/* Emit a matrix of real numbers */
void emit_matrix(FILE *fp, const char *tblname, emit_matrix_cb tbl_cb, int sizeh, int sizel)
{
int i, j;
fprintf(fp, "static const fluid_real_t %s[%d][%d] = {\n {\n", tblname, sizeh, sizel);
for (i = 0; i < sizeh; i++)
{
for (j = 0; j < sizel; j++)
{
write_value(fp, tbl_cb(i, j), i*sizel+j);
}
if (i < (sizeh-1))
fprintf(fp, " }, {\n");
else
fprintf(fp, " }\n};\n\n");
}
}
static void open_table(FILE**fp, const char* dir, const char* file)
{
char buf[2048] = {0};
strcat(buf, dir);
strcat(buf, file);
/* open the output file */
*fp = fopen(buf, "w");
if (*fp == NULL)
{
exit(-2);
}
/* Emit warning header */
fprintf(*fp, "/* THIS FILE HAS BEEN AUTOMATICALLY GENERATED. DO NOT EDIT. */\n\n");
}
int main (int argc, char *argv[])
{
FILE *fp;
// make sure we have enough arguments
if (argc < 2)
return -1;
open_table(&fp, argv[1], "fluid_conv_tables.c");
gen_conv_table(fp);
fclose(fp);
open_table(&fp, argv[1], "fluid_rvoice_dsp_tables.c");
gen_rvoice_table_dsp(fp);
fclose(fp);
return 0;
}

View File

@ -0,0 +1,21 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#define EMIT_ARRAY(__fp__, __arr__) emit_array(__fp__, #__arr__, __arr__, sizeof(__arr__)/sizeof(*__arr__))
/* callback for general access to matrices */
typedef double (*emit_matrix_cb)(int y, int x);
/* Generators */
void gen_rvoice_table_dsp(FILE *fp);
void gen_conv_table(FILE *fp);
/* Emit an array of real numbers */
void emit_array(FILE *fp, const char *tblname, const double *tbl, int size);
/* Emit a matrix of real numbers */
void emit_matrix(FILE *fp, const char *tblname, emit_matrix_cb tbl_cb, int sizeh, int sizel);

View File

@ -22,8 +22,6 @@
#ifndef _FLUID_PHASE_H
#define _FLUID_PHASE_H
#include "config.h"
/*
* phase
*/
@ -31,7 +29,7 @@
#define FLUID_INTERP_BITS 8
#define FLUID_INTERP_BITS_MASK 0xff000000
#define FLUID_INTERP_BITS_SHIFT 24
#define FLUID_INTERP_MAX 256
#define FLUID_FRACT_MAX ((double)4294967296.0)

View File

@ -22,6 +22,7 @@
#include "fluid_phase.h"
#include "fluid_rvoice.h"
#include "fluid_sys.h"
#include "fluid_rvoice_dsp_tables.c"
/* Purpose:
*
@ -47,85 +48,6 @@
/* 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)
{
double arg = M_PI * i_shifted;
v = (fluid_real_t)sin(arg) / (arg);
/* Hanning window */
v *= (fluid_real_t)0.5 * (1.0 + cos(2.0 * arg / (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");
}
static FLUID_INLINE fluid_real_t
fluid_rvoice_get_float_sample(const short int *dsp_msb, const char *dsp_lsb, unsigned int idx)
{

View File

@ -0,0 +1,8 @@
#ifndef _FLUID_RVOICE_DSP_TABLES_H
#define _FLUID_RVOICE_DSP_TABLES_H
#define FLUID_INTERP_MAX 256
#define SINC_INTERP_ORDER 7 /* 7th order constant */
#endif

View File

@ -285,10 +285,6 @@ fluid_synth_init(void)
feenableexcept(FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID);
#endif
fluid_conversion_config();
fluid_rvoice_dsp_config();
init_dither();
/* custom_breath2att_mod is not a default modulator specified in SF2.01.

View File

@ -19,74 +19,7 @@
*/
#include "fluid_conv.h"
#define FLUID_CENTS_HZ_SIZE 1200
#define FLUID_VEL_CB_SIZE 128
#define FLUID_CB_AMP_SIZE 1441
#define FLUID_PAN_SIZE 1002
/* conversion tables */
static fluid_real_t fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE];
static fluid_real_t fluid_cb2amp_tab[FLUID_CB_AMP_SIZE];
static fluid_real_t fluid_concave_tab[FLUID_VEL_CB_SIZE];
static fluid_real_t fluid_convex_tab[FLUID_VEL_CB_SIZE];
static fluid_real_t fluid_pan_tab[FLUID_PAN_SIZE];
/*
* void fluid_synth_init
*
* Does all the initialization for this module.
*/
void
fluid_conversion_config(void)
{
int i;
double x;
for(i = 0; i < FLUID_CENTS_HZ_SIZE; i++)
{
fluid_ct2hz_tab[i] = (fluid_real_t) pow(2.0, (double) i / 1200.0);
}
/* centibels to amplitude conversion
* Note: SF2.01 section 8.1.3: Initial attenuation range is
* between 0 and 144 dB. Therefore a negative attenuation is
* not allowed.
*/
for(i = 0; i < FLUID_CB_AMP_SIZE; i++)
{
fluid_cb2amp_tab[i] = (fluid_real_t) pow(10.0, (double) i / -200.0);
}
/* initialize the conversion tables (see fluid_mod.c
fluid_mod_get_value cases 4 and 8) */
/* concave unipolar positive transform curve */
fluid_concave_tab[0] = 0.0;
fluid_concave_tab[FLUID_VEL_CB_SIZE - 1] = 1.0;
/* convex unipolar positive transform curve */
fluid_convex_tab[0] = 0;
fluid_convex_tab[FLUID_VEL_CB_SIZE - 1] = 1.0;
/* There seems to be an error in the specs. The equations are
implemented according to the pictures on SF2.01 page 73. */
for(i = 1; i < FLUID_VEL_CB_SIZE - 1; i++)
{
x = (-200.0 / FLUID_PEAK_ATTENUATION) * log((i * i) / (fluid_real_t)((FLUID_VEL_CB_SIZE - 1) * (FLUID_VEL_CB_SIZE - 1))) / M_LN10;
fluid_convex_tab[i] = (fluid_real_t)(1.0 - x);
fluid_concave_tab[(FLUID_VEL_CB_SIZE - 1) - i] = (fluid_real_t) x;
}
/* initialize the pan conversion table */
x = M_PI / 2.0 / (FLUID_PAN_SIZE - 1.0);
for(i = 0; i < FLUID_PAN_SIZE; i++)
{
fluid_pan_tab[i] = (fluid_real_t) sin(i * x);
}
}
#include "fluid_conv_tables.c"
/*
* fluid_ct2hz
@ -403,3 +336,4 @@ fluid_convex(fluid_real_t val)
return fluid_convex_tab[(int) val];
}

View File

@ -22,39 +22,7 @@
#define _FLUID_CONV_H
#include "fluidsynth_priv.h"
/*
Attenuation range in centibels.
Attenuation range is the dynamic range of the volume envelope generator
from 0 to the end of attack segment.
fluidsynth is a 24 bit synth, it could (should??) be 144 dB of attenuation.
However the spec makes no distinction between 16 or 24 bit synths, so use
96 dB here.
Note about usefulness of 24 bits:
1)Even fluidsynth is a 24 bit synth, this format is only relevant if
the sample format coming from the soundfont is 24 bits and the audio sample format
choosen by the application (audio.sample.format) is not 16 bits.
2)When the sample soundfont is 16 bits, the internal 24 bits number have
16 bits msb and lsb to 0. Consequently, at the DAC output, the dynamic range of
this 24 bit sample is reduced to the the dynamic of a 16 bits sample (ie 90 db)
even if this sample is produced by the audio driver using an audio sample format
compatible for a 24 bit DAC.
3)When the audio sample format settings is 16 bits (audio.sample.format), the
audio driver will make use of a 16 bit DAC, and the dynamic will be reduced to 96 dB
even if the initial sample comes from a 24 bits soundfont.
In both cases (2) or (3), the real dynamic range is only 96 dB.
Other consideration for FLUID_NOISE_FLOOR related to case (1),(2,3):
- for case (1), FLUID_NOISE_FLOOR should be the noise floor for 24 bits (i.e -138 dB).
- for case (2) or (3), FLUID_NOISE_FLOOR should be the noise floor for 16 bits (i.e -90 dB).
*/
#define FLUID_PEAK_ATTENUATION 960.0f
void fluid_conversion_config(void);
#include "utils/fluid_conv_tables.h"
fluid_real_t fluid_ct2hz_real(fluid_real_t cents);
fluid_real_t fluid_ct2hz(fluid_real_t cents);

View File

@ -0,0 +1,41 @@
#ifndef _FLUID_CONV_TABLES_H
#define _FLUID_CONV_TABLES_H
/*
Attenuation range in centibels.
Attenuation range is the dynamic range of the volume envelope generator
from 0 to the end of attack segment.
fluidsynth is a 24 bit synth, it could (should??) be 144 dB of attenuation.
However the spec makes no distinction between 16 or 24 bit synths, so use
96 dB here.
Note about usefulness of 24 bits:
1)Even fluidsynth is a 24 bit synth, this format is only relevant if
the sample format coming from the soundfont is 24 bits and the audio sample format
choosen by the application (audio.sample.format) is not 16 bits.
2)When the sample soundfont is 16 bits, the internal 24 bits number have
16 bits msb and lsb to 0. Consequently, at the DAC output, the dynamic range of
this 24 bit sample is reduced to the the dynamic of a 16 bits sample (ie 90 db)
even if this sample is produced by the audio driver using an audio sample format
compatible for a 24 bit DAC.
3)When the audio sample format settings is 16 bits (audio.sample.format), the
audio driver will make use of a 16 bit DAC, and the dynamic will be reduced to 96 dB
even if the initial sample comes from a 24 bits soundfont.
In both cases (2) or (3), the real dynamic range is only 96 dB.
Other consideration for FLUID_NOISE_FLOOR related to case (1),(2,3):
- for case (1), FLUID_NOISE_FLOOR should be the noise floor for 24 bits (i.e -138 dB).
- for case (2) or (3), FLUID_NOISE_FLOOR should be the noise floor for 16 bits (i.e -90 dB).
*/
#define FLUID_PEAK_ATTENUATION 960.0f
#define FLUID_CENTS_HZ_SIZE 1200
#define FLUID_VEL_CB_SIZE 128
#define FLUID_CB_AMP_SIZE 1441
#define FLUID_PAN_SIZE 1002
#endif