Merge pull request #511 from diizy/wavetablesynth

Watsyn: Add smart oversampling interpolation
This commit is contained in:
Tobias Doerffel
2014-03-26 09:30:34 +01:00
2 changed files with 82 additions and 22 deletions

View File

@@ -81,6 +81,7 @@ WatsynObject::WatsynObject( float * _A1wave, float * _A2wave,
m_rphase[B2_OSC] = 0.0f;
// copy wavegraphs to the synth object to prevent race conditions
memcpy( &m_A1wave, _A1wave, sizeof( m_A1wave ) );
memcpy( &m_A2wave, _A2wave, sizeof( m_A2wave ) );
memcpy( &m_B1wave, _B1wave, sizeof( m_B1wave ) );
@@ -251,10 +252,10 @@ WatsynInstrument::WatsynInstrument( InstrumentTrack * _instrument_track ) :
b1_rtune( 0.0f, -600.0f, 600.0f, 1.0f, this, tr( "Right detune B1" ) ),
b2_rtune( 0.0f, -600.0f, 600.0f, 1.0f, this, tr( "Right detune B2" ) ),
a1_graph( -1.0f, 1.0f, WAVELEN, this ),
a2_graph( -1.0f, 1.0f, WAVELEN, this ),
b1_graph( -1.0f, 1.0f, WAVELEN, this ),
b2_graph( -1.0f, 1.0f, WAVELEN, this ),
a1_graph( -1.0f, 1.0f, GRAPHLEN, this ),
a2_graph( -1.0f, 1.0f, GRAPHLEN, this ),
b1_graph( -1.0f, 1.0f, GRAPHLEN, this ),
b2_graph( -1.0f, 1.0f, GRAPHLEN, this ),
m_abmix( 0.0f, -100.0f, 100.0f, 0.1f, this, tr( "A-B Mix" ) ),
m_envAmt( 0.0f, -200.0f, 200.0f, 1.0f, this, tr( "A-B Mix envelope amount" ) ),
@@ -294,6 +295,11 @@ WatsynInstrument::WatsynInstrument( InstrumentTrack * _instrument_track ) :
connect( &a2_rtune, SIGNAL( dataChanged() ), this, SLOT( updateFreq() ) );
connect( &b1_rtune, SIGNAL( dataChanged() ), this, SLOT( updateFreq() ) );
connect( &b2_rtune, SIGNAL( dataChanged() ), this, SLOT( updateFreq() ) );
connect( &a1_graph, SIGNAL( samplesChanged( int, int ) ), this, SLOT( updateWaves() ) );
connect( &a2_graph, SIGNAL( samplesChanged( int, int ) ), this, SLOT( updateWaves() ) );
connect( &b1_graph, SIGNAL( samplesChanged( int, int ) ), this, SLOT( updateWaves() ) );
connect( &b2_graph, SIGNAL( samplesChanged( int, int ) ), this, SLOT( updateWaves() ) );
a1_graph.setWaveToSine();
a2_graph.setWaveToSine();
@@ -302,6 +308,7 @@ WatsynInstrument::WatsynInstrument( InstrumentTrack * _instrument_track ) :
updateVolumes();
updateFreq();
updateWaves();
}
@@ -315,10 +322,11 @@ void WatsynInstrument::playNote( NotePlayHandle * _n,
{
if ( _n->totalFramesPlayed() == 0 || _n->m_pluginData == NULL )
{
WatsynObject * w = new WatsynObject( const_cast<float*>( a1_graph.samples() ),
const_cast<float*>( a2_graph.samples() ),
const_cast<float*>( b1_graph.samples() ),
const_cast<float*>( b2_graph.samples() ),
WatsynObject * w = new WatsynObject(
&A1_wave[0],
&A2_wave[0],
&B1_wave[0],
&B2_wave[0],
m_amod.value(), m_bmod.value(),
engine::mixer()->processingSampleRate(), _n,
engine::mixer()->framesPerPeriod(), this );
@@ -491,7 +499,7 @@ void WatsynInstrument::saveSettings( QDomDocument & _doc,
m_amod.saveSettings( _doc, _this, "amod" );
m_bmod.saveSettings( _doc, _this, "bmod" );
m_selectedGraph.saveSettings( _doc, _this, "selgraph" );
/* m_selectedGraph.saveSettings( _doc, _this, "selgraph" );*/
}
@@ -548,7 +556,7 @@ void WatsynInstrument::loadSettings( const QDomElement & _this )
m_amod.loadSettings( _this, "amod" );
m_bmod.loadSettings( _this, "bmod" );
m_selectedGraph.loadSettings( _this, "selgraph" );
/* m_selectedGraph.loadSettings( _this, "selgraph" );*/
}
@@ -596,6 +604,16 @@ void WatsynInstrument::updateFreq()
}
void WatsynInstrument::updateWaves()
{
// do cip+oversampling on the wavetables to improve quality
cipcpy( &A1_wave[0], const_cast<float*>( a1_graph.samples() ) );
cipcpy( &A2_wave[0], const_cast<float*>( a2_graph.samples() ) );
cipcpy( &B1_wave[0], const_cast<float*>( b1_graph.samples() ) );
cipcpy( &B2_wave[0], const_cast<float*>( b2_graph.samples() ) );
}
WatsynView::WatsynView( Instrument * _instrument,
QWidget * _parent ) :
InstrumentView( _instrument, _parent )

View File

@@ -56,20 +56,24 @@
#define B2ROW 95
extern const int WAVELEN = 220;
extern const int PMOD_AMT = 110;
const int GRAPHLEN = 220;
const int WAVELEN = 4400;
extern const int MOD_MIX = 0;
extern const int MOD_AM = 1;
extern const int MOD_RM = 2;
extern const int MOD_PM = 3;
extern const int NUM_MODS = 4;
const int WAVERATIO = WAVELEN / GRAPHLEN;
extern const int A1_OSC = 0;
extern const int A2_OSC = 1;
extern const int B1_OSC = 2;
extern const int B2_OSC = 3;
extern const int NUM_OSCS = 4;
const int PMOD_AMT = WAVELEN / 2;
const int MOD_MIX = 0;
const int MOD_AM = 1;
const int MOD_RM = 2;
const int MOD_PM = 3;
const int NUM_MODS = 4;
const int A1_OSC = 0;
const int A2_OSC = 1;
const int B1_OSC = 2;
const int B2_OSC = 3;
const int NUM_OSCS = 4;
class WatsynInstrument;
@@ -131,6 +135,7 @@ private:
( ( x3 - x2 ) * m2 );
}*/
int m_amod;
int m_bmod;
@@ -181,6 +186,7 @@ public:
public slots:
void updateVolumes();
void updateFreq();
void updateWaves();
protected:
float m_lvol [NUM_OSCS];
@@ -200,6 +206,37 @@ private:
return ( _pan >= 0 ? 1.0 : 1.0 + ( _pan / 100.0 ) ) * _vol / 100.0;
}
// memcpy with cubic interpolation (cip for short) and 10x oversampling to increase wavetable quality
inline void cipcpy( float * _dst, float * _src )
{
// calculate cyclic tangents
float tang[GRAPHLEN];
tang[0] = ( _src[1] - _src[ GRAPHLEN - 1] ) / 2;
tang[ GRAPHLEN - 1 ] = ( _src[0] - _src[ GRAPHLEN - 2 ] ) / 2;
for( int i = 1; i < GRAPHLEN-1; i++ )
{
tang[i] = ( _src[i+1] - _src[i-1] ) / 2;
}
// calculate cspline
for( int i=0; i < WAVELEN; i++ )
{
const float s1 = _src[ i / WAVERATIO ];
const float s2 = _src[ ( i / WAVERATIO + 1 ) % GRAPHLEN ];
const float m1 = tang[ i / WAVERATIO ];
const float m2 = tang[ ( i / WAVERATIO + 1 ) % GRAPHLEN ];
const float x = static_cast<float>( i % WAVERATIO ) / WAVERATIO;
const float x2 = x * x;
const float x3 = x * x * x;
_dst[i] = ( ( x3 * 2.0 - x2 * 3.0 + 1.0 ) * s1 ) +
( ( x3 * -2.0 + x2 * 3.0 ) * s2 ) +
( ( x3 - x2 * 2 + x ) * m1 ) +
( ( x3 - x2 ) * m2 );
}
}
FloatModel a1_vol;
FloatModel a2_vol;
FloatModel b1_vol;
@@ -244,6 +281,11 @@ private:
IntModel m_bmod;
IntModel m_selectedGraph;
float A1_wave [WAVELEN];
float A2_wave [WAVELEN];
float B1_wave [WAVELEN];
float B2_wave [WAVELEN];
friend class WatsynObject;
friend class WatsynView;