Path: blob/master/libs/fluidsynth/src/utils/fluid_conv.c
4396 views
/* FluidSynth - A Software Synthesizer1*2* Copyright (C) 2003 Peter Hanappe and others.3*4* This library is free software; you can redistribute it and/or5* modify it under the terms of the GNU Lesser General Public License6* as published by the Free Software Foundation; either version 2.1 of7* the License, or (at your option) any later version.8*9* This library is distributed in the hope that it will be useful, but10* WITHOUT ANY WARRANTY; without even the implied warranty of11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU12* Lesser General Public License for more details.13*14* You should have received a copy of the GNU Lesser General Public15* License along with this library; if not, write to the Free16* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA17* 02110-1301, USA18*/1920#include "fluid_conv.h"21#include "fluid_sys.h"22#include "fluid_conv_tables.inc.h"2324/*25* Converts absolute cents to Hertz26*27* As per sfspec section 9.3:28*29* ABSOLUTE CENTS - An absolute logarithmic measure of frequency based on a30* reference of MIDI key number scaled by 100.31* A cent is 1/1200 of an octave [which is the twelve hundredth root of two],32* and value 6900 is 440 Hz (A-440).33*34* Implemented below basically is the following:35* 440 * 2^((cents-6900)/1200)36* = 440 * 2^((int)((cents-6900)/1200)) * 2^(((int)cents-6900)%1200))37* = 2^((int)((cents-6900)/1200)) * (440 * 2^(((int)cents-6900)%1200)))38* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^39* This second factor is stored in the lookup table.40*41* The first factor can be implemented with a fast shift when the exponent42* is always an int. This is the case when using 440/2^6 Hz rather than 440Hz43* reference.44*/45fluid_real_t46fluid_ct2hz_real(fluid_real_t cents)47{48if(FLUID_UNLIKELY(cents < 0))49{50return fluid_act2hz(cents);51}52else53{54unsigned int mult, fac, rem;55unsigned int icents = (unsigned int)cents;56icents += 300u;5758// don't use stdlib div() here, it turned out have poor performance59fac = icents / 1200u;60rem = icents % 1200u;6162// Think of "mult" as the factor that we multiply (440/2^6)Hz with,63// or in other words mult is the "first factor" of the above64// functions comment.65//66// Assuming sizeof(uint)==4 this will give us a maximum range of67// 32 * 1200cents - 300cents == 38100 cents == 29,527,900,160 Hz68// which is much more than ever needed. For bigger values, just69// safely wrap around (the & is just a replacement for the quick70// modulo operation % 32).71mult = 1u << (fac & (sizeof(mult)*8u - 1u));7273// don't use ldexp() either (poor performance)74return mult * fluid_ct2hz_tab[rem];75}76}7778/*79* fluid_ct2hz80*/81fluid_real_t82fluid_ct2hz(fluid_real_t cents)83{84/* Filter fc limit: SF2.01 page 48 # 8 */85if(cents >= 13500)86{87cents = 13500; /* 20 kHz */88}89else if(cents < 1500)90{91cents = 1500; /* 20 Hz */92}9394return fluid_ct2hz_real(cents);95}9697/*98* fluid_cb2amp99*100* in: a value between 0 and 1440, 0 is no attenuation101* out: a value between 1 and 0102*/103fluid_real_t104fluid_cb2amp(fluid_real_t cb)105{106/*107* cb: an attenuation in 'centibels' (1/10 dB)108* SF2.01 page 49 # 48 limits it to 144 dB.109* 96 dB is reasonable for 16 bit systems, 144 would make sense for 24 bit.110*/111112/* minimum attenuation: 0 dB */113if(FLUID_UNLIKELY(cb < 0))114{115/* Issue #1374: it seems that by using modLfoToVolEnv, the attenuation can become negative and116* therefore the signal needs to be amplified.117* In such a rare case, calculate the attenuation on the fly.118*119* This behavior is backed by the spec saying:120* modLfoToVolume: "A positive number indicates a positive LFO excursion increases volume;121* a negative number indicates a positive excursion decreases volume.122* [...] For example, a value of 100 indicates that the volume will first rise ten dB, then fall ten dB."123*124* And in order to rise, a negative attenuation must be permitted.125*/126return FLUID_POW(10.0f, cb / -200.0f);127}128129if(cb >= FLUID_CB_AMP_SIZE)130{131return 0.0;132}133134return fluid_cb2amp_tab[(int) cb];135}136137/*138* fluid_tc2sec_delay139*/140fluid_real_t141fluid_tc2sec_delay(fluid_real_t tc)142{143/* SF2.01 section 8.1.2 items 21, 23, 25, 33144* SF2.01 section 8.1.3 items 21, 23, 25, 33145*146* The most negative number indicates a delay of 0. Range is limited147* from -12000 to 5000 */148if(tc <= -32768.0f)149{150return (fluid_real_t) 0.0f;151};152153if(tc < -12000.f)154{155tc = (fluid_real_t) -12000.0f;156}157158if(tc > 5000.0f)159{160tc = (fluid_real_t) 5000.0f;161}162163return fluid_tc2sec(tc);164}165166/*167* fluid_tc2sec_attack168*/169fluid_real_t170fluid_tc2sec_attack(fluid_real_t tc)171{172/* SF2.01 section 8.1.2 items 26, 34173* SF2.01 section 8.1.3 items 26, 34174* The most negative number indicates a delay of 0175* Range is limited from -12000 to 8000 */176if(tc <= -32768.f)177{178return (fluid_real_t) 0.f;179};180181if(tc < -12000.f)182{183tc = (fluid_real_t) -12000.f;184};185186if(tc > 8000.f)187{188tc = (fluid_real_t) 8000.f;189};190191return fluid_tc2sec(tc);192}193194/*195* fluid_tc2sec196*/197fluid_real_t198fluid_tc2sec(fluid_real_t tc)199{200/* No range checking here! */201return FLUID_POW(2.f, tc / 1200.f);202}203204/*205* fluid_sec2tc206*207* seconds to timecents208*/209fluid_real_t210fluid_sec2tc(fluid_real_t sec)211{212fluid_real_t res;213if(sec < 0)214{215// would require a complex solution of fluid_tc2sec(), but this is real-only216return -32768.f;217}218219res = (1200.f / M_LN2) * FLUID_LOGF(sec);220if(res < -32768.f)221{222res = -32768.f;223}224return res;225}226227/*228* fluid_tc2sec_release229*/230fluid_real_t231fluid_tc2sec_release(fluid_real_t tc)232{233/* SF2.01 section 8.1.2 items 30, 38234* SF2.01 section 8.1.3 items 30, 38235* No 'most negative number' rule here!236* Range is limited from -12000 to 8000 */237if(tc <= -32768.f)238{239return (fluid_real_t) 0.f;240};241242if(tc < -12000.f)243{244tc = (fluid_real_t) -12000.f;245};246247if(tc > 8000.f)248{249tc = (fluid_real_t) 8000.f;250};251252return fluid_tc2sec(tc);253}254255/**256* The inverse operation, converting from Hertz to cents257*/258fluid_real_t fluid_hz2ct(fluid_real_t f)259{260return 6900.f + (1200.f / FLUID_M_LN2) * FLUID_LOGF(f / 440.0f);261}262263/*264* fluid_act2hz265*266* Convert from absolute cents to Hertz267*/268double269fluid_act2hz(double c)270{271// do not use FLUID_POW, otherwise the unit tests will fail when compiled in single precision272return 8.1757989156437073336828122976032719176391831357 * pow(2.f, c / 1200.f);273}274275/*276* fluid_pan277*/278fluid_real_t279fluid_pan(fluid_real_t c, int left)280{281if(left)282{283c = -c;284}285286if(c <= -500.f)287{288return (fluid_real_t) 0.f;289}290else if(c >= 500.f)291{292return (fluid_real_t) 1.f;293}294else295{296return fluid_pan_tab[(int)(c) + 500];297}298}299300/*301* Return the amount of attenuation based on the balance for the specified302* channel. If balance is negative (turned toward left channel, only the right303* channel is attenuated. If balance is positive, only the left channel is304* attenuated.305*306* @params balance left/right balance, range [-960;960] in absolute centibels307* @return amount of attenuation [0.0;1.0]308*/309fluid_real_t fluid_balance(fluid_real_t balance, int left)310{311/* This is the most common case */312if(balance == 0.f)313{314return 1.0f;315}316317if((left && balance < 0.f) || (!left && balance > 0.f))318{319return 1.0f;320}321322if(balance < 0.f)323{324balance = -balance;325}326327return fluid_cb2amp(balance);328}329330/*331* fluid_concave332*/333fluid_real_t334fluid_concave(fluid_real_t val)335{336int ival = (int)val;337if(val < 0.f)338{339return 0.f;340}341else if (ival >= FLUID_VEL_CB_SIZE - 1)342{343return fluid_concave_tab[FLUID_VEL_CB_SIZE - 1];344}345346return fluid_concave_tab[ival] + (fluid_concave_tab[ival + 1] - fluid_concave_tab[ival]) * (val - ival);347}348349/*350* fluid_convex351*/352fluid_real_t353fluid_convex(fluid_real_t val)354{355int ival = (int)val;356if(val < 0.f)357{358return 0.f;359}360else if (ival >= FLUID_VEL_CB_SIZE - 1)361{362return fluid_convex_tab[FLUID_VEL_CB_SIZE - 1];363}364365// interpolation between convex steps: fixes bad sounds with modenv and filter cutoff366return fluid_convex_tab[ival] + (fluid_convex_tab[ival + 1] - fluid_convex_tab[ival]) * (val - ival);367}368369370