- faster oscillator

- tempo-based arpeggiator and LFOs
- bug-fixes
- added another Moog-filter


git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@14 0778d3d1-df1d-0410-868b-ea421aaaa00d
This commit is contained in:
Tobias Doerffel
2005-10-06 08:24:23 +00:00
parent 6ba478ff6b
commit 5d9e7fe0ed
21 changed files with 832 additions and 130 deletions

View File

@@ -46,13 +46,15 @@
#include "types.h"
class QComboBox;
class QPixmap;
class channelTrack;
class groupBox;
class knob;
class pixmapButton;
class QComboBox;
class QPixmap;
class notePlayHandle;
class pixmapButton;
class tempoSyncKnob;
const int MAX_CHORD_POLYPHONY = 10;
@@ -123,7 +125,7 @@ private:
groupBox * m_arpGroupBox;
QComboBox * m_arpComboBox;
knob * m_arpRangeKnob;
knob * m_arpTimeKnob;
tempoSyncKnob * m_arpTimeKnob;
knob * m_arpGateKnob;
QLabel * m_arpDirectionLbl;

View File

@@ -38,6 +38,7 @@
#include "mixer.h"
#include "templates.h"
const int MOOG_VOLTAGE = 40000;
template<Uint8 CHANNELS = DEFAULT_CHANNELS>
class basicFilters
@@ -53,10 +54,23 @@ public:
NOTCH,
ALLPASS,
MOOG,
DOUBLE_LOWPASS,
DOUBLE_MOOG
MOOG2,
SIMPLE_FLT_CNT,
DOUBLE_LOWPASS = 16+LOWPASS,
DOUBLE_MOOG = 16+MOOG,
DOUBLE_MOOG2 = 16+MOOG2
} ;
static inline filterTypes getFilterType( const int _idx )
{
if( _idx < SIMPLE_FLT_CNT )
{
return( static_cast<filterTypes>( _idx ) );
}
return( static_cast<filterTypes>( DOUBLE_LOWPASS + _idx -
SIMPLE_FLT_CNT ) );
}
inline basicFilters( const float _sampleRate ) :
m_b0a0( 0.0f ),
m_b1a0( 0.0f ),
@@ -87,40 +101,112 @@ public:
inline sampleType update( sampleType _in0, Uint8 _chnl )
{
sampleType out;
if( m_type != MOOG )
switch( m_type )
{
// filter
out = m_b0a0*_in0 + m_b1a0*m_in1[_chnl] +
m_b2a0*m_in2[_chnl] - m_a1a0*m_ou1[_chnl] -
m_a2a0*m_ou2[_chnl];
// push in/out buffers
m_in2[_chnl] = m_in1[_chnl];
m_in1[_chnl] = _in0;
m_ou2[_chnl] = m_ou1[_chnl];
m_ou1[_chnl] = out;
}
else
{
sampleType x = _in0 - m_r*m_y4[_chnl];
case MOOG:
case DOUBLE_MOOG:
{
sampleType x = _in0 - m_r*m_y4[_chnl];
// Four cascaded onepole filters (bilinear transform)
m_y1[_chnl] = x*m_p + m_oldx[_chnl]*m_p -
// four cascaded onepole filters
// (bilinear transform)
m_y1[_chnl] = x*m_p + m_oldx[_chnl]*m_p -
m_k*m_y1[_chnl];
m_y2[_chnl] = m_y1[_chnl]*m_p+m_oldy1[_chnl]*m_p -
m_k*m_y2[_chnl];
m_y3[_chnl] = m_y2[_chnl]*m_p+m_oldy2[_chnl]*m_p -
m_k*m_y3[_chnl];
m_y4[_chnl] = m_y3[_chnl]*m_p+m_oldy3[_chnl]*m_p -
m_k*m_y4[_chnl];
m_y2[_chnl] = m_y1[_chnl]*m_p+m_oldy1[_chnl]*
m_p - m_k*m_y2[_chnl];
m_y3[_chnl] = m_y2[_chnl]*m_p+m_oldy2[_chnl]*
m_p - m_k*m_y3[_chnl];
m_y4[_chnl] = m_y3[_chnl]*m_p+m_oldy3[_chnl]*
m_p - m_k*m_y4[_chnl];
m_oldx[_chnl] = x;
m_oldy1[_chnl] = m_y1[_chnl];
m_oldy2[_chnl] = m_y2[_chnl];
m_oldy3[_chnl] = m_y3[_chnl];
out = m_y4[_chnl] - m_y4[_chnl] * m_y4[_chnl] *
m_y4[_chnl] * ( 1.0f/6.0f );
m_oldx[_chnl] = x;
m_oldy1[_chnl] = m_y1[_chnl];
m_oldy2[_chnl] = m_y2[_chnl];
m_oldy3[_chnl] = m_y3[_chnl];
out = m_y4[_chnl] - m_y4[_chnl] * m_y4[_chnl] *
m_y4[_chnl] * ( 1.0f / 6.0f );
break;
}
case MOOG2:
case DOUBLE_MOOG2:
{
const float x1 = ( _in0 - m_r *
m_oldx[_chnl] ) / MOOG_VOLTAGE;
const float tanh1 = tanhf( x1 );
const float x2 = m_oldy1[_chnl] / MOOG_VOLTAGE;
const float tanh2 = tanhf( x2 );
m_y1[_chnl] = m_oldy1[_chnl] + m_p *
( tanh1 - tanh2 );
m_oldy1[_chnl] = m_y1[_chnl];
m_y2[_chnl] = m_oldy2[_chnl] + m_p *
( tanhf( m_y1[_chnl] /
MOOG_VOLTAGE ) -
tanhf( m_oldy2[_chnl] /
MOOG_VOLTAGE ) );
m_oldy2[_chnl] = m_y2[_chnl];
m_y3[_chnl] = m_oldy3[_chnl] + m_p *
( tanhf( m_y2[_chnl] /
MOOG_VOLTAGE ) -
tanhf( m_oldy3[_chnl] /
MOOG_VOLTAGE ) );
m_oldy3[_chnl] = m_y3[_chnl];
m_y4[_chnl] = m_ou1[_chnl] + m_p *
( tanhf( m_y3[_chnl] /
MOOG_VOLTAGE ) -
tanhf( m_ou1[_chnl] /
MOOG_VOLTAGE ) );
m_ou1[_chnl] = m_y4[_chnl];
m_oldx[_chnl] = ( m_y4[_chnl] +
m_ou2[_chnl] ) * 0.5f;
m_ou2[_chnl] = m_y4[_chnl];
// the same code again...
m_y1[_chnl] = m_oldy1[_chnl] + m_p *
( tanh1 - tanh2 );
m_oldy1[_chnl] = m_y1[_chnl];
m_y2[_chnl] = m_oldy2[_chnl] + m_p *
( tanhf( m_y1[_chnl] /
MOOG_VOLTAGE ) -
tanhf( m_oldy2[_chnl] /
MOOG_VOLTAGE ) );
m_oldy2[_chnl] = m_y2[_chnl];
m_y3[_chnl] = m_oldy3[_chnl] + m_p *
( tanhf( m_y2[_chnl] /
MOOG_VOLTAGE ) -
tanhf( m_oldy3[_chnl] /
MOOG_VOLTAGE ) );
m_oldy3[_chnl] = m_y3[_chnl];
m_y4[_chnl] = m_ou1[_chnl] + m_p *
( tanhf( m_y3[_chnl] /
MOOG_VOLTAGE ) -
tanhf( m_ou1[_chnl] /
MOOG_VOLTAGE ) );
m_ou1[_chnl] = m_y4[_chnl];
m_oldx[_chnl] = ( m_y4[_chnl] +
m_ou2[_chnl] ) * 0.5f;
m_ou2[_chnl] = m_y4[_chnl];
out = m_oldx[_chnl];
break;
}
default:
// filter
out = m_b0a0*_in0 +
m_b1a0*m_in1[_chnl] +
m_b2a0*m_in2[_chnl] -
m_a1a0*m_ou1[_chnl] -
m_a2a0*m_ou2[_chnl];
// push in/out buffers
m_in2[_chnl] = m_in1[_chnl];
m_in1[_chnl] = _in0;
m_ou2[_chnl] = m_ou1[_chnl];
m_ou1[_chnl] = out;
break;
}
if( m_subFilter != NULL )
{
@@ -140,13 +226,9 @@ public:
_freq = tMax( _freq, 0.01f );// limit freq for not getting
// bad noise out of the filter...
if( m_type == MOOG || m_type == DOUBLE_MOOG )
switch( m_type )
{
const float f = 2 * _freq / m_sampleRate; // [0 - 1]
m_k = 3.6f*f - 1.6f*f*f - 1; // (Empirical tunning)
m_p = (m_k+1)*0.5f;
m_r = _q*powf( M_E, ( ( 1-m_p ) * 1.386249f ) );
if( m_type == DOUBLE_MOOG )
case DOUBLE_MOOG:
{
if( m_subFilter == NULL )
{
@@ -157,78 +239,121 @@ public:
m_subFilter->calcFilterCoeffs( MOOG, _freq,
_q );
}
}
else
{
// other filters
const float omega = 2.0f * M_PI * _freq /
m_sampleRate;
const float tsin = sinf( omega );
const float tcos = cosf( omega );
//float alpha;
//if (q_is_bandwidth)
//alpha = tsin*sinhf(logf(2.0f)/2.0f*q*omega/tsin);
//else
const float alpha = tsin / ( 2.0f*_q );
const float a0 = 1.0f / ( 1.0f+alpha );
if( m_type == LOWPASS || m_type == DOUBLE_LOWPASS )
case MOOG:
{
m_b0a0 = ((1.0f-tcos)/2.0f)*a0;
m_b1a0 = (1.0f-tcos)*a0;
m_b2a0 = m_b0a0;//((1.0f-tcos)/2.0f)*a0;
m_a1a0 = (-2.0f*tcos)*a0;
if( m_type == DOUBLE_LOWPASS )
// [ 0 - 1 ]
const float f = 2 * _freq / m_sampleRate;
// (Empirical tunning)
m_k = 3.6f*f - 1.6f*f*f - 1;
m_p = (m_k+1)*0.5f;
m_r = _q*powf( M_E, ( ( 1-m_p ) * 1.386249f ) );
break;
}
case DOUBLE_MOOG2:
{
if( m_subFilter == NULL )
{
if( m_subFilter == NULL )
m_subFilter =
new basicFilters<CHANNELS>(
m_sampleRate );
}
m_subFilter->calcFilterCoeffs( MOOG2, _freq,
_q );
}
case MOOG2:
{
const float kfc = 2 * _freq / m_sampleRate;
const float kf = _freq / m_sampleRate;
const float kfcr = 1.8730 * ( kfc*kfc*kfc ) +
0.4955 * ( kfc*kfc ) +
0.6490 * kfc + 0.9988;
const float kacr = -3.9364 * ( kfc*kfc ) +
1.8409 * kfc + 0.9968;
m_p = MOOG_VOLTAGE * ( 1 - expf( -2.0 * M_PI *
kfcr * kf ) );
m_r = 4 * _q * kacr;
break;
}
default:
{
// other filters
const float omega = 2.0f * M_PI * _freq /
m_sampleRate;
const float tsin = sinf( omega );
const float tcos = cosf( omega );
//float alpha;
//if (q_is_bandwidth)
//alpha = tsin*sinhf(logf(2.0f)/2.0f*q*omega/
// tsin);
//else
const float alpha = tsin / ( 2.0f * _q );
const float a0 = 1.0f / ( 1.0f+alpha );
if( m_type == LOWPASS ||
m_type == DOUBLE_LOWPASS )
{
m_b0a0 = ((1.0f-tcos)/2.0f)*a0;
m_b1a0 = (1.0f-tcos)*a0;
m_b2a0 = m_b0a0;//((1.0f-tcos)/2.0f)*a0;
m_a1a0 = (-2.0f*tcos)*a0;
if( m_type == DOUBLE_LOWPASS )
{
m_subFilter =
if( m_subFilter == NULL )
{
m_subFilter =
new basicFilters<CHANNELS>( m_sampleRate );
}
m_subFilter->calcFilterCoeffs( LOWPASS,
}
m_subFilter->calcFilterCoeffs(
LOWPASS,
_freq,
_q );
}
}
else if( m_type == HIPASS )
{
m_b0a0 = ((1.0f+tcos)/2.0f)*a0;
m_b1a0 = (-1.0f-tcos)*a0;
m_b2a0 = m_b0a0;//((1.0f+tcos)/2.0f)*a0;
m_a1a0 = (-2.0f*tcos)*a0;
}
else if( m_type == BANDPASS_CSG )
{
m_b0a0 = (tsin/2.0f)*a0;
m_b1a0 = 0.0f;
m_b2a0 = (-tsin/2.0f)*a0;
m_a1a0 = (-2.0f*tcos)*a0;
}
else if( m_type == BANDPASS_CZPG )
{
m_b0a0 = alpha*a0;
m_b1a0 = 0.0f;
m_b2a0 = (-alpha)*a0;
m_a1a0 = (-2.0f*tcos)*a0;
}
else if( m_type == NOTCH )
{
m_b0a0 = a0;
m_b1a0 = (-2.0f*tcos)*a0;
m_b2a0 = a0;
m_a1a0 = m_b1a0;//(-2.0f*tcos)*a0;
}
else if( m_type == ALLPASS )
{
m_b0a0 = (1.0f-alpha)*a0;
m_b1a0 = (-2.0f*tcos)*a0;
m_b2a0 = 1.0;//(1.0f+alpha)*a0;
m_a1a0 = m_b1a0;//(-2.0f*tcos)*a0;
//m_a2a0 = m_b0a0;//(1.0f-alpha)*a0;
}
m_a2a0 = (1.0f-alpha)*a0;
break;
}
else if( m_type == HIPASS )
{
m_b0a0 = ((1.0f+tcos)/2.0f)*a0;
m_b1a0 = (-1.0f-tcos)*a0;
m_b2a0 = m_b0a0;//((1.0f+tcos)/2.0f)*a0;
m_a1a0 = (-2.0f*tcos)*a0;
}
else if( m_type == BANDPASS_CSG )
{
m_b0a0 = (tsin/2.0f)*a0;
m_b1a0 = 0.0f;
m_b2a0 = (-tsin/2.0f)*a0;
m_a1a0 = (-2.0f*tcos)*a0;
}
else if( m_type == BANDPASS_CZPG )
{
m_b0a0 = alpha*a0;
m_b1a0 = 0.0f;
m_b2a0 = (-alpha)*a0;
m_a1a0 = (-2.0f*tcos)*a0;
}
else if( m_type == NOTCH )
{
m_b0a0 = a0;
m_b1a0 = (-2.0f*tcos)*a0;
m_b2a0 = a0;
m_a1a0 = m_b1a0;//(-2.0f*tcos)*a0;
}
else if( m_type == ALLPASS )
{
m_b0a0 = (1.0f-alpha)*a0;
m_b1a0 = (-2.0f*tcos)*a0;
m_b2a0 = 1.0;//(1.0f+alpha)*a0;
m_a1a0 = m_b1a0;//(-2.0f*tcos)*a0;
//m_a2a0 = m_b0a0;//(1.0f-alpha)*a0;
}
m_a2a0 = (1.0f-alpha)*a0;
}
}

View File

@@ -55,7 +55,7 @@ class envelopeTabWidget;
class knob;
class ledCheckBox;
class pixmapButton;
class tempoSyncKnob;
class envelopeAndLFOWidget : public QWidget, public settings,
@@ -138,7 +138,7 @@ private:
// LFO-stuff
knob * m_lfoPredelayKnob;
knob * m_lfoAttackKnob;
knob * m_lfoSpeedKnob;
tempoSyncKnob * m_lfoSpeedKnob;
knob * m_lfoAmountKnob;
pixmapButton * m_sinLfoBtn;
pixmapButton * m_triangleLfoBtn;

View File

@@ -1,7 +1,8 @@
/*
* knob.h - powerful knob-widget
*
* This file is based on the knob-widget of the Qwt Widget Library by Josef Wilgen
* This file is based on the knob-widget of the Qwt Widget Library by
* Josef Wilgen
*
* Linux MultiMedia Studio
* Copyright (c) 2004-2005 Tobias Doerffel <tobydox@users.sourceforge.net>
@@ -132,8 +133,11 @@ protected:
void drawKnob( QPainter * _p );
void setPosition( const QPoint & _p );
private:
// TODO: Need to figure out what is really used by tempoSyncKnob
// to get the private/protected attributes sorted out. Right
// now, just make everything protected.
//private:
void layoutKnob( bool _update = TRUE );
float getValue( const QPoint & _p );
void getScrollMode( const QPoint & _p, int & _scroll_mode,

View File

@@ -103,8 +103,7 @@ public:
static oscillator * FASTCALL createOsc( waveShapes _wave_shape,
modulationAlgos _modulation_algo, float _freq,
Sint16 _phase_offset, float _volume_factor,
oscillator * _m_subOsc = NULL );
oscillator * _m_subOsc = NULL );
inline bool syncOk( void )
{
const float v1 = m_sample * m_oscCoeff;
@@ -112,19 +111,20 @@ public:
// check whether v2 is in next period
return( floorf( v2 ) > floorf( v1 ) );
}
#define FLOAT_TO_INT(in,out) \
register const float round_const = -0.5f; \
__asm__ __volatile__ ("fadd %%st,%%st(0)\n" \
"fadd %2\n" \
"fistpl %0\n" \
"shrl $1,%0" : "=m" (out) : "t" (in),"m"(round_const) : "st") ;
static inline float phase( float _sample )
static inline float phase( const float _sample )
{
#ifndef modff
float t;
#else
double t;
#endif
return( modff( _sample, &t ) );
//return( _sample - floorf( _sample ) );
return( _sample - static_cast<int>( _sample ) );
}
// now follow the wave-shape-routines...
static inline sampleType sinSample( float _sample )
{
return( sinf( _sample * static_cast<sampleType>( 2.0f * M_PI
@@ -157,7 +157,7 @@ public:
static inline sampleType moogSawSample( float _sample )
{
const float ph= phase( _sample );
const float ph = phase( _sample );
if( ph < 0.5f )
{
return( -1.0f + ph * 4.0f );

View File

@@ -154,6 +154,8 @@ public:
return( m_playPos[_pm] );
}
int getBPM( void );
// every function that replaces current file (e.g. creates new file,
// opens another file...) has to call this before and may only process
// if this function returns true

109
include/tempo_sync_knob.h Normal file
View File

@@ -0,0 +1,109 @@
/*
* tempo_sync_knob.h - adds bpm to ms conversion for knob class
*
* This derived from the knob-widget by Tobias Doerffel
*
* Linux MultiMedia Studio
* Copyright (c) 2004-2005 Danny McRae <khjklujn@yahoo.com>
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
*/
#ifndef _TEMPO_SYNC_KNOB_H
#define _TEMPO_SYNC_KNOB_H
#ifdef QT4
#include <QPixmap.h>
#else
#include <qpixmap.h>
#endif
#include "knob.h"
enum tempoSyncMode
{
NO_SYNC,
DOUBLE_WHOLE_NOTE,
WHOLE_NOTE,
HALF_NOTE,
QUARTER_NOTE,
EIGHTH_NOTE,
SIXTEENTH_NOTE,
THIRTYSECOND_NOTE
} ;
class tempoSyncKnob : public knob
{
Q_OBJECT
public:
tempoSyncKnob( int _knob_num, QWidget * _parent, const QString & _name,
float _scale = 1.0f );
virtual ~tempoSyncKnob();
tempoSyncMode getSyncMode( void );
void setSyncMode( tempoSyncMode _new_mode );
float getScale( void );
void setScale( float _new_scale );
const QString & getSyncDescription( void );
void setSyncDescription( const QString & _new_description );
const QPixmap & getSyncIcon( void );
void setSyncIcon( const QPixmap & _new_pix );
signals:
void syncModeChanged( tempoSyncMode _new_mode );
void scaleChanged( float _new_scale );
void syncDescriptionChanged( const QString & _new_description );
void syncIconChanged( void );
public slots:
void setTempoSync( int _note_type );
protected:
virtual void mouseMoveEvent( QMouseEvent * _me );
virtual void contextMenuEvent( QContextMenuEvent * _me );
virtual void wheelEvent( QWheelEvent * _me );
protected slots:
void calculateTempoSyncTime( int _bpm );
private:
tempoSyncMode m_tempoSyncMode;
float m_scale;
QPixmap m_tempoSyncIcon;
QString m_tempoSyncDescription;
tempoSyncMode m_tempoLastSyncMode;
} ;
#endif