Merge pull request #578 from diizy/bandlimit
Bandlimited waveform generation
This commit is contained in:
192
include/BandLimitedWave.h
Normal file
192
include/BandLimitedWave.h
Normal file
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* BandLimitedWave.h - helper functions for band-limited
|
||||
* waveform generation
|
||||
*
|
||||
* Copyright (c) 2014 Vesa Kivimäki <contact/dot/diizy/at/nbl/dot/fi>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BANDLIMITEDWAVE_H
|
||||
#define BANDLIMITEDWAVE_H
|
||||
|
||||
#include "interpolation.h"
|
||||
#include "lmms_basics.h"
|
||||
#include "lmms_math.h"
|
||||
#include "engine.h"
|
||||
#include "Mixer.h"
|
||||
|
||||
#define MAXLEN 12
|
||||
#define MIPMAPSIZE 1 << ( MAXLEN + 1 )
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
public:
|
||||
inline sample_t sampleAt( int _table, int _ph )
|
||||
{
|
||||
return m_data[ ( 1 << _table ) + _ph ];
|
||||
}
|
||||
inline void setSampleAt( int _table, int _ph, sample_t _sample )
|
||||
{
|
||||
m_data[ ( 1 << _table ) + _ph ] = _sample;
|
||||
}
|
||||
private:
|
||||
sample_t m_data [ MIPMAPSIZE ];
|
||||
} WaveMipMap;
|
||||
|
||||
|
||||
class BandLimitedWave
|
||||
{
|
||||
public:
|
||||
enum Waveforms
|
||||
{
|
||||
BLSaw,
|
||||
BLSquare,
|
||||
BLTriangle,
|
||||
BLMoog,
|
||||
NumBLWaveforms
|
||||
};
|
||||
|
||||
BandLimitedWave() {};
|
||||
virtual ~BandLimitedWave() {};
|
||||
|
||||
/*! \brief This method converts frequency to wavelength. The oscillate function takes wavelength as argument so
|
||||
* use this to convert your note frequency to wavelength before using it.
|
||||
*/
|
||||
static inline float freqToLen( float _f )
|
||||
{
|
||||
return freqToLen( _f, engine::mixer()->processingSampleRate() );
|
||||
}
|
||||
|
||||
/*! \brief This method converts frequency to wavelength, but you can use any custom sample rate with it.
|
||||
*/
|
||||
static inline float freqToLen( float _f, sample_rate_t _sr )
|
||||
{
|
||||
return static_cast<float>( _sr ) / _f;
|
||||
}
|
||||
|
||||
/*! \brief This method converts phase delta to wavelength. It assumes a phase scale of 0 to 1. */
|
||||
static inline float pdToLen( float _pd )
|
||||
{
|
||||
return 1.0f / _pd;
|
||||
}
|
||||
|
||||
/*! \brief This method provides interpolated samples of bandlimited waveforms.
|
||||
* \param _ph The phase of the sample.
|
||||
* \param _wavelen The wavelength (length of one cycle, ie. the inverse of frequency) of the wanted oscillation, measured in sample frames
|
||||
* \param _wave The wanted waveform. Options currently are saw, triangle, square and moog saw.
|
||||
*/
|
||||
static inline sample_t oscillate( float _ph, float _wavelen, Waveforms _wave )
|
||||
{
|
||||
// high wavelen/ low freq
|
||||
if( _wavelen >= 1 << MAXLEN )
|
||||
{
|
||||
const int t = MAXLEN;
|
||||
const int tlen = 1 << t;
|
||||
const float ph = fraction( _ph );
|
||||
const float lookupf = ph * static_cast<float>( tlen );
|
||||
const int lookup = static_cast<int>( 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 ) );
|
||||
}
|
||||
// low wavelen/ high freq
|
||||
if( _wavelen <= 2.0f )
|
||||
{
|
||||
const int t = 1;
|
||||
const int tlen = 2;
|
||||
const float ph = fraction( _ph );
|
||||
const float lookupf = ph * static_cast<float>( tlen );
|
||||
const int lookup = static_cast<int>( 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 ) );
|
||||
}
|
||||
|
||||
// get the next higher tlen
|
||||
int t = 2;
|
||||
while( ( 1 << t ) < _wavelen ) { t++; }
|
||||
|
||||
const int tlen = 1 << t;
|
||||
const float ph = fraction( _ph );
|
||||
const float lookupf = ph * static_cast<float>( tlen );
|
||||
const int lookup = static_cast<int>( 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 int tlen1 = 1 << t;
|
||||
const int tlen2 = 1 << ( t - 1 );
|
||||
|
||||
const float ph = fraction( _ph );
|
||||
const float lookupf = ph * static_cast<float>( tlen1 );
|
||||
const int lookup1 = static_cast<int>( lookupf );
|
||||
const int lookup2 = static_cast<int>( ph * static_cast<float>( 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<float>( tlen1 - _wavelen ) / static_cast<float>( tlen2 );
|
||||
|
||||
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<int>( 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<int>( ph * tlen );
|
||||
return s_waveforms[ _wave ].sampleAt( t, lookup );
|
||||
};
|
||||
|
||||
|
||||
static void generateWaves();
|
||||
|
||||
static WaveMipMap s_waveforms [NumBLWaveforms];
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -79,6 +79,11 @@ MonstroSynth::MonstroSynth( MonstroInstrument * _i, NotePlayHandle * _nph,
|
||||
m_osc3l_phase = 0.0f;
|
||||
m_osc3r_phase = 0.0f;
|
||||
|
||||
m_ph2l_last = 0.0f;
|
||||
m_ph2r_last = 0.0f;
|
||||
m_ph3l_last = 0.0f;
|
||||
m_ph3r_last = 0.0f;
|
||||
|
||||
m_env1_phase = 0.0f;
|
||||
m_env2_phase = 0.0f;
|
||||
|
||||
@@ -97,7 +102,7 @@ MonstroSynth::MonstroSynth( MonstroInstrument * _i, NotePlayHandle * _nph,
|
||||
m_l_last = 0.0f;
|
||||
m_r_last = 0.0f;
|
||||
|
||||
// constants for very simple antialias/bandlimiting by amp delta capping
|
||||
// constants for amp delta capping
|
||||
m_adcap1 = ADCAP1 / m_samplerate;
|
||||
m_adcap2 = ADCAP2 / m_samplerate;
|
||||
}
|
||||
@@ -282,6 +287,10 @@ void MonstroSynth::renderOutput( fpp_t _frames, sampleFrame * _buf )
|
||||
float o3l_p;
|
||||
float o3r_p;
|
||||
float sub;
|
||||
|
||||
// phase delta calculation vars
|
||||
float pd_l;
|
||||
float pd_r;
|
||||
|
||||
// begin for loop
|
||||
for( f_cnt_t f = 0; f < _frames; f++ )
|
||||
@@ -399,11 +408,17 @@ void MonstroSynth::renderOutput( fpp_t _frames, sampleFrame * _buf )
|
||||
if( o2l_p < 0 ) o2l_p -= floorf( o2l_p );
|
||||
if( o2r_p < 0 ) o2r_p -= floorf( o2r_p );
|
||||
|
||||
// phase delta
|
||||
pd_l = qAbs( o2l_p - m_ph2l_last );
|
||||
if( pd_l > 0.5 ) pd_l = 1.0 - pd_l;
|
||||
pd_r = qAbs( o2r_p - m_ph2r_last );
|
||||
if( pd_r > 0.5 ) pd_r = 1.0 - pd_r;
|
||||
|
||||
// multi-wave DC Oscillator
|
||||
sample_t O2L = oscillate( o2w, o2l_p );
|
||||
sample_t O2R = oscillate( o2w, o2r_p );
|
||||
sample_t O2L = oscillate( o2w, o2l_p, BandLimitedWave::pdToLen( pd_l ) );
|
||||
sample_t O2R = oscillate( o2w, o2r_p, BandLimitedWave::pdToLen( pd_r ) );
|
||||
|
||||
// do simple alias reduction filtering before volume is touched, by capping amplitude delta
|
||||
// do amplitude delta cap
|
||||
O2L = qBound( m_osc2l_last - m_adcap1, O2L, m_osc2l_last + m_adcap1 );
|
||||
O2R = qBound( m_osc2r_last - m_adcap1, O2R, m_osc2r_last + m_adcap1 );
|
||||
m_osc2l_last = O2L;
|
||||
@@ -416,8 +431,11 @@ void MonstroSynth::renderOutput( fpp_t _frames, sampleFrame * _buf )
|
||||
modulatevol( O2R, o2v )
|
||||
|
||||
// update osc2 phases
|
||||
m_ph2l_last = m_osc2l_phase;
|
||||
m_ph2r_last = m_osc2r_phase;
|
||||
m_osc2l_phase = fraction( m_osc2l_phase + 1.0f / ( static_cast<float>( m_samplerate ) / o2l_f ) );
|
||||
m_osc2r_phase = fraction( m_osc2r_phase + 1.0f / ( static_cast<float>( m_samplerate ) / o2r_f ) );
|
||||
|
||||
|
||||
/////////////////////////////
|
||||
// //
|
||||
@@ -453,13 +471,19 @@ void MonstroSynth::renderOutput( fpp_t _frames, sampleFrame * _buf )
|
||||
if( o3l_p < 0 ) o3l_p -= floorf( o3l_p );
|
||||
if( o3r_p < 0 ) o3r_p -= floorf( o3r_p );
|
||||
|
||||
// phase delta
|
||||
pd_l = qAbs( o3l_p - m_ph3l_last );
|
||||
if( pd_l > 0.5 ) pd_l = 1.0 - pd_l;
|
||||
pd_r = qAbs( o3r_p - m_ph3r_last );
|
||||
if( pd_r > 0.5 ) pd_r = 1.0 - pd_r;
|
||||
|
||||
// multi-wave DC Oscillator, sub-osc 1
|
||||
sample_t O3AL = oscillate( o3w1, o3l_p );
|
||||
sample_t O3AR = oscillate( o3w1, o3r_p );
|
||||
sample_t O3AL = oscillate( o3w1, o3l_p, BandLimitedWave::pdToLen( pd_l ) );
|
||||
sample_t O3AR = oscillate( o3w1, o3r_p, BandLimitedWave::pdToLen( pd_r ) );
|
||||
|
||||
// multi-wave DC Oscillator, sub-osc 2
|
||||
sample_t O3BL = oscillate( o3w2, o3l_p );
|
||||
sample_t O3BR = oscillate( o3w2, o3r_p );
|
||||
sample_t O3BL = oscillate( o3w2, o3l_p, BandLimitedWave::pdToLen( pd_l ) );
|
||||
sample_t O3BR = oscillate( o3w2, o3r_p, BandLimitedWave::pdToLen( pd_r ) );
|
||||
|
||||
// calc and modulate sub
|
||||
sub = o3sub;
|
||||
@@ -468,7 +492,7 @@ void MonstroSynth::renderOutput( fpp_t _frames, sampleFrame * _buf )
|
||||
sample_t O3L = linearInterpolate( O3AL, O3BL, sub );
|
||||
sample_t O3R = linearInterpolate( O3AR, O3BR, sub );
|
||||
|
||||
// do very simple bandlimit filtering by amp delta capping, before volume is touched
|
||||
// do amp delta capping, before volume is touched
|
||||
O3L = qBound( m_osc3l_last - m_adcap1, O3L, m_osc3l_last + m_adcap1 );
|
||||
O3R = qBound( m_osc3r_last - m_adcap1, O3R, m_osc3r_last + m_adcap1 );
|
||||
m_osc3l_last = O3L;
|
||||
@@ -487,10 +511,12 @@ void MonstroSynth::renderOutput( fpp_t _frames, sampleFrame * _buf )
|
||||
}
|
||||
|
||||
// update osc3 phases
|
||||
m_ph3l_last = m_osc3l_phase;
|
||||
m_ph3r_last = m_osc3r_phase;
|
||||
m_osc3l_phase = fraction( m_osc3l_phase + 1.0f / ( static_cast<float>( m_samplerate ) / o3l_f ) );
|
||||
m_osc3r_phase = fraction( m_osc3r_phase + 1.0f / ( static_cast<float>( m_samplerate ) / o3r_f ) );
|
||||
|
||||
// simple bandlimiting
|
||||
// amp delta caps
|
||||
sample_t L = O1L + O3L + ( omod == MOD_MIX ? O2L : 0.0f );
|
||||
sample_t R = O1R + O3R + ( omod == MOD_MIX ? O2R : 0.0f );
|
||||
|
||||
@@ -591,7 +617,7 @@ void MonstroSynth::renderModulators( fpp_t _frames )
|
||||
{
|
||||
const f_cnt_t t = f + tfp;
|
||||
const float ph = m_lfo1_phase + lfo1_po;
|
||||
lfo1_s = oscillate( WAVE_SQRSOFT, ph );
|
||||
lfo1_s = oscillate( WAVE_SQRSOFT, ph, lfo1_r );
|
||||
if( t < m_parent->m_lfo1_att ) lfo1_s *= ( static_cast<sample_t>( t ) / m_parent->m_lfo1_att );
|
||||
m_lfo1_buf[f] = lfo1_s;
|
||||
m_lfo1_phase += 1.0f / lfo1_r;
|
||||
@@ -613,7 +639,7 @@ void MonstroSynth::renderModulators( fpp_t _frames )
|
||||
{
|
||||
const f_cnt_t t = f + tfp;
|
||||
const float ph = m_lfo1_phase + lfo1_po;
|
||||
lfo1_s = oscillate( WAVE_SINABS, ph );
|
||||
lfo1_s = oscillate( WAVE_SINABS, ph, lfo1_r );
|
||||
if( t < m_parent->m_lfo1_att ) lfo1_s *= ( static_cast<sample_t>( t ) / m_parent->m_lfo1_att );
|
||||
m_lfo1_buf[f] = lfo1_s;
|
||||
m_lfo1_phase += 1.0f / lfo1_r;
|
||||
@@ -708,7 +734,7 @@ void MonstroSynth::renderModulators( fpp_t _frames )
|
||||
{
|
||||
const f_cnt_t t = f + tfp;
|
||||
const float ph = m_lfo2_phase + lfo2_po;
|
||||
lfo2_s = oscillate( WAVE_SQRSOFT, ph );
|
||||
lfo2_s = oscillate( WAVE_SQRSOFT, ph, lfo2_r );
|
||||
if( t < m_parent->m_lfo2_att ) lfo2_s *= ( static_cast<sample_t>( t ) / m_parent->m_lfo2_att );
|
||||
m_lfo2_buf[f] = lfo2_s;
|
||||
m_lfo2_phase += 1.0f / lfo2_r;
|
||||
@@ -730,7 +756,7 @@ void MonstroSynth::renderModulators( fpp_t _frames )
|
||||
{
|
||||
const f_cnt_t t = f + tfp;
|
||||
const float ph = m_lfo2_phase + lfo2_po;
|
||||
lfo2_s = oscillate( WAVE_SINABS, ph );
|
||||
lfo2_s = oscillate( WAVE_SINABS, ph, lfo2_r );
|
||||
if( t < m_parent->m_lfo2_att ) lfo2_s *= ( static_cast<sample_t>( t ) / m_parent->m_lfo2_att );
|
||||
m_lfo2_buf[f] = lfo2_s;
|
||||
m_lfo2_phase += 1.0f / lfo2_r;
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "combobox.h"
|
||||
#include "Oscillator.h"
|
||||
#include "lmms_math.h"
|
||||
#include "BandLimitedWave.h"
|
||||
|
||||
//
|
||||
// UI Macros
|
||||
@@ -66,15 +67,21 @@
|
||||
|
||||
#define setwavemodel( name ) \
|
||||
name .addItem( tr( "Sine wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "sin" ) ) ); \
|
||||
name .addItem( tr( "Triangle wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "tri" ) ) ); \
|
||||
name .addItem( tr( "Saw wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "saw" ) ) ); \
|
||||
name .addItem( tr( "Ramp wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "ramp" ) ) ); \
|
||||
name .addItem( tr( "Square wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "sqr" ) ) ); \
|
||||
name .addItem( tr( "Bandlimited Triangle wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "tri" ) ) ); \
|
||||
name .addItem( tr( "Bandlimited Saw wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "saw" ) ) ); \
|
||||
name .addItem( tr( "Bandlimited Ramp wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "ramp" ) ) ); \
|
||||
name .addItem( tr( "Bandlimited Square wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "sqr" ) ) ); \
|
||||
name .addItem( tr( "Bandlimited Moog saw wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "moog" ) ) ); \
|
||||
name .addItem( tr( "Soft square wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "sqrsoft" ) ) ); \
|
||||
name .addItem( tr( "Moog saw wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "moog" ) ) ); \
|
||||
name .addItem( tr( "Abs. sine wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "sinabs" ) ) ); \
|
||||
name .addItem( tr( "Absolute sine wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "sinabs" ) ) ); \
|
||||
name .addItem( tr( "Exponential wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "exp" ) ) ); \
|
||||
name .addItem( tr( "White noise" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "noise" ) ) );
|
||||
name .addItem( tr( "White noise" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "noise" ) ) ); \
|
||||
name .addItem( tr( "Digital Triangle wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "tri" ) ) ); \
|
||||
name .addItem( tr( "Digital Saw wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "saw" ) ) ); \
|
||||
name .addItem( tr( "Digital Ramp wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "ramp" ) ) ); \
|
||||
name .addItem( tr( "Digital Square wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "sqr" ) ) ); \
|
||||
name .addItem( tr( "Digital Moog saw wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "moog" ) ) ); \
|
||||
|
||||
|
||||
#define setlfowavemodel( name ) \
|
||||
name .addItem( tr( "Sine wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "sin" ) ) ); \
|
||||
@@ -82,8 +89,8 @@
|
||||
name .addItem( tr( "Saw wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "saw" ) ) ); \
|
||||
name .addItem( tr( "Ramp wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "ramp" ) ) ); \
|
||||
name .addItem( tr( "Square wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "sqr" ) ) ); \
|
||||
name .addItem( tr( "Soft square wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "sqrsoft" ) ) ); \
|
||||
name .addItem( tr( "Moog saw wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "moog" ) ) ); \
|
||||
name .addItem( tr( "Soft square wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "sqrsoft" ) ) ); \
|
||||
name .addItem( tr( "Abs. sine wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "sinabs" ) ) ); \
|
||||
name .addItem( tr( "Exponential wave" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "exp" ) ) ); \
|
||||
name .addItem( tr( "Random" ), static_cast<PixmapLoader*>( new PluginPixmapLoader( "rand" ) ) );
|
||||
@@ -136,12 +143,20 @@ const int WAVE_TRI = 1;
|
||||
const int WAVE_SAW = 2;
|
||||
const int WAVE_RAMP = 3;
|
||||
const int WAVE_SQR = 4;
|
||||
const int WAVE_SQRSOFT = 5;
|
||||
const int WAVE_MOOG = 6;
|
||||
const int WAVE_MOOG = 5;
|
||||
|
||||
const int WAVE_SQRSOFT = 6;
|
||||
const int WAVE_SINABS = 7;
|
||||
const int WAVE_EXP = 8;
|
||||
const int WAVE_NOISE = 9;
|
||||
const int NUM_WAVES = 10;
|
||||
|
||||
const int WAVE_TRI_D = 10;
|
||||
const int WAVE_SAW_D = 11;
|
||||
const int WAVE_RAMP_D = 12;
|
||||
const int WAVE_SQR_D = 13;
|
||||
const int WAVE_MOOG_D = 14;
|
||||
|
||||
const int NUM_WAVES = 15;
|
||||
|
||||
// modulation enumerators
|
||||
const int MOD_MIX = 0;
|
||||
@@ -156,8 +171,8 @@ const float MIN_FREQ = 18.0f;
|
||||
const float MAX_FREQ = 48000.0f;
|
||||
|
||||
// constants for amp delta capping - these will be divided by samplerate by the synth
|
||||
const float ADCAP1 = 44100 / 4;
|
||||
const float ADCAP2 = 44100 / 4.5;
|
||||
const float ADCAP1 = 44100 / 2;
|
||||
const float ADCAP2 = 44100 / 3;
|
||||
|
||||
|
||||
class MonstroInstrument;
|
||||
@@ -205,7 +220,7 @@ private:
|
||||
return fastPow( _s, exp );
|
||||
}
|
||||
|
||||
inline sample_t oscillate( int _wave, const float _ph )
|
||||
inline sample_t oscillate( int _wave, const float _ph, float _wavelen )
|
||||
{
|
||||
switch( _wave )
|
||||
{
|
||||
@@ -213,16 +228,20 @@ private:
|
||||
return Oscillator::sinSample( _ph );
|
||||
break;
|
||||
case WAVE_TRI:
|
||||
return Oscillator::triangleSample( _ph );
|
||||
//return Oscillator::triangleSample( _ph );
|
||||
return BandLimitedWave::oscillate( _ph, _wavelen, BandLimitedWave::BLTriangle );
|
||||
break;
|
||||
case WAVE_SAW:
|
||||
return Oscillator::sawSample( _ph );
|
||||
//return Oscillator::sawSample( _ph );
|
||||
return BandLimitedWave::oscillate( _ph, _wavelen, BandLimitedWave::BLSaw );
|
||||
break;
|
||||
case WAVE_RAMP:
|
||||
return Oscillator::sawSample( _ph ) * -1.0;
|
||||
//return Oscillator::sawSample( _ph ) * -1.0;
|
||||
return BandLimitedWave::oscillate( _ph, _wavelen, BandLimitedWave::BLSaw ) * -1.0;
|
||||
break;
|
||||
case WAVE_SQR:
|
||||
return Oscillator::squareSample( _ph );
|
||||
//return Oscillator::squareSample( _ph );
|
||||
return BandLimitedWave::oscillate( _ph, _wavelen, BandLimitedWave::BLSquare );
|
||||
break;
|
||||
case WAVE_SQRSOFT:
|
||||
{
|
||||
@@ -234,7 +253,8 @@ private:
|
||||
break;
|
||||
}
|
||||
case WAVE_MOOG:
|
||||
return Oscillator::moogSawSample( _ph );
|
||||
//return Oscillator::moogSawSample( _ph );
|
||||
return BandLimitedWave::oscillate( _ph, _wavelen, BandLimitedWave::BLMoog );
|
||||
break;
|
||||
case WAVE_SINABS:
|
||||
return qAbs( Oscillator::sinSample( _ph ) );
|
||||
@@ -243,9 +263,25 @@ private:
|
||||
return Oscillator::expSample( _ph );
|
||||
break;
|
||||
case WAVE_NOISE:
|
||||
default:
|
||||
return Oscillator::noiseSample( _ph );
|
||||
break;
|
||||
|
||||
case WAVE_TRI_D:
|
||||
return Oscillator::triangleSample( _ph );
|
||||
break;
|
||||
case WAVE_SAW_D:
|
||||
return Oscillator::sawSample( _ph );
|
||||
break;
|
||||
case WAVE_RAMP_D:
|
||||
return Oscillator::sawSample( _ph ) * -1.0;
|
||||
break;
|
||||
case WAVE_SQR_D:
|
||||
return Oscillator::squareSample( _ph );
|
||||
break;
|
||||
case WAVE_MOOG_D:
|
||||
return Oscillator::moogSawSample( _ph );
|
||||
break;
|
||||
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
@@ -275,10 +311,16 @@ private:
|
||||
|
||||
sample_t m_osc3l_last;
|
||||
sample_t m_osc3r_last;
|
||||
|
||||
|
||||
sample_t m_l_last;
|
||||
sample_t m_r_last;
|
||||
|
||||
|
||||
float m_ph2l_last;
|
||||
float m_ph2r_last;
|
||||
|
||||
float m_ph3l_last;
|
||||
float m_ph3r_last;
|
||||
|
||||
float m_adcap1;
|
||||
float m_adcap2;
|
||||
};
|
||||
|
||||
141
src/core/BandLimitedWave.cpp
Normal file
141
src/core/BandLimitedWave.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* BandLimitedWave.h - helper functions for band-limited
|
||||
* waveform generation
|
||||
*
|
||||
* Copyright (c) 2014 Vesa Kivimäki <contact/dot/diizy/at/nbl/dot/fi>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "BandLimitedWave.h"
|
||||
|
||||
|
||||
WaveMipMap BandLimitedWave::s_waveforms[4] = { };
|
||||
|
||||
|
||||
void BandLimitedWave::generateWaves()
|
||||
{
|
||||
int i;
|
||||
|
||||
// saw wave - BLSaw
|
||||
for( i = 1; i <= MAXLEN; i++ )
|
||||
{
|
||||
const int len = 1 << 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;
|
||||
do
|
||||
{
|
||||
const double amp = -1.0 / static_cast<double>( harm );
|
||||
const double a2 = cos( om * harm * F_2PI );
|
||||
s += amp * a2 * sin( static_cast<double>( ph * harm ) / static_cast<double>( len ) * F_2PI );
|
||||
harm++;
|
||||
} while( len/harm > 2 );
|
||||
s_waveforms[ BandLimitedWave::BLSaw ].setSampleAt( i, ph, s );
|
||||
max = qMax( max, qAbs( s ) );
|
||||
}
|
||||
// normalize
|
||||
for( int ph = 0; ph < len; ph++ )
|
||||
{
|
||||
sample_t s = s_waveforms[ BandLimitedWave::BLSaw ].sampleAt( i, ph ) / max;
|
||||
s_waveforms[ BandLimitedWave::BLSaw ].setSampleAt( i, ph, s );
|
||||
}
|
||||
}
|
||||
|
||||
// square wave - BLSquare
|
||||
for( i = 1; i <= MAXLEN; i++ )
|
||||
{
|
||||
const int len = 1 << 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;
|
||||
do
|
||||
{
|
||||
const double amp = 1.0 / static_cast<double>( harm );
|
||||
const double a2 = cos( om * harm * F_2PI );
|
||||
s += amp * a2 * sin( static_cast<double>( ph * harm ) / static_cast<double>( len ) * F_2PI );
|
||||
harm += 2;
|
||||
} while( len/harm > 2 );
|
||||
s_waveforms[ BandLimitedWave::BLSquare ].setSampleAt( i, ph, s );
|
||||
max = qMax( max, qAbs( s ) );
|
||||
}
|
||||
// normalize
|
||||
for( int ph = 0; ph < len; ph++ )
|
||||
{
|
||||
sample_t s = s_waveforms[ BandLimitedWave::BLSquare ].sampleAt( i, ph ) / max;
|
||||
s_waveforms[ BandLimitedWave::BLSquare ].setSampleAt( i, ph, s );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// triangle wave - BLTriangle
|
||||
for( i = 1; i <= MAXLEN; i++ )
|
||||
{
|
||||
const int len = 1 << 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;
|
||||
do
|
||||
{
|
||||
const double amp = 1.0 / static_cast<double>( harm * harm );
|
||||
//const double a2 = cos( om * harm * F_2PI );
|
||||
s += amp * /*a2 **/ sin( ( static_cast<double>( ph * harm ) / static_cast<double>( len ) +
|
||||
( ( harm + 1 ) % 4 == 0 ? 0.5 : 0.0 ) ) * F_2PI );
|
||||
harm += 2;
|
||||
} while( len/harm > 2 );
|
||||
s_waveforms[ BandLimitedWave::BLTriangle ].setSampleAt( i, ph, s );
|
||||
max = qMax( max, qAbs( s ) );
|
||||
}
|
||||
// normalize
|
||||
for( int ph = 0; ph < len; ph++ )
|
||||
{
|
||||
sample_t s = s_waveforms[ BandLimitedWave::BLTriangle ].sampleAt( i, ph ) / max;
|
||||
s_waveforms[ BandLimitedWave::BLTriangle ].setSampleAt( i, ph, s );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// moog saw wave - BLMoog
|
||||
// basically, just add in triangle + 270-phase saw
|
||||
for( i = 1; i <= MAXLEN; i++ )
|
||||
{
|
||||
const int len = 1 << i;
|
||||
|
||||
for( int ph = 0; ph < len; ph++ )
|
||||
{
|
||||
const int sawph = ( ph + static_cast<int>( len * 0.75 ) ) % len;
|
||||
const sample_t saw = s_waveforms[ BandLimitedWave::BLSaw ].sampleAt( i, sawph );
|
||||
const sample_t tri = s_waveforms[ BandLimitedWave::BLTriangle ].sampleAt( i, ph );
|
||||
s_waveforms[ BandLimitedWave::BLMoog ].setSampleAt( i, ph, ( saw + tri ) * 0.5f );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -65,6 +65,7 @@
|
||||
#include "ProjectRenderer.h"
|
||||
#include "DataFile.h"
|
||||
#include "song.h"
|
||||
#include "BandLimitedWave.h"
|
||||
|
||||
static inline QString baseName( const QString & _file )
|
||||
{
|
||||
@@ -119,6 +120,8 @@ 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,
|
||||
|
||||
Reference in New Issue
Block a user