From 5397bbeaf4c2cc9c9a73cf4fdb3015c5ddb910c4 Mon Sep 17 00:00:00 2001 From: Vesa Date: Tue, 8 Apr 2014 12:06:39 +0300 Subject: [PATCH 1/4] Bandlimit changes/algorithm tweaking, add sinc function to lmms_math --- include/lmms_math.h | 7 +++++++ src/core/BandLimitedWave.cpp | 18 ++++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/include/lmms_math.h b/include/lmms_math.h index b82e589a7..d7ca24561 100644 --- a/include/lmms_math.h +++ b/include/lmms_math.h @@ -120,5 +120,12 @@ static inline double fastPow( double a, double b ) return u.d; } +// sinc function +static inline double sinc( double _x ) +{ + return sin( F_PI * _x ) / ( F_PI * _x ); +} + + #endif diff --git a/src/core/BandLimitedWave.cpp b/src/core/BandLimitedWave.cpp index 65cc39119..cfa0a3c7d 100644 --- a/src/core/BandLimitedWave.cpp +++ b/src/core/BandLimitedWave.cpp @@ -44,13 +44,15 @@ void BandLimitedWave::generateWaves() { int harm = 1; double s = 0.0f; + double hlen; do { - const double amp = -1.0 / static_cast( harm ); + hlen = static_cast( len ) / static_cast( harm ); + const double amp = -1.0 / static_cast( harm ) * ( hlen <= 4 ? 0.5 : 1.0 ) * ( hlen < 8 ? 0.75 : 1.0 ); const double a2 = cos( om * harm * F_2PI ); s += amp * a2 * sin( static_cast( ph * harm ) / static_cast( len ) * F_2PI ); harm++; - } while( len/harm > 2 ); + } while( hlen >= 2.0 ); s_waveforms[ BandLimitedWave::BLSaw ].setSampleAt( i, ph, s ); max = qMax( max, qAbs( s ) ); } @@ -73,13 +75,15 @@ void BandLimitedWave::generateWaves() { int harm = 1; double s = 0.0f; + double hlen; do { - const double amp = 1.0 / static_cast( harm ); + hlen = static_cast( len ) / static_cast( harm ); + const double amp = 1.0 / static_cast( harm ) * ( hlen <= 4 ? 0.5 : 1.0 ) * ( hlen < 8 ? 0.75 : 1.0 ); const double a2 = cos( om * harm * F_2PI ); s += amp * a2 * sin( static_cast( ph * harm ) / static_cast( len ) * F_2PI ); harm += 2; - } while( len/harm > 2 ); + } while( hlen >= 2.0 ); s_waveforms[ BandLimitedWave::BLSquare ].setSampleAt( i, ph, s ); max = qMax( max, qAbs( s ) ); } @@ -103,14 +107,16 @@ void BandLimitedWave::generateWaves() { int harm = 1; double s = 0.0f; + double hlen; do { - const double amp = 1.0 / static_cast( harm * harm ); + hlen = static_cast( len ) / static_cast( harm ); + const double amp = 1.0 / static_cast( harm * harm ) * ( hlen <= 4 ? 0.5 : 1.0 ) * ( hlen < 8 ? 0.75 : 1.0 ); //const double a2 = cos( om * harm * F_2PI ); s += amp * /*a2 **/ sin( ( static_cast( ph * harm ) / static_cast( len ) + ( ( harm + 1 ) % 4 == 0 ? 0.5 : 0.0 ) ) * F_2PI ); harm += 2; - } while( len/harm > 2 ); + } while( hlen >= 2.0 ); s_waveforms[ BandLimitedWave::BLTriangle ].setSampleAt( i, ph, s ); max = qMax( max, qAbs( s ) ); } From 7b0d829cc483f725db99a049e8d305fda99cbf30 Mon Sep 17 00:00:00 2001 From: Vesa Date: Tue, 8 Apr 2014 23:07:16 +0300 Subject: [PATCH 2/4] More fixes --- include/BandLimitedWave.h | 60 +++++++++--------------------------- include/interpolation.h | 9 ++++-- include/lmms_math.h | 7 +++-- src/core/BandLimitedWave.cpp | 24 +++++++-------- 4 files changed, 36 insertions(+), 64 deletions(-) diff --git a/include/BandLimitedWave.h b/include/BandLimitedWave.h index 9971cf200..f8467b1bb 100644 --- a/include/BandLimitedWave.h +++ b/include/BandLimitedWave.h @@ -128,58 +128,26 @@ public: const float ph = fraction( _ph ); const float lookupf = ph * static_cast( tlen ); const int lookup = static_cast( lookupf ); + const float ip = fraction( lookupf ); + const sample_t s1 = s_waveforms[ _wave ].sampleAt( t, lookup ); const sample_t s2 = s_waveforms[ _wave ].sampleAt( t, ( lookup + 1 ) % tlen ); - return linearInterpolate( s1, s2, fraction( lookupf ) ); + const sample_t s12 = linearInterpolate( s1, s2, ip ); + return s12; + /*if( _wavelen > 0.75 * tlen ) return s12; - /*const int tlen1 = 1 << t; - const int tlen2 = 1 << ( t - 1 ); + lookup = lookup >> 1; + tlen = tlen >> 1; + t -= 1; + const sample_t s3 = s_waveforms[ _wave ].sampleAt( t, lookup ); + const sample_t s4 = s_waveforms[ _wave ].sampleAt( t, ( lookup + 1 ) % tlen ); + const sample_t s34 = linearInterpolate( s3, s4, ip ); - const float ph = fraction( _ph ); - const float lookupf = ph * static_cast( tlen1 ); - const int lookup1 = static_cast( lookupf ); - const int lookup2 = static_cast( ph * static_cast( tlen2 ) ); - - const sample_t s1 = linearInterpolate( s_waveforms[ _wave ].sampleAt( t, lookup1 ), - s_waveforms[ _wave ].sampleAt( t, ( lookup1 + 1 ) % tlen1 ), - fraction( lookupf ) ); - const sample_t s2 = s_waveforms[ _wave ].sampleAt( t - 1, lookup2 ); - - const float ip = static_cast( tlen1 - _wavelen ) / static_cast( tlen2 ); + const float ip2 = ( ( _wavelen - tlen ) / tlen - 0.5 ) * 2.0; + + return linearInterpolate( s34, s12, ip2 );*/ - return linearInterpolate( s1, s2, ip );*/ - }; - - /*! \brief The same as oscillate but uses cosinus interpolation instead of linear. - */ - static inline sample_t oscillateCos( float _ph, float _wavelen, Waveforms _wave ) - { - int t = MAXLEN; - while( ( 1 << t ) > _wavelen ) { t--; } - t = qMax( 1, t ); - - const int tlen = 1 << t; - const float ph = fraction( _ph ); - const int lookup = static_cast( ph * tlen ); - const sample_t s1 = s_waveforms[ _wave ].sampleAt( t, lookup ); - const sample_t s2 = s_waveforms[ _wave ].sampleAt( t, ( lookup + 1 ) % tlen ); - - return cosinusInterpolate( s1, s2, ph ); - }; - - /*! \brief The same as oscillate but without any interpolation. - */ - static inline sample_t oscillateNoip( float _ph, float _wavelen, Waveforms _wave ) - { - int t = MAXLEN; - while( ( 1 << t ) > _wavelen ) { t--; } - t = qMax( 1, t ); - - const int tlen = 1 << t; - const float ph = fraction( _ph ); - const int lookup = static_cast( ph * tlen ); - return s_waveforms[ _wave ].sampleAt( t, lookup ); }; diff --git a/include/interpolation.h b/include/interpolation.h index 43c50e102..0ca1d4e06 100644 --- a/include/interpolation.h +++ b/include/interpolation.h @@ -79,13 +79,16 @@ inline float cubicInterpolate( float v0, float v1, float v2, float v3, float x ) inline float cosinusInterpolate( float v0, float v1, float x ) { - float f = cosf( x * ( F_PI_2 ) ); - return( v1 - f * (v1-v0) ); + const float f = ( 1.0f - cosf( x * F_PI ) ) * 0.5f; +#ifdef FP_FAST_FMAF + return fmaf( x, v1-v0, v0 ); +#else + return f * (v1-v0) + v0; +#endif // return( v0*f + v1*( 1.0f-f ) ); } - inline float linearInterpolate( float v0, float v1, float x ) { // take advantage of fma function if present in hardware diff --git a/include/lmms_math.h b/include/lmms_math.h index d7ca24561..36656101a 100644 --- a/include/lmms_math.h +++ b/include/lmms_math.h @@ -23,10 +23,11 @@ */ -#ifndef _LMMS_MATH_H -#define _LMMS_MATH_H +#ifndef LMMS_MATH_H +#define LMMS_MATH_H #include +#include "lmms_constants.h" #ifdef __INTEL_COMPILER @@ -123,7 +124,7 @@ static inline double fastPow( double a, double b ) // sinc function static inline double sinc( double _x ) { - return sin( F_PI * _x ) / ( F_PI * _x ); + return _x == 0.0 ? 1.0 : sin( F_PI * _x ) / ( F_PI * _x ); } diff --git a/src/core/BandLimitedWave.cpp b/src/core/BandLimitedWave.cpp index cfa0a3c7d..c820eea0e 100644 --- a/src/core/BandLimitedWave.cpp +++ b/src/core/BandLimitedWave.cpp @@ -37,7 +37,7 @@ void BandLimitedWave::generateWaves() for( i = 1; i <= MAXLEN; i++ ) { const int len = 1 << i; - const double om = 1.0 / len; + //const double om = 1.0 / len; double max = 0.0; for( int ph = 0; ph < len; ph++ ) @@ -48,11 +48,11 @@ void BandLimitedWave::generateWaves() do { hlen = static_cast( len ) / static_cast( harm ); - const double amp = -1.0 / static_cast( harm ) * ( hlen <= 4 ? 0.5 : 1.0 ) * ( hlen < 8 ? 0.75 : 1.0 ); - const double a2 = cos( om * harm * F_2PI ); - s += amp * a2 * sin( static_cast( ph * harm ) / static_cast( len ) * F_2PI ); + const double amp = -1.0 / static_cast( harm ); + //const double a2 = cos( om * harm * F_2PI ); + s += amp * /*a2 **/sin( static_cast( ph * harm ) / static_cast( len ) * F_2PI ); harm++; - } while( hlen >= 2.0 ); + } while( hlen >= 4.0 ); s_waveforms[ BandLimitedWave::BLSaw ].setSampleAt( i, ph, s ); max = qMax( max, qAbs( s ) ); } @@ -68,7 +68,7 @@ void BandLimitedWave::generateWaves() for( i = 1; i <= MAXLEN; i++ ) { const int len = 1 << i; - const double om = 1.0 / len; + //const double om = 1.0 / len; double max = 0.0; for( int ph = 0; ph < len; ph++ ) @@ -79,11 +79,11 @@ void BandLimitedWave::generateWaves() do { hlen = static_cast( len ) / static_cast( harm ); - const double amp = 1.0 / static_cast( harm ) * ( hlen <= 4 ? 0.5 : 1.0 ) * ( hlen < 8 ? 0.75 : 1.0 ); - const double a2 = cos( om * harm * F_2PI ); - s += amp * a2 * sin( static_cast( ph * harm ) / static_cast( len ) * F_2PI ); + const double amp = 1.0 / static_cast( harm ); + //const double a2 = cos( om * harm * F_2PI ); + s += amp * /*a2 **/ sin( static_cast( ph * harm ) / static_cast( len ) * F_2PI ); harm += 2; - } while( hlen >= 2.0 ); + } while( hlen >= 4.0 ); s_waveforms[ BandLimitedWave::BLSquare ].setSampleAt( i, ph, s ); max = qMax( max, qAbs( s ) ); } @@ -111,12 +111,12 @@ void BandLimitedWave::generateWaves() do { hlen = static_cast( len ) / static_cast( harm ); - const double amp = 1.0 / static_cast( harm * harm ) * ( hlen <= 4 ? 0.5 : 1.0 ) * ( hlen < 8 ? 0.75 : 1.0 ); + const double amp = 1.0 / static_cast( harm * harm ); //const double a2 = cos( om * harm * F_2PI ); s += amp * /*a2 **/ sin( ( static_cast( ph * harm ) / static_cast( len ) + ( ( harm + 1 ) % 4 == 0 ? 0.5 : 0.0 ) ) * F_2PI ); harm += 2; - } while( hlen >= 2.0 ); + } while( hlen >= 4.0 ); s_waveforms[ BandLimitedWave::BLTriangle ].setSampleAt( i, ph, s ); max = qMax( max, qAbs( s ) ); } From d5fa2568ed5841d4a6ddde71d7625feea8312f89 Mon Sep 17 00:00:00 2001 From: Vesa Date: Wed, 9 Apr 2014 13:03:34 +0300 Subject: [PATCH 3/4] Further improvements to bandlimited waves --- include/BandLimitedWave.h | 66 ++++++++++++++++++++++++------------ src/core/BandLimitedWave.cpp | 16 ++++----- 2 files changed, 52 insertions(+), 30 deletions(-) diff --git a/include/BandLimitedWave.h b/include/BandLimitedWave.h index f8467b1bb..b4ef306f1 100644 --- a/include/BandLimitedWave.h +++ b/include/BandLimitedWave.h @@ -32,23 +32,41 @@ #include "engine.h" #include "Mixer.h" -#define MAXLEN 12 -#define MIPMAPSIZE 1 << ( MAXLEN + 1 ) +#define MAXLEN 11 +#define MIPMAPSIZE 2 << ( MAXLEN + 1 ) +#define MIPMAPSIZE3 3 << ( MAXLEN + 1 ) +#define MAXTBL 23 +#define MINTLEN 2 << 0 +#define MAXTLEN 3 << MAXLEN +// table for table sizes +const int TLENS[MAXTBL+1] = { 2 << 0, 3 << 0, 2 << 1, 3 << 1, + 2 << 2, 3 << 2, 2 << 3, 3 << 3, + 2 << 4, 3 << 4, 2 << 5, 3 << 5, + 2 << 6, 3 << 6, 2 << 7, 3 << 7, + 2 << 8, 3 << 8, 2 << 9, 3 << 9, + 2 << 10, 3 << 10, 2 << 11, 3 << 11 }; typedef struct { public: inline sample_t sampleAt( int _table, int _ph ) { - return m_data[ ( 1 << _table ) + _ph ]; + if( _table % 2 == 0 ) + { return m_data[ TLENS[ _table ] + _ph ]; } + else + { return m_data3[ TLENS[ _table ] + _ph ]; } } inline void setSampleAt( int _table, int _ph, sample_t _sample ) { - m_data[ ( 1 << _table ) + _ph ] = _sample; + if( _table % 2 == 0 ) + { m_data[ TLENS[ _table ] + _ph ] = _sample; } + else + { m_data3[ TLENS[ _table ] + _ph ] = _sample; } } private: sample_t m_data [ MIPMAPSIZE ]; + sample_t m_data3 [ MIPMAPSIZE3 ]; } WaveMipMap; @@ -96,10 +114,10 @@ public: static inline sample_t oscillate( float _ph, float _wavelen, Waveforms _wave ) { // high wavelen/ low freq - if( _wavelen >= 1 << MAXLEN ) + if( _wavelen > TLENS[ MAXTBL -1 ] ) { - const int t = MAXLEN; - const int tlen = 1 << t; + const int t = MAXTBL; + const int tlen = TLENS[t]; const float ph = fraction( _ph ); const float lookupf = ph * static_cast( tlen ); const int lookup = static_cast( lookupf ); @@ -110,8 +128,8 @@ public: // low wavelen/ high freq if( _wavelen <= 2.0f ) { - const int t = 1; - const int tlen = 2; + const int t = 0; + const int tlen = TLENS[t]; const float ph = fraction( _ph ); const float lookupf = ph * static_cast( tlen ); const int lookup = static_cast( lookupf ); @@ -121,33 +139,37 @@ public: } // get the next higher tlen - int t = 2; - while( ( 1 << t ) < _wavelen ) { t++; } + int t = 1; + while( TLENS[t] < _wavelen ) { t++; } - const int tlen = 1 << t; + int tlen = TLENS[t]; const float ph = fraction( _ph ); const float lookupf = ph * static_cast( tlen ); - const int lookup = static_cast( lookupf ); + int lookup = static_cast( lookupf ); const float ip = fraction( lookupf ); const sample_t s1 = s_waveforms[ _wave ].sampleAt( t, lookup ); const sample_t s2 = s_waveforms[ _wave ].sampleAt( t, ( lookup + 1 ) % tlen ); - const sample_t s12 = linearInterpolate( s1, s2, ip ); + //const sample_t sr = linearInterpolate( s1, s2, ip ); - return s12; - /*if( _wavelen > 0.75 * tlen ) return s12; + const int lm = lookup == 0 ? tlen - 1 : lookup - 1; + const sample_t s0 = s_waveforms[ _wave ].sampleAt( t, lm ); + const sample_t s3 = s_waveforms[ _wave ].sampleAt( t, ( lookup + 2 ) % tlen ); + const sample_t sr = cubicInterpolate( s0, s1, s2, s3, ip ); - lookup = lookup >> 1; - tlen = tlen >> 1; - t -= 1; + return sr; + +/* lookup = lookup << 1; + tlen = tlen << 1; + t += 1; const sample_t s3 = s_waveforms[ _wave ].sampleAt( t, lookup ); const sample_t s4 = s_waveforms[ _wave ].sampleAt( t, ( lookup + 1 ) % tlen ); const sample_t s34 = linearInterpolate( s3, s4, ip ); - const float ip2 = ( ( _wavelen - tlen ) / tlen - 0.5 ) * 2.0; - - return linearInterpolate( s34, s12, ip2 );*/ + const float ip2 = ( ( tlen - _wavelen ) / tlen - 0.5 ) * 2.0; + return linearInterpolate( s12, s34, ip2 ); + */ }; diff --git a/src/core/BandLimitedWave.cpp b/src/core/BandLimitedWave.cpp index c820eea0e..92b438a01 100644 --- a/src/core/BandLimitedWave.cpp +++ b/src/core/BandLimitedWave.cpp @@ -34,9 +34,9 @@ void BandLimitedWave::generateWaves() int i; // saw wave - BLSaw - for( i = 1; i <= MAXLEN; i++ ) + for( i = 0; i <= MAXTBL; i++ ) { - const int len = 1 << i; + const int len = TLENS[i]; //const double om = 1.0 / len; double max = 0.0; @@ -65,9 +65,9 @@ void BandLimitedWave::generateWaves() } // square wave - BLSquare - for( i = 1; i <= MAXLEN; i++ ) + for( i = 0; i <= MAXTBL; i++ ) { - const int len = 1 << i; + const int len = TLENS[i]; //const double om = 1.0 / len; double max = 0.0; @@ -97,9 +97,9 @@ void BandLimitedWave::generateWaves() // triangle wave - BLTriangle - for( i = 1; i <= MAXLEN; i++ ) + for( i = 0; i <= MAXTBL; i++ ) { - const int len = 1 << i; + const int len = TLENS[i]; //const double om = 1.0 / len; double max = 0.0; @@ -131,9 +131,9 @@ void BandLimitedWave::generateWaves() // moog saw wave - BLMoog // basically, just add in triangle + 270-phase saw - for( i = 1; i <= MAXLEN; i++ ) + for( i = 0; i <= MAXTBL; i++ ) { - const int len = 1 << i; + const int len = TLENS[i]; for( int ph = 0; ph < len; ph++ ) { From 2bfd19cc44852dff6e8a56cb81c9e1823895f8af Mon Sep 17 00:00:00 2001 From: Vesa Date: Wed, 9 Apr 2014 13:30:17 +0300 Subject: [PATCH 4/4] Move waveform generation away from main --- include/BandLimitedWave.h | 2 ++ plugins/monstro/Monstro.cpp | 4 ++++ src/core/BandLimitedWave.cpp | 7 ++++++- src/core/main.cpp | 4 ---- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/include/BandLimitedWave.h b/include/BandLimitedWave.h index b4ef306f1..fdb33b23a 100644 --- a/include/BandLimitedWave.h +++ b/include/BandLimitedWave.h @@ -174,6 +174,8 @@ public: static void generateWaves(); + + static bool s_wavesGenerated; static WaveMipMap s_waveforms [NumBLWaveforms]; }; diff --git a/plugins/monstro/Monstro.cpp b/plugins/monstro/Monstro.cpp index 644bf3168..7ca7477d4 100644 --- a/plugins/monstro/Monstro.cpp +++ b/plugins/monstro/Monstro.cpp @@ -1026,6 +1026,10 @@ MonstroInstrument::MonstroInstrument( InstrumentTrack * _instrument_track ) : m_sub3lfo2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Sub3-LFO2" ) ) { +// make sure the wavetables exist: +// generate bandlimited wavetables + BandLimitedWave::generateWaves(); + // setup waveboxes setwavemodel( m_osc2Wave ) setwavemodel( m_osc3Wave1 ) diff --git a/src/core/BandLimitedWave.cpp b/src/core/BandLimitedWave.cpp index 92b438a01..55214765f 100644 --- a/src/core/BandLimitedWave.cpp +++ b/src/core/BandLimitedWave.cpp @@ -27,10 +27,13 @@ WaveMipMap BandLimitedWave::s_waveforms[4] = { }; - +bool BandLimitedWave::s_wavesGenerated = false; void BandLimitedWave::generateWaves() { +// don't generate if they already exist + if( s_wavesGenerated ) return; + int i; // saw wave - BLSaw @@ -144,4 +147,6 @@ void BandLimitedWave::generateWaves() } } + s_wavesGenerated = true; + } diff --git a/src/core/main.cpp b/src/core/main.cpp index 646b25713..fbf7f9507 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -65,7 +65,6 @@ #include "ProjectRenderer.h" #include "DataFile.h" #include "song.h" -#include "BandLimitedWave.h" static inline QString baseName( const QString & _file ) { @@ -120,9 +119,6 @@ int main( int argc, char * * argv ) new QCoreApplication( argc, argv ) : new QApplication( argc, argv ) ; - // generate bandlimited wavetables for instruments to use - BandLimitedWave::generateWaves(); - Mixer::qualitySettings qs( Mixer::qualitySettings::Mode_HighQuality ); ProjectRenderer::OutputSettings os( 44100, false, 160, ProjectRenderer::Depth_16Bit );