diff --git a/include/BandLimitedWave.h b/include/BandLimitedWave.h index 9971cf200..fdb33b23a 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,69 +139,43 @@ 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 ); - return linearInterpolate( s1, s2, fraction( lookupf ) ); + //const sample_t sr = linearInterpolate( s1, s2, ip ); + 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 ); - /*const int tlen1 = 1 << t; - const int tlen2 = 1 << ( t - 1 ); + return sr; - 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 ); +/* 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 ); - 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 ); + const float ip2 = ( ( tlen - _wavelen ) / tlen - 0.5 ) * 2.0; + + return linearInterpolate( s12, s34, ip2 ); + */ }; static void generateWaves(); + + static bool s_wavesGenerated; static WaveMipMap s_waveforms [NumBLWaveforms]; }; 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 b82e589a7..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 @@ -120,5 +121,12 @@ static inline double fastPow( double a, double b ) return u.d; } +// sinc function +static inline double sinc( double _x ) +{ + return _x == 0.0 ? 1.0 : sin( F_PI * _x ) / ( F_PI * _x ); +} + + #endif 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 65cc39119..55214765f 100644 --- a/src/core/BandLimitedWave.cpp +++ b/src/core/BandLimitedWave.cpp @@ -27,30 +27,35 @@ 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 - for( i = 1; i <= MAXLEN; i++ ) + for( i = 0; i <= MAXTBL; i++ ) { - const int len = 1 << i; - const double om = 1.0 / len; + const int len = TLENS[i]; + //const double om = 1.0 / len; double max = 0.0; for( int ph = 0; ph < len; ph++ ) { int harm = 1; double s = 0.0f; + double hlen; do { + hlen = static_cast( len ) / static_cast( harm ); 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 ); + //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 >= 4.0 ); s_waveforms[ BandLimitedWave::BLSaw ].setSampleAt( i, ph, s ); max = qMax( max, qAbs( s ) ); } @@ -63,23 +68,25 @@ void BandLimitedWave::generateWaves() } // square wave - BLSquare - for( i = 1; i <= MAXLEN; i++ ) + for( i = 0; i <= MAXTBL; i++ ) { - const int len = 1 << i; - const double om = 1.0 / len; + const int len = TLENS[i]; + //const double om = 1.0 / len; double max = 0.0; for( int ph = 0; ph < len; ph++ ) { int harm = 1; double s = 0.0f; + double hlen; do { + hlen = static_cast( len ) / static_cast( harm ); 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 ); + //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 >= 4.0 ); s_waveforms[ BandLimitedWave::BLSquare ].setSampleAt( i, ph, s ); max = qMax( max, qAbs( s ) ); } @@ -93,9 +100,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; @@ -103,14 +110,16 @@ void BandLimitedWave::generateWaves() { int harm = 1; double s = 0.0f; + double hlen; do { + hlen = static_cast( len ) / static_cast( harm ); 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( len/harm > 2 ); + } while( hlen >= 4.0 ); s_waveforms[ BandLimitedWave::BLTriangle ].setSampleAt( i, ph, s ); max = qMax( max, qAbs( s ) ); } @@ -125,9 +134,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++ ) { @@ -138,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 );