Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More efficient and unlimited implementation of fluid_ct2hz() #569

Merged
merged 9 commits into from
Oct 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/gentables/gen_conv.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
91 changes: 40 additions & 51 deletions src/utils/fluid_conv.c
Original file line number Diff line number Diff line change
Expand Up @@ -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];
}
}

Expand Down
3 changes: 3 additions & 0 deletions test/test_ct2hz.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,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));
Expand Down