mirror of
https://github.com/ZDoom/fluidsynth.git
synced 2024-11-10 06:51:54 +00:00
More efficient implementation of fluid_ct2hz() (#569)
This provides a less branchy and therefore more instruction-cache-friendly version of fluid_ct2hz(), which also significantly reduces the number of floating-point comparisons.
This commit is contained in:
parent
41e77afe84
commit
fa7354a336
3 changed files with 47 additions and 52 deletions
|
@ -22,7 +22,10 @@ static void fluid_conversion_config(void)
|
|||
|
||||
for(i = 0; i < FLUID_CENTS_HZ_SIZE; i++)
|
||||
{
|
||||
fluid_ct2hz_tab[i] = powl(2.0, (double) i / 1200.0);
|
||||
// 6,875 is just a factor that we already multiply into the lookup table to save
|
||||
// that multiplication in fluid_ct2hz_real()
|
||||
// 6.875 Hz because 440Hz / 2^6
|
||||
fluid_ct2hz_tab[i] = 6.875 * powl(2.0, (double) i / 1200.0);
|
||||
}
|
||||
|
||||
/* centibels to amplitude conversion
|
||||
|
|
|
@ -23,66 +23,55 @@
|
|||
#include "fluid_conv_tables.c"
|
||||
|
||||
/*
|
||||
* fluid_ct2hz
|
||||
* Converts absolute cents to Hertz
|
||||
*
|
||||
* As per sfspec section 9.3:
|
||||
*
|
||||
* ABSOLUTE CENTS - An absolute logarithmic measure of frequency based on a
|
||||
* reference of MIDI key number scaled by 100.
|
||||
* A cent is 1/1200 of an octave [which is the twelve hundredth root of two],
|
||||
* and value 6900 is 440 Hz (A-440).
|
||||
*
|
||||
* Implemented below basically is the following:
|
||||
* 440 * 2^((cents-6900)/1200)
|
||||
* = 440 * 2^((int)((cents-6900)/1200)) * 2^(((int)cents-6900)%1200))
|
||||
* = 2^((int)((cents-6900)/1200)) * (440 * 2^(((int)cents-6900)%1200)))
|
||||
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
* This second factor is stored in the lookup table.
|
||||
*
|
||||
* The first factor can be implemented with a fast shift when the exponent
|
||||
* is always an int. This is the case when using 440/2^6 Hz rather than 440Hz
|
||||
* reference.
|
||||
*/
|
||||
fluid_real_t
|
||||
fluid_ct2hz_real(fluid_real_t cents)
|
||||
{
|
||||
if(cents < 0)
|
||||
if(FLUID_UNLIKELY(cents < 0))
|
||||
{
|
||||
return (fluid_real_t) 1.0;
|
||||
}
|
||||
else if(cents < 900)
|
||||
{
|
||||
return (fluid_real_t) 6.875 * fluid_ct2hz_tab[(int)(cents + 300)];
|
||||
}
|
||||
else if(cents < 2100)
|
||||
{
|
||||
return (fluid_real_t) 13.75 * fluid_ct2hz_tab[(int)(cents - 900)];
|
||||
}
|
||||
else if(cents < 3300)
|
||||
{
|
||||
return (fluid_real_t) 27.5 * fluid_ct2hz_tab[(int)(cents - 2100)];
|
||||
}
|
||||
else if(cents < 4500)
|
||||
{
|
||||
return (fluid_real_t) 55.0 * fluid_ct2hz_tab[(int)(cents - 3300)];
|
||||
}
|
||||
else if(cents < 5700)
|
||||
{
|
||||
return (fluid_real_t) 110.0 * fluid_ct2hz_tab[(int)(cents - 4500)];
|
||||
}
|
||||
else if(cents < 6900)
|
||||
{
|
||||
return (fluid_real_t) 220.0 * fluid_ct2hz_tab[(int)(cents - 5700)];
|
||||
}
|
||||
else if(cents < 8100)
|
||||
{
|
||||
return (fluid_real_t) 440.0 * fluid_ct2hz_tab[(int)(cents - 6900)];
|
||||
}
|
||||
else if(cents < 9300)
|
||||
{
|
||||
return (fluid_real_t) 880.0 * fluid_ct2hz_tab[(int)(cents - 8100)];
|
||||
}
|
||||
else if(cents < 10500)
|
||||
{
|
||||
return (fluid_real_t) 1760.0 * fluid_ct2hz_tab[(int)(cents - 9300)];
|
||||
}
|
||||
else if(cents < 11700)
|
||||
{
|
||||
return (fluid_real_t) 3520.0 * fluid_ct2hz_tab[(int)(cents - 10500)];
|
||||
}
|
||||
else if(cents < 12900)
|
||||
{
|
||||
return (fluid_real_t) 7040.0 * fluid_ct2hz_tab[(int)(cents - 11700)];
|
||||
}
|
||||
else if(cents < 14100)
|
||||
{
|
||||
return (fluid_real_t) 14080.0 * fluid_ct2hz_tab[(int)(cents - 12900)];
|
||||
}
|
||||
else
|
||||
{
|
||||
return (fluid_real_t) 1.0; /* some loony trying to make you deaf */
|
||||
unsigned int mult, fac, rem;
|
||||
unsigned int icents = (unsigned int)cents;
|
||||
icents += 300u;
|
||||
|
||||
// don't use stdlib div() here, it turned out have poor performance
|
||||
fac = icents / 1200u;
|
||||
rem = icents % 1200u;
|
||||
|
||||
// Think of "mult" as the factor that we multiply (440/2^6)Hz with,
|
||||
// or in other words mult is the "first factor" of the above
|
||||
// functions comment.
|
||||
//
|
||||
// Assuming sizeof(uint)==4 this will give us a maximum range of
|
||||
// 32 * 1200cents - 300cents == 38100 cents == 29,527,900,160 Hz
|
||||
// which is much more than ever needed. For bigger values, just
|
||||
// safely wrap around.
|
||||
mult = 1u << (fac & (sizeof(mult)*8u - 1u));
|
||||
|
||||
// don't use ldexp() either (poor performance)
|
||||
return mult * fluid_ct2hz_tab[rem];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,9 @@ int main(void)
|
|||
{
|
||||
// 440 * 2^((x-6900)/1200) where x is the cent value given to ct2hz()
|
||||
|
||||
|
||||
TEST_ASSERT(float_eq(fluid_ct2hz_real(38099), 2.9510849101059895e10));
|
||||
|
||||
TEST_ASSERT(float_eq(fluid_ct2hz_real(13500), 19912.12696));
|
||||
|
||||
TEST_ASSERT(float_eq(fluid_ct2hz_real(12900), 14080));
|
||||
|
|
Loading…
Reference in a new issue