ardour/libs/fluidsynth/src/fluid_conv.c

331 lines
7.2 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 Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA
*/
#include "fluid_conv.h"
#include "fluid_sys.h"
#include "fluid_conv_tables.inc.h"
/*
* 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(FLUID_UNLIKELY(cents < 0))
{
return (fluid_real_t) 1.0;
}
else
{
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 (the & is just a replacement for the quick
// modulo operation % 32).
mult = 1u << (fac & (sizeof(mult)*8u - 1u));
// don't use ldexp() either (poor performance)
return mult * fluid_ct2hz_tab[rem];
}
}
/*
* fluid_ct2hz
*/
fluid_real_t
fluid_ct2hz(fluid_real_t cents)
{
/* Filter fc limit: SF2.01 page 48 # 8 */
if(cents >= 13500)
{
cents = 13500; /* 20 kHz */
}
else if(cents < 1500)
{
cents = 1500; /* 20 Hz */
}
return fluid_ct2hz_real(cents);
}
/*
* fluid_cb2amp
*
* in: a value between 0 and 1440, 0 is no attenuation
* out: a value between 1 and 0
*/
fluid_real_t
fluid_cb2amp(fluid_real_t cb)
{
/*
* cb: an attenuation in 'centibels' (1/10 dB)
* SF2.01 page 49 # 48 limits it to 144 dB.
* 96 dB is reasonable for 16 bit systems, 144 would make sense for 24 bit.
*/
/* minimum attenuation: 0 dB */
if(cb < 0)
{
return 1.0;
}
if(cb >= FLUID_CB_AMP_SIZE)
{
return 0.0;
}
return fluid_cb2amp_tab[(int) cb];
}
/*
* fluid_tc2sec_delay
*/
fluid_real_t
fluid_tc2sec_delay(fluid_real_t tc)
{
/* SF2.01 section 8.1.2 items 21, 23, 25, 33
* SF2.01 section 8.1.3 items 21, 23, 25, 33
*
* The most negative number indicates a delay of 0. Range is limited
* from -12000 to 5000 */
if(tc <= -32768.0f)
{
return (fluid_real_t) 0.0f;
};
if(tc < -12000.f)
{
tc = (fluid_real_t) -12000.0f;
}
if(tc > 5000.0f)
{
tc = (fluid_real_t) 5000.0f;
}
return FLUID_POW(2.f, tc / 1200.f);
}
/*
* fluid_tc2sec_attack
*/
fluid_real_t
fluid_tc2sec_attack(fluid_real_t tc)
{
/* SF2.01 section 8.1.2 items 26, 34
* SF2.01 section 8.1.3 items 26, 34
* The most negative number indicates a delay of 0
* Range is limited from -12000 to 8000 */
if(tc <= -32768.f)
{
return (fluid_real_t) 0.f;
};
if(tc < -12000.f)
{
tc = (fluid_real_t) -12000.f;
};
if(tc > 8000.f)
{
tc = (fluid_real_t) 8000.f;
};
return FLUID_POW(2.f, tc / 1200.f);
}
/*
* fluid_tc2sec
*/
fluid_real_t
fluid_tc2sec(fluid_real_t tc)
{
/* No range checking here! */
return FLUID_POW(2.f, tc / 1200.f);
}
/*
* fluid_tc2sec_release
*/
fluid_real_t
fluid_tc2sec_release(fluid_real_t tc)
{
/* SF2.01 section 8.1.2 items 30, 38
* SF2.01 section 8.1.3 items 30, 38
* No 'most negative number' rule here!
* Range is limited from -12000 to 8000 */
if(tc <= -32768.f)
{
return (fluid_real_t) 0.f;
};
if(tc < -12000.f)
{
tc = (fluid_real_t) -12000.f;
};
if(tc > 8000.f)
{
tc = (fluid_real_t) 8000.f;
};
return FLUID_POW(2.f, tc / 1200.f);
}
/*
* fluid_act2hz
*
* Convert from absolute cents to Hertz
*
* The inverse operation, converting from Hertz to cents, was unused and implemented as
*
fluid_hz2ct(fluid_real_t f)
{
return 6900.f + (1200.f / FLUID_M_LN2) * FLUID_LOGF(f / 440.0f));
}
*/
fluid_real_t
fluid_act2hz(fluid_real_t c)
{
return 8.176f * FLUID_POW(2.f, c / 1200.f);
}
/*
* fluid_pan
*/
fluid_real_t
fluid_pan(fluid_real_t c, int left)
{
if(left)
{
c = -c;
}
if(c <= -500.f)
{
return (fluid_real_t) 0.f;
}
else if(c >= 500.f)
{
return (fluid_real_t) 1.f;
}
else
{
return fluid_pan_tab[(int)(c) + 500];
}
}
/*
* Return the amount of attenuation based on the balance for the specified
* channel. If balance is negative (turned toward left channel, only the right
* channel is attenuated. If balance is positive, only the left channel is
* attenuated.
*
* @params balance left/right balance, range [-960;960] in absolute centibels
* @return amount of attenuation [0.0;1.0]
*/
fluid_real_t fluid_balance(fluid_real_t balance, int left)
{
/* This is the most common case */
if(balance == 0.f)
{
return 1.0f;
}
if((left && balance < 0.f) || (!left && balance > 0.f))
{
return 1.0f;
}
if(balance < 0.f)
{
balance = -balance;
}
return fluid_cb2amp(balance);
}
/*
* fluid_concave
*/
fluid_real_t
fluid_concave(fluid_real_t val)
{
if(val < 0.f)
{
return 0.f;
}
else if(val >= (fluid_real_t)FLUID_VEL_CB_SIZE)
{
return 1.f;
}
return fluid_concave_tab[(int) val];
}
/*
* fluid_convex
*/
fluid_real_t
fluid_convex(fluid_real_t val)
{
if(val < 0.f)
{
return 0.f;
}
else if(val >= (fluid_real_t)FLUID_VEL_CB_SIZE)
{
return 1.f;
}
return fluid_convex_tab[(int) val];
}