LadspaEffect: renamed source directory to match coding style

This commit is contained in:
Tobias Doerffel
2014-02-23 15:23:43 +01:00
parent 360bfe5089
commit 9c9372f0c8
402 changed files with 155945 additions and 1 deletions

View File

@@ -6,7 +6,7 @@ ADD_SUBDIRECTORY(flp_import)
ADD_SUBDIRECTORY(HydrogenImport)
ADD_SUBDIRECTORY(kicker)
ADD_SUBDIRECTORY(ladspa_browser)
ADD_SUBDIRECTORY(ladspa_effect)
ADD_SUBDIRECTORY(LadspaEffect)
ADD_SUBDIRECTORY(lb302)
#ADD_SUBDIRECTORY(lb303)
ADD_SUBDIRECTORY(midi_import)

View File

@@ -0,0 +1,26 @@
IF(WANT_CAPS)
ADD_SUBDIRECTORY(caps)
ENDIF(WANT_CAPS)
IF(WANT_TAP)
ADD_SUBDIRECTORY(tap)
ENDIF(WANT_TAP)
IF(WANT_SWH)
ADD_SUBDIRECTORY(swh)
ENDIF(WANT_SWH)
IF(WANT_CMT)
ADD_SUBDIRECTORY(cmt)
ENDIF(WANT_CMT)
IF(WANT_CALF)
ADD_SUBDIRECTORY(calf)
ENDIF(WANT_CALF)
INCLUDE(BuildPlugin)
BUILD_PLUGIN(ladspaeffect LadspaEffect.cpp LadspaControls.cpp LadspaControlDialog.cpp LadspaSubPluginFeatures.cpp LadspaEffect.h LadspaControls.h LadspaControlDialog.h LadspaSubPluginFeatures.h MOCFILES LadspaEffect.h LadspaControls.h LadspaControlDialog.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png")

View File

@@ -0,0 +1,147 @@
/*
* LadspaControlDialog.cpp - dialog for displaying and editing control port
* values for LADSPA plugins
*
* Copyright (c) 2006-2008 Danny McRae <khjklujn/at/users.sourceforge.net>
* Copyright (c) 2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* 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 <QtGui/QGroupBox>
#include <QtGui/QLayout>
#include "LadspaEffect.h"
#include "LadspaControlDialog.h"
#include "LadspaControlView.h"
#include "led_checkbox.h"
LadspaControlDialog::LadspaControlDialog( LadspaControls * _ctl ) :
EffectControlDialog( _ctl ),
m_effectLayout( NULL ),
m_stereoLink( NULL )
{
QVBoxLayout * mainLay = new QVBoxLayout( this );
m_effectLayout = new QHBoxLayout();
mainLay->addLayout( m_effectLayout );
updateEffectView( _ctl );
if( _ctl->m_processors > 1 )
{
mainLay->addSpacing( 3 );
QHBoxLayout * center = new QHBoxLayout();
mainLay->addLayout( center );
m_stereoLink = new ledCheckBox( tr( "Link Channels" ), this );
m_stereoLink->setModel( &_ctl->m_stereoLinkModel );
center->addWidget( m_stereoLink );
}
}
LadspaControlDialog::~LadspaControlDialog()
{
}
void LadspaControlDialog::updateEffectView( LadspaControls * _ctl )
{
QList<QGroupBox *> list = findChildren<QGroupBox *>();
for( QList<QGroupBox *>::iterator it = list.begin(); it != list.end();
++it )
{
delete *it;
}
m_effectControls = _ctl;
const int cols = static_cast<int>( sqrt(
static_cast<double>( _ctl->m_controlCount /
_ctl->m_processors ) ) );
for( ch_cnt_t proc = 0; proc < _ctl->m_processors; proc++ )
{
control_list_t & controls = _ctl->m_controls[proc];
int row = 0;
int col = 0;
buffer_data_t last_port = NONE;
QGroupBox * grouper;
if( _ctl->m_processors > 1 )
{
grouper = new QGroupBox( tr( "Channel " ) +
QString::number( proc + 1 ),
this );
}
else
{
grouper = new QGroupBox( this );
}
QGridLayout * gl = new QGridLayout( grouper );
grouper->setLayout( gl );
grouper->setAlignment( Qt::Vertical );
for( control_list_t::iterator it = controls.begin();
it != controls.end(); it++ )
{
if( (*it)->port()->proc == proc )
{
if( last_port != NONE &&
(*it)->port()->data_type == TOGGLED &&
!( (*it)->port()->data_type == TOGGLED &&
last_port == TOGGLED ) )
{
++row;
col = 0;
}
gl->addWidget( new LadspaControlView( grouper, *it ), row, col );
if( ++col == cols )
{
++row;
col = 0;
}
last_port = (*it)->port()->data_type;
}
}
m_effectLayout->addWidget( grouper );
}
if( _ctl->m_processors > 1 && m_stereoLink != NULL )
{
m_stereoLink->setModel( &_ctl->m_stereoLinkModel );
}
connect( _ctl, SIGNAL( effectModelChanged( LadspaControls * ) ),
this, SLOT( updateEffectView( LadspaControls * ) ),
Qt::DirectConnection );
}
#include "moc_LadspaControlDialog.cxx"

View File

@@ -0,0 +1,56 @@
/*
* LadspaControlDialog.h - dialog for displaying and editing control port
* values for LADSPA plugins
*
* Copyright (c) 2006-2008 Danny McRae <khjklujn/at/users.sourceforge.net>
* Copyright (c) 2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* 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 _LADSPA_CONTROL_DIALOG_H
#define _LADSPA_CONTROL_DIALOG_H
#include "EffectControlDialog.h"
class QHBoxLayout;
class LadspaControls;
class ledCheckBox;
class LadspaControlDialog : public EffectControlDialog
{
Q_OBJECT
public:
LadspaControlDialog( LadspaControls * _ctl );
~LadspaControlDialog();
private slots:
void updateEffectView( LadspaControls * _ctl );
private:
QHBoxLayout * m_effectLayout;
ledCheckBox * m_stereoLink;
} ;
#endif

View File

@@ -0,0 +1,187 @@
/*
* LadspaControls.cpp - model for LADSPA plugin controls
*
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* 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 <QtXml/QDomElement>
#include "LadspaEffect.h"
LadspaControls::LadspaControls( LadspaEffect * _eff ) :
EffectControls( _eff ),
m_effect( _eff ),
m_processors( _eff->processorCount() ),
m_noLink( false ),
m_stereoLinkModel( true, this )
{
connect( &m_stereoLinkModel, SIGNAL( dataChanged() ),
this, SLOT( updateLinkStatesFromGlobal() ) );
multi_proc_t controls = m_effect->getPortControls();
m_controlCount = controls.count();
for( ch_cnt_t proc = 0; proc < m_processors; proc++ )
{
control_list_t p;
const bool linked_control = ( m_processors > 1 && proc == 0 );
for( multi_proc_t::Iterator it = controls.begin(); it != controls.end(); it++ )
{
if( (*it)->proc == proc )
{
(*it)->control = new LadspaControl( this, *it,
linked_control );
p.append( (*it)->control );
if( linked_control )
{
connect( (*it)->control, SIGNAL( linkChanged( int, bool ) ),
this, SLOT( linkPort( int, bool ) ) );
}
}
}
m_controls.append( p );
}
// now link all controls
if( m_processors > 1 )
{
for( multi_proc_t::Iterator it = controls.begin();
it != controls.end(); it++ )
{
if( (*it)->proc == 0 )
{
linkPort( ( *it )->control_id, true );
}
}
}
}
LadspaControls::~LadspaControls()
{
for( ch_cnt_t proc = 0; proc < m_processors; proc++ )
{
m_controls[proc].clear();
}
m_controls.clear();
}
void LadspaControls::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
if( m_processors > 1 )
{
_this.setAttribute( "link", m_stereoLinkModel.value() );
}
multi_proc_t controls = m_effect->getPortControls();
_this.setAttribute( "ports", controls.count() );
for( multi_proc_t::Iterator it = controls.begin();
it != controls.end(); it++ )
{
QString n = "port" + QString::number( (*it)->proc ) +
QString::number( (*it)->port_id );
(*it)->control->saveSettings( _doc, _this, n );
}
}
void LadspaControls::loadSettings( const QDomElement & _this )
{
if( m_processors > 1 )
{
m_stereoLinkModel.setValue( _this.attribute( "link" ).toInt() );
}
multi_proc_t controls = m_effect->getPortControls();
for( multi_proc_t::Iterator it = controls.begin();
it != controls.end(); it++ )
{
QString n = "port" + QString::number( (*it)->proc ) +
QString::number( (*it)->port_id );
(*it)->control->loadSettings( _this, n );
}
}
void LadspaControls::linkPort( int _port, bool _state )
{
LadspaControl * first = m_controls[0][_port];
if( _state )
{
for( ch_cnt_t proc = 1; proc < m_processors; proc++ )
{
first->linkControls( m_controls[proc][_port] );
}
}
else
{
for( ch_cnt_t proc = 1; proc < m_processors; proc++ )
{
first->unlinkControls( m_controls[proc][_port] );
}
m_noLink = true;
m_stereoLinkModel.setValue( false );
}
}
void LadspaControls::updateLinkStatesFromGlobal()
{
if( m_stereoLinkModel.value() )
{
for( int port = 0; port < m_controlCount / m_processors; port++ )
{
m_controls[0][port]->setLink( true );
}
}
else if( !m_noLink )
{
for( int port = 0; port < m_controlCount / m_processors; port++ )
{
m_controls[0][port]->setLink( false );
}
}
// if global channel link state has changed, always ignore link
// status of individual ports in the future
m_noLink = false;
}
#include "moc_LadspaControls.cxx"

View File

@@ -0,0 +1,86 @@
/*
* LadspaControls.h - model for LADSPA plugin controls
*
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* 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 _LADSPA_CONTROLS_H
#define _LADSPA_CONTROLS_H
#include "EffectControls.h"
#include "LadspaControl.h"
#include "LadspaControlDialog.h"
typedef QVector<LadspaControl *> control_list_t;
class LadspaEffect;
class LadspaControls : public EffectControls
{
Q_OBJECT
public:
LadspaControls( LadspaEffect * _eff );
virtual ~LadspaControls();
inline int controlCount()
{
return m_controlCount;
}
virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent );
virtual void loadSettings( const QDomElement & _this );
inline virtual QString nodeName() const
{
return "ladspacontrols";
}
virtual EffectControlDialog * createView()
{
return new LadspaControlDialog( this );
}
protected slots:
void updateLinkStatesFromGlobal();
void linkPort( int _port, bool _state );
private:
LadspaEffect * m_effect;
ch_cnt_t m_processors;
ch_cnt_t m_controlCount;
bool m_noLink;
BoolModel m_stereoLinkModel;
QVector<control_list_t> m_controls;
friend class LadspaControlDialog;
friend class LadspaEffect;
signals:
void effectModelChanged( LadspaControls * );
} ;
#endif

View File

@@ -0,0 +1,580 @@
/*
* LadspaEffect.cpp - class for processing LADSPA effects
*
* Copyright (c) 2006-2008 Danny McRae <khjklujn/at/users.sourceforge.net>
* Copyright (c) 2009-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* 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 <QtGui/QMessageBox>
#include "LadspaEffect.h"
#include "DataFile.h"
#include "AudioDevice.h"
#include "config_mgr.h"
#include "ladspa_2_lmms.h"
#include "LadspaControl.h"
#include "LadspaSubPluginFeatures.h"
#include "Mixer.h"
#include "EffectChain.h"
#include "AutomationPattern.h"
#include "ControllerConnection.h"
#include "embed.cpp"
extern "C"
{
Plugin::Descriptor PLUGIN_EXPORT ladspaeffect_plugin_descriptor =
{
STRINGIFY( PLUGIN_NAME ),
"LADSPA Effect",
QT_TRANSLATE_NOOP( "pluginBrowser",
"plugin for using arbitrary LADSPA-effects "
"inside LMMS." ),
"Danny McRae <khjklujn/at/users.sourceforge.net>",
0x0100,
Plugin::Effect,
new PluginPixmapLoader( "logo" ),
NULL,
new LadspaSubPluginFeatures( Plugin::Effect )
} ;
}
LadspaEffect::LadspaEffect( Model * _parent,
const Descriptor::SubPluginFeatures::Key * _key ) :
Effect( &ladspaeffect_plugin_descriptor, _parent, _key ),
m_controls( NULL ),
m_maxSampleRate( 0 ),
m_key( LadspaSubPluginFeatures::subPluginKeyToLadspaKey( _key ) )
{
ladspa2LMMS * manager = engine::getLADSPAManager();
if( manager->getDescription( m_key ) == NULL )
{
if( !engine::suppressMessages() )
{
QMessageBox::warning( 0, tr( "Effect" ),
tr( "Unknown LADSPA plugin %1 requested." ).
arg( m_key.second ),
QMessageBox::Ok, QMessageBox::NoButton );
}
setOkay( FALSE );
return;
}
setDisplayName( manager->getShortName( m_key ) );
pluginInstantiation();
connect( engine::mixer(), SIGNAL( sampleRateChanged() ),
this, SLOT( changeSampleRate() ) );
}
LadspaEffect::~LadspaEffect()
{
pluginDestruction();
}
void LadspaEffect::changeSampleRate()
{
DataFile dataFile( DataFile::EffectSettings );
m_controls->saveState( dataFile, dataFile.content() );
LadspaControls * controls = m_controls;
m_controls = NULL;
m_pluginMutex.lock();
pluginDestruction();
pluginInstantiation();
m_pluginMutex.unlock();
controls->effectModelChanged( m_controls );
delete controls;
m_controls->restoreState( dataFile.content().firstChild().toElement() );
// the IDs of re-created controls have been saved and now need to be
// resolved again
AutomationPattern::resolveAllIDs();
// make sure, connections are ok
ControllerConnection::finalizeConnections();
}
bool LadspaEffect::processAudioBuffer( sampleFrame * _buf,
const fpp_t _frames )
{
m_pluginMutex.lock();
if( !isOkay() || dontRun() || !isRunning() || !isEnabled() )
{
m_pluginMutex.unlock();
return( FALSE );
}
int frames = _frames;
sampleFrame * o_buf = NULL;
if( m_maxSampleRate < engine::mixer()->processingSampleRate() )
{
o_buf = _buf;
_buf = new sampleFrame[_frames];
sampleDown( o_buf, _buf, m_maxSampleRate );
frames = _frames * m_maxSampleRate /
engine::mixer()->processingSampleRate();
}
// Copy the LMMS audio buffer to the LADSPA input buffer and initialize
// the control ports. Need to change this to handle non-in-place-broken
// plugins--would speed things up to use the same buffer for both
// LMMS and LADSPA.
ch_cnt_t channel = 0;
for( ch_cnt_t proc = 0; proc < processorCount(); ++proc )
{
for( int port = 0; port < m_portCount; ++port )
{
port_desc_t * pp = m_ports.at( proc ).at( port );
switch( pp->rate )
{
case CHANNEL_IN:
for( fpp_t frame = 0;
frame < frames; ++frame )
{
pp->buffer[frame] =
_buf[frame][channel];
}
++channel;
break;
case AUDIO_RATE_INPUT:
pp->value = static_cast<LADSPA_Data>(
pp->control->value() / pp->scale );
// This only supports control rate ports, so the audio rates are
// treated as though they were control rate by setting the
// port buffer to all the same value.
for( fpp_t frame = 0;
frame < frames; ++frame )
{
pp->buffer[frame] =
pp->value;
}
break;
case CONTROL_RATE_INPUT:
if( pp->control == NULL )
{
break;
}
pp->value = static_cast<LADSPA_Data>(
pp->control->value() / pp->scale );
pp->buffer[0] =
pp->value;
break;
case CHANNEL_OUT:
case AUDIO_RATE_OUTPUT:
case CONTROL_RATE_OUTPUT:
break;
default:
break;
}
}
}
// Process the buffers.
for( ch_cnt_t proc = 0; proc < processorCount(); ++proc )
{
(m_descriptor->run)( m_handles[proc], frames );
}
// Copy the LADSPA output buffers to the LMMS buffer.
double out_sum = 0.0;
channel = 0;
const float d = dryLevel();
const float w = wetLevel();
for( ch_cnt_t proc = 0; proc < processorCount(); ++proc )
{
for( int port = 0; port < m_portCount; ++port )
{
port_desc_t * pp = m_ports.at( proc ).at( port );
switch( pp->rate )
{
case CHANNEL_IN:
case AUDIO_RATE_INPUT:
case CONTROL_RATE_INPUT:
break;
case CHANNEL_OUT:
for( fpp_t frame = 0;
frame < frames; ++frame )
{
_buf[frame][channel] = d * _buf[frame][channel] + w * pp->buffer[frame];
out_sum += _buf[frame][channel] * _buf[frame][channel];
}
++channel;
break;
case AUDIO_RATE_OUTPUT:
case CONTROL_RATE_OUTPUT:
break;
default:
break;
}
}
}
if( o_buf != NULL )
{
sampleBack( _buf, o_buf, m_maxSampleRate );
delete[] _buf;
}
checkGate( out_sum / frames );
bool is_running = isRunning();
m_pluginMutex.unlock();
return( is_running );
}
void LadspaEffect::setControl( int _control, LADSPA_Data _value )
{
if( !isOkay() )
{
return;
}
m_portControls[_control]->value = _value;
}
void LadspaEffect::pluginInstantiation()
{
m_maxSampleRate = maxSamplerate( displayName() );
ladspa2LMMS * manager = engine::getLADSPAManager();
// Calculate how many processing units are needed.
const ch_cnt_t lmms_chnls = engine::mixer()->audioDev()->channels();
int effect_channels = manager->getDescription( m_key )->inputChannels;
setProcessorCount( lmms_chnls / effect_channels );
// Categorize the ports, and create the buffers.
m_portCount = manager->getPortCount( m_key );
for( ch_cnt_t proc = 0; proc < processorCount(); proc++ )
{
multi_proc_t ports;
for( int port = 0; port < m_portCount; port++ )
{
port_desc_t * p = new PortDescription;
p->name = manager->getPortName( m_key, port );
p->proc = proc;
p->port_id = port;
p->control = NULL;
// Determine the port's category.
if( manager->isPortAudio( m_key, port ) )
{
// Nasty manual memory management--was having difficulty
// with some prepackaged plugins that were segfaulting
// during cleanup. It was easier to troubleshoot with the
// memory management all taking place in one file.
p->buffer =
new LADSPA_Data[engine::mixer()->framesPerPeriod()];
if( p->name.toUpper().contains( "IN" ) &&
manager->isPortInput( m_key, port ) )
{
p->rate = CHANNEL_IN;
}
else if( p->name.toUpper().contains( "OUT" ) &&
manager->isPortOutput( m_key, port ) )
{
p->rate = CHANNEL_OUT;
}
else if( manager->isPortInput( m_key, port ) )
{
p->rate = AUDIO_RATE_INPUT;
}
else
{
p->rate = AUDIO_RATE_OUTPUT;
}
}
else
{
p->buffer = new LADSPA_Data[1];
if( manager->isPortInput( m_key, port ) )
{
p->rate = CONTROL_RATE_INPUT;
}
else
{
p->rate = CONTROL_RATE_OUTPUT;
}
}
p->scale = 1.0f;
if( manager->isPortToggled( m_key, port ) )
{
p->data_type = TOGGLED;
}
else if( manager->isInteger( m_key, port ) )
{
p->data_type = INTEGER;
}
else if( p->name.toUpper().contains( "(SECONDS)" ) )
{
p->data_type = TIME;
p->scale = 1000.0f;
int loc = p->name.toUpper().indexOf(
"(SECONDS)" );
p->name.replace( loc, 9, "(ms)" );
}
else if( p->name.toUpper().contains( "(S)" ) )
{
p->data_type = TIME;
p->scale = 1000.0f;
int loc = p->name.toUpper().indexOf( "(S)" );
p->name.replace( loc, 3, "(ms)" );
}
else if( p->name.toUpper().contains( "(MS)" ) )
{
p->data_type = TIME;
int loc = p->name.toUpper().indexOf( "(MS)" );
p->name.replace( loc, 4, "(ms)" );
}
else
{
p->data_type = FLOATING;
}
// Get the range and default values.
p->max = manager->getUpperBound( m_key, port );
if( p->max == NOHINT )
{
p->max = p->name.toUpper() == "GAIN" ? 10.0f :
1.0f;
}
if( manager->areHintsSampleRateDependent(
m_key, port ) )
{
p->max *= m_maxSampleRate;
}
p->min = manager->getLowerBound( m_key, port );
if( p->min == NOHINT )
{
p->min = 0.0f;
}
if( manager->areHintsSampleRateDependent(
m_key, port ) )
{
p->min *= m_maxSampleRate;
}
p->def = manager->getDefaultSetting( m_key, port );
if( p->def == NOHINT )
{
if( p->data_type != TOGGLED )
{
p->def = ( p->min + p->max ) / 2.0f;
}
else
{
p->def = 1.0f;
}
}
else if( manager->areHintsSampleRateDependent( m_key, port ) )
{
p->def *= m_maxSampleRate;
}
p->max *= p->scale;
p->min *= p->scale;
p->def *= p->scale;
p->value = p->def;
ports.append( p );
// For convenience, keep a separate list of the ports that are used
// to control the processors.
if( p->rate == AUDIO_RATE_INPUT ||
p->rate == CONTROL_RATE_INPUT )
{
p->control_id = m_portControls.count();
m_portControls.append( p );
}
}
m_ports.append( ports );
}
// Instantiate the processing units.
m_descriptor = manager->getDescriptor( m_key );
if( m_descriptor == NULL )
{
QMessageBox::warning( 0, "Effect",
"Can't get LADSPA descriptor function: " + m_key.second,
QMessageBox::Ok, QMessageBox::NoButton );
setOkay( FALSE );
return;
}
if( m_descriptor->run == NULL )
{
QMessageBox::warning( 0, "Effect",
"Plugin has no processor: " + m_key.second,
QMessageBox::Ok, QMessageBox::NoButton );
setDontRun( TRUE );
}
for( ch_cnt_t proc = 0; proc < processorCount(); proc++ )
{
LADSPA_Handle effect = manager->instantiate( m_key,
m_maxSampleRate );
if( effect == NULL )
{
QMessageBox::warning( 0, "Effect",
"Can't get LADSPA instance: " + m_key.second,
QMessageBox::Ok, QMessageBox::NoButton );
setOkay( FALSE );
return;
}
m_handles.append( effect );
}
// Connect the ports.
for( ch_cnt_t proc = 0; proc < processorCount(); proc++ )
{
for( int port = 0; port < m_portCount; port++ )
{
port_desc_t * pp = m_ports.at( proc ).at( port );
if( !manager->connectPort( m_key,
m_handles[proc],
port,
pp->buffer ) )
{
QMessageBox::warning( 0, "Effect",
"Failed to connect port: " + m_key.second,
QMessageBox::Ok, QMessageBox::NoButton );
setDontRun( TRUE );
return;
}
}
}
// Activate the processing units.
for( ch_cnt_t proc = 0; proc < processorCount(); proc++ )
{
manager->activate( m_key, m_handles[proc] );
}
m_controls = new LadspaControls( this );
}
void LadspaEffect::pluginDestruction()
{
if( !isOkay() )
{
return;
}
delete m_controls;
for( ch_cnt_t proc = 0; proc < processorCount(); proc++ )
{
ladspa2LMMS * manager = engine::getLADSPAManager();
manager->deactivate( m_key, m_handles[proc] );
manager->cleanup( m_key, m_handles[proc] );
for( int port = 0; port < m_portCount; port++ )
{
port_desc_t * pp = m_ports.at( proc ).at( port );
delete[] pp->buffer;
delete pp;
}
m_ports[proc].clear();
}
m_ports.clear();
m_handles.clear();
m_portControls.clear();
}
static QMap<QString, sample_rate_t> __buggy_plugins;
sample_rate_t LadspaEffect::maxSamplerate( const QString & _name )
{
if( __buggy_plugins.isEmpty() )
{
__buggy_plugins["C* AmpVTS"] = 88200;
__buggy_plugins["Chorus2"] = 44100;
__buggy_plugins["Notch Filter"] = 96000;
__buggy_plugins["Freeverb"] = 44100;
__buggy_plugins["TAP Reflector"] = 192000;
}
if( __buggy_plugins.contains( _name ) )
{
return( __buggy_plugins[_name] );
}
return( engine::mixer()->processingSampleRate() );
}
extern "C"
{
// necessary for getting instance out of shared lib
Plugin * PLUGIN_EXPORT lmms_plugin_main( Model * _parent, void * _data )
{
return new LadspaEffect( _parent,
static_cast<const Plugin::Descriptor::SubPluginFeatures::Key *>(
_data ) );
}
}
#include "moc_LadspaEffect.cxx"

View File

@@ -0,0 +1,88 @@
/*
* LadspaEffect.h - class for handling LADSPA effect plugins
*
* Copyright (c) 2006-2008 Danny McRae <khjklujn/at/users.sourceforge.net>
* Copyright (c) 2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* 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 _LADSPA_EFFECT_H
#define _LADSPA_EFFECT_H
#include <QtCore/QMutex>
#include "Effect.h"
#include "LadspaBase.h"
#include "LadspaControls.h"
typedef QVector<port_desc_t *> multi_proc_t;
class LadspaEffect : public Effect
{
Q_OBJECT
public:
LadspaEffect( Model * _parent,
const Descriptor::SubPluginFeatures::Key * _key );
virtual ~LadspaEffect();
virtual bool processAudioBuffer( sampleFrame * _buf,
const fpp_t _frames );
void setControl( int _control, LADSPA_Data _data );
virtual EffectControls * controls()
{
return m_controls;
}
inline const multi_proc_t & getPortControls()
{
return m_portControls;
}
private slots:
void changeSampleRate();
private:
void pluginInstantiation();
void pluginDestruction();
static sample_rate_t maxSamplerate( const QString & _name );
QMutex m_pluginMutex;
LadspaControls * m_controls;
sample_rate_t m_maxSampleRate;
ladspa_key_t m_key;
int m_portCount;
const LADSPA_Descriptor * m_descriptor;
QVector<LADSPA_Handle> m_handles;
QVector<multi_proc_t> m_ports;
multi_proc_t m_portControls;
} ;
#endif

View File

@@ -0,0 +1,171 @@
/*
* LadspaSubPluginFeatures.cpp - derivation from
* Plugin::Descriptor::SubPluginFeatures for
* hosting LADSPA-plugins
*
* Copyright (c) 2006-2007 Danny McRae <khjklujn/at/users.sourceforge.net>
* Copyright (c) 2006-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* 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 <QtGui/QHBoxLayout>
#include <QtGui/QLabel>
#include "LadspaSubPluginFeatures.h"
#include "AudioDevice.h"
#include "engine.h"
#include "ladspa_2_lmms.h"
#include "LadspaBase.h"
#include "Mixer.h"
LadspaSubPluginFeatures::LadspaSubPluginFeatures( Plugin::PluginTypes _type ) :
SubPluginFeatures( _type )
{
}
void LadspaSubPluginFeatures::fillDescriptionWidget( QWidget * _parent,
const Key * _key ) const
{
const ladspa_key_t & lkey = subPluginKeyToLadspaKey( _key );
ladspa2LMMS * lm = engine::getLADSPAManager();
QLabel * label = new QLabel( _parent );
label->setText( QWidget::tr( "Name: " ) + lm->getName( lkey ) );
QLabel* fileInfo = new QLabel( _parent );
fileInfo->setText( QWidget::tr( "File: %1" ).arg( lkey.first ) );
QWidget * maker = new QWidget( _parent );
QHBoxLayout * l = new QHBoxLayout( maker );
l->setMargin( 0 );
l->setSpacing( 0 );
QLabel * maker_label = new QLabel( maker );
maker_label->setText( QWidget::tr( "Maker: " ) );
maker_label->setAlignment( Qt::AlignTop );
QLabel * maker_content = new QLabel( maker );
maker_content->setText( lm->getMaker( lkey ) );
maker_content->setWordWrap( true );
l->addWidget( maker_label );
l->addWidget( maker_content, 1 );
QWidget * copyright = new QWidget( _parent );
l = new QHBoxLayout( copyright );
l->setMargin( 0 );
l->setSpacing( 0 );
copyright->setMinimumWidth( _parent->minimumWidth() );
QLabel * copyright_label = new QLabel( copyright );
copyright_label->setText( QWidget::tr( "Copyright: " ) );
copyright_label->setAlignment( Qt::AlignTop );
QLabel * copyright_content = new QLabel( copyright );
copyright_content->setText( lm->getCopyright( lkey ) );
copyright_content->setWordWrap( true );
l->addWidget( copyright_label );
l->addWidget( copyright_content, 1 );
QLabel * requiresRealTime = new QLabel( _parent );
requiresRealTime->setText( QWidget::tr( "Requires Real Time: " ) +
( lm->hasRealTimeDependency( lkey ) ?
QWidget::tr( "Yes" ) :
QWidget::tr( "No" ) ) );
QLabel * realTimeCapable = new QLabel( _parent );
realTimeCapable->setText( QWidget::tr( "Real Time Capable: " ) +
( lm->isRealTimeCapable( lkey ) ?
QWidget::tr( "Yes" ) :
QWidget::tr( "No" ) ) );
QLabel * inplaceBroken = new QLabel( _parent );
inplaceBroken->setText( QWidget::tr( "In Place Broken: " ) +
( lm->isInplaceBroken( lkey ) ?
QWidget::tr( "Yes" ) :
QWidget::tr( "No" ) ) );
QLabel * channelsIn = new QLabel( _parent );
channelsIn->setText( QWidget::tr( "Channels In: " ) +
QString::number( lm->getDescription( lkey )->inputChannels ) );
QLabel * channelsOut = new QLabel( _parent );
channelsOut->setText( QWidget::tr( "Channels Out: " ) +
QString::number( lm->getDescription( lkey )->outputChannels ) );
}
void LadspaSubPluginFeatures::listSubPluginKeys(
const Plugin::Descriptor * _desc, KeyList & _kl ) const
{
ladspa2LMMS * lm = engine::getLADSPAManager();
l_sortable_plugin_t plugins;
switch( m_type )
{
case Plugin::Instrument:
plugins = lm->getInstruments();
break;
case Plugin::Effect:
plugins = lm->getValidEffects();
//plugins += lm->getInvalidEffects();
break;
case Plugin::Tool:
plugins = lm->getAnalysisTools();
break;
case Plugin::Other:
plugins = lm->getOthers();
break;
default:
break;
}
for( l_sortable_plugin_t::const_iterator it = plugins.begin();
it != plugins.end(); ++it )
{
if( lm->getDescription( ( *it ).second )->inputChannels <=
engine::mixer()->audioDev()->channels() )
{
_kl.push_back( ladspaKeyToSubPluginKey( _desc, ( *it ).first, ( *it ).second ) );
}
}
}
ladspa_key_t LadspaSubPluginFeatures::subPluginKeyToLadspaKey(
const Key * _key )
{
QString file = _key->attributes["file"].toLower();
return( ladspa_key_t( file.remove( QRegExp( "\\.so$" ) ).
remove( QRegExp( "\\.dll$" ) ) +
#ifdef LMMS_BUILD_WIN32
".dll"
#else
".so"
#endif
, _key->attributes["plugin"] ) );
}

View File

@@ -0,0 +1,50 @@
/*
* LadspaSubPluginFeatures.h - derivation from
* Plugin::Descriptor::SubPluginFeatures for
* hosting LADSPA-plugins
*
* Copyright (c) 2006-2007 Danny McRae <khjklujn/at/users.sourceforge.net>
* Copyright (c) 2006-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* 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 _LADSPA_SUBPLUGIN_FEATURES_H
#define _LADSPA_SUBPLUGIN_FEATURES_H
#include "ladspa_manager.h"
#include "Plugin.h"
class LadspaSubPluginFeatures : public Plugin::Descriptor::SubPluginFeatures
{
public:
LadspaSubPluginFeatures( Plugin::PluginTypes _type );
virtual void fillDescriptionWidget( QWidget * _parent,
const Key * _key ) const;
virtual void listSubPluginKeys( const Plugin::Descriptor * _desc,
KeyList & _kl ) const;
static ladspa_key_t subPluginKeyToLadspaKey( const Key * _key );
} ;
#endif

View File

@@ -0,0 +1,16 @@
Krzysztof Foltman <wdev@foltman.com>
Hermann Meyer <brummer-@web.de>
Thor Harald Johansen <thj@thj.no>
Thorsten Wilms <t_w_@freenet.de>
Hans Baier <hansfbaier@googlemail.com>
Torben Hohn <torbenh@gmx.de>
Markus Schmidt <schmidt@boomshop.net>
Tom Szilagyi <tomszilagyi@gmail.com>
Damien Zammit <damien.zammit@gmail.com>
Christian Holschuh
Additional bugfixes/enhancement patches:
David Täht <d@teklibre.com>
Dave Robillard <dave@drobilla.net>
Alexandre Prokoudine <alexandre.prokoudine@gmail.com>
Carl Hetherington <cth@carlh.net>

View File

@@ -0,0 +1,21 @@
FILE(GLOB SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
ADD_LIBRARY(calf MODULE ${SOURCES})
INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/include"
"${CMAKE_BINARY_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}/src")
INSTALL(TARGETS calf LIBRARY DESTINATION "${PLUGIN_DIR}/ladspa")
SET_TARGET_PROPERTIES(calf PROPERTIES PREFIX "")
SET(INLINE_FLAGS "")
IF(NOT LMMS_BUILD_APPLE)
SET(INLINE_FLAGS "-finline-functions-called-once")
ENDIF(NOT LMMS_BUILD_APPLE)
SET_TARGET_PROPERTIES(calf PROPERTIES COMPILE_FLAGS "-O2 -finline-limit=80 -finline-functions ${INLINE_FLAGS}")
IF(LMMS_BUILD_WIN32)
ADD_CUSTOM_COMMAND(TARGET calf POST_BUILD COMMAND "${STRIP}" "\"${CMAKE_CURRENT_BINARY_DIR}/calf.dll\"")
ENDIF(LMMS_BUILD_WIN32)
IF(NOT LMMS_BUILD_APPLE)
SET_TARGET_PROPERTIES(calf PROPERTIES LINK_FLAGS "${LINK_FLAGS} -shared -Wl,-no-undefined")
ENDIF(NOT LMMS_BUILD_APPLE)

View File

@@ -0,0 +1,504 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

View File

@@ -0,0 +1,220 @@
Version 0.0.60.0 (unreleased)
+ Awesome new bitmap-based GUI by Markus Schmidt
+ New plugins by Markus Schmidt:
* several EQs (5, 8, 12 bands)
* new compressors (sidechain, multiband, deesser)
* new distortion plugins (based on code by Tom Szilagyi)
* amplitude modulator plugin (pulsator)
+ New experimental plugin - a simple wrapper for Fluidsynth
+ JACK host: save/load of sessions
+ Vintage Delay: fix another reinitialisation bug that caused,
noise bursts on enable/disable, add Width and LR/RL modes
+ many improvements to Monosynth:
* modulation matrix (not compatible with all plugin standards yet)
* PWM in both oscillators
* stretch (pseudo-hard-sync) for oscillator 1
* detune scaling (depending on pitch)
* second envelope
+ envelopes now have an extra stage called 'Fade': when enabled,
it replaces Sustain with either ramp down to 0% or ramp up to 100%
+ more options in the build system (LASH use can now be disabled)
+ support for LADISH level 1 in calfjackhost (SIGUSR1-triggered Save)
+ uses more recent LV2 extensions (external UI, persist and others)
+ many bugfixes
- removed small plugins - if anyone's interested, please use the old code
in some new project
Version 0.0.18.6
+ LADSPA: do not delete singletons after .so is unloaded
+ Rotary speaker: fix spelling of plugin class
Version 0.0.18.5
+ Vintage Delay: clear buffer on startup and reactivation
+ GUI: fix dodgy icons
+ JACK host: fix a problem with numeric variant of -M option and the new
versions of JACK
Version 0.0.18.4
+ Framework: gcc-4.4 compilation fix (Orcan Ogetbil)
Version 0.0.18.3
+ Framework: do not use x86 assembler code on non-x86 platforms
+ Monosynth, Organ: fix serious audio quality issues
+ Monosynth: implement inertia for cutoff knob and pitch bend, make
pitch bend range adjustable
+ Organ: fix polyphony limit bug
Version 0.0.18.2
+ Organ: fix voice stealing of released notes, sort out GUI, add quadratic
mode for amplitude envelope (enabled by default) - sounds more natural
+ Monosynth: fix the bug that caused JACK to kick the client out due
to precalculating waves in a completely wrong place, fix portamento
for off-stack notes
+ Presets: 3 new presets for Organ, 4 for Monosynth, 2 for Reverb
Version 0.0.18.1
+ Filter: fixed subtle redraw bugs
+ Icons: fixed packaging-incompatible paths
Version 0.0.18
+ Filterclavier: new plugin (a MIDI controlled filter) by Hans Baier
+ DSSI: added a basic implementation of live graphs. The graphs have a
limited resolution (128 data points), and are rather inefficient
(as the graph data need to be transmitted via OSC to a different
process), but it's better than nothing
+ GUI: Torben Hohn's drawing optimizations (critical for Intel graphics
cards, but should also reduce CPU usage on other hardware)
+ Phaser: added frequency response graph
+ JACK host: discontinue the broken option -p; allow giving preset names
after a colon sign (reverb:DiscoVerb instead of -p DiscoVerb reverb)
+ Reverb: less modulation; tone controls; 2 more room types
+ MultiChorus: add double bandpass filter on input
+ GUI: added frequency grid
+ Organ: added progress reporting on load (works with JACK host and LV2)
+ JACK host: use sensible port names (possibly breaking new LASH sessions)
+ Organ: added polyphony limit
+ Small plugins: added support for polymorphic port extension to allow
the same plugins to be used for control and audio signals
+ DSSI: renamed all the plugins from "plugin LADSPA" to "plugin DSSI"
+ LADSPA: more reasonable default value hints, fixed locale issue in LRDF
+ JACK host: added icons by Thorsten Wilms (thanks!)
+ Organ, Monosynth: better memory usage
+ LV2: attempt at supporting configure-like parameters (key mapping curve
in Organ) by the new String Port extension
+ AutoHell: header files are not installed anymore (they are of little
use anyway)
+ AutoHell: configure script prints if --enable-experimental was specified
Version 0.0.17
+ Compressor: new plugin by Thor Harald Johansen
+ GUI: control improvements (new LED control, improved VU meter, XML
improvements, line graph with dots and grid lines - no legend yet), move
autolayout code from the plugin libraries to makerdf executable,
+ Most plugins: use custom GUI layouts instead of autogenerated ones
+ Most plugins: add dry amount (for aux bus type uses)
+ Flanger, Filter, MultiChorus: added live graphs displaying frequency
response and (in case of MultiChorus) LFO positions
+ LV2 GUI: added a way to display live graphs in Ardour and Zynjacku/LV2Rack
(only works when the plugin and the GUI are in the same process)
+ Framework: general improvements/cleanups to reduce the chance of the
kind of errors that were introduced in 0.0.16 and reduce dependencies
+ Monosynth: removed soft clipper on output
Version 0.0.16.3
+ Fixed compilation without LV2 core installed
Version 0.0.16.2
+ Fixed DSSI GUI for MultiChorus
+ Fixed LV2 GUI for MultiChorus
+ Make knob control mouse wheel handling work better in Ingen
Version 0.0.16
+ New MultiChorus plugin (stereo multitap chorus with maximum of 8 voices)
+ Experimental set of plugins for modular synthesizers like Ingen by
Dave Robillard (enabled using --enable-experimental option in configure
script)
+ Minor improvements to other plugins (like Rotary Speaker)
+ More work on API documentation
Version 0.0.15
+ Organ: new percussive section, using 2-operator FM synthesis for
monophonic or polyphonic percussive attack; added global transpose and
detune; rearrangement of controls between sections
+ Rotary Speaker: another attempt at making it useful (thanks FishB8)
+ JACK host: eliminate deadlock on exit
+ GUI: bipolar knobs now have a "dead zone" (magnet) in the middle point
+ GUI: dragging a knob with SHIFT held allows for fine adjustments
+ GUI: new controls - curve editor and keyboard
+ LV2: improved extension support (supports my "extended port properties"
extension now)
+ Added some API documentation
Version 0.0.14
+ OSC: totally new OSC wrapper, to allow for realtime-safe parsing (doesn't
matter as far as functionality goes, will probably be rewritten again
anyway)
+ Everything: memory management fixes (should improve stability and
compatibility)
+ Organ: improved memory usage
+ GUI: improved bipolar knobs, added endless knobs
+ Presets: separate 'built-in' and 'user' presets (so that built-in presets
can be upgraded without affecting user's own presets)
+ Monosynth: new presets
Version 0.0.13
+ Fixed several problems related to 64-bit environments and OpenSUSE (thanks
oc2pus!)
+ Added NOCONFIGURE environment variable support to autogen.sh
Version 0.0.12
+ RotarySpeaker: work in progress; enabled by default just in case it's
useful for anyone
+ Organ: reworked to add a complete subtractive synth section, a selection
of waveform (settable on a per-drawbar basis), individual settings of
phase, detune, panning, routing for each drawbar, as well as improved(?)
percussive section and vibrato/phaser section. It is usable (and sounds
good!), but some parameters, waveform set etc. may change in future. May
take up to 100 MB of RAM due to pre-calculated bandlimited waveforms.
+ Added half-complete implementation of LV2 (including GUI and events).
+ Lots of small "polishing" kind of fixes in many places (like proper
rounding of values in the GUIs, another set of hold/sostenuto fixes etc)
Version 0.0.11
+ Fixed x86-64 bugs
+ JackHost: implemented LASH support
+ RotarySpeaker: fixed panning bug, implemented acceleration/decceleration
for "off" state
Version 0.0.10
+ First attempt at DSSI GUI, does not support some features from JACK host,
but that's inevitable because of API limitations
+ Reverb: improvements (more parameters, fixed denormals)
+ Knob: added custom support for scroll wheel (instead of one inherited from
GtkRange)
Version 0.0.9
+ started creating an XML-based GUI
+ LineGraph: new GTK+ control for displaying waveforms and filter response
graphs in Monosynth (and maybe others in future)
+ Monosynth: notch filter changes (made notch bandwidth proportional to Q,
just for fun, might be a bad idea)
+ Monosynth: more waveforms (these might be final?)
+ Monosynth: capped Sustain level to 0.999 so that decay time actually means
something with Sustain = 100% (not a great way to do it, but acceptable in
this case)
+ Monosynth: GUI refreshes less often (which means less CPU use)
+ Monosynth: less clicks on sounds using LP filter with very low cutoff
(using ramp of 256 samples instead of 64 samples as before)
+ Knob: new GTK+ control based on GtkRange, with my primitive bitmap set
(generated with Python and Cairo)
+ Organ: added a GUI too, very provisional
+ Organ: fixed Hold pedal (doesn't release the notes which are still depressed)
+ RotarySpeaker: new effect (split off Organ)
+ all: denormal fixes (still some denormals present in reverb)
+ Reverb: better time setting (decay time somewhat corresponds to -60dB
attenuation time)
+ JackHost: -M switch allows for automatic connection to JACK MIDI event source
(use -M system:midi_capture_2 or -M 2 for autoconnection to
system:midi_capture_2; of course, the short numeric form only work for
system:midi_capture_ ports)
+ JackHost: -p switch selects a preset automatically
+ JackHost: better size setting algorithm
+ JackHost: duplicate client name (causing JACK to rename the client) doesn't
break autoconnecting functionality
+ autotools configuration update (detect Cairo and require newer GTK+)
+ more presets

View File

@@ -0,0 +1,254 @@
Installation Instructions
*************************
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
Software Foundation, Inc.
Copyright (C) 2007-2008 Krzysztof Foltman
This file is free documentation; the Free Software Foundation gives
unlimited permission to copy, distribute and modify it.
Prerequisites
=============
To compile and install Calf, you need:
- POSIX-compliant operating system
- G++ version 4.0 or higher (tested with 4.1.3)
- GTK+2 headers and libraries (glib 2.10, gtk+ 2.12)
- Cairo headers and libraries
- Glade 2 headers and libraries
Optional but recommended:
- JACK header and libraries (tested with 0.109.0)
- LADSPA header
- DSSI header
- LV2 core
Basic Installation
==================
These are generic installation instructions.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. (Caching is
disabled by default to prevent problems with accidental use of stale
cache files.)
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You only need
`configure.ac' if you want to change it or regenerate `configure' using
a newer version of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system. If you're
using `csh' on an old version of System V, you might need to type
`sh ./configure' instead to prevent `csh' from trying to execute
`configure' itself.
Running `configure' takes awhile. While running, it prints some
messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package.
4. Type `make install' to install the programs and any data files and
documentation.
5. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that the
`configure' script does not know about. Run `./configure --help' for
details on some of the pertinent environment variables.
You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here
is an example:
./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you must use a version of `make' that
supports the `VPATH' variable, such as GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'.
If you have to use a `make' that does not support the `VPATH'
variable, you have to compile the package for one architecture at a
time in the source code directory. After you have installed the
package for one architecture, use `make distclean' before reconfiguring
for another architecture.
Installation Names
==================
By default, `make install' installs the package's commands under
`/usr/local/bin', include files under `/usr/local/include', etc. You
can specify an installation prefix other than `/usr/local' by giving
`configure' the option `--prefix=PREFIX'.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
PREFIX as the prefix for installing programs and libraries.
Documentation and other data files still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=DIR' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them.
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Optional Features
=================
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Specifying the System Type
==========================
There may be some features `configure' cannot figure out automatically,
but needs to determine by the type of machine the package will run on.
Usually, assuming the package is built to be run on the _same_
architectures, `configure' can figure that out, but if it prints a
message saying it cannot guess the machine type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the option `--target=TYPE' to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share, you
can create a site shell script called `config.site' that gives default
values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to `configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script). Here is a another example:
/bin/bash ./configure CONFIG_SHELL=/bin/bash
Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
configuration-related scripts to be executed by `/bin/bash'.
`configure' Invocation
======================
`configure' recognizes the following options to control how it operates.
`--help'
`-h'
Print a summary of the options to `configure', and exit.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.

View File

View File

@@ -0,0 +1,49 @@
Calf is a pack of audio plugins - effects and instruments, currently in
development. The goal is to create a set of plugins using decent algorithms
and parameter settings, available in a form which is compatible with as many
open source applications as possible.
How to use Calf plugins:
* LADSPA plugins
Calf is installed as calf.so library in your LADSPA directory (typically
/usr/lib/ladspa). It means that typical LADSPA host should be able to find
Calf's plugins.
* DSSI plugins
Calf .so module is also installed in your DSSI plugin directory, which means
your DSSI host (like jack-dssi-host or rosegarden) should find it and
include its plugins in the plugin list.
* JACK client application
You can also use Calf plugins as separate applications, connecting to other
applications using JACK Audio Connection Kit (version 0.103 or newer is
required). To run the client, type:
calfjackhost monosynth !
(! means "connect", last "!" means "connect to output")
Other examples:
calfjackhost monosynth ! vintagedelay ! flanger !
(runs monosynth into vintagedelay and vintagedelay into flanger, then to
output)
calfjackhost ! reverb !
(takes signal from system:capture_1 and _2, puts it through reverb, and then
sends to system:playback_1 and _2)
You can also change client name or input/output port names with command-line
options (type calfjackhost --help). Use qjackctl, patchage or jack_connect
to connect the Calf JACK client to your sound card or other applications, if
"!" is inadequate for any reason (if I didn't explain it properly, or if it
doesn't provide the connectivity options needed).
Keep in mind this project is in the early development phase. It is usable
for certain purposes, but drop me a note if you need something.

View File

@@ -0,0 +1,40 @@
1. More effects
- auto-wah (might be integrated into filter)
- envelope follower
- better reverb (more features, use nested allpasses, use 1-pole
1-zero allpass instead of fractional delays)
- dynamics processing (Thor already did the compressor)
- distortion?
- windy rotary speakery stuff
- filter: more types
2. Some instruments
- some virtual analogue thing (something larger than Monosynth)
- FM (by reusing my MMX code, or something)
3. DSP library
- profiling framework
- optimized code (the one I have now only pretends to be optimized :) )
- underflow handling
4. Wrappers
- LADSPA: proper rdf (get clearance from drobilla ;) )
- better jack host (controls etc)
- BSE
- buzztard
- Linux VST
- LV2
Message Context (for Organ)
EPP (the rest of them)
Mixing Controls
5. Organization stuff (autotools etc)
- correct compilation and installation of LADSPA plugins (current version is a hack!)
- switch to -O3
- get to work on 64-bit architectures
- i18n (gettext or whatever)

View File

@@ -0,0 +1,829 @@
/* Calf DSP Library
* Reusable audio effect classes - implementation.
*
* Copyright (C) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen and others
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include <calf/audio_fx.h>
#include <calf/giface.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
using namespace calf_plugins;
using namespace dsp;
simple_phaser::simple_phaser(int _max_stages, float *x1vals, float *y1vals)
{
max_stages = _max_stages;
x1 = x1vals;
y1 = y1vals;
set_base_frq(1000);
set_mod_depth(1000);
set_fb(0);
state = 0;
cnt = 0;
stages = 0;
set_stages(_max_stages);
}
void simple_phaser::set_stages(int _stages)
{
if (_stages > stages)
{
assert(_stages <= max_stages);
if (_stages > max_stages)
_stages = max_stages;
for (int i = stages; i < _stages; i++)
{
x1[i] = x1[stages-1];
y1[i] = y1[stages-1];
}
}
stages = _stages;
}
void simple_phaser::reset()
{
cnt = 0;
state = 0;
phase.set(0);
for (int i = 0; i < max_stages; i++)
x1[i] = y1[i] = 0;
control_step();
}
void simple_phaser::control_step()
{
cnt = 0;
int v = phase.get() + 0x40000000;
int sign = v >> 31;
v ^= sign;
// triangle wave, range from 0 to INT_MAX
double vf = (double)((v >> 16) * (1.0 / 16384.0) - 1);
float freq = base_frq * pow(2.0, vf * mod_depth / 1200.0);
freq = dsp::clip<float>(freq, 10.0, 0.49 * sample_rate);
stage1.set_ap_w(freq * (M_PI / 2.0) * odsr);
phase += dphase * 32;
for (int i = 0; i < stages; i++)
{
dsp::sanitize(x1[i]);
dsp::sanitize(y1[i]);
}
dsp::sanitize(state);
}
void simple_phaser::process(float *buf_out, float *buf_in, int nsamples)
{
for (int i=0; i<nsamples; i++) {
cnt++;
if (cnt == 32)
control_step();
float in = *buf_in++;
float fd = in + state * fb;
for (int j = 0; j < stages; j++)
fd = stage1.process_ap(fd, x1[j], y1[j]);
state = fd;
float sdry = in * gs_dry.get();
float swet = fd * gs_wet.get();
*buf_out++ = sdry + swet;
}
}
float simple_phaser::freq_gain(float freq, float sr) const
{
typedef std::complex<double> cfloat;
freq *= 2.0 * M_PI / sr;
cfloat z = 1.0 / exp(cfloat(0.0, freq)); // z^-1
cfloat p = cfloat(1.0);
cfloat stg = stage1.h_z(z);
for (int i = 0; i < stages; i++)
p = p * stg;
p = p / (cfloat(1.0) - cfloat(fb) * p);
return std::abs(cfloat(gs_dry.get_last()) + cfloat(gs_wet.get_last()) * p);
}
///////////////////////////////////////////////////////////////////////////////////
void biquad_filter_module::calculate_filter(float freq, float q, int mode, float gain)
{
if (mode <= mode_36db_lp) {
order = mode + 1;
left[0].set_lp_rbj(freq, pow(q, 1.0 / order), srate, gain);
} else if ( mode_12db_hp <= mode && mode <= mode_36db_hp ) {
order = mode - mode_12db_hp + 1;
left[0].set_hp_rbj(freq, pow(q, 1.0 / order), srate, gain);
} else if ( mode_6db_bp <= mode && mode <= mode_18db_bp ) {
order = mode - mode_6db_bp + 1;
left[0].set_bp_rbj(freq, pow(q, 1.0 / order), srate, gain);
} else { // mode_6db_br <= mode <= mode_18db_br
order = mode - mode_6db_br + 1;
left[0].set_br_rbj(freq, order * 0.1 * q, srate, gain);
}
right[0].copy_coeffs(left[0]);
for (int i = 1; i < order; i++) {
left[i].copy_coeffs(left[0]);
right[i].copy_coeffs(left[0]);
}
}
void biquad_filter_module::filter_activate()
{
for (int i=0; i < order; i++) {
left[i].reset();
right[i].reset();
}
}
void biquad_filter_module::sanitize()
{
for (int i=0; i < order; i++) {
left[i].sanitize();
right[i].sanitize();
}
}
int biquad_filter_module::process_channel(uint16_t channel_no, const float *in, float *out, uint32_t numsamples, int inmask) {
dsp::biquad_d1<float> *filter;
switch (channel_no) {
case 0:
filter = left;
break;
case 1:
filter = right;
break;
default:
assert(false);
return 0;
}
if (inmask) {
switch(order) {
case 1:
for (uint32_t i = 0; i < numsamples; i++)
out[i] = filter[0].process(in[i]);
break;
case 2:
for (uint32_t i = 0; i < numsamples; i++)
out[i] = filter[1].process(filter[0].process(in[i]));
break;
case 3:
for (uint32_t i = 0; i < numsamples; i++)
out[i] = filter[2].process(filter[1].process(filter[0].process(in[i])));
break;
}
} else {
if (filter[order - 1].empty())
return 0;
switch(order) {
case 1:
for (uint32_t i = 0; i < numsamples; i++)
out[i] = filter[0].process_zeroin();
break;
case 2:
if (filter[0].empty())
for (uint32_t i = 0; i < numsamples; i++)
out[i] = filter[1].process_zeroin();
else
for (uint32_t i = 0; i < numsamples; i++)
out[i] = filter[1].process(filter[0].process_zeroin());
break;
case 3:
if (filter[1].empty())
for (uint32_t i = 0; i < numsamples; i++)
out[i] = filter[2].process_zeroin();
else
for (uint32_t i = 0; i < numsamples; i++)
out[i] = filter[2].process(filter[1].process(filter[0].process_zeroin()));
break;
}
}
for (int i = 0; i < order; i++)
filter[i].sanitize();
return filter[order - 1].empty() ? 0 : inmask;
}
float biquad_filter_module::freq_gain(int subindex, float freq, float srate) const
{
float level = 1.0;
for (int j = 0; j < order; j++)
level *= left[j].freq_gain(freq, srate);
return level;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
void reverb::update_times()
{
switch(type)
{
case 0:
tl[0] = 397 << 16, tr[0] = 383 << 16;
tl[1] = 457 << 16, tr[1] = 429 << 16;
tl[2] = 549 << 16, tr[2] = 631 << 16;
tl[3] = 649 << 16, tr[3] = 756 << 16;
tl[4] = 773 << 16, tr[4] = 803 << 16;
tl[5] = 877 << 16, tr[5] = 901 << 16;
break;
case 1:
tl[0] = 697 << 16, tr[0] = 783 << 16;
tl[1] = 957 << 16, tr[1] = 929 << 16;
tl[2] = 649 << 16, tr[2] = 531 << 16;
tl[3] = 1049 << 16, tr[3] = 1177 << 16;
tl[4] = 473 << 16, tr[4] = 501 << 16;
tl[5] = 587 << 16, tr[5] = 681 << 16;
break;
case 2:
default:
tl[0] = 697 << 16, tr[0] = 783 << 16;
tl[1] = 957 << 16, tr[1] = 929 << 16;
tl[2] = 649 << 16, tr[2] = 531 << 16;
tl[3] = 1249 << 16, tr[3] = 1377 << 16;
tl[4] = 1573 << 16, tr[4] = 1671 << 16;
tl[5] = 1877 << 16, tr[5] = 1781 << 16;
break;
case 3:
tl[0] = 1097 << 16, tr[0] = 1087 << 16;
tl[1] = 1057 << 16, tr[1] = 1031 << 16;
tl[2] = 1049 << 16, tr[2] = 1039 << 16;
tl[3] = 1083 << 16, tr[3] = 1055 << 16;
tl[4] = 1075 << 16, tr[4] = 1099 << 16;
tl[5] = 1003 << 16, tr[5] = 1073 << 16;
break;
case 4:
tl[0] = 197 << 16, tr[0] = 133 << 16;
tl[1] = 357 << 16, tr[1] = 229 << 16;
tl[2] = 549 << 16, tr[2] = 431 << 16;
tl[3] = 949 << 16, tr[3] = 1277 << 16;
tl[4] = 1173 << 16, tr[4] = 1671 << 16;
tl[5] = 1477 << 16, tr[5] = 1881 << 16;
break;
case 5:
tl[0] = 197 << 16, tr[0] = 133 << 16;
tl[1] = 257 << 16, tr[1] = 179 << 16;
tl[2] = 549 << 16, tr[2] = 431 << 16;
tl[3] = 619 << 16, tr[3] = 497 << 16;
tl[4] = 1173 << 16, tr[4] = 1371 << 16;
tl[5] = 1577 << 16, tr[5] = 1881 << 16;
break;
}
float fDec=1000 + 2400.f * diffusion;
for (int i = 0 ; i < 6; i++) {
ldec[i]=exp(-float(tl[i] >> 16) / fDec),
rdec[i]=exp(-float(tr[i] >> 16) / fDec);
}
}
void reverb::reset()
{
apL1.reset();apR1.reset();
apL2.reset();apR2.reset();
apL3.reset();apR3.reset();
apL4.reset();apR4.reset();
apL5.reset();apR5.reset();
apL6.reset();apR6.reset();
lp_left.reset();lp_right.reset();
old_left = 0; old_right = 0;
}
void reverb::process(float &left, float &right)
{
unsigned int ipart = phase.ipart();
// the interpolated LFO might be an overkill here
int lfo = phase.lerp_by_fract_int<int, 14, int>(sine.data[ipart], sine.data[ipart+1]) >> 2;
phase += dphase;
left += old_right;
left = apL1.process_allpass_comb_lerp16(left, tl[0] - 45*lfo, ldec[0]);
left = apL2.process_allpass_comb_lerp16(left, tl[1] + 47*lfo, ldec[1]);
float out_left = left;
left = apL3.process_allpass_comb_lerp16(left, tl[2] + 54*lfo, ldec[2]);
left = apL4.process_allpass_comb_lerp16(left, tl[3] - 69*lfo, ldec[3]);
left = apL5.process_allpass_comb_lerp16(left, tl[4] + 69*lfo, ldec[4]);
left = apL6.process_allpass_comb_lerp16(left, tl[5] - 46*lfo, ldec[5]);
old_left = lp_left.process(left * fb);
sanitize(old_left);
right += old_left;
right = apR1.process_allpass_comb_lerp16(right, tr[0] - 45*lfo, rdec[0]);
right = apR2.process_allpass_comb_lerp16(right, tr[1] + 47*lfo, rdec[1]);
float out_right = right;
right = apR3.process_allpass_comb_lerp16(right, tr[2] + 54*lfo, rdec[2]);
right = apR4.process_allpass_comb_lerp16(right, tr[3] - 69*lfo, rdec[3]);
right = apR5.process_allpass_comb_lerp16(right, tr[4] + 69*lfo, rdec[4]);
right = apR6.process_allpass_comb_lerp16(right, tr[5] - 46*lfo, rdec[5]);
old_right = lp_right.process(right * fb);
sanitize(old_right);
left = out_left, right = out_right;
}
/// Distortion Module by Tom Szilagyi
///
/// This module provides a blendable saturation stage
///////////////////////////////////////////////////////////////////////////////////////////////
tap_distortion::tap_distortion()
{
is_active = false;
srate = 0;
meter = 0.f;
prev_med = prev_out = 0.f;
drive_old = blend_old = -1.f;
}
void tap_distortion::activate()
{
is_active = true;
set_params(0.f, 0.f);
}
void tap_distortion::deactivate()
{
is_active = false;
}
void tap_distortion::set_params(float blend, float drive)
{
// set distortion coeffs
if ((drive_old != drive) || (blend_old != blend)) {
rdrive = 12.0f / drive;
rbdr = rdrive / (10.5f - blend) * 780.0f / 33.0f;
kpa = D(2.0f * (rdrive*rdrive) - 1.0f) + 1.0f;
kpb = (2.0f - kpa) / 2.0f;
ap = ((rdrive*rdrive) - kpa + 1.0f) / 2.0f;
kc = kpa / D(2.0f * D(2.0f * (rdrive*rdrive) - 1.0f) - 2.0f * rdrive*rdrive);
srct = (0.1f * srate) / (0.1f * srate + 1.0f);
sq = kc*kc + 1.0f;
knb = -1.0f * rbdr / D(sq);
kna = 2.0f * kc * rbdr / D(sq);
an = rbdr*rbdr / sq;
imr = 2.0f * knb + D(2.0f * kna + 4.0f * an - 1.0f);
pwrq = 2.0f / (imr + 1.0f);
drive_old = drive;
blend_old = blend;
}
}
void tap_distortion::set_sample_rate(uint32_t sr)
{
srate = sr;
}
float tap_distortion::process(float in)
{
meter = 0.f;
float out = 0.f;
float proc = in;
float med;
if (proc >= 0.0f) {
med = (D(ap + proc * (kpa - proc)) + kpb) * pwrq;
} else {
med = (D(an - proc * (kna + proc)) + knb) * pwrq * -1.0f;
}
proc = srct * (med - prev_med + prev_out);
prev_med = M(med);
prev_out = M(proc);
out = proc;
meter = proc;
return out;
}
float tap_distortion::get_distortion_level()
{
return meter;
}
////////////////////////////////////////////////////////////////////////////////
simple_lfo::simple_lfo()
{
is_active = false;
phase = 0.f;
}
void simple_lfo::activate()
{
is_active = true;
phase = 0.f;
}
void simple_lfo::deactivate()
{
is_active = false;
}
float simple_lfo::get_value()
{
return get_value_from_phase(phase, offset) * amount;
}
float simple_lfo::get_value_from_phase(float ph, float off) const
{
float val = 0.f;
float phs = ph + off;
if (phs >= 1.0)
phs = fmod(phs, 1.f);
switch (mode) {
default:
case 0:
// sine
val = sin((phs * 360.f) * M_PI / 180);
break;
case 1:
// triangle
if(phs > 0.75)
val = (phs - 0.75) * 4 - 1;
else if(phs > 0.5)
val = (phs - 0.5) * 4 * -1;
else if(phs > 0.25)
val = 1 - (phs - 0.25) * 4;
else
val = phs * 4;
break;
case 2:
// square
val = (phs < 0.5) ? -1 : +1;
break;
case 3:
// saw up
val = phs * 2.f - 1;
break;
case 4:
// saw down
val = 1 - phs * 2.f;
break;
}
return val;
}
void simple_lfo::advance(uint32_t count)
{
//this function walks from 0.f to 1.f and starts all over again
phase += count * freq * (1.0 / srate);
if (phase >= 1.0)
phase = fmod(phase, 1.f);
}
void simple_lfo::set_phase(float ph)
{
//set the phase from outsinde
phase = fabs(ph);
if (phase >= 1.0)
phase = fmod(phase, 1.f);
}
void simple_lfo::set_params(float f, int m, float o, uint32_t sr, float a)
{
// freq: a value in Hz
// mode: sine=0, triangle=1, square=2, saw_up=3, saw_down=4
// offset: value between 0.f and 1.f to offset the lfo in time
freq = f;
mode = m;
offset = o;
srate = sr;
amount = a;
}
bool simple_lfo::get_graph(float *data, int points, cairo_iface *context) const
{
if (!is_active)
return false;
for (int i = 0; i < points; i++) {
float ph = (float)i / (float)points;
data[i] = get_value_from_phase(ph, offset) * amount;
}
return true;
}
bool simple_lfo::get_dot(float &x, float &y, int &size, cairo_iface *context) const
{
if (!is_active)
return false;
float phs = phase + offset;
if (phs >= 1.0)
phs = fmod(phs, 1.f);
x = phase;
y = get_value_from_phase(phase, offset) * amount;
return true;
}
/// Lookahead Limiter by Christian Holschuh and Markus Schmidt
lookahead_limiter::lookahead_limiter() {
is_active = false;
channels = 2;
id = 0;
buffer_size = 0;
overall_buffer_size = 0;
att = 1.f;
att_max = 1.0;
pos = 0;
delta = 0.f;
_delta = 0.f;
peak = 0.f;
over_s = 0;
over_c = 1.f;
attack = 0.005;
use_multi = false;
weight = 1.f;
_sanitize = false;
auto_release = false;
asc_active = false;
nextiter = 0;
nextlen = 0;
asc = 0.f;
asc_c = 0;
asc_pos = -1;
asc_changed = false;
asc_coeff = 1.f;
}
void lookahead_limiter::activate()
{
is_active = true;
pos = 0;
}
void lookahead_limiter::set_multi(bool set) { use_multi = set; }
void lookahead_limiter::deactivate()
{
is_active = false;
}
float lookahead_limiter::get_attenuation()
{
float a = att_max;
att_max = 1.0;
return a;
}
void lookahead_limiter::set_sample_rate(uint32_t sr)
{
srate = sr;
// rebuild buffer
overall_buffer_size = (int)(srate * (100.f / 1000.f) * channels) + channels; // buffer size attack rate multiplied by 2 channels
buffer = (float*) calloc(overall_buffer_size, sizeof(float));
memset(buffer, 0, overall_buffer_size * sizeof(float)); // reset buffer to zero
pos = 0;
nextpos = (int*) calloc(overall_buffer_size, sizeof(int));
nextdelta = (float*) calloc(overall_buffer_size, sizeof(float));
memset(nextpos, -1, overall_buffer_size * sizeof(int));
}
void lookahead_limiter::set_params(float l, float a, float r, float w, bool ar, float arc, bool d)
{
limit = l;
attack = a / 1000.f;
release = r / 1000.f;
auto_release = ar;
asc_coeff = arc;
debug = d;
weight = w;
}
void lookahead_limiter::reset() {
int bs = (int)(srate * attack * channels);
buffer_size = bs - bs % channels; // buffer size attack rate
_sanitize = true;
pos = 0;
nextpos[0] = -1;
nextlen = 0;
nextiter = 0;
delta = 0.f;
att = 1.f;
reset_asc();
}
void lookahead_limiter::reset_asc() {
asc = 0.f;
asc_c = 0;
asc_pos = pos;
asc_changed = true;
}
void lookahead_limiter::process(float &left, float &right, float * multi_buffer)
{
// PROTIP: harming paying customers enough to make them develop a competing
// product may be considered an example of a less than sound business practice.
// fill lookahead buffer
if(_sanitize) {
// if we're sanitizing (zeroing) the buffer on attack time change,
// don't write the samples to the buffer
buffer[pos] = 0.f;
buffer[pos + 1] = 0.f;
} else {
buffer[pos] = left;
buffer[pos + 1] = right;
}
// are we using multiband? get the multiband coefficient or use 1.f
float multi_coeff = (use_multi) ? multi_buffer[pos] : 1.f;
// input peak - impact higher in left or right channel?
peak = fabs(left) > fabs(right) ? fabs(left) : fabs(right);
// calc the real limit including weight and multi coeff
float _limit = limit * multi_coeff * weight;
// add an eventually appearing peak to the asc fake buffer if asc active
if(auto_release and peak > _limit) {
asc += peak;
asc_c ++;
}
if(peak > _limit or multi_coeff < 1.0) {
float _multi_coeff = 1.f;
float _peak;
// calc the attenuation needed to reduce incoming peak
float _att = std::min(_limit / peak, 1.f);
// calc a release delta from this attenuation
float _rdelta = (1.0 - _att) / (srate * release);
if(auto_release and asc_c > 0) {
// check if releasing to average level of peaks is steeper than
// releasing to 1.f
float _delta = std::max((limit * weight) / (asc_coeff * asc) * (float)asc_c - _att, 0.000001f) / (srate * release);
if(_delta < _rdelta) {
asc_active = true;
_rdelta = _delta;
}
}
// calc the delta for walking to incoming peak attenuation
float _delta = (_limit / peak - att) / buffer_size * channels;
if(_delta < delta) {
// is the delta more important than the actual one?
// if so, we can forget about all stored deltas (because they can't
// be more important - we already checked that earlier) and use this
// delta now. and we have to create a release delta in nextpos buffer
nextpos[0] = pos;
nextpos[1] = -1;
nextdelta[0] = _rdelta;
nextlen = 1;
nextiter = 0;
delta = _delta;
} else {
// we have a peak on input its delta is less important than the
// actual delta. But what about the stored deltas we're following?
bool _found = false;
int i = 0;
for(i = nextiter; i < nextiter + nextlen; i++) {
// walk through our nextpos buffer
int j = i % buffer_size;
// calculate a delta for the next stored peak
// are we using multiband? then get the multi_coeff for the
// stored position
_multi_coeff = (use_multi) ? multi_buffer[nextpos[j]] : 1.f;
// is the left or the right channel on this position more
// important?
_peak = fabs(buffer[nextpos[j]]) > fabs(buffer[nextpos[j] + 1]) ? fabs(buffer[nextpos[j]]) : fabs(buffer[nextpos[j] + 1]);
// calc a delta to use to reach our incoming peak from the
// stored position
_delta = (_limit / peak - (limit * _multi_coeff * weight) / _peak) / (((buffer_size - nextpos[j] + pos) % buffer_size) / channels);
if(_delta < nextdelta[j]) {
// if the buffered delta is more important than the delta
// used to reach our peak from the stored position, store
// the new delta at that position and stop the loop
nextdelta[j] = _delta;
_found = true;
break;
}
}
if(_found) {
// there was something more important in the next-buffer.
// throw away any position and delta after the important
// position and add a new release delta
nextlen = i - nextiter + 1;
nextpos[(nextiter + nextlen) % buffer_size] = pos;
nextdelta[(nextiter + nextlen) % buffer_size] = _rdelta;
// set the next following position value to -1 (cleaning up the
// nextpos buffer)
nextpos[(nextiter + nextlen + 1) % buffer_size] = -1;
// and raise the length of our nextpos buffer for keeping the
// release value
nextlen ++;
}
}
}
// switch left and right pointers in buffer to output position
left = buffer[(pos + channels) % buffer_size];
right = buffer[(pos + channels + 1) % buffer_size];
// if a peak leaves the buffer, remove it from asc fake buffer
// but only if we're not sanitizing asc buffer
float _peak = fabs(left) > fabs(right) ? fabs(left) : fabs(right);
float _multi_coeff = (use_multi) ? multi_buffer[(pos + channels) % buffer_size] : 1.f;
if(pos == asc_pos and !asc_changed) {
asc_pos = -1;
}
if(auto_release and asc_pos == -1 and _peak > (limit * weight * _multi_coeff)) {
asc -= _peak;
asc_c --;
}
// change the attenuation level
att += delta;
// ...and calculate outpout from it
left *= att;
right *= att;
if((pos + channels) % buffer_size == nextpos[nextiter]) {
// if we reach a buffered position, change the actual delta and erase
// this (the first) element from nextpos and nextdelta buffer
delta = nextdelta[nextiter];
nextlen = (nextlen - 1) % buffer_size;
nextpos[nextiter] = -1;
nextiter = (nextiter + 1) % buffer_size;
}
if (att > 1.0f) {
// release time seems over, reset attenuation and delta
att = 1.0f;
delta = 0.0f;
}
// main limiting party is over, let's cleanup the puke
if(_sanitize) {
// we're sanitizing? then send 0.f as output
left = 0.f;
right = 0.f;
}
// security personnel pawing your values
if(att <= 0.f) {
// if this happens we're doomed!!
// may happen on manually lowering attack
att = 0.0000000000001;
delta = (1.0f - att) / (srate * release);
}
if(att != 1.f and 1 - att < 0.0000000000001) {
// denormalize att
att = 1.f;
}
if(delta != 0.f and fabs(delta) < 0.00000000000001) {
// denormalize delta
delta = 0.f;
}
// post treatment (denormal, limit)
denormal(&left);
denormal(&right);
// store max attenuation for meter output
att_max = (att < att_max) ? att : att_max;
// step forward in our sample ring buffer
pos = (pos + channels) % buffer_size;
// sanitizing is always done after a full cycle through the lookahead buffer
if(_sanitize and pos == 0) _sanitize = false;
asc_changed = false;
}
bool lookahead_limiter::get_asc() {
if(!asc_active) return false;
asc_active = false;
return true;
}

View File

@@ -0,0 +1,625 @@
/* Calf DSP Library
* Reusable audio effect classes.
*
* Copyright (C) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen and others
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#ifndef CALF_AUDIOFX_H
#define CALF_AUDIOFX_H
#include "biquad.h"
#include "delay.h"
#include "fixed_point.h"
#include "inertia.h"
#include "onepole.h"
#include <complex>
namespace calf_plugins {
struct cairo_iface;
};
namespace dsp {
#if 0
}; to keep editor happy
#endif
/**
* Audio effect base class. Not really useful until it gets more developed.
*/
class audio_effect
{
public:
virtual void setup(int sample_rate)=0;
virtual ~audio_effect() {}
};
class modulation_effect: public audio_effect
{
protected:
int sample_rate;
float rate, wet, dry, odsr;
gain_smoothing gs_wet, gs_dry;
public:
fixed_point<unsigned int, 20> phase, dphase;
float get_rate() const {
return rate;
}
void set_rate(float rate) {
this->rate = rate;
dphase = rate/sample_rate*4096;
}
float get_wet() const {
return wet;
}
void set_wet(float wet) {
this->wet = wet;
gs_wet.set_inertia(wet);
}
float get_dry() const {
return dry;
}
void set_dry(float dry) {
this->dry = dry;
gs_dry.set_inertia(dry);
}
void reset_phase(float req_phase)
{
phase = req_phase * 4096.0;
}
void inc_phase(float req_phase)
{
phase += fixed_point<unsigned int, 20>(req_phase * 4096.0);
}
void setup(int sample_rate)
{
this->sample_rate = sample_rate;
this->odsr = 1.0 / sample_rate;
phase = 0;
set_rate(get_rate());
}
};
/**
* A monophonic phaser. If you want stereo, combine two :)
* Also, gave up on using template args for signal type.
*/
class simple_phaser: public modulation_effect
{
protected:
float base_frq, mod_depth, fb;
float state;
int cnt, stages, max_stages;
dsp::onepole<float, float> stage1;
float *x1, *y1;
public:
simple_phaser(int _max_stages, float *x1vals, float *y1vals);
float get_base_frq() const {
return base_frq;
}
void set_base_frq(float _base_frq) {
base_frq = _base_frq;
}
int get_stages() const {
return stages;
}
void set_stages(int _stages);
float get_mod_depth() const {
return mod_depth;
}
void set_mod_depth(float _mod_depth) {
mod_depth = _mod_depth;
}
float get_fb() const {
return fb;
}
void set_fb(float fb) {
this->fb = fb;
}
virtual void setup(int sample_rate) {
modulation_effect::setup(sample_rate);
reset();
}
void reset();
void control_step();
void process(float *buf_out, float *buf_in, int nsamples);
float freq_gain(float freq, float sr) const;
};
/**
* Base class for chorus and flanger. Wouldn't be needed if it wasn't
* for odd behaviour of GCC when deriving templates from template
* base classes (not seeing fields from base classes!).
*/
class chorus_base: public modulation_effect
{
protected:
int min_delay_samples, mod_depth_samples;
float min_delay, mod_depth;
sine_table<int, 4096, 65536> sine;
public:
float get_min_delay() const {
return min_delay;
}
void set_min_delay(float min_delay) {
this->min_delay = min_delay;
this->min_delay_samples = (int)(min_delay * 65536.0 * sample_rate);
}
float get_mod_depth() const {
return mod_depth;
}
void set_mod_depth(float mod_depth) {
this->mod_depth = mod_depth;
// 128 because it's then multiplied by (hopefully) a value of 32768..-32767
this->mod_depth_samples = (int)(mod_depth * 32.0 * sample_rate);
}
};
/**
* Single-tap chorus without feedback.
* Perhaps MaxDelay should be a bit longer!
*/
template<class T, int MaxDelay=512>
class simple_chorus: public chorus_base
{
protected:
simple_delay<MaxDelay,T> delay;
public:
simple_chorus() {
rate = 0.63f;
dry = 0.5f;
wet = 0.5f;
min_delay = 0.005f;
mod_depth = 0.0025f;
setup(44100);
}
void reset() {
delay.reset();
}
virtual void setup(int sample_rate) {
modulation_effect::setup(sample_rate);
delay.reset();
set_min_delay(get_min_delay());
set_mod_depth(get_mod_depth());
}
template<class OutIter, class InIter>
void process(OutIter buf_out, InIter buf_in, int nsamples) {
int mds = min_delay_samples + mod_depth_samples * 1024 + 2*65536;
int mdepth = mod_depth_samples;
for (int i=0; i<nsamples; i++) {
phase += dphase;
unsigned int ipart = phase.ipart();
float in = *buf_in++;
int lfo = phase.lerp_by_fract_int<int, 14, int>(sine.data[ipart], sine.data[ipart+1]);
int v = mds + (mdepth * lfo >> 6);
// if (!(i & 7)) printf("%d\n", v);
int ifv = v >> 16;
delay.put(in);
T fd; // signal from delay's output
delay.get_interp(fd, ifv, (v & 0xFFFF)*(1.0/65536.0));
T sdry = in * gs_dry.get();
T swet = fd * gs_wet.get();
*buf_out++ = sdry + swet;
}
}
};
/**
* Single-tap flanger (chorus plus feedback).
*/
template<class T, int MaxDelay=1024>
class simple_flanger: public chorus_base
{
protected:
simple_delay<MaxDelay,T> delay;
float fb;
int last_delay_pos, last_actual_delay_pos;
int ramp_pos, ramp_delay_pos;
public:
simple_flanger()
: fb(0) {}
void reset() {
delay.reset();
last_delay_pos = last_actual_delay_pos = ramp_delay_pos = 0;
ramp_pos = 1024;
}
virtual void setup(int sample_rate) {
this->sample_rate = sample_rate;
this->odsr = 1.0 / sample_rate;
delay.reset();
phase = 0;
set_rate(get_rate());
set_min_delay(get_min_delay());
}
float get_fb() const {
return fb;
}
void set_fb(float fb) {
this->fb = fb;
}
template<class OutIter, class InIter>
void process(OutIter buf_out, InIter buf_in, int nsamples) {
if (!nsamples)
return;
int mds = this->min_delay_samples + this->mod_depth_samples * 1024 + 2 * 65536;
int mdepth = this->mod_depth_samples;
int delay_pos;
unsigned int ipart = this->phase.ipart();
int lfo = phase.lerp_by_fract_int<int, 14, int>(this->sine.data[ipart], this->sine.data[ipart+1]);
delay_pos = mds + (mdepth * lfo >> 6);
if (delay_pos != last_delay_pos || ramp_pos < 1024)
{
if (delay_pos != last_delay_pos) {
// we need to ramp from what the delay tap length actually was,
// not from old (ramp_delay_pos) or desired (delay_pos) tap length
ramp_delay_pos = last_actual_delay_pos;
ramp_pos = 0;
}
int64_t dp = 0;
for (int i=0; i<nsamples; i++) {
float in = *buf_in++;
T fd; // signal from delay's output
dp = (((int64_t)ramp_delay_pos) * (1024 - ramp_pos) + ((int64_t)delay_pos) * ramp_pos) >> 10;
ramp_pos++;
if (ramp_pos > 1024) ramp_pos = 1024;
this->delay.get_interp(fd, dp >> 16, (dp & 0xFFFF)*(1.0/65536.0));
sanitize(fd);
T sdry = in * this->dry;
T swet = fd * this->wet;
*buf_out++ = sdry + swet;
this->delay.put(in+fb*fd);
this->phase += this->dphase;
ipart = this->phase.ipart();
lfo = phase.lerp_by_fract_int<int, 14, int>(this->sine.data[ipart], this->sine.data[ipart+1]);
delay_pos = mds + (mdepth * lfo >> 6);
}
last_actual_delay_pos = dp;
}
else {
for (int i=0; i<nsamples; i++) {
float in = *buf_in++;
T fd; // signal from delay's output
this->delay.get_interp(fd, delay_pos >> 16, (delay_pos & 0xFFFF)*(1.0/65536.0));
sanitize(fd);
T sdry = in * this->gs_dry.get();
T swet = fd * this->gs_wet.get();
*buf_out++ = sdry + swet;
this->delay.put(in+fb*fd);
this->phase += this->dphase;
ipart = this->phase.ipart();
lfo = phase.lerp_by_fract_int<int, 14, int>(this->sine.data[ipart], this->sine.data[ipart+1]);
delay_pos = mds + (mdepth * lfo >> 6);
}
last_actual_delay_pos = delay_pos;
}
last_delay_pos = delay_pos;
}
float freq_gain(float freq, float sr) const
{
typedef std::complex<double> cfloat;
freq *= 2.0 * M_PI / sr;
cfloat z = 1.0 / exp(cfloat(0.0, freq)); // z^-1
float ldp = last_delay_pos / 65536.0;
float fldp = floor(ldp);
cfloat zn = std::pow(z, fldp); // z^-N
cfloat zn1 = zn * z; // z^-(N+1)
// simulate a lerped comb filter - H(z) = 1 / (1 + fb * (lerp(z^-N, z^-(N+1), fracpos))), N = int(pos), fracpos = pos - int(pos)
cfloat delayed = zn + (zn1 - zn) * cfloat(ldp - fldp);
cfloat h = cfloat(delayed) / (cfloat(1.0) - cfloat(fb) * delayed);
// mix with dry signal
float v = std::abs(cfloat(gs_dry.get_last()) + cfloat(gs_wet.get_last()) * h);
return v;
}
};
/**
* A classic allpass loop reverb with modulated allpass filter.
* Just started implementing it, so there is no control over many
* parameters.
*/
class reverb: public audio_effect
{
simple_delay<2048, float> apL1, apL2, apL3, apL4, apL5, apL6;
simple_delay<2048, float> apR1, apR2, apR3, apR4, apR5, apR6;
fixed_point<unsigned int, 25> phase, dphase;
sine_table<int, 128, 10000> sine;
onepole<float> lp_left, lp_right;
float old_left, old_right;
int type;
float time, fb, cutoff, diffusion;
int tl[6], tr[6];
float ldec[6], rdec[6];
int sr;
public:
reverb()
{
phase = 0.0;
time = 1.0;
cutoff = 9000;
type = 2;
diffusion = 1.f;
setup(44100);
}
virtual void setup(int sample_rate) {
sr = sample_rate;
set_time(time);
set_cutoff(cutoff);
phase = 0.0;
dphase = 0.5*128/sr;
update_times();
}
void update_times();
float get_time() const {
return time;
}
void set_time(float time) {
this->time = time;
// fb = pow(1.0f/4096.0f, (float)(1700/(time*sr)));
fb = 1.0 - 0.3 / (time * sr / 44100.0);
}
float get_type() const {
return type;
}
void set_type(int type) {
this->type = type;
update_times();
}
float get_diffusion() const {
return diffusion;
}
void set_diffusion(float diffusion) {
this->diffusion = diffusion;
update_times();
}
void set_type_and_diffusion(int type, float diffusion) {
this->type = type;
this->diffusion = diffusion;
update_times();
}
float get_fb() const
{
return this->fb;
}
void set_fb(float fb)
{
this->fb = fb;
}
float get_cutoff() const {
return cutoff;
}
void set_cutoff(float cutoff) {
this->cutoff = cutoff;
lp_left.set_lp(cutoff,sr);
lp_right.set_lp(cutoff,sr);
}
void reset();
void process(float &left, float &right);
void extra_sanitize()
{
lp_left.sanitize();
lp_right.sanitize();
}
};
class filter_module_iface
{
public:
virtual void calculate_filter(float freq, float q, int mode, float gain = 1.0) = 0;
virtual void filter_activate() = 0;
virtual void sanitize() = 0;
virtual int process_channel(uint16_t channel_no, const float *in, float *out, uint32_t numsamples, int inmask) = 0;
virtual float freq_gain(int subindex, float freq, float srate) const = 0;
virtual ~filter_module_iface() {}
};
class biquad_filter_module: public filter_module_iface
{
private:
dsp::biquad_d1<float> left[3], right[3];
int order;
public:
uint32_t srate;
enum { mode_12db_lp = 0, mode_24db_lp = 1, mode_36db_lp = 2,
mode_12db_hp = 3, mode_24db_hp = 4, mode_36db_hp = 5,
mode_6db_bp = 6, mode_12db_bp = 7, mode_18db_bp = 8,
mode_6db_br = 9, mode_12db_br = 10, mode_18db_br = 11,
mode_count
};
public:
biquad_filter_module()
: order(0) {}
/// Calculate filter coefficients based on parameters - cutoff/center frequency, q, filter type, output gain
void calculate_filter(float freq, float q, int mode, float gain = 1.0);
/// Reset filter state
void filter_activate();
/// Remove denormals
void sanitize();
/// Process a single channel (float buffer) of data
int process_channel(uint16_t channel_no, const float *in, float *out, uint32_t numsamples, int inmask);
/// Determine gain (|H(z)|) for a given frequency
float freq_gain(int subindex, float freq, float srate) const;
};
class two_band_eq
{
private:
dsp::onepole<float> lowcut, highcut;
float low_gain, high_gain;
public:
void reset()
{
lowcut.reset();
highcut.reset();
}
inline float process(float v)
{
v = dsp::lerp(lowcut.process_hp(v), v, low_gain);
v = dsp::lerp(highcut.process_lp(v), v, high_gain);
return v;
}
inline void copy_coeffs(const two_band_eq &src)
{
lowcut.copy_coeffs(src.lowcut);
highcut.copy_coeffs(src.highcut);
low_gain = src.low_gain;
high_gain = src.high_gain;
}
void sanitize()
{
lowcut.sanitize();
highcut.sanitize();
}
void set(float _low_freq, float _low_gain, float _high_freq, float _high_gain, float sr)
{
lowcut.set_hp(_low_freq, sr);
highcut.set_lp(_high_freq, sr);
low_gain = _low_gain;
high_gain = _high_gain;
}
};
/// Tom Szilagyi's distortion code, used with permission
/// KF: I'm not 100% sure how this is supposed to work, but it does.
/// I'm planning to rewrite it using more modular approach when I have more time.
class tap_distortion {
private:
float blend_old, drive_old;
float meter;
float rdrive, rbdr, kpa, kpb, kna, knb, ap, an, imr, kc, srct, sq, pwrq;
float prev_med, prev_out;
public:
uint32_t srate;
bool is_active;
tap_distortion();
void activate();
void deactivate();
void set_params(float blend, float drive);
void set_sample_rate(uint32_t sr);
float process(float in);
float get_distortion_level();
static inline float M(float x)
{
return (fabs(x) > 0.000000001f) ? x : 0.0f;
}
static inline float D(float x)
{
x = fabs(x);
return (x > 0.000000001f) ? sqrtf(x) : 0.0f;
}
};
/// LFO module by Markus
/// This module provides simple LFO's (sine=0, triangle=1, square=2, saw_up=3, saw_down=4)
/// get_value() returns a value between -1 and 1
class simple_lfo {
private:
float phase, freq, offset, amount;
int mode;
uint32_t srate;
bool is_active;
public:
simple_lfo();
void set_params(float f, int m, float o, uint32_t sr, float amount = 1.f);
float get_value();
void advance(uint32_t count);
void set_phase(float ph);
void activate();
void deactivate();
float get_value_from_phase(float ph, float off) const;
bool get_graph(float *data, int points, calf_plugins::cairo_iface *context) const;
bool get_dot(float &x, float &y, int &size, calf_plugins::cairo_iface *context) const;
};
/// Lookahead Limiter by Markus Schmidt and Christian Holschuh
class lookahead_limiter {
private:
public:
float limit, attack, release, weight;
uint32_t srate;
float att; // a coefficient the output is multiplied with
float att_max; // a memory for the highest attenuation - used for display
int pos; // where we are actually in our sample buffer
int buffer_size;
int overall_buffer_size;
bool is_active;
bool debug;
bool auto_release;
bool asc_active;
float *buffer;
int channels;
float delta;
float _delta;
float peak;
unsigned int over_s;
float over_c;
bool use_multi;
unsigned int id;
bool _sanitize;
int nextiter;
int nextlen;
int * nextpos;
float * nextdelta;
int asc_c;
float asc;
int asc_pos;
bool asc_changed;
float asc_coeff;
static inline void denormal(volatile float *f) {
*f += 1e-18;
*f -= 1e-18;
}
void reset();
void reset_asc();
bool get_asc();
lookahead_limiter();
void set_multi(bool set);
void process(float &left, float &right, float *multi_buffer);
void set_sample_rate(uint32_t sr);
void set_params(float l, float a, float r, float weight = 1.f, bool ar = false, float arc = 1.f, bool d = false);
float get_attenuation();
void activate();
void deactivate();
};
#if 0
{ to keep editor happy
#endif
}
#endif

View File

@@ -0,0 +1,653 @@
/* Calf DSP Library
* Biquad filters
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* Most of code in this file is based on freely
* available other work of other people (filter equations).
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#ifndef __CALF_BIQUAD_H
#define __CALF_BIQUAD_H
#include <complex>
#include "primitives.h"
namespace dsp {
/**
* Coefficients for two-pole two-zero filter, for floating point values,
* plus a bunch of functions to set them to typical values.
*
* Coefficient calculation is based on famous Robert Bristow-Johnson's equations,
* except where it's not.
* The coefficient calculation is NOT mine, the only exception is the lossy
* optimization in Zoelzer and rbj HP filter code.
*
* See http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt for reference.
*
* don't use this for integers because it won't work
*/
template<class Coeff = float>
class biquad_coeffs
{
public:
// filter coefficients
Coeff a0, a1, a2, b1, b2;
typedef std::complex<double> cfloat;
biquad_coeffs()
{
set_null();
}
inline void set_null()
{
a0 = 1.0;
b1 = b2 = a1 = a2 = 0.f;
}
/** Lowpass filter based on Robert Bristow-Johnson's equations
* Perhaps every synth code that doesn't use SVF uses these
* equations :)
* @param fc resonant frequency
* @param q resonance (gain at fc)
* @param sr sample rate
* @param gain amplification (gain at 0Hz)
*/
inline void set_lp_rbj(float fc, float q, float sr, float gain = 1.0)
{
float omega=(float)(2*M_PI*fc/sr);
float sn=sin(omega);
float cs=cos(omega);
float alpha=(float)(sn/(2*q));
float inv=(float)(1.0/(1.0+alpha));
a2 = a0 = (float)(gain*inv*(1 - cs)*0.5f);
a1 = a0 + a0;
b1 = (float)(-2*cs*inv);
b2 = (float)((1 - alpha)*inv);
}
// different lowpass filter, based on Zoelzer's equations, modified by
// me (kfoltman) to use polynomials to approximate tangent function
// not very accurate, but perhaps good enough for synth work :)
// odsr is "one divided by samplerate"
// from how it looks, it perhaps uses bilinear transform - but who knows :)
inline void set_lp_zoelzer(float fc, float q, float odsr, float gain=1.0)
{
Coeff omega=(Coeff)(M_PI*fc*odsr);
Coeff omega2=omega*omega;
Coeff K=omega*(1+omega2*omega2*Coeff(1.0/1.45));
Coeff KK=K*K;
Coeff QK=q*(KK+1.f);
Coeff iQK=1.0f/(QK+K);
Coeff inv=q*iQK;
b2 = (Coeff)(iQK*(QK-K));
b1 = (Coeff)(2.f*(KK-1.f)*inv);
a2 = a0 = (Coeff)(inv*gain*KK);
a1 = a0 + a0;
}
/** Highpass filter based on Robert Bristow-Johnson's equations
* @param fc resonant frequency
* @param q resonance (gain at fc)
* @param sr sample rate
* @param gain amplification (gain at sr/2)
*/
inline void set_hp_rbj(float fc, float q, float esr, float gain=1.0)
{
Coeff omega=(float)(2*M_PI*fc/esr);
Coeff sn=sin(omega);
Coeff cs=cos(omega);
Coeff alpha=(float)(sn/(2*q));
float inv=(float)(1.0/(1.0+alpha));
a0 = (Coeff)(gain*inv*(1 + cs)/2);
a1 = -2.f * a0;
a2 = a0;
b1 = (Coeff)(-2*cs*inv);
b2 = (Coeff)((1 - alpha)*inv);
}
// this replaces sin/cos with polynomial approximation
inline void set_hp_rbj_optimized(float fc, float q, float esr, float gain=1.0)
{
Coeff omega=(float)(2*M_PI*fc/esr);
Coeff sn=omega+omega*omega*omega*(1.0/6.0)+omega*omega*omega*omega*omega*(1.0/120);
Coeff cs=1-omega*omega*(1.0/2.0)+omega*omega*omega*omega*(1.0/24);
Coeff alpha=(float)(sn/(2*q));
float inv=(float)(1.0/(1.0+alpha));
a0 = (Coeff)(gain*inv*(1 + cs)*(1.0/2.0));
a1 = -2.f * a0;
a2 = a0;
b1 = (Coeff)(-2*cs*inv);
b2 = (Coeff)((1 - alpha)*inv);
}
/** Bandpass filter based on Robert Bristow-Johnson's equations (normalized to 1.0 at center frequency)
* @param fc center frequency (gain at fc = 1.0)
* @param q =~ fc/bandwidth (not quite, but close) - 1/Q = 2*sinh(ln(2)/2*BW*w0/sin(w0))
* @param sr sample rate
* @param gain amplification (gain at sr/2)
*/
inline void set_bp_rbj(double fc, double q, double esr, double gain=1.0)
{
float omega=(float)(2*M_PI*fc/esr);
float sn=sin(omega);
float cs=cos(omega);
float alpha=(float)(sn/(2*q));
float inv=(float)(1.0/(1.0+alpha));
a0 = (float)(gain*inv*alpha);
a1 = 0.f;
a2 = (float)(-gain*inv*alpha);
b1 = (float)(-2*cs*inv);
b2 = (float)((1 - alpha)*inv);
}
// rbj's bandreject
inline void set_br_rbj(double fc, double q, double esr, double gain=1.0)
{
float omega=(float)(2*M_PI*fc/esr);
float sn=sin(omega);
float cs=cos(omega);
float alpha=(float)(sn/(2*q));
float inv=(float)(1.0/(1.0+alpha));
a0 = (Coeff)(gain*inv);
a1 = (Coeff)(-gain*inv*2*cs);
a2 = (Coeff)(gain*inv);
b1 = (Coeff)(-2*cs*inv);
b2 = (Coeff)((1 - alpha)*inv);
}
// this is mine (and, I guess, it sucks/doesn't work)
void set_allpass(float freq, float pole_r, float sr)
{
float a=prewarp(freq, sr);
float q=pole_r;
set_bilinear(a*a+q*q, -2.0f*a, 1, a*a+q*q, 2.0f*a, 1);
}
/// prewarping for bilinear transform, maps given digital frequency to analog counterpart for analog filter design
static inline float prewarp(float freq, float sr)
{
if (freq>sr*0.49) freq=(float)(sr*0.49);
return (float)(tan(M_PI*freq/sr));
}
/// convert analog angular frequency value to digital
static inline float unwarp(float omega, float sr)
{
float T = 1.0 / sr;
return (2 / T) * atan(omega * T / 2);
}
/// convert analog filter time constant to digital counterpart
static inline float unwarpf(float t, float sr)
{
// this is most likely broken and works by pure accident!
float omega = 1.0 / t;
omega = unwarp(omega, sr);
// I really don't know why does it have to be M_PI and not 2 * M_PI!
float f = M_PI / omega;
return f / sr;
}
/// set digital filter parameters based on given analog filter parameters
void set_bilinear(float aa0, float aa1, float aa2, float ab0, float ab1, float ab2)
{
float q=(float)(1.0/(ab0+ab1+ab2));
a0 = (aa0+aa1+aa2)*q;
a1 = 2*(aa0-aa2)*q;
a2 = (aa0-aa1+aa2)*q;
b1 = 2*(ab0-ab2)*q;
b2 = (ab0-ab1+ab2)*q;
}
/// RBJ peaking EQ
/// @param freq peak frequency
/// @param q q (correlated to freq/bandwidth, @see set_bp_rbj)
/// @param peak peak gain (1.0 means no peak, >1.0 means a peak, less than 1.0 is a dip)
inline void set_peakeq_rbj(float freq, float q, float peak, float sr)
{
float A = sqrt(peak);
float w0 = freq * 2 * M_PI * (1.0 / sr);
float alpha = sin(w0) / (2 * q);
float ib0 = 1.0 / (1 + alpha/A);
a1 = b1 = -2*cos(w0) * ib0;
a0 = ib0 * (1 + alpha*A);
a2 = ib0 * (1 - alpha*A);
b2 = ib0 * (1 - alpha/A);
}
/// RBJ low shelf EQ - amplitication of 'peak' at 0 Hz and of 1.0 (0dB) at sr/2 Hz
/// @param freq corner frequency (gain at freq is sqrt(peak))
/// @param q q (relates bandwidth and peak frequency), the higher q, the louder the resonant peak (situated below fc) is
/// @param peak shelf gain (1.0 means no peak, >1.0 means a peak, less than 1.0 is a dip)
inline void set_lowshelf_rbj(float freq, float q, float peak, float sr)
{
float A = sqrt(peak);
float w0 = freq * 2 * M_PI * (1.0 / sr);
float alpha = sin(w0) / (2 * q);
float cw0 = cos(w0);
float tmp = 2 * sqrt(A) * alpha;
float b0 = 0.f, ib0 = 0.f;
a0 = A*( (A+1) - (A-1)*cw0 + tmp);
a1 = 2*A*( (A-1) - (A+1)*cw0);
a2 = A*( (A+1) - (A-1)*cw0 - tmp);
b0 = (A+1) + (A-1)*cw0 + tmp;
b1 = -2*( (A-1) + (A+1)*cw0);
b2 = (A+1) + (A-1)*cw0 - tmp;
ib0 = 1.0 / b0;
b1 *= ib0;
b2 *= ib0;
a0 *= ib0;
a1 *= ib0;
a2 *= ib0;
}
/// RBJ high shelf EQ - amplitication of 0dB at 0 Hz and of peak at sr/2 Hz
/// @param freq corner frequency (gain at freq is sqrt(peak))
/// @param q q (relates bandwidth and peak frequency), the higher q, the louder the resonant peak (situated above fc) is
/// @param peak shelf gain (1.0 means no peak, >1.0 means a peak, less than 1.0 is a dip)
inline void set_highshelf_rbj(float freq, float q, float peak, float sr)
{
float A = sqrt(peak);
float w0 = freq * 2 * M_PI * (1.0 / sr);
float alpha = sin(w0) / (2 * q);
float cw0 = cos(w0);
float tmp = 2 * sqrt(A) * alpha;
float b0 = 0.f, ib0 = 0.f;
a0 = A*( (A+1) + (A-1)*cw0 + tmp);
a1 = -2*A*( (A-1) + (A+1)*cw0);
a2 = A*( (A+1) + (A-1)*cw0 - tmp);
b0 = (A+1) - (A-1)*cw0 + tmp;
b1 = 2*( (A-1) - (A+1)*cw0);
b2 = (A+1) - (A-1)*cw0 - tmp;
ib0 = 1.0 / b0;
b1 *= ib0;
b2 *= ib0;
a0 *= ib0;
a1 *= ib0;
a2 *= ib0;
}
/// copy coefficients from another biquad
template<class U>
inline void copy_coeffs(const biquad_coeffs<U> &src)
{
a0 = src.a0;
a1 = src.a1;
a2 = src.a2;
b1 = src.b1;
b2 = src.b2;
}
/// Return the filter's gain at frequency freq
/// @param freq Frequency to look up
/// @param sr Filter sample rate (used to convert frequency to angular frequency)
float freq_gain(float freq, float sr) const
{
typedef std::complex<double> cfloat;
freq *= 2.0 * M_PI / sr;
cfloat z = 1.0 / exp(cfloat(0.0, freq));
return std::abs(h_z(z));
}
/// Return H(z) the filter's gain at frequency freq
/// @param z Z variable (e^jw)
cfloat h_z(const cfloat &z) const
{
return (cfloat(a0) + double(a1) * z + double(a2) * z*z) / (cfloat(1.0) + double(b1) * z + double(b2) * z*z);
}
};
/**
* Two-pole two-zero filter, for floating point values.
* Uses "traditional" Direct I form (separate FIR and IIR halves).
* don't use this for integers because it won't work
*/
template<class Coeff = float, class T = float>
struct biquad_d1: public biquad_coeffs<Coeff>
{
using biquad_coeffs<Coeff>::a0;
using biquad_coeffs<Coeff>::a1;
using biquad_coeffs<Coeff>::a2;
using biquad_coeffs<Coeff>::b1;
using biquad_coeffs<Coeff>::b2;
/// input[n-1]
T x1;
/// input[n-2]
T x2;
/// output[n-1]
T y1;
/// output[n-2]
T y2;
/// Constructor (initializes state to all zeros)
biquad_d1()
{
reset();
}
/// direct I form with four state variables
inline T process(T in)
{
T out = in * a0 + x1 * a1 + x2 * a2 - y1 * b1 - y2 * b2;
x2 = x1;
y2 = y1;
x1 = in;
y1 = out;
return out;
}
/// direct I form with zero input
inline T process_zeroin()
{
T out = - y1 * b1 - y2 * b2;
y2 = y1;
y1 = out;
return out;
}
/// simplified version for lowpass case with two zeros at -1
inline T process_lp(T in)
{
T out = a0*(in + x1 + x1 + x2) - y1 * b1 - y2 * b2;
x2 = x1;
y2 = y1;
x1 = in;
y1 = out;
return out;
}
/// Sanitize (set to 0 if potentially denormal) filter state
inline void sanitize()
{
dsp::sanitize(x1);
dsp::sanitize(y1);
dsp::sanitize(x2);
dsp::sanitize(y2);
}
/// Reset state variables
inline void reset()
{
dsp::zero(x1);
dsp::zero(y1);
dsp::zero(x2);
dsp::zero(y2);
}
inline bool empty() const {
return (y1 == 0.f && y2 == 0.f);
}
};
/**
* Two-pole two-zero filter, for floating point values.
* Uses slightly faster Direct II form (combined FIR and IIR halves).
* However, when used with wildly varying coefficients, it may
* make more zipper noise than Direct I form, so it's better to
* use it when filter coefficients are not changed mid-stream.
*/
template<class Coeff = float, class T = float>
struct biquad_d2: public biquad_coeffs<Coeff>
{
using biquad_coeffs<Coeff>::a0;
using biquad_coeffs<Coeff>::a1;
using biquad_coeffs<Coeff>::a2;
using biquad_coeffs<Coeff>::b1;
using biquad_coeffs<Coeff>::b2;
/// state[n-1]
float w1;
/// state[n-2]
float w2;
/// Constructor (initializes state to all zeros)
biquad_d2()
{
reset();
}
/// direct II form with two state variables
inline T process(T in)
{
dsp::sanitize_denormal(in);
dsp::sanitize(in);
dsp::sanitize(w1);
dsp::sanitize(w2);
T tmp = in - w1 * b1 - w2 * b2;
T out = tmp * a0 + w1 * a1 + w2 * a2;
w2 = w1;
w1 = tmp;
return out;
}
// direct II form with two state variables, lowpass version
// interesting fact: this is actually slower than the general version!
inline T process_lp(T in)
{
T tmp = in - w1 * b1 - w2 * b2;
T out = (tmp + w2 + w1* 2) * a0;
w2 = w1;
w1 = tmp;
return out;
}
/// Is the filter state completely silent? (i.e. set to 0 by sanitize function)
inline bool empty() const {
return (w1 == 0.f && w2 == 0.f);
}
/// Sanitize (set to 0 if potentially denormal) filter state
inline void sanitize()
{
dsp::sanitize(w1);
dsp::sanitize(w2);
}
/// Reset state variables
inline void reset()
{
dsp::zero(w1);
dsp::zero(w2);
}
};
/**
* Two-pole two-zero filter, for floating point values.
* Uses "traditional" Direct I form (separate FIR and IIR halves).
* don't use this for integers because it won't work
*/
template<class Coeff = float, class T = float>
struct biquad_d1_lerp: public biquad_coeffs<Coeff>
{
using biquad_coeffs<Coeff>::a0;
using biquad_coeffs<Coeff>::a1;
using biquad_coeffs<Coeff>::a2;
using biquad_coeffs<Coeff>::b1;
using biquad_coeffs<Coeff>::b2;
Coeff a0cur, a1cur, a2cur, b1cur, b2cur;
Coeff a0delta, a1delta, a2delta, b1delta, b2delta;
/// input[n-1]
T x1;
/// input[n-2]
T x2;
/// output[n-1]
T y1;
/// output[n-2]
T y2;
/// Constructor (initializes state to all zeros)
biquad_d1_lerp()
{
reset();
}
#define _DO_COEFF(coeff) coeff##delta = (coeff - coeff##cur) * (frac)
void big_step(Coeff frac)
{
_DO_COEFF(a0);
_DO_COEFF(a1);
_DO_COEFF(a2);
_DO_COEFF(b1);
_DO_COEFF(b2);
}
#undef _DO_COEFF
/// direct I form with four state variables
inline T process(T in)
{
T out = in * a0cur + x1 * a1cur + x2 * a2cur - y1 * b1cur - y2 * b2cur;
x2 = x1;
y2 = y1;
x1 = in;
y1 = out;
a0cur += a0delta;
a1cur += a1delta;
a2cur += a2delta;
b1cur += b1delta;
b2cur += b2delta;
return out;
}
/// direct I form with zero input
inline T process_zeroin()
{
T out = - y1 * b1 - y2 * b2;
y2 = y1;
y1 = out;
b1cur += b1delta;
b2cur += b2delta;
return out;
}
/// simplified version for lowpass case with two zeros at -1
inline T process_lp(T in)
{
T out = a0*(in + x1 + x1 + x2) - y1 * b1 - y2 * b2;
x2 = x1;
y2 = y1;
x1 = in;
y1 = out;
return out;
}
/// Sanitize (set to 0 if potentially denormal) filter state
inline void sanitize()
{
dsp::sanitize(x1);
dsp::sanitize(y1);
dsp::sanitize(x2);
dsp::sanitize(y2);
dsp::sanitize(a0cur);
dsp::sanitize(a1cur);
dsp::sanitize(a2cur);
dsp::sanitize(b1cur);
dsp::sanitize(b2cur);
}
/// Reset state variables
inline void reset()
{
dsp::zero(x1);
dsp::zero(y1);
dsp::zero(x2);
dsp::zero(y2);
dsp::zero(a0cur);
dsp::zero(a1cur);
dsp::zero(a2cur);
dsp::zero(b1cur);
dsp::zero(b2cur);
}
inline bool empty() {
return (y1 == 0.f && y2 == 0.f);
}
};
/// Compose two filters in series
template<class F1, class F2>
class filter_compose {
public:
typedef std::complex<float> cfloat;
F1 f1;
F2 f2;
public:
float process(float value) {
return f2.process(f1.process(value));
}
inline cfloat h_z(const cfloat &z) const {
return f1.h_z(z) * f2.h_z(z);
}
/// Return the filter's gain at frequency freq
/// @param freq Frequency to look up
/// @param sr Filter sample rate (used to convert frequency to angular frequency)
float freq_gain(float freq, float sr) const
{
typedef std::complex<double> cfloat;
freq *= 2.0 * M_PI / sr;
cfloat z = 1.0 / exp(cfloat(0.0, freq));
return std::abs(h_z(z));
}
void sanitize() {
f1.sanitize();
f2.sanitize();
}
};
/// Compose two filters in parallel
template<class F1, class F2>
class filter_sum {
public:
typedef std::complex<double> cfloat;
F1 f1;
F2 f2;
public:
float process(float value) {
return f2.process(value) + f1.process(value);
}
inline cfloat h_z(const cfloat &z) const {
return f1.h_z(z) + f2.h_z(z);
}
/// Return the filter's gain at frequency freq
/// @param freq Frequency to look up
/// @param sr Filter sample rate (used to convert frequency to angular frequency)
float freq_gain(float freq, float sr) const
{
typedef std::complex<double> cfloat;
freq *= 2.0 * M_PI / sr;
cfloat z = 1.0 / exp(cfloat(0.0, freq));
return std::abs(h_z(z));
}
void sanitize() {
f1.sanitize();
f2.sanitize();
}
};
};
#endif

View File

@@ -0,0 +1,229 @@
/* Calf DSP Library
* Buffer abstractions.
*
* Copyright (C) 2007 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#ifndef __BUFFER_H
#define __BUFFER_H
namespace dsp {
/// decrease by N if >= N (useful for circular buffers)
template<int N> inline int wrap_around(int a) {
return (a >= N) ? a - N : a;
}
// provide fast specializations for powers of 2
template<> inline int wrap_around<2>(int a) { return a & 1; }
template<> inline int wrap_around<4>(int a) { return a & 3; }
template<> inline int wrap_around<8>(int a) { return a & 7; }
template<> inline int wrap_around<16>(int a) { return a & 15; }
template<> inline int wrap_around<32>(int a) { return a & 31; }
template<> inline int wrap_around<64>(int a) { return a & 63; }
template<> inline int wrap_around<128>(int a) { return a & 127; }
template<> inline int wrap_around<256>(int a) { return a & 255; }
template<> inline int wrap_around<512>(int a) { return a & 511; }
template<> inline int wrap_around<1024>(int a) { return a & 1023; }
template<> inline int wrap_around<2048>(int a) { return a & 2047; }
template<> inline int wrap_around<4096>(int a) { return a & 4095; }
template<> inline int wrap_around<8192>(int a) { return a & 8191; }
template<> inline int wrap_around<16384>(int a) { return a & 16383; }
template<> inline int wrap_around<32768>(int a) { return a & 32767; }
template<> inline int wrap_around<65536>(int a) { return a & 65535; }
template<class Buf, class T>
void fill(Buf &buf, T value) {
T* data = buf.data();
int size = buf.size();
for (int i=0; i<size; i++)
*data++ = value;
}
template<class T>
void fill(T *data, int size, T value) {
for (int i=0; i<size; i++)
*data++ = value;
}
template<class T, class U>
void copy(T *dest, U *src, int size, T scale = 1, T add = 0) {
for (int i=0; i<size; i++)
*dest++ = (*src++) * scale + add;
}
template<class T>
struct sample_traits {
enum {
channels = 1,
bps = sizeof(T)*8
};
};
template<class T>
struct sample_traits<stereo_sample<T> > {
enum {
channels = 2,
bps = sizeof(T)*8
};
};
template<int N, class T = float>
class fixed_size_buffer {
public:
typedef T data_type;
enum { buffer_size = N };
inline int size() { return N; }
};
template<int N, class T = float>
class mem_fixed_size_buffer: public fixed_size_buffer<N, T> {
T *buf;
public:
mem_fixed_size_buffer(T ubuf[N]) { buf = ubuf; }
void set_data(T buf[N]) { this->buf = buf; }
inline T* data() { return buf; }
inline const T* data() const { return buf; }
inline T& operator[](int pos) { return buf[pos]; }
inline const T& operator[](int pos) const { return buf[pos]; }
};
template<int N, class T = float>
class auto_buffer: public fixed_size_buffer<N, T> {
T buf[N];
public:
T* data() const { return buf; }
inline T& operator[](int pos) { return buf[pos]; }
inline const T& operator[](int pos) const { return buf[pos]; }
};
template<class T = float>
class dynamic_buffer {
T *buf;
int buf_size;
bool owns;
public:
dynamic_buffer() { owns = false; }
dynamic_buffer(T *_buf, int _buf_size, bool _own)
: buf(_buf), buf_size(_buf_size), owns(_own) {
}
dynamic_buffer(int _size) {
buf = new T[_size];
buf_size = _size;
owns = true;
}
inline T* data() { return buf; }
inline const T* data() const { return buf; }
inline int size() { return buf_size; }
void resize(int new_size, bool fill_with_zeros = false) {
T *new_buf = new T[new_size];
memcpy(new_buf, buf, std::min(buf_size, new_size));
if (fill_with_zeros && buf_size < new_size)
dsp::zero(new_buf + buf_size, new_size - buf_size);
if (owns)
delete []buf;
buf = new_buf;
buf_size = new_size;
owns = true;
}
inline T& operator[](int pos) { return buf[pos]; }
inline const T& operator[](int pos) const { return buf[pos]; }
~dynamic_buffer() {
if (owns)
delete []buf;
}
};
template<class T, class U>
void copy_buf(T &dest_buf, const U &src_buf, T scale = 1, T add = 0) {
typedef typename T::data_type data_type;
data_type *dest = dest_buf.data();
const data_type *src = src_buf.data();
int size = src.size();
for (int i=0; i<size; i++)
*dest++ = (*src++) * scale + add;
}
template<class T>
struct buffer_traits {
};
/// this class template defines some basic position operations for fixed_size_buffers
template<int N, class T>
struct buffer_traits<fixed_size_buffer<N, T> > {
int inc_wrap(int pos) const {
return wrap_around<T::size>(pos+1);
}
int pos_diff(int pos1, int pos2) const {
int pos = pos1 - pos2;
if (pos < 0) pos += T::size;
return pos;
}
};
/// this is useless for now (and untested too)
template<class B>
class circular_buffer: public B {
typedef typename B::data_type data_type;
typedef class buffer_traits<B> traits;
B buffer;
int rpos, wpos;
circular_buffer() {
clear();
}
void clear() {
rpos = 0;
wpos = 0;
}
inline void put(data_type data) {
buffer[wpos] = data;
wpos = traits::inc_wrap(wpos);
}
inline bool empty() {
return rpos == wpos;
}
inline bool full() {
return rpos == traits::inc_wrap(wpos);
}
inline const data_type& get() {
int oldrpos = rpos;
rpos = traits::inc_wrap(rpos);
return buffer[oldrpos];
}
inline int get_rbytes() {
return traits::pos_diff(wpos, rpos);
}
inline int get_wbytes() {
if (full()) return 0;
return traits::pos_diff(rpos, wpos);
}
};
/// this is useless for now
template<int N, class T = float>
class mono_auto_buffer: public auto_buffer<N, T> {
};
/// this is useless for now
template<int N, class T = float>
class stereo_auto_buffer: public auto_buffer<N, stereo_sample<T> > {
};
};
#endif

View File

@@ -0,0 +1,185 @@
/* Calf DSP Library
* Reusable audio effect classes.
*
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#ifndef __CALF_DELAY_H
#define __CALF_DELAY_H
#include "primitives.h"
#include "buffer.h"
#include "onepole.h"
namespace dsp {
/**
* Delay primitive. Can be used for most delay stuff, including
* variable (modulated) delays like chorus/flanger. Note that
* for modulated delay effects use of GetInterp is preferred,
* because it handles fractional positions and uses linear
* interpolation, which sounds better most of the time.
*
* @param N maximum length
* @param C number of channels read/written for each sample (1 mono, 2 stereo etc)
*/
template<int N, class T>
struct simple_delay {
auto_buffer<N, T> data;
int pos;
simple_delay() {
reset();
}
void reset() {
pos = 0;
for (int i=0; i<N; i++)
zero(data[i]);
}
/** Write one C-channel sample from idata[0], idata[1] etc into buffer */
inline void put(T idata) {
data[pos] = idata;
pos = wrap_around<N>(pos+1);
}
/**
* Read one C-channel sample into odata[0], odata[1] etc into buffer.
* Don't use for modulated delays (chorus/flanger etc) unless you
* want them to crackle and generally sound ugly
* @param odata pointer to write into
* @param delay delay relative to current writing pos
*/
template<class U>
inline void get(U &odata, int delay) {
assert(delay >= 0 && delay < N);
int ppos = wrap_around<N>(pos + N - delay);
odata = data[ppos];
}
/**
* Read and write during the same function call
*/
inline T process(T idata, int delay)
{
assert(delay >= 0 && delay < N);
int ppos = wrap_around<N>(pos + N - delay);
T odata = data[ppos];
data[pos] = idata;
pos = wrap_around<N>(pos+1);
return odata;
}
/** Read one C-channel sample at fractional position.
* This version can be used for modulated delays, because
* it uses linear interpolation.
* @param odata value to write into
* @param delay delay relative to current writing pos
* @param udelay fractional delay (0..1)
*/
template<class U>
inline void get_interp(U &odata, int delay, float udelay) {
// assert(delay >= 0 && delay <= N-1);
int ppos = wrap_around<N>(pos + N - delay);
int pppos = wrap_around<N>(ppos + N - 1);
odata = lerp(data[ppos], data[pppos], udelay);
}
/** Read one C-channel sample at fractional position.
* This version can be used for modulated delays, because
* it uses linear interpolation.
* @param odata value to write into
* @param delay delay relative to current writing pos
* @param udelay fractional delay (0..1)
*/
inline T get_interp_1616(unsigned int delay) {
float udelay = (float)((delay & 0xFFFF) * (1.0 / 65536.0));
delay = delay >> 16;
// assert(delay >= 0 && delay < N-1);
int ppos = wrap_around<N>(pos + N - delay);
int pppos = wrap_around<N>(ppos + N - 1);
return lerp(data[ppos], data[pppos], udelay);
}
/**
* Comb filter. Feedback delay line with given delay and feedback values
* @param in input signal
* @param delay delay length (must be <N and integer)
* @param fb feedback (must be <1 or it will be unstable)
*/
inline T process_comb(T in, unsigned int delay, float fb)
{
T old, cur;
get(old, delay);
cur = in + fb*old;
sanitize(cur);
put(cur);
return old;
}
/**
* Comb filter with linear interpolation. Feedback delay line with given delay and feedback values
* Note that linear interpolation introduces some weird effects in frequency response.
* @param in input signal
* @param delay fractional delay length (must be < 65536 * N)
* @param fb feedback (must be <1 or it will be unstable)
*/
inline T process_comb_lerp16(T in, unsigned int delay, float udelay, float fb)
{
T old, cur;
get_interp(old, delay>>16, dsp::fract16(delay));
cur = in + fb*old;
sanitize(cur);
put(cur);
return old;
}
/**
* Comb allpass filter. The comb filter with additional direct path, which is supposed to cancel the coloration.
* @param in input signal
* @param delay delay length (must be <N and integer)
* @param fb feedback (must be <1 or it will be unstable)
*/
inline T process_allpass_comb(T in, unsigned int delay, float fb)
{
T old, cur;
get(old, delay);
cur = in + fb*old;
sanitize(cur);
put(cur);
return old - fb * cur;
}
/**
* Comb allpass filter. The comb filter with additional direct path, which is supposed to cancel the coloration.
* @param in input signal
* @param delay fractional delay length (must be < 65536 * N)
* @param fb feedback (must be <1 or it will be unstable)
*/
inline T process_allpass_comb_lerp16(T in, unsigned int delay, float fb)
{
T old, cur;
get_interp(old, delay>>16, dsp::fract16(delay));
cur = in + fb*old;
sanitize(cur);
put(cur);
return old - fb * cur;
}
};
};
#endif

View File

@@ -0,0 +1,282 @@
/* Calf DSP Library
* ADSR envelope class (and other envelopes in future)
*
* Copyright (C) 2007-2008 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#ifndef __CALF_ENVELOPE_H
#define __CALF_ENVELOPE_H
#include "primitives.h"
namespace dsp {
/// Rate-based ADSFR envelope class. Note that if release rate is slower than decay
/// rate, this envelope won't use release rate until output level falls below sustain level
/// it's different to what certain hardware synth companies did, but it prevents the very
/// un-musical (IMHO) behaviour known from (for example) SoundFont 2.
class adsr
{
public:
enum env_state {
STOP, ///< envelope is stopped
ATTACK, ///< attack - rise from 0 to 1
DECAY, ///< decay - fall from 1 to sustain level
SUSTAIN, ///< sustain - remain at sustain level (unless sustain is 0 - then it gets stopped); with fade != 0 it goes towards 0% (positive fade) or 100% (negative fade)
RELEASE, ///< release - fall from sustain (or pre-sustain) level to 0
LOCKDECAY, ///< locked decay
};
/// Current envelope stage
env_state state;
/// @note these are *rates*, not times
double attack, decay, sustain, release, fade;
/// Requested release time (not the rate!) in frames, used for recalculating the rate if sustain is changed
double release_time;
/// Current envelope (output) level
double value;
/// Release rate used for the current note (calculated from this note's sustain level, and not the current sustain level,
/// which may have changed after note has been released)
double thisrelease;
/// Sustain level used for the current note (used to calculate release rate if sustain changed during release stage
/// of the current note)
double thiss;
/// Value from the time before advance() was called last time
double old_value;
adsr()
{
attack = decay = sustain = release = thisrelease = thiss = 0.f;
reset();
}
/// Stop (reset) the envelope
inline void reset()
{
old_value = value = 0.0;
thiss = 0.0;
state = STOP;
}
/// Set the envelope parameters (updates rate member variables based on values passed)
/// @param a attack time
/// @param d decay time
/// @param s sustain level
/// @param r release time
/// @param er Envelope (update) rate
/// @param f fade time (if applicable)
inline void set(float a, float d, float s, float r, float er, float f = 0.f)
{
attack = 1.0 / (a * er);
decay = (1 - s) / (d * er);
sustain = s;
release_time = r * er;
release = s / release_time;
if (fabs(f) > small_value<float>())
fade = 1.0 / (f * er);
else
fade = 0.0;
// in release:
// lock thiss setting (start of release for current note) and unlock thisrelease setting (current note's release rate)
if (state != RELEASE)
thiss = s;
else
thisrelease = thiss / release_time;
}
/// @retval true if envelope is in released state (forced decay, release or stopped)
inline bool released() const
{
return state == LOCKDECAY || state == RELEASE || state == STOP;
}
/// @retval true if envelope is stopped (has not been started or has run till its end)
inline bool stopped() const
{
return state == STOP;
}
/// Start the envelope
inline void note_on()
{
state = ATTACK;
thiss = sustain;
}
/// Release the envelope
inline void note_off()
{
// Do nothing if envelope is already stopped
if (state == STOP)
return;
// XXXKF what if envelope is already released? (doesn't happen in any current synth, but who knows?)
// Raise sustain value if it has been changed... I'm not sure if it's needed
thiss = std::max(sustain, value);
// Calculate release rate from sustain level
thisrelease = thiss / release_time;
// we're in attack or decay, and if decay is faster than release
if (value > sustain && decay > thisrelease) {
// use standard release time later (because we'll be switching at sustain point)
thisrelease = release;
state = LOCKDECAY;
} else {
// in attack/decay, but use fixed release time
// in case value fell below sustain, assume it didn't (for the purpose of calculating release rate only)
state = RELEASE;
}
}
/// Calculate next envelope value
inline void advance()
{
old_value = value;
// XXXKF This may use a state array instead of a switch some day (at least for phases other than attack and possibly sustain)
switch(state)
{
case ATTACK:
value += attack;
if (value >= 1.0) {
value = 1.0;
state = DECAY;
}
break;
case DECAY:
value -= decay;
if (value < sustain)
{
value = sustain;
state = SUSTAIN;
}
break;
case LOCKDECAY:
value -= decay;
if (value < sustain)
{
if (value < 0.f)
value = 0.f;
state = RELEASE;
thisrelease = release;
}
break;
case SUSTAIN:
if (fade != 0.f)
{
value -= fade;
if (value > 1.f)
value = 1.f;
}
else
value = sustain;
if (value < 0.00001f) {
value = 0;
state = STOP;
}
break;
case RELEASE:
value -= thisrelease;
if (value <= 0.f) {
value = 0.f;
state = STOP;
}
break;
case STOP:
value = 0.f;
break;
}
}
/// Return a value between old_value (previous step) and value (current step)
/// @param pos between 0 and 1
inline double interpolate(double pos)
{
return old_value + (value - old_value) * pos;
}
inline float get_amp_value()
{
if (state == RELEASE && sustain > 0 && value < sustain)
{
return value * value * value / (sustain * sustain);
}
return value;
}
};
/// Simple linear fade out for note tails
struct fadeout
{
float value;
float step, step_orig;
bool done, undoing;
fadeout(int steps = 256)
{
step_orig = (float)(1.f / steps);
value = 1.f;
reset();
}
/// Prepare fade out
void reset()
{
value = 1.f;
step = -step_orig;
done = false;
undoing = false;
}
/// Fade back in with double speed (to prevent click on note restart)
void undo()
{
step = step_orig;
done = false;
undoing = true;
}
/// Reset if fully faded out; fade back in if in the middle of fading out
void reset_soft()
{
if (value <= 0.f || value >= 1.f)
reset();
else
undo();
}
void process(float *buffer, int len)
{
int i = 0;
if (!done)
{
for (; value > 0 && value <= 1.0 && i < len; i++)
{
buffer[i] *= value;
value += step;
}
if (value <= 0 || value > 1)
done = true;
}
if (done && value <= 0)
{
while (i < len)
buffer[i++] = 0.f;
}
if (done && undoing && value >= 1)
{
undoing = false;
done = false;
// prepare for the next fade-out
value = 1.f;
}
}
};
};
#endif

View File

@@ -0,0 +1,113 @@
/* Calf DSP Library
* FFT class
*
* Copyright (C) 2007 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_FFT_H
#define __CALF_FFT_H
#include <complex>
namespace dsp {
/// FFT routine copied from my old OneSignal library, modified to
/// match Calf's style. It's not fast at all, just a straightforward
/// implementation.
template<class T, int O>
class fft
{
typedef typename std::complex<T> complex;
int scramble[1<<O];
complex sines[1<<O];
public:
fft()
{
int N=1<<O;
assert(N >= 4);
for (int i=0; i<N; i++)
{
int v=0;
for (int j=0; j<O; j++)
if (i&(1<<j))
v+=(N>>(j+1));
scramble[i]=v;
}
int N90 = N >> 2;
T divN = 2 * M_PI / N;
// use symmetry
for (int i=0; i<N90; i++)
{
T angle = divN * i;
T c = cos(angle), s = sin(angle);
sines[i + 3 * N90] = -(sines[i + N90] = complex(-s, c));
sines[i + 2 * N90] = -(sines[i] = complex(c, s));
}
}
void calculate(complex *input, complex *output, bool inverse)
{
int N=1<<O;
int N1=N-1;
int i;
// Scramble the input data
if (inverse)
{
float mf=1.0/N;
for (i=0; i<N; i++)
{
complex &c=input[scramble[i]];
output[i]=mf*complex(c.imag(),c.real());
}
}
else
for (i=0; i<N; i++)
output[i]=input[scramble[i]];
// O butterfiles
for (i=0; i<O; i++)
{
int PO=1<<i, PNO=1<<(O-i-1);
int j,k;
for (j=0; j<PNO; j++)
{
int base=j<<(i+1);
for (k=0; k<PO; k++)
{
int B1=base+k;
int B2=base+k+(1<<i);
complex r1=output[B1];
complex r2=output[B2];
output[B1]=r1+r2*sines[(B1<<(O-i-1))&N1];
output[B2]=r1+r2*sines[(B2<<(O-i-1))&N1];
}
}
}
if (inverse)
{
for (i=0; i<N; i++)
{
const complex &c=output[i];
output[i]=complex(c.imag(),c.real());
}
}
}
};
};
#endif

View File

@@ -0,0 +1,269 @@
/* Calf DSP Library
* DSP primitives.
*
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_FIXED_POINT_H
#define __CALF_FIXED_POINT_H
namespace dsp {
inline uint32_t shr(uint32_t v, int bits = 1) { return v>>bits; };
inline int32_t shr(int32_t v, int bits = 1) { return v>>bits; };
inline uint64_t shr(uint64_t v, int bits = 1) { return v>>bits; };
inline int64_t shr(int64_t v, int bits = 1) { return v>>bits; };
inline float shr(float v, int bits = 1) { return v*(1.0/(1<<bits)); };
inline double shr(double v, int bits = 1) { return v*(1.0/(1<<bits)); };
template<class T, int FracBits>
inline T shr(T v, int bits = 1) {
v.set(v >> bits);
return v;
}
template<class T, int FracBits> class fixed_point {
T value;
enum { IntBits = (sizeof(T)/8) - FracBits };
public:
/// default constructor, does not initialize the value, just like - say - float doesn't
inline fixed_point() {
}
/// copy constructor from any other fixed_point value
template<class U, int FracBits2> inline fixed_point(const fixed_point<U, FracBits2> &v) {
if (FracBits == FracBits2) value = v.get();
else if (FracBits > FracBits2) value = v.get() << abs(FracBits - FracBits2);
else value = v.get() >> abs(FracBits - FracBits2);
}
/* this would be way too confusing, it wouldn't be obvious if it expects a whole fixed point or an integer part
explicit inline fixed_point(T v) {
this->value = v;
}
*/
explicit inline fixed_point(double v) {
value = (T)(v*one());
}
/// Makes an instance from a representation value (ie. same type of value as is used for internal storage and get/set)
static inline fixed_point from_base(const T &v)
{
fixed_point result;
result.value = v;
return result;
}
inline static T one() {
return (T)(1) << FracBits;
}
inline void set(T value) {
this->value = value;
}
inline T get() const {
return value;
}
inline operator double() const {
return value * (1.0/one());
}
inline fixed_point &operator=(double v) {
value = (T)(v*one());
return *this;
}
template<class U, int FracBits2> static inline T rebase(const fixed_point<U, FracBits2> &v) {
if (FracBits == FracBits2)
return v.get();
if (FracBits > FracBits2)
return v.get() << abs(FracBits - FracBits2);
return v.get() >> abs(FracBits2 - FracBits);
}
template<class U, int FracBits2> inline fixed_point &operator=(const fixed_point<U, FracBits2> &v) {
value = rebase<U, FracBits2>(v);
return *this;
}
template<class U, int FracBits2> inline fixed_point &operator+=(const fixed_point<U, FracBits2> &v) {
value += rebase<U, FracBits2>(v);
return *this;
}
template<class U, int FracBits2> inline fixed_point &operator-=(const fixed_point<U, FracBits2> &v) {
value -= rebase<U, FracBits2>(v);
return *this;
}
template<class U, int FracBits2> inline fixed_point operator+(const fixed_point<U, FracBits2> &v) const {
fixed_point fpv;
fpv.set(value + rebase<U, FracBits2>(v));
return fpv;
}
template<class U, int FracBits2> inline fixed_point operator-(const fixed_point<U, FracBits2> &v) const {
fixed_point fpv;
fpv.set(value - rebase<U, FracBits2>(v));
return fpv;
}
/// multiply two fixed point values, using long long int to store the temporary multiplication result
template<class U, int FracBits2> inline fixed_point operator*(const fixed_point<U, FracBits2> &v) const {
fixed_point tmp;
tmp.set(((int64_t)value) * v.get() >> FracBits2);
return tmp;
}
/// multiply two fixed point values, using BigType (usually 64-bit int) to store the temporary multiplication result
template<class U, int FracBits2, class BigType> inline fixed_point& operator*=(const fixed_point<U, FracBits2> &v) {
value = (T)(((BigType)value) * v.get() >> FracBits2);
return *this;
}
inline fixed_point operator+(int v) const {
fixed_point tmp;
tmp.set(value + (v << FracBits));
return tmp;
}
inline fixed_point operator-(int v) const {
fixed_point tmp;
tmp.set(value - (v << FracBits));
return tmp;
}
inline fixed_point operator*(int v) const {
fixed_point tmp;
tmp.value = value*v;
return tmp;
}
inline fixed_point& operator+=(int v) {
value += (v << FracBits);
return *this;
}
inline fixed_point& operator-=(int v) {
value -= (v << FracBits);
return *this;
}
inline fixed_point& operator*=(int v) {
value *= v;
return *this;
}
/// return integer part
inline T ipart() const {
return value >> FracBits;
}
/// return integer part as unsigned int
inline unsigned int uipart() const {
return ((unsigned)value) >> FracBits;
}
/// return integer part as unsigned int
inline unsigned int ui64part() const {
return ((uint64_t)value) >> FracBits;
}
/// return fractional part as 0..(2^FracBits-1)
inline T fpart() const {
return value & ((1 << FracBits)-1);
}
/// return fractional part as 0..(2^Bits-1)
template<int Bits>
inline T fpart() const {
int fbits = value & ((1 << FracBits)-1);
if (Bits == FracBits) return fbits;
int shift = abs(Bits-FracBits);
return (Bits < FracBits) ? (fbits >> shift) : (fbits << shift);
}
/// return fractional part as 0..1
inline double fpart_as_double() const {
return (value & ((1 << FracBits)-1)) * (1.0 / (1 << FracBits));
}
/// use fractional part (either whole or given number of most significant bits) for interpolating between two values
/// note that it uses integer arithmetic only, and isn't suitable for floating point or fixed point U!
/// @param UseBits can be used when there's a risk of exceeding range of U because max(fpart)*max(v1 or v2) > range of U
template<class U, int UseBits, class MulType>
inline U lerp_by_fract_int(U v1, U v2) const {
int fp = fpart<UseBits>();
assert ( fp >=0 && fp <= (1<<UseBits));
// printf("diff =
return v1 + shr(((MulType)(v2-v1) * fp), UseBits);
}
template<class U, int UseBits>
inline U lerp_table_lookup_int(U data[(1<<IntBits)+1]) const {
unsigned int pos = uipart();
return lerp_by_fract_int<U, UseBits>(data[pos], data[pos+1]);
}
/// Untested... I've started it to get a sin/cos readout for rotaryorgan, but decided to use table-less solution instead
/// Do not assume it works, because it most probably doesn't
template<class U, int UseBits>
inline U lerp_table_lookup_int_shift(U data[(1<<IntBits)+1], unsigned int shift) {
unsigned int pos = (uipart() + shift) & ((1 << IntBits) - 1);
return lerp_by_fract_int<U, UseBits>(data[pos], data[pos+1]);
}
template<class U>
inline U lerp_table_lookup_float(U data[(1<<IntBits)+1]) const {
unsigned int pos = uipart();
return data[pos] + (data[pos+1]-data[pos]) * fpart_as_double();
}
template<class U>
inline U lerp_table_lookup_float_mask(U data[(1<<IntBits)+1], unsigned int mask) const {
unsigned int pos = ui64part() & mask;
// printf("full = %lld pos = %d + %f\n", value, pos, fpart_as_double());
return data[pos] + (data[pos+1]-data[pos]) * fpart_as_double();
}
template<class U, int UseBits, class MulType>
inline U lerp_ptr_lookup_int(U *data) const {
unsigned int pos = ui64part();
return lerp_by_fract_int<U, UseBits, MulType>(data[pos], data[pos+1]);
}
template<class U>
inline U lerp_ptr_lookup_float(U *data) const {
unsigned int pos = ui64part();
return data[pos] + (data[pos+1]-data[pos]) * fpart_as_double();
}
};
template<class T, int FractBits>
inline fixed_point<T, FractBits> operator*(int v, fixed_point<T, FractBits> v2) {
v2 *= v;
return v2;
}
/// wave position (unsigned 64-bit int including 24-bit fractional part)
typedef fixed_point<unsigned long long int, 24> wpos;
};
#endif

View File

@@ -0,0 +1,722 @@
/* Calf DSP Library
* Common plugin interface definitions (shared between LADSPA/LV2/DSSI/standalone).
*
* Copyright (C) 2007-2010 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef CALF_GIFACE_H
#define CALF_GIFACE_H
#include <config.h>
#include "primitives.h"
#include <complex>
#include <exception>
#include <string>
#include <vector>
namespace osctl {
struct osc_client;
}
namespace calf_plugins {
enum {
MAX_SAMPLE_RUN = 256
};
/// Values ORed together for flags field in parameter_properties
enum parameter_flags
{
PF_TYPEMASK = 0x000F, ///< bit mask for type
PF_FLOAT = 0x0000, ///< any float value
PF_INT = 0x0001, ///< integer value (still represented as float)
PF_BOOL = 0x0002, ///< bool value (usually >=0.5f is treated as TRUE, which is inconsistent with LV2 etc. which treats anything >0 as TRUE)
PF_ENUM = 0x0003, ///< enum value (min, min+1, ..., max, only guaranteed to work when min = 0)
PF_ENUM_MULTI = 0x0004, ///< SET / multiple-choice
PF_SCALEMASK = 0xF0, ///< bit mask for scale
PF_SCALE_DEFAULT = 0x00, ///< no scale given
PF_SCALE_LINEAR = 0x10, ///< linear scale
PF_SCALE_LOG = 0x20, ///< log scale
PF_SCALE_GAIN = 0x30, ///< gain = -96dB..0 or -inf dB
PF_SCALE_PERC = 0x40, ///< percent
PF_SCALE_QUAD = 0x50, ///< quadratic scale (decent for some gain/amplitude values)
PF_SCALE_LOG_INF = 0x60, ///< log scale + +inf (FAKE_INFINITY)
PF_CTLMASK = 0x0F00, ///< bit mask for control type
PF_CTL_DEFAULT = 0x0000, ///< try to figure out automatically
PF_CTL_KNOB = 0x0100, ///< knob
PF_CTL_FADER = 0x0200, ///< fader (slider)
PF_CTL_TOGGLE = 0x0300, ///< toggle button
PF_CTL_COMBO = 0x0400, ///< combo box
PF_CTL_RADIO = 0x0500, ///< radio button
PF_CTL_BUTTON = 0x0600, ///< push button
PF_CTL_METER = 0x0700, ///< volume meter
PF_CTL_LED = 0x0800, ///< light emitting diode
PF_CTL_LABEL = 0x0900, ///< label
PF_CTLOPTIONS = 0x00F000, ///< bit mask for control (widget) options
PF_CTLO_HORIZ = 0x001000, ///< horizontal version of the control (unused)
PF_CTLO_VERT = 0x002000, ///< vertical version of the control (unused)
PF_CTLO_LABEL = 0x004000, ///< add a text display to the control (meters only)
PF_CTLO_REVERSE = 0x008000, ///< use VU_MONOCHROME_REVERSE mode (meters only)
PF_PROP_NOBOUNDS = 0x010000, ///< no epp:hasStrictBounds
PF_PROP_EXPENSIVE = 0x020000, ///< epp:expensive, may trigger expensive calculation
PF_PROP_OUTPUT_GAIN=0x050000, ///< epp:outputGain + skip epp:hasStrictBounds
PF_PROP_OUTPUT = 0x080000, ///< output port
PF_PROP_OPTIONAL = 0x100000, ///< connection optional
PF_PROP_GRAPH = 0x200000, ///< add graph
PF_UNITMASK = 0xFF000000, ///< bit mask for units \todo reduce to use only 5 bits
PF_UNIT_DB = 0x01000000, ///< decibels
PF_UNIT_COEF = 0x02000000, ///< multiply-by factor
PF_UNIT_HZ = 0x03000000, ///< Hertz
PF_UNIT_SEC = 0x04000000, ///< second
PF_UNIT_MSEC = 0x05000000, ///< millisecond
PF_UNIT_CENTS = 0x06000000, ///< cents (1/100 of a semitone, 1/1200 of an octave)
PF_UNIT_SEMITONES = 0x07000000,///< semitones
PF_UNIT_BPM = 0x08000000, ///< beats per minute
PF_UNIT_DEG = 0x09000000, ///< degrees
PF_UNIT_NOTE = 0x0A000000, ///< MIDI note number
PF_UNIT_RPM = 0x0B000000, ///< revolutions per minute
};
/// A fake infinity value (because real infinity may break some hosts)
#define FAKE_INFINITY (65536.0 * 65536.0)
/// Check for infinity (with appropriate-ish tolerance)
#define IS_FAKE_INFINITY(value) (fabs(value-FAKE_INFINITY) < 1.0)
/// Information record about plugin's menu command
struct plugin_command_info
{
const char *label; ///< short command name / label
const char *name; ///< human-readable command name
const char *description; ///< description (for status line etc.)
};
/// Range, default value, flags and names for a parameter
struct parameter_properties
{
/// default value
float def_value;
/// minimum value
float min;
/// maximum value
float max;
/// number of steps (for an integer value from 0 to 100 this will be 101; for 0/90/180/270/360 this will be 5), or 0 for continuous
float step;
/// logical OR of parameter_flags
uint32_t flags;
/// for PF_ENUM: array of text values (from min to max step 1), otherwise NULL
const char **choices;
/// parameter label (for use in LV2 label field etc.)
const char *short_name;
/// parameter human-readable name
const char *name;
/// convert from [0, 1] range to [min, max] (applying scaling)
float from_01(double value01) const;
/// convert from [min, max] to [0, 1] range (applying reverse scaling)
double to_01(float value) const;
/// stringify (in sensible way)
std::string to_string(float value) const;
/// get required width (for reserving GUI space)
int get_char_count() const;
/// get increment step based on step value (if specified) and other factors
float get_increment() const;
};
struct cairo_iface
{
virtual void set_source_rgba(float r, float g, float b, float a = 1.f) = 0;
virtual void set_line_width(float width) = 0;
virtual ~cairo_iface() {}
};
struct progress_report_iface
{
virtual void report_progress(float percentage, const std::string &message) = 0;
virtual ~progress_report_iface() {}
};
/// 'provides live line graph values' interface
struct line_graph_iface
{
/// Obtain subindex'th graph of parameter 'index'
/// @param index parameter/graph number (usually tied to particular plugin control port)
/// @param subindex graph number (there may be multiple overlaid graphs for one parameter, eg. for monosynth 2x12dB filters)
/// @param data buffer for normalized output values
/// @param points number of points to fill
/// @param context cairo context to adjust (for multicolour graphs etc.)
/// @retval true graph data was returned; subindex+1 graph may or may not be available
/// @retval false graph data was not returned; subindex+1 graph does not exist either
virtual bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const { return false; }
/// Obtain subindex'th dot of parameter 'index'
/// @param index parameter/dot number (usually tied to particular plugin control port)
/// @param subindex dot number (there may be multiple dots graphs for one parameter)
virtual bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const { return false; }
/// Obtain subindex'th dot of parameter 'index'
/// @param index parameter/dot number (usually tied to particular plugin control port)
/// @param subindex dot number (there may be multiple dots graphs for one parameter)
virtual bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const { return false; }
/// Obtain subindex'th static graph of parameter index (static graphs are only dependent on parameter value, not plugin state)
/// @param index parameter/graph number (usually tied to particular plugin control port)
/// @param subindex graph number (there may be multiple overlaid graphs for one parameter, eg. for monosynth 2x12dB filters)
/// @param value parameter value to pick the graph for
/// @param data buffer for normalized output values
/// @param points number of points to fill
/// @param context cairo context to adjust (for multicolour graphs etc.)
/// @retval true graph data was returned; subindex+1 graph may or may not be available
/// @retval false graph data was not returned; subindex+1 graph does not exist either
virtual bool get_static_graph(int index, int subindex, float value, float *data, int points, cairo_iface *context) const { return false; }
/// Return which graphs need to be redrawn and which can be cached for later reuse
/// @param index Parameter/graph number (usually tied to particular plugin control port)
/// @param generation 0 (at start) or the last value returned by the function (corresponds to a set of input values)
/// @param subindex_graph First graph that has to be redrawn (because it depends on values that might have changed)
/// @param subindex_dot First dot that has to be redrawn
/// @param subindex_gridline First gridline/legend that has to be redrawn
/// @retval Current generation (to pass when calling the function next time); if different than passed generation value, call the function again to retrieve which graph offsets should be put into cache
virtual int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const { subindex_graph = subindex_dot = subindex_gridline = 0; return 0; }
/// Standard destructor to make compiler happy
virtual ~line_graph_iface() {}
};
enum table_column_type
{
TCT_UNKNOWN, ///< guard invalid type
TCT_FLOAT, ///< float value (encoded as C locale string)
TCT_ENUM, ///< enum value (see: 'values' array in table_column_info) - encoded as string base 10 representation of integer
TCT_STRING, ///< string value (encoded as C-escaped string)
TCT_OBJECT, ///< external object, encoded as string
TCT_LABEL, ///< string value (encoded as C-escaped string)
};
/// parameters of
struct table_column_info
{
const char *name; ///< column label
table_column_type type; ///< column data type
float min; ///< minimum value (for float)
float max; ///< maximum value (for float and enum)
float def_value; ///< default value (for float and enum)
const char **values; ///< NULL unless a TCT_ENUM, where it represents a NULL-terminated list of choices
};
/// 'has string parameters containing tabular data' interface
struct table_metadata_iface
{
/// retrieve the table layout for specific parameter
virtual const table_column_info *get_table_columns() const = 0;
/// return the fixed number of rows, or 0 if the number of rows is variable
virtual uint32_t get_table_rows() const = 0;
virtual ~table_metadata_iface() {}
};
/// 'may receive configure variables' interface
struct send_configure_iface
{
/// Called to set configure variable
/// @param key variable name
/// @param value variable content
virtual void send_configure(const char *key, const char *value) = 0;
virtual ~send_configure_iface() {}
};
/// 'may receive new status values' interface
struct send_updates_iface
{
/// Called to set configure variable
/// @param key variable name
/// @param value variable content
virtual void send_status(const char *key, const char *value) = 0;
virtual ~send_updates_iface() {}
};
struct plugin_command_info;
/// General information about the plugin - @todo XXXKF lacks the "new" id-label-name triple
struct ladspa_plugin_info
{
/// LADSPA ID
uint32_t unique_id;
/// plugin short name (camel case)
const char *label;
/// plugin human-readable name
const char *name;
/// maker (author)
const char *maker;
/// copyright notice
const char *copyright;
/// plugin type for LRDF/LV2
const char *plugin_type;
};
/// An interface returning metadata about a plugin
struct plugin_metadata_iface
{
/// @return plugin long name
virtual const char *get_name() const = 0;
/// @return plugin LV2 label
virtual const char *get_id() const = 0;
/// @return plugin human-readable label
virtual const char *get_label() const = 0;
/// @return total number of parameters
virtual int get_param_count() const = 0;
/// Return custom XML
virtual const char *get_gui_xml() const = 0;
/// @return number of audio inputs
virtual int get_input_count() const =0;
/// @return number of audio outputs
virtual int get_output_count() const =0;
/// @return number of optional inputs
virtual int get_inputs_optional() const =0;
/// @return number of optional outputs
virtual int get_outputs_optional() const =0;
/// @return true if plugin can work in hard-realtime conditions
virtual bool is_rt_capable() const =0;
/// @return true if plugin has MIDI input
virtual bool get_midi() const =0;
/// @return true if plugin has MIDI input
virtual bool requires_midi() const =0;
/// @return port offset of first control (parameter) port (= number of audio inputs + number of audio outputs in all existing plugins as for 1 Aug 2008)
virtual int get_param_port_offset() const = 0;
/// @return NULL-terminated list of menu commands
virtual plugin_command_info *get_commands() const { return NULL; }
/// @return description structure for given parameter
virtual const parameter_properties *get_param_props(int param_no) const = 0;
/// @return retrieve names of audio ports (@note control ports are named in parameter_properties, not here)
virtual const char **get_port_names() const = 0;
/// @return description structure for the plugin
virtual const ladspa_plugin_info &get_plugin_info() const = 0;
/// is a given parameter a control voltage?
virtual bool is_cv(int param_no) const = 0;
/// is the given parameter non-interpolated?
virtual bool is_noisy(int param_no) const = 0;
/// does the plugin require string port extension? (or DSSI configure) may be slow
virtual bool requires_configure() const = 0;
/// obtain array of names of configure variables (or NULL is none needed)
virtual const char *const *get_configure_vars() const { return NULL; }
/// @return table_metadata_iface if any
virtual const table_metadata_iface *get_table_metadata_iface(const char *key) const { return NULL; }
/// Do-nothing destructor to silence compiler warning
virtual ~plugin_metadata_iface() {}
};
/// Interface for host-GUI-plugin interaction (should be really split in two, but ... meh)
struct plugin_ctl_iface
{
/// @return value of given parameter
virtual float get_param_value(int param_no) = 0;
/// Set value of given parameter
virtual void set_param_value(int param_no, float value) = 0;
/// Load preset with given number
virtual bool activate_preset(int bank, int program) = 0;
/// @return volume level for port'th port (if supported by the implementation, currently only jack_host<Module> implements that by measuring signal level on plugin ports)
virtual float get_level(unsigned int port)=0;
/// Execute menu command with given number
virtual void execute(int cmd_no)=0;
/// Set a configure variable on a plugin
virtual char *configure(const char *key, const char *value) = 0;
/// Send all configure variables set within a plugin to given destination (which may be limited to only those that plugin understands)
virtual void send_configures(send_configure_iface *)=0;
/// Restore all state (parameters and configure vars) to default values - implemented in giface.cpp
virtual void clear_preset();
/// Call a named function in a plugin - this will most likely be redesigned soon - and never used
/// @retval false call has failed, result contains an error message
virtual bool blobcall(const char *command, const std::string &request, std::string &result) { result = "Call not supported"; return false; }
/// Update status variables changed since last_serial
/// @return new last_serial
virtual int send_status_updates(send_updates_iface *sui, int last_serial) = 0;
/// Return metadata object
virtual const plugin_metadata_iface *get_metadata_iface() const = 0;
/// @return line_graph_iface if any
virtual const line_graph_iface *get_line_graph_iface() const = 0;
/// Do-nothing destructor to silence compiler warning
virtual ~plugin_ctl_iface() {}
};
struct plugin_list_info_iface;
/// A class to retrieve and query the list of Calf plugins
class plugin_registry
{
public:
typedef std::vector<const plugin_metadata_iface *> plugin_vector;
private:
plugin_vector plugins;
plugin_registry();
public:
/// Get the singleton object.
static plugin_registry &instance();
/// Get all plugin metadata objects
const plugin_vector &get_all() { return plugins; }
/// Get single plugin metadata object by URI
const plugin_metadata_iface *get_by_uri(const char *URI);
/// Get single plugin metadata object by URI
const plugin_metadata_iface *get_by_id(const char *id, bool case_sensitive = false);
};
/// Load and strdup a text file with GUI definition
extern const char *load_gui_xml(const std::string &plugin_id);
/// Interface to audio processing plugins (the real things, not only metadata)
struct audio_module_iface
{
/// Handle MIDI Note On
virtual void note_on(int channel, int note, int velocity) = 0;
/// Handle MIDI Note Off
virtual void note_off(int channel, int note, int velocity) = 0;
/// Handle MIDI Program Change
virtual void program_change(int channel, int program) = 0;
/// Handle MIDI Control Change
virtual void control_change(int channel, int controller, int value) = 0;
/// Handle MIDI Pitch Bend
/// @param value pitch bend value (-8192 to 8191, defined as in MIDI ie. 8191 = 200 ct by default)
virtual void pitch_bend(int channel, int value) = 0;
/// Handle MIDI Channel Pressure
/// @param value channel pressure (0 to 127)
virtual void channel_pressure(int channel, int value) = 0;
/// Called when params are changed (before processing)
virtual void params_changed() = 0;
/// LADSPA-esque activate function, except it is called after ports are connected, not before
virtual void activate() = 0;
/// LADSPA-esque deactivate function
virtual void deactivate() = 0;
/// Set sample rate for the plugin
virtual void set_sample_rate(uint32_t sr) = 0;
/// Execute menu command with given number
virtual void execute(int cmd_no) = 0;
/// DSSI configure call, value = NULL = reset to default
virtual char *configure(const char *key, const char *value) = 0;
/// Send all understood configure vars (none by default)
virtual void send_configures(send_configure_iface *sci) = 0;
/// Send all supported status vars (none by default)
virtual int send_status_updates(send_updates_iface *sui, int last_serial) = 0;
/// Reset parameter values for epp:trigger type parameters (ones activated by oneshot push button instead of check box)
virtual void params_reset() = 0;
/// Called after instantiating (after all the feature pointers are set - including interfaces like progress_report_iface)
virtual void post_instantiate() = 0;
/// Return the arrays of port buffer pointers
virtual void get_port_arrays(float **&ins_ptrs, float **&outs_ptrs, float **&params_ptrs) = 0;
/// Return metadata object
virtual const plugin_metadata_iface *get_metadata_iface() const = 0;
/// Set the progress report interface to communicate progress to
virtual void set_progress_report_iface(progress_report_iface *iface) = 0;
/// Clear a part of output buffers that have 0s at mask; subdivide the buffer so that no runs > MAX_SAMPLE_RUN are fed to process function
virtual uint32_t process_slice(uint32_t offset, uint32_t end) = 0;
/// The audio processing loop; assumes numsamples <= MAX_SAMPLE_RUN, for larger buffers, call process_slice
virtual uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask) = 0;
/// Message port processing function
virtual uint32_t message_run(const void *valid_ports, void *output_ports) = 0;
/// @return line_graph_iface if any
virtual const line_graph_iface *get_line_graph_iface() const = 0;
virtual ~audio_module_iface() {}
};
/// Empty implementations for plugin functions.
template<class Metadata>
class audio_module: public Metadata, public audio_module_iface
{
public:
typedef Metadata metadata_type;
using Metadata::in_count;
using Metadata::out_count;
using Metadata::param_count;
float *ins[Metadata::in_count];
float *outs[Metadata::out_count];
float *params[Metadata::param_count];
progress_report_iface *progress_report;
audio_module() {
progress_report = NULL;
memset(ins, 0, sizeof(ins));
memset(outs, 0, sizeof(outs));
memset(params, 0, sizeof(params));
}
/// Handle MIDI Note On
void note_on(int channel, int note, int velocity) {}
/// Handle MIDI Note Off
void note_off(int channel, int note, int velocity) {}
/// Handle MIDI Program Change
void program_change(int channel, int program) {}
/// Handle MIDI Control Change
void control_change(int channel, int controller, int value) {}
/// Handle MIDI Pitch Bend
/// @param value pitch bend value (-8192 to 8191, defined as in MIDI ie. 8191 = 200 ct by default)
void pitch_bend(int channel, int value) {}
/// Handle MIDI Channel Pressure
/// @param value channel pressure (0 to 127)
void channel_pressure(int channel, int value) {}
/// Called when params are changed (before processing)
void params_changed() {}
/// LADSPA-esque activate function, except it is called after ports are connected, not before
void activate() {}
/// LADSPA-esque deactivate function
void deactivate() {}
/// Set sample rate for the plugin
void set_sample_rate(uint32_t sr) { }
/// Execute menu command with given number
void execute(int cmd_no) {}
/// DSSI configure call
virtual char *configure(const char *key, const char *value) { return NULL; }
/// Send all understood configure vars (none by default)
void send_configures(send_configure_iface *sci) {}
/// Send all supported status vars (none by default)
int send_status_updates(send_updates_iface *sui, int last_serial) { return last_serial; }
/// Reset parameter values for epp:trigger type parameters (ones activated by oneshot push button instead of check box)
void params_reset() {}
/// Called after instantiating (after all the feature pointers are set - including interfaces like progress_report_iface)
void post_instantiate() {}
/// Handle 'message context' port message
/// @arg output_ports pointer to bit array of output port "changed" flags, note that 0 = first audio input, not first parameter (use input_count + output_count)
uint32_t message_run(const void *valid_ports, void *output_ports) {
fprintf(stderr, "ERROR: message run not implemented\n");
return 0;
}
/// Return the array of input port pointers
virtual void get_port_arrays(float **&ins_ptrs, float **&outs_ptrs, float **&params_ptrs)
{
ins_ptrs = ins;
outs_ptrs = outs;
params_ptrs = params;
}
/// Return metadata object
virtual const plugin_metadata_iface *get_metadata_iface() const { return this; }
/// Set the progress report interface to communicate progress to
virtual void set_progress_report_iface(progress_report_iface *iface) { progress_report = iface; }
/// utility function: zero port values if mask is 0
inline void zero_by_mask(uint32_t mask, uint32_t offset, uint32_t nsamples)
{
for (int i=0; i<Metadata::out_count; i++) {
if ((mask & (1 << i)) == 0) {
dsp::zero(outs[i] + offset, nsamples);
}
}
}
/// utility function: call process, and if it returned zeros in output masks, zero out the relevant output port buffers
uint32_t process_slice(uint32_t offset, uint32_t end)
{
uint32_t total_out_mask = 0;
while(offset < end)
{
uint32_t newend = std::min(offset + MAX_SAMPLE_RUN, end);
uint32_t out_mask = process(offset, newend - offset, -1, -1);
total_out_mask |= out_mask;
zero_by_mask(out_mask, offset, newend - offset);
offset = newend;
}
return total_out_mask;
}
/// @return line_graph_iface if any
virtual const line_graph_iface *get_line_graph_iface() const { return dynamic_cast<const line_graph_iface *>(this); }
};
#if USE_EXEC_GUI || USE_DSSI
enum line_graph_item
{
LGI_END = 0,
LGI_GRAPH,
LGI_SUBGRAPH,
LGI_LEGEND,
LGI_DOT,
LGI_END_ITEM,
LGI_SET_RGBA,
LGI_SET_WIDTH,
};
/// A class to send status updates via OSC
struct dssi_feedback_sender
{
/// OSC client object used to send updates
osctl::osc_client *client;
/// Is client shared with something else?
bool is_client_shared;
/// Quit flag (used to terminate the thread)
bool quit;
/// Indices of graphs to send
std::vector<int> indices;
/// Source for the graph data (interface to marshal)
const calf_plugins::line_graph_iface *graph;
/// Create using a new client
dssi_feedback_sender(const char *URI, const line_graph_iface *_graph);
dssi_feedback_sender(osctl::osc_client *_client, const line_graph_iface *_graph);
void add_graphs(const calf_plugins::parameter_properties *props, int num_params);
void update();
~dssi_feedback_sender();
};
#endif
/// Metadata base class template, to provide default versions of interface functions
template<class Metadata>
class plugin_metadata: public plugin_metadata_iface
{
public:
static const char *port_names[];
static parameter_properties param_props[];
static ladspa_plugin_info plugin_info;
typedef plugin_metadata<Metadata> metadata_class;
// These below are stock implementations based on enums and static members in Metadata classes
// they may be overridden to provide more interesting functionality
const char *get_name() const { return Metadata::impl_get_name(); }
const char *get_id() const { return Metadata::impl_get_id(); }
const char *get_label() const { return Metadata::impl_get_label(); }
int get_input_count() const { return Metadata::in_count; }
int get_output_count() const { return Metadata::out_count; }
int get_inputs_optional() const { return Metadata::ins_optional; }
int get_outputs_optional() const { return Metadata::outs_optional; }
int get_param_count() const { return Metadata::param_count; }
bool get_midi() const { return Metadata::support_midi; }
bool requires_midi() const { return Metadata::require_midi; }
bool is_rt_capable() const { return Metadata::rt_capable; }
int get_param_port_offset() const { return Metadata::in_count + Metadata::out_count; }
const char *get_gui_xml() const { static const char *data_ptr = calf_plugins::load_gui_xml(get_id()); return data_ptr; }
plugin_command_info *get_commands() const { return NULL; }
const parameter_properties *get_param_props(int param_no) const { return &param_props[param_no]; }
const char **get_port_names() const { return port_names; }
bool is_cv(int param_no) const { return true; }
bool is_noisy(int param_no) const { return false; }
const ladspa_plugin_info &get_plugin_info() const { return plugin_info; }
bool requires_configure() const { return false; }
};
#define CALF_PORT_NAMES(name) template<> const char *::plugin_metadata<name##_metadata>::port_names[]
#define CALF_PORT_PROPS(name) template<> parameter_properties plugin_metadata<name##_metadata>::param_props[name##_metadata::param_count + 1]
#define CALF_PLUGIN_INFO(name) template<> calf_plugins::ladspa_plugin_info plugin_metadata<name##_metadata>::plugin_info
#define PLUGIN_NAME_ID_LABEL(name, id, label) \
static const char *impl_get_name() { return name; } \
static const char *impl_get_id() { return id; } \
static const char *impl_get_label() { return label; } \
extern const char *calf_copyright_info;
bool get_freq_gridline(int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context, bool use_frequencies = true, float res = 256, float ofs = 0.4);
/// convert amplitude value to normalized grid-ish value
static inline float dB_grid(float amp, float res = 256, float ofs = 0.4)
{
return log(amp) * (1.0 / log(res)) + ofs;
}
template<class Fx>
static bool get_graph(Fx &fx, int subindex, float *data, int points, float res = 256, float ofs = 0.4)
{
for (int i = 0; i < points; i++)
{
double freq = 20.0 * pow (20000.0 / 20.0, i * 1.0 / points);
data[i] = dB_grid(fx.freq_gain(subindex, freq, fx.srate), res, ofs);
}
return true;
}
/// convert normalized grid-ish value back to amplitude value
static inline float dB_grid_inv(float pos)
{
return pow(256.0, pos - 0.4);
}
/// Line graph interface implementation for frequency response graphs
class frequency_response_line_graph: public line_graph_iface
{
public:
bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
virtual int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const;
};
/// set drawing color based on channel index (0 or 1)
void set_channel_color(cairo_iface *context, int channel);
struct preset_access_iface
{
virtual void store_preset() = 0;
virtual void activate_preset(int preset, bool builtin) = 0;
virtual ~preset_access_iface() {}
};
/// Implementation of table_metadata_iface providing metadata for mod matrices
class mod_matrix_metadata: public table_metadata_iface
{
public:
/// Mapping modes
enum mapping_mode {
map_positive, ///< 0..100%
map_bipolar, ///< -100%..100%
map_negative, ///< -100%..0%
map_squared, ///< x^2
map_squared_bipolar, ///< x^2 scaled to -100%..100%
map_antisquared, ///< 1-(1-x)^2 scaled to 0..100%
map_antisquared_bipolar, ///< 1-(1-x)^2 scaled to -100..100%
map_parabola, ///< inverted parabola (peaks at 0.5, then decreases to 0)
map_type_count
};
const char **mod_src_names, **mod_dest_names;
mod_matrix_metadata(unsigned int _rows, const char **_src_names, const char **_dest_names);
virtual const table_column_info *get_table_columns() const;
virtual uint32_t get_table_rows() const;
protected:
/// Column descriptions for table widget
table_column_info table_columns[6];
unsigned int matrix_rows;
};
/// Check if a given key is either prefix + rows or prefix + i2s(row) + "," + i2s(column)
/// @arg key key to parse
/// @arg prefix table prefix (e.g. "modmatrix:")
/// @arg is_rows[out] set to true if key == prefix + "rows"
/// @arg row[out] if key is of a form: prefix + row + "," + i2s(column), returns row, otherwise returns -1
/// @arg column[out] if key is of a form: prefix + row + "," + i2s(column), returns row, otherwise returns -1
/// @retval true if this is one of the recognized string forms
extern bool parse_table_key(const char *key, const char *prefix, bool &is_rows, int &row, int &column);
#if USE_EXEC_GUI
class table_via_configure
{
protected:
typedef std::pair<int, int> coord;
std::vector<table_column_info> columns;
std::map<coord, std::string> values;
int rows;
public:
table_via_configure();
void configure(const char *key, const char *value);
virtual ~table_via_configure();
};
#endif
};
#endif

View File

@@ -0,0 +1,258 @@
/* Calf DSP Library
* Basic "inertia" (parameter smoothing) classes.
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_INERTIA_H
#define __CALF_INERTIA_H
#include "primitives.h"
namespace dsp {
/// Algorithm for a constant time linear ramp
class linear_ramp
{
public:
int ramp_len;
float mul, delta;
public:
/// Construct for given ramp length
linear_ramp(int _ramp_len) {
ramp_len = _ramp_len;
mul = (float)(1.0f / ramp_len);
delta = 0.f;
}
/// Change ramp length
inline void set_length(int _ramp_len) {
ramp_len = _ramp_len;
mul = (float)(1.0f / ramp_len);
}
inline int length()
{
return ramp_len;
}
inline void start_ramp(float start, float end)
{
delta = mul * (end - start);
}
/// Return value after single step
inline float ramp(float value)
{
return value + delta;
}
/// Return value after many steps
inline float ramp_many(float value, int count)
{
return value + delta * count;
}
};
/// Algorithm for a constant time linear ramp
class exponential_ramp
{
public:
int ramp_len;
float root, delta;
public:
exponential_ramp(int _ramp_len) {
ramp_len = _ramp_len;
root = (float)(1.0f / ramp_len);
delta = 1.0;
}
inline void set_length(int _ramp_len) {
ramp_len = _ramp_len;
root = (float)(1.0f / ramp_len);
}
inline int length()
{
return ramp_len;
}
inline void start_ramp(float start, float end)
{
delta = pow(end / start, root);
}
/// Return value after single step
inline float ramp(float value)
{
return value * delta;
}
/// Return value after many steps
inline float ramp_many(float value, float count)
{
return value * pow(delta, count);
}
};
/// Generic inertia using ramping algorithm specified as template argument. The basic idea
/// is producing smooth(ish) output for discrete input, using specified algorithm to go from
/// last output value to input value. It is not the same as classic running average lowpass
/// filter, because ramping time is finite and pre-determined (it calls ramp algorithm's length()
/// function to obtain the expected ramp length)
template<class Ramp>
class inertia
{
public:
float old_value;
float value;
unsigned int count;
Ramp ramp;
public:
inertia(const Ramp &_ramp, float init_value = 0.f)
: ramp(_ramp)
{
value = old_value = init_value;
count = 0;
}
/// Set value immediately (no inertia)
void set_now(float _value)
{
value = old_value = _value;
count = 0;
}
/// Set with inertia
void set_inertia(float source)
{
if (source != old_value) {
ramp.start_ramp(value, source);
count = ramp.length();
old_value = source;
}
}
/// Get smoothed value of given source value
inline float get(float source)
{
if (source != old_value) {
ramp.start_ramp(value, source);
count = ramp.length();
old_value = source;
}
if (!count)
return old_value;
value = ramp.ramp(value);
count--;
if (!count) // finished ramping, set to desired value to get rid of accumulated rounding errors
value = old_value;
return value;
}
/// Get smoothed value assuming no new input
inline float get()
{
if (!count)
return old_value;
value = ramp.ramp(value);
count--;
if (!count) // finished ramping, set to desired value to get rid of accumulated rounding errors
value = old_value;
return value;
}
/// Do one inertia step, without returning the new value and without changing destination value
inline void step()
{
if (count) {
value = ramp.ramp(value);
count--;
if (!count) // finished ramping, set to desired value to get rid of accumulated rounding errors
value = old_value;
}
}
/// Do many inertia steps, without returning the new value and without changing destination value
inline void step_many(unsigned int steps)
{
if (steps < count) {
// Skip only a part of the current ramping period
value = ramp.ramp_many(value, steps);
count -= steps;
if (!count) // finished ramping, set to desired value to get rid of accumulated rounding errors
value = old_value;
}
else
{
// The whole ramping period has been skipped, just go to destination
value = old_value;
count = 0;
}
}
/// Get last smoothed value, without affecting anything
inline float get_last() const
{
return value;
}
/// Is it still ramping?
inline bool active() const
{
return count > 0;
}
};
class once_per_n
{
public:
unsigned int frequency;
unsigned int left;
public:
once_per_n(unsigned int _frequency)
: frequency(_frequency), left(_frequency)
{}
inline void start()
{
left = frequency;
}
/// Set timer to "elapsed" state (elapsed() will return true during next call)
inline void signal()
{
left = 0;
}
inline unsigned int get(unsigned int desired)
{
if (desired > left) {
desired = left;
left = 0;
return desired;
}
left -= desired;
return desired;
}
inline bool elapsed()
{
if (!left) {
left = frequency;
return true;
}
return false;
}
};
class gain_smoothing: public inertia<linear_ramp>
{
public:
gain_smoothing()
: inertia<linear_ramp>(linear_ramp(64))
{
}
void set_sample_rate(int sr)
{
ramp = linear_ramp(sr / 100);
}
// to change param, use set_inertia(value)
// to read param, use get()
};
}
#endif

View File

@@ -0,0 +1,130 @@
/* Calf DSP Library
* API wrappers for LADSPA/DSSI
*
* Copyright (C) 2007-2008 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_LADSPA_WRAP_H
#define __CALF_LADSPA_WRAP_H
#if USE_LADSPA
#include <string.h>
#include <ladspa.h>
#if USE_DSSI
#include <dssi.h>
#endif
#include "giface.h"
#include "preset.h"
namespace calf_plugins {
struct ladspa_plugin_metadata_set;
/// A template implementing plugin_ctl_iface for a given plugin
struct ladspa_instance: public plugin_ctl_iface
{
audio_module_iface *module;
const plugin_metadata_iface *metadata;
ladspa_plugin_metadata_set *ladspa;
bool activate_flag;
float **ins, **outs, **params;
#if USE_DSSI
dssi_feedback_sender *feedback_sender;
#endif
ladspa_instance(audio_module_iface *_module, ladspa_plugin_metadata_set *_ladspa, int sample_rate);
virtual const line_graph_iface *get_line_graph_iface() const { return module->get_line_graph_iface(); }
virtual float get_param_value(int param_no);
virtual void set_param_value(int param_no, float value);
virtual bool activate_preset(int bank, int program);
virtual char *configure(const char *key, const char *value);
virtual float get_level(unsigned int port) { return 0.f; }
virtual void execute(int cmd_no) {
module->execute(cmd_no);
}
virtual void send_configures(send_configure_iface *sci) {
module->send_configures(sci);
}
virtual int send_status_updates(send_updates_iface *sui, int last_serial) { return module->send_status_updates(sui, last_serial); }
void run(unsigned long SampleCount);
#if USE_DSSI
/// Utility function: handle MIDI event (only handles a subset in this version)
void process_dssi_event(snd_seq_event_t &event);
void run_synth(unsigned long SampleCount, snd_seq_event_t *Events, unsigned long EventCount);
#endif
virtual const plugin_metadata_iface *get_metadata_iface() const
{
return metadata;
}
};
/// Set of metadata produced by LADSPA wrapper for LADSPA-related purposes
struct ladspa_plugin_metadata_set
{
/// LADSPA descriptor
LADSPA_Descriptor descriptor;
/// LADSPA descriptor for DSSI (uses a different name for the plugin, otherwise same as descriptor)
LADSPA_Descriptor descriptor_for_dssi;
#if USE_DSSI
/// Extended DSSI descriptor (points to descriptor_for_dssi for things like name/label/port info etc.)
DSSI_Descriptor dssi_descriptor;
DSSI_Program_Descriptor dssi_default_program;
std::vector<plugin_preset> *presets;
std::vector<DSSI_Program_Descriptor> *preset_descs;
#endif
int input_count, output_count, param_count;
const plugin_metadata_iface *metadata;
ladspa_plugin_metadata_set();
void prepare(const plugin_metadata_iface *md, LADSPA_Handle (*cb_instantiate)(const struct _LADSPA_Descriptor * Descriptor, unsigned long sample_rate));
void prepare_dssi();
~ladspa_plugin_metadata_set();
};
/// A wrapper class for plugin class object (there is only one ladspa_wrapper singleton for many instances of the same plugin)
template<class Module>
struct ladspa_wrapper
{
static ladspa_plugin_metadata_set output;
private:
ladspa_wrapper(const plugin_metadata_iface *md)
{
output.prepare(md, cb_instantiate);
}
public:
/// LADSPA instantiation function (create a plugin instance)
static LADSPA_Handle cb_instantiate(const struct _LADSPA_Descriptor * Descriptor, unsigned long sample_rate)
{
return new ladspa_instance(new Module, &output, sample_rate);
}
/// Get a wrapper singleton - used to prevent initialization order problems which were present in older versions
static ladspa_plugin_metadata_set &get() {
static ladspa_wrapper instance(new typename Module::metadata_class);
return instance.output;
}
};
};
#endif
#endif

View File

@@ -0,0 +1,90 @@
/* Calf DSP Library
* A-weighting filter for
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* Most of code in this file is based on freely
* available other work of other people (filter equations).
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_LOUDNESS_H
#define __CALF_LOUDNESS_H
#include "biquad.h"
namespace dsp {
class aweighter {
public:
biquad_d2<float> bq1, bq2, bq3;
/// Produce one output sample from one input sample
float process(float sample)
{
return bq1.process(bq2.process(bq3.process(sample)));
}
/// Set sample rate (updates filter coefficients)
void set(float sr)
{
// analog coeffs taken from: http://www.diracdelta.co.uk/science/source/a/w/aweighting/source.html
// first we need to adjust them by doing some obscene sort of reverse pre-warping (a broken one, too!)
float f1 = biquad_coeffs<float>::unwarpf(20.6f, sr);
float f2 = biquad_coeffs<float>::unwarpf(107.7f, sr);
float f3 = biquad_coeffs<float>::unwarpf(738.f, sr);
float f4 = biquad_coeffs<float>::unwarpf(12200.f, sr);
// then map s domain to z domain using bilinear transform
// note: f1 and f4 are double poles
bq1.set_bilinear(0, 0, 1, f1*f1, 2 * f1, 1);
bq2.set_bilinear(1, 0, 0, f2*f3, f2 + f3, 1);
bq3.set_bilinear(0, 0, 1, f4*f4, 2 * f4, 1);
// the coeffs above give non-normalized value, so it should be normalized to produce 0dB at 1 kHz
// find actual gain
float gain1kHz = freq_gain(1000.0, sr);
// divide one filter's x[n-m] coefficients by that value
float gc = 1.0 / gain1kHz;
bq1.a0 *= gc;
bq1.a1 *= gc;
bq1.a2 *= gc;
}
/// Reset to zero if at risk of denormals
void sanitize()
{
bq1.sanitize();
bq2.sanitize();
bq3.sanitize();
}
/// Reset state to zero
void reset()
{
bq1.reset();
bq2.reset();
bq3.reset();
}
/// Gain and a given frequency
float freq_gain(float freq, float sr)
{
return bq1.freq_gain(freq, sr) * bq2.freq_gain(freq, sr) * bq3.freq_gain(freq, sr);
}
};
};
#endif

View File

@@ -0,0 +1,101 @@
/* -*- Mode: C ; c-basic-offset: 2 -*- */
/*****************************************************************************
*
* This work is in public domain.
*
* This file 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.
*
* If you have questions, contact Nedko Arnaudov <nedko@arnaudov.name> or
* ask in #lad channel, FreeNode IRC network.
*
*****************************************************************************/
#ifndef LV2_EXTERNAL_UI_H__5AFE09A5_0FB7_47AF_924E_2AF0F8DE8873__INCLUDED
#define LV2_EXTERNAL_UI_H__5AFE09A5_0FB7_47AF_924E_2AF0F8DE8873__INCLUDED
/** UI extension suitable for out-of-process UIs */
#define LV2_EXTERNAL_UI_URI "http://lv2plug.in/ns/extensions/ui#external"
#ifdef __cplusplus
extern "C" {
#endif
#if 0
} /* Adjust editor indent */
#endif
/**
* When LV2_EXTERNAL_UI_URI UI is instantiated, the returned
* LV2UI_Widget handle must be cast to pointer to struct lv2_external_ui.
* UI is created in invisible state.
*/
struct lv2_external_ui
{
/**
* Host calls this function regulary. UI library implementing the
* callback may do IPC or redraw the UI.
*
* @param _this_ the UI context
*/
void (* run)(struct lv2_external_ui * _this_);
/**
* Host calls this function to make the plugin UI visible.
*
* @param _this_ the UI context
*/
void (* show)(struct lv2_external_ui * _this_);
/**
* Host calls this function to make the plugin UI invisible again.
*
* @param _this_ the UI context
*/
void (* hide)(struct lv2_external_ui * _this_);
};
#define LV2_EXTERNAL_UI_RUN(ptr) (ptr)->run(ptr)
#define LV2_EXTERNAL_UI_SHOW(ptr) (ptr)->show(ptr)
#define LV2_EXTERNAL_UI_HIDE(ptr) (ptr)->hide(ptr)
/**
* On UI instantiation, host must supply LV2_EXTERNAL_UI_URI
* feature. LV2_Feature::data must be pointer to struct lv2_external_ui_host. */
struct lv2_external_ui_host
{
/**
* Callback that plugin UI will call
* when UI (GUI window) is closed by user.
* This callback wil; be called during execution of lv2_external_ui::run()
* (i.e. not from background thread).
*
* After this callback is called, UI is defunct. Host must call
* LV2UI_Descriptor::cleanup(). If host wants to make the UI visible
* again UI must be reinstantiated.
*
* @param controller Host context associated with plugin UI, as
* supplied to LV2UI_Descriptor::instantiate()
*/
void (* ui_closed)(LV2UI_Controller controller);
/**
* Optional (may be NULL) "user friendly" identifier which the UI
* may display to allow a user to easily associate this particular
* UI instance with the correct plugin instance as it is represented
* by the host (e.g. "track 1" or "channel 4").
*
* If supplied by host, the string will be referenced only during
* LV2UI_Descriptor::instantiate()
*/
const char * plugin_human_id;
};
#if 0
{ /* Adjust editor indent */
#endif
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* #ifndef LV2_EXTERNAL_UI_H__5AFE09A5_0FB7_47AF_924E_2AF0F8DE8873__INCLUDED */

View File

@@ -0,0 +1,30 @@
/* Calf DSP Library
* LV2-related helper classes and functions
*
* Copyright (C) 2001-2008 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef CALF_LV2HELPERS_H
#define CALF_LV2HELPERS_H
#if USE_LV2
#include <calf/lv2_event.h>
#include <calf/lv2_uri_map.h>
#endif
#endif

View File

@@ -0,0 +1,350 @@
/* Calf DSP Library
* LV2 wrapper templates
*
* Copyright (C) 2001-2008 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef CALF_LV2WRAP_H
#define CALF_LV2WRAP_H
#if USE_LV2
#include <string>
#include <vector>
#include <lv2.h>
#include <calf/giface.h>
#include <calf/lv2_event.h>
#include <calf/lv2_state.h>
#include <calf/lv2_progress.h>
#include <calf/lv2_uri_map.h>
#include <string.h>
namespace calf_plugins {
struct lv2_instance: public plugin_ctl_iface, public progress_report_iface
{
const plugin_metadata_iface *metadata;
audio_module_iface *module;
bool set_srate;
int srate_to_set;
LV2_Event_Buffer *event_data;
LV2_URI_Map_Feature *uri_map;
LV2_Event_Feature *event_feature;
uint32_t midi_event_type;
LV2_Progress *progress_report_feature;
float **ins, **outs, **params;
int out_count;
int real_param_count;
lv2_instance(audio_module_iface *_module)
{
module = _module;
module->get_port_arrays(ins, outs, params);
metadata = module->get_metadata_iface();
out_count = metadata->get_output_count();
real_param_count = metadata->get_param_count();
uri_map = NULL;
event_data = NULL;
progress_report_feature = NULL;
midi_event_type = 0xFFFFFFFF;
srate_to_set = 44100;
set_srate = true;
}
/// This, and not Module::post_instantiate, is actually called by lv2_wrapper class
void post_instantiate()
{
if (progress_report_feature)
module->set_progress_report_iface(this);
module->post_instantiate();
}
virtual bool activate_preset(int bank, int program) {
return false;
}
virtual float get_level(unsigned int port) { return 0.f; }
virtual void execute(int cmd_no) {
module->execute(cmd_no);
}
virtual void report_progress(float percentage, const std::string &message) {
if (progress_report_feature)
(*progress_report_feature->progress)(progress_report_feature->context, percentage, !message.empty() ? message.c_str() : NULL);
}
void send_configures(send_configure_iface *sci) {
module->send_configures(sci);
}
void impl_restore(LV2_State_Retrieve_Function retrieve, void *callback_data)
{
const char *const *vars = module->get_metadata_iface()->get_configure_vars();
if (!vars)
return;
assert(uri_map);
uint32_t string_type = uri_map->uri_to_id(uri_map->callback_data, NULL, "http://lv2plug.in/ns/ext/atom#String");
assert(string_type);
for (unsigned int i = 0; vars[i]; i++)
{
const uint32_t key = uri_map->uri_to_id(uri_map->callback_data, NULL, vars[i]);
size_t len = 0;
uint32_t type = 0;
uint32_t flags = 0;
const void *ptr = (*retrieve)(callback_data, key, &len, &type, &flags);
if (ptr)
{
if (type != string_type)
fprintf(stderr, "Warning: type is %d, expected %d\n", (int)type, (int)string_type);
printf("Calling configure on %s\n", vars[i]);
configure(vars[i], std::string((const char *)ptr, len).c_str());
}
else
configure(vars[i], NULL);
}
}
char *configure(const char *key, const char *value) {
// disambiguation - the plugin_ctl_iface version is just a stub, so don't use it
return module->configure(key, value);
}
void process_events(uint32_t &offset) {
struct LV2_Midi_Event: public LV2_Event {
unsigned char data[1];
};
unsigned char *data = (unsigned char *)(event_data->data);
for (uint32_t i = 0; i < event_data->event_count; i++) {
LV2_Midi_Event *item = (LV2_Midi_Event *)data;
uint32_t ts = item->frames;
// printf("Event: timestamp %d subframes %d type %d vs %d\n", item->frames, item->subframes, item->type, mod->midi_event_type);
if (ts > offset)
{
module->process_slice(offset, ts);
offset = ts;
}
if (item->type == midi_event_type)
{
// printf("Midi message %x %x %x %x %d\n", item->data[0], item->data[1], item->data[2], item->data[3], item->size);
int channel = item->data[0] & 15;
switch(item->data[0] >> 4)
{
case 8: module->note_off(channel, item->data[1], item->data[2]); break;
case 9: module->note_on(channel, item->data[1], item->data[2]); break;
case 11: module->control_change(channel, item->data[1], item->data[2]); break;
case 12: module->program_change(channel, item->data[1]); break;
case 13: module->channel_pressure(channel, item->data[1]); break;
case 14: module->pitch_bend(channel, item->data[1] + 128 * item->data[2] - 8192); break;
}
}
else
if (item->type == 0 && event_feature)
event_feature->lv2_event_unref(event_feature->callback_data, item);
// printf("timestamp %f item size %d first byte %x\n", item->timestamp, item->size, item->data[0]);
data += ((sizeof(LV2_Event) + item->size + 7))&~7;
}
}
virtual float get_param_value(int param_no)
{
// XXXKF hack
if (param_no >= real_param_count)
return 0;
return (*params)[param_no];
}
virtual void set_param_value(int param_no, float value)
{
// XXXKF hack
if (param_no >= real_param_count)
return;
*params[param_no] = value;
}
virtual const plugin_metadata_iface *get_metadata_iface() const { return metadata; }
virtual const line_graph_iface *get_line_graph_iface() const { return module->get_line_graph_iface(); }
virtual int send_status_updates(send_updates_iface *sui, int last_serial) { return module->send_status_updates(sui, last_serial); }
};
struct LV2_Calf_Descriptor {
plugin_ctl_iface *(*get_pci)(LV2_Handle Instance);
};
template<class Module>
struct lv2_wrapper
{
typedef lv2_instance instance;
static LV2_Descriptor descriptor;
static LV2_Calf_Descriptor calf_descriptor;
static LV2_State_Interface state_iface;
std::string uri;
lv2_wrapper()
{
ladspa_plugin_info &info = Module::plugin_info;
uri = "http://calf.sourceforge.net/plugins/" + std::string(info.label);
descriptor.URI = uri.c_str();
descriptor.instantiate = cb_instantiate;
descriptor.connect_port = cb_connect;
descriptor.activate = cb_activate;
descriptor.run = cb_run;
descriptor.deactivate = cb_deactivate;
descriptor.cleanup = cb_cleanup;
descriptor.extension_data = cb_ext_data;
state_iface.save = cb_state_save;
state_iface.restore = cb_state_restore;
calf_descriptor.get_pci = cb_get_pci;
}
static void cb_connect(LV2_Handle Instance, uint32_t port, void *DataLocation)
{
instance *const mod = (instance *)Instance;
const plugin_metadata_iface *md = mod->metadata;
unsigned long ins = md->get_input_count();
unsigned long outs = md->get_output_count();
unsigned long params = md->get_param_count();
if (port < ins)
mod->ins[port] = (float *)DataLocation;
else if (port < ins + outs)
mod->outs[port - ins] = (float *)DataLocation;
else if (port < ins + outs + params) {
int i = port - ins - outs;
mod->params[i] = (float *)DataLocation;
}
else if (md->get_midi() && port == ins + outs + params) {
mod->event_data = (LV2_Event_Buffer *)DataLocation;
}
}
static void cb_activate(LV2_Handle Instance)
{
instance *const mod = (instance *)Instance;
mod->set_srate = true;
}
static void cb_deactivate(LV2_Handle Instance)
{
instance *const mod = (instance *)Instance;
mod->module->deactivate();
}
static LV2_Handle cb_instantiate(const LV2_Descriptor * Descriptor, double sample_rate, const char *bundle_path, const LV2_Feature *const *features)
{
instance *mod = new instance(new Module);
// XXXKF some people use fractional sample rates; we respect them ;-)
mod->srate_to_set = (uint32_t)sample_rate;
mod->set_srate = true;
while(*features)
{
if (!strcmp((*features)->URI, LV2_URI_MAP_URI))
{
mod->uri_map = (LV2_URI_Map_Feature *)((*features)->data);
mod->midi_event_type = mod->uri_map->uri_to_id(
mod->uri_map->callback_data,
"http://lv2plug.in/ns/ext/event",
"http://lv2plug.in/ns/ext/midi#MidiEvent");
}
else if (!strcmp((*features)->URI, LV2_EVENT_URI))
{
mod->event_feature = (LV2_Event_Feature *)((*features)->data);
}
else if (!strcmp((*features)->URI, LV2_PROGRESS_URI))
{
mod->progress_report_feature = (LV2_Progress *)((*features)->data);
}
features++;
}
mod->post_instantiate();
return mod;
}
static plugin_ctl_iface *cb_get_pci(LV2_Handle Instance)
{
return static_cast<plugin_ctl_iface *>(Instance);
}
static void cb_run(LV2_Handle Instance, uint32_t SampleCount)
{
instance *const inst = (instance *)Instance;
audio_module_iface *mod = inst->module;
if (inst->set_srate) {
mod->set_sample_rate(inst->srate_to_set);
mod->activate();
inst->set_srate = false;
}
mod->params_changed();
uint32_t offset = 0;
if (inst->event_data)
{
inst->process_events(offset);
}
inst->module->process_slice(offset, SampleCount);
}
static void cb_cleanup(LV2_Handle Instance)
{
instance *const mod = (instance *)Instance;
delete mod;
}
static const void *cb_ext_data(const char *URI)
{
if (!strcmp(URI, "http://foltman.com/ns/calf-plugin-instance"))
return &calf_descriptor;
if (!strcmp(URI, LV2_STATE_INTERFACE_URI))
return &state_iface;
return NULL;
}
static void cb_state_save(LV2_Handle Instance,
LV2_State_Store_Function store, LV2_State_Handle handle,
uint32_t flags, const LV2_Feature *const * features)
{
instance *const inst = (instance *)Instance;
struct store_state: public send_configure_iface
{
LV2_State_Store_Function store;
void *callback_data;
instance *inst;
uint32_t string_data_type;
virtual void send_configure(const char *key, const char *value)
{
(*store)(callback_data,
inst->uri_map->uri_to_id(inst->uri_map->callback_data, NULL, key),
value,
strlen(value) + 1,
string_data_type,
LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE);
}
};
// A host that supports State MUST support URI-Map as well.
assert(inst->uri_map);
store_state s;
s.store = store;
s.callback_data = handle;
s.inst = inst;
s.string_data_type = inst->uri_map->uri_to_id(inst->uri_map->callback_data, NULL, "http://lv2plug.in/ns/ext/atom#String");
inst->send_configures(&s);
}
static void cb_state_restore(LV2_Handle Instance,
LV2_State_Retrieve_Function retrieve, LV2_State_Handle callback_data,
uint32_t flags, const LV2_Feature *const * features)
{
instance *const inst = (instance *)Instance;
inst->impl_restore(retrieve, callback_data);
}
static lv2_wrapper &get() {
static lv2_wrapper *instance = new lv2_wrapper;
return *instance;
}
};
};
#endif
#endif

View File

@@ -0,0 +1,595 @@
/* Calf DSP Library
* Audio module (plugin) metadata - header file
*
* Copyright (C) 2007-2008 Krzysztof Foltman
* Copyright (C) 2008 Thor Harald Johansen <thj@thj.no>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_METADATA_H
#define __CALF_METADATA_H
#include "giface.h"
#define MONO_VU_METER_PARAMS param_meter_in, param_meter_out, param_clip_in, param_clip_out
#define STEREO_VU_METER_PARAMS param_meter_inL, param_meter_inR, param_meter_outL, param_meter_outR, param_clip_inL, param_clip_inR, param_clip_outL, param_clip_outR
namespace calf_plugins {
struct flanger_metadata: public plugin_metadata<flanger_metadata>
{
public:
enum { par_delay, par_depth, par_rate, par_fb, par_stereo, par_reset, par_amount, par_dryamount, param_count };
enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = false, require_midi = false, rt_capable = true };
PLUGIN_NAME_ID_LABEL("flanger", "flanger", "Flanger")
};
struct phaser_metadata: public plugin_metadata<phaser_metadata>
{
enum { par_freq, par_depth, par_rate, par_fb, par_stages, par_stereo, par_reset, par_amount, par_dryamount, param_count };
enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = false, require_midi = false, rt_capable = true };
PLUGIN_NAME_ID_LABEL("phaser", "phaser", "Phaser")
};
struct filter_metadata: public plugin_metadata<filter_metadata>
{
enum { par_cutoff, par_resonance, par_mode, par_inertia, param_count };
enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, rt_capable = true, require_midi = false, support_midi = false };
PLUGIN_NAME_ID_LABEL("filter", "filter", "Filter")
/// do not export mode and inertia as CVs, as those are settings and not parameters
bool is_cv(int param_no) { return param_no != par_mode && param_no != par_inertia; }
};
/// Filterclavier - metadata
struct filterclavier_metadata: public plugin_metadata<filterclavier_metadata>
{
enum { par_transpose, par_detune, par_max_resonance, par_mode, par_inertia, param_count };
enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, rt_capable = true, require_midi = true, support_midi = true };
PLUGIN_NAME_ID_LABEL("filterclavier", "filterclavier", "Filterclavier")
/// do not export mode and inertia as CVs, as those are settings and not parameters
bool is_cv(int param_no) { return param_no != par_mode && param_no != par_inertia; }
};
struct reverb_metadata: public plugin_metadata<reverb_metadata>
{
enum { par_clip, par_meter_wet, par_meter_out, par_decay, par_hfdamp, par_roomsize, par_diffusion, par_amount, par_dry, par_predelay, par_basscut, par_treblecut, param_count };
enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = false, require_midi = false, rt_capable = true };
PLUGIN_NAME_ID_LABEL("reverb", "reverb", "Reverb")
};
struct vintage_delay_metadata: public plugin_metadata<vintage_delay_metadata>
{
enum { par_bpm, par_divide, par_time_l, par_time_r, par_feedback, par_amount, par_mixmode, par_medium, par_dryamount, par_width, param_count };
enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, rt_capable = true, support_midi = false, require_midi = false };
PLUGIN_NAME_ID_LABEL("vintage_delay", "vintagedelay", "Vintage Delay")
};
struct rotary_speaker_metadata: public plugin_metadata<rotary_speaker_metadata>
{
public:
enum { par_speed, par_spacing, par_shift, par_moddepth, par_treblespeed, par_bassspeed, par_micdistance, par_reflection, par_am_depth, par_test, par_meter_l, par_meter_h, param_count };
enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = true, require_midi = false, rt_capable = true };
PLUGIN_NAME_ID_LABEL("rotary_speaker", "rotaryspeaker", "Rotary Speaker")
};
/// A multitap stereo chorus thing - metadata
struct multichorus_metadata: public plugin_metadata<multichorus_metadata>
{
public:
enum { par_delay, par_depth, par_rate, par_stereo, par_voices, par_vphase, par_amount, par_dryamount, par_freq, par_freq2, par_q, par_overlap, param_count };
enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, rt_capable = true, support_midi = false, require_midi = false };
PLUGIN_NAME_ID_LABEL("multichorus", "multichorus", "Multi Chorus")
};
enum CalfEqMode {
MODE12DB,
MODE24DB,
MODE36DB
};
/// Monosynth - metadata
struct monosynth_metadata: public plugin_metadata<monosynth_metadata>
{
enum { wave_saw, wave_sqr, wave_pulse, wave_sine, wave_triangle, wave_varistep, wave_skewsaw, wave_skewsqr, wave_test1, wave_test2, wave_test3, wave_test4, wave_test5, wave_test6, wave_test7, wave_test8, wave_count };
enum { flt_lp12, flt_lp24, flt_2lp12, flt_hp12, flt_lpbr, flt_hpbr, flt_bp6, flt_2bp6 };
enum { par_wave1, par_wave2, par_pw1, par_pw2, par_detune, par_osc2xpose, par_oscmode, par_oscmix, par_filtertype, par_cutoff, par_resonance, par_cutoffsep, par_env1tocutoff, par_env1tores, par_env1toamp,
par_env1attack, par_env1decay, par_env1sustain, par_env1fade, par_env1release,
par_keyfollow, par_legato, par_portamento, par_vel2filter, par_vel2amp, par_master, par_pwhlrange,
par_lforate, par_lfodelay, par_lfofilter, par_lfopitch, par_lfopw, par_mwhl_lfo, par_scaledetune,
par_env2tocutoff, par_env2tores, par_env2toamp,
par_env2attack, par_env2decay, par_env2sustain, par_env2fade, par_env2release,
par_stretch1, par_window1,
par_lfo1trig, par_lfo2trig,
par_lfo2rate, par_lfo2delay,
param_count };
enum { in_count = 0, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = true, require_midi = true, rt_capable = true };
enum { step_size = 64, step_shift = 6 };
enum { mod_matrix_slots = 10 };
enum {
modsrc_none,
modsrc_velocity,
modsrc_pressure,
modsrc_modwheel,
modsrc_env1,
modsrc_env2,
modsrc_lfo1,
modsrc_lfo2,
modsrc_count,
};
enum {
moddest_none,
moddest_attenuation,
moddest_oscmix,
moddest_cutoff,
moddest_resonance,
moddest_o1detune,
moddest_o2detune,
moddest_o1pw,
moddest_o2pw,
moddest_o1stretch,
moddest_count,
};
PLUGIN_NAME_ID_LABEL("monosynth", "monosynth", "Monosynth")
mod_matrix_metadata mm_metadata;
monosynth_metadata();
/// Lookup of table edit interface
virtual const table_metadata_iface *get_table_metadata_iface(const char *key) const { if (!strcmp(key, "mod_matrix")) return &mm_metadata; else return NULL; }
const char *const *get_configure_vars() const;
};
/// Thor's compressor - metadata
/// Added some meters and stripped the weighting part
struct compressor_metadata: public plugin_metadata<compressor_metadata>
{
enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = false, require_midi = false, rt_capable = true };
enum { param_bypass, param_level_in, MONO_VU_METER_PARAMS,
param_threshold, param_ratio, param_attack, param_release, param_makeup, param_knee, param_detection, param_stereo_link, param_compression,
param_count };
PLUGIN_NAME_ID_LABEL("compressor", "compressor", "Compressor")
};
/// Markus's sidechain compressor - metadata
struct sidechaincompressor_metadata: public plugin_metadata<sidechaincompressor_metadata>
{
enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = false, require_midi = false, rt_capable = true };
enum { param_bypass, param_level_in, MONO_VU_METER_PARAMS,
param_threshold, param_ratio, param_attack, param_release, param_makeup, param_knee, param_detection, param_stereo_link, param_compression,
param_sc_mode, param_f1_freq, param_f2_freq, param_f1_level, param_f2_level,
param_sc_listen, param_f1_active, param_f2_active, param_sc_route, param_sc_level, param_count };
PLUGIN_NAME_ID_LABEL("sidechaincompressor", "sidechaincompressor", "Sidechain Compressor")
};
/// Markus's multibandcompressor - metadata
struct multibandcompressor_metadata: public plugin_metadata<multibandcompressor_metadata>
{
enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = false, require_midi = false, rt_capable = true };
enum { param_bypass, param_level_in, param_level_out,
STEREO_VU_METER_PARAMS,
param_freq0, param_freq1, param_freq2,
param_sep0, param_sep1, param_sep2,
param_q0, param_q1, param_q2,
param_mode,
param_threshold0, param_ratio0, param_attack0, param_release0, param_makeup0, param_knee0,
param_detection0, param_compression0, param_output0, param_bypass0, param_solo0,
param_threshold1, param_ratio1, param_attack1, param_release1, param_makeup1, param_knee1,
param_detection1, param_compression1, param_output1, param_bypass1, param_solo1,
param_threshold2, param_ratio2, param_attack2, param_release2, param_makeup2, param_knee2,
param_detection2, param_compression2, param_output2, param_bypass2, param_solo2,
param_threshold3, param_ratio3, param_attack3, param_release3, param_makeup3, param_knee3,
param_detection3, param_compression3, param_output3, param_bypass3, param_solo3,
param_count };
PLUGIN_NAME_ID_LABEL("multiband_compressor", "multibandcompressor", "Multiband Compressor")
};
/// Markus's deesser - metadata
struct deesser_metadata: public plugin_metadata<deesser_metadata>
{
enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = false, require_midi = false, rt_capable = true };
enum { param_bypass, param_detected, param_compression, param_detected_led, param_clip_out,
param_detection, param_mode,
param_threshold, param_ratio, param_laxity, param_makeup,
param_f1_freq, param_f2_freq, param_f1_level, param_f2_level, param_f2_q,
param_sc_listen, param_count };
PLUGIN_NAME_ID_LABEL("deesser", "deesser", "Deesser")
};
/// Damiens' Gate - metadata
/// Added some meters and stripped the weighting part
struct gate_metadata: public plugin_metadata<gate_metadata>
{
enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = false, require_midi = false, rt_capable = true };
enum { param_bypass, param_level_in, MONO_VU_METER_PARAMS,
param_range, param_threshold, param_ratio, param_attack, param_release, param_makeup, param_knee, param_detection, param_stereo_link, param_gating,
param_count };
PLUGIN_NAME_ID_LABEL("gate", "gate", "Gate")
};
/// Markus's sidechain gate - metadata
struct sidechaingate_metadata: public plugin_metadata<sidechaingate_metadata>
{
enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = false, require_midi = false, rt_capable = true };
enum { param_bypass, param_level_in, MONO_VU_METER_PARAMS,
param_range, param_threshold, param_ratio, param_attack, param_release, param_makeup, param_knee, param_detection, param_stereo_link, param_gating,
param_sc_mode, param_f1_freq, param_f2_freq, param_f1_level, param_f2_level,
param_sc_listen, param_f1_active, param_f2_active, param_sc_route, param_sc_level, param_count };
PLUGIN_NAME_ID_LABEL("sidechaingate", "sidechaingate", "Sidechain Gate")
};
/// Markus's multiband gate - metadata
struct multibandgate_metadata: public plugin_metadata<multibandgate_metadata>
{
enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = false, require_midi = false, rt_capable = true };
enum { param_bypass, param_level_in, param_level_out,
STEREO_VU_METER_PARAMS,
param_freq0, param_freq1, param_freq2,
param_sep0, param_sep1, param_sep2,
param_q0, param_q1, param_q2,
param_mode,
param_range0, param_threshold0, param_ratio0, param_attack0, param_release0, param_makeup0, param_knee0,
param_detection0, param_gating0, param_output0, param_bypass0, param_solo0,
param_range1, param_threshold1, param_ratio1, param_attack1, param_release1, param_makeup1, param_knee1,
param_detection1, param_gating1, param_output1, param_bypass1, param_solo1,
param_range2, param_threshold2, param_ratio2, param_attack2, param_release2, param_makeup2, param_knee2,
param_detection2, param_gating2, param_output2, param_bypass2, param_solo2,
param_range3, param_threshold3, param_ratio3, param_attack3, param_release3, param_makeup3, param_knee3,
param_detection3, param_gating3, param_output3, param_bypass3, param_solo3,
param_count };
PLUGIN_NAME_ID_LABEL("multiband_gate", "multibandgate", "Multiband Gate")
};
/// Markus's limiter - metadata
struct limiter_metadata: public plugin_metadata<limiter_metadata>
{
enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = false, require_midi = false, rt_capable = true };
enum { param_bypass, param_level_in, param_level_out,
STEREO_VU_METER_PARAMS,
param_limit, param_attack, param_release,
param_att,
param_asc, param_asc_led, param_asc_coeff,
param_count };
PLUGIN_NAME_ID_LABEL("limiter", "limiter", "Limiter")
};
/// Markus's multibandlimiter - metadata
struct multibandlimiter_metadata: public plugin_metadata<multibandlimiter_metadata>
{
enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = false, require_midi = false, rt_capable = true };
enum { param_bypass, param_level_in, param_level_out,
STEREO_VU_METER_PARAMS,
param_freq0, param_freq1, param_freq2,
param_sep0, param_sep1, param_sep2,
param_q0, param_q1, param_q2,
param_mode,
param_limit, param_attack, param_release, param_minrel,
param_att0, param_att1, param_att2, param_att3,
param_weight0, param_weight1, param_weight2, param_weight3,
param_release0, param_release1, param_release2, param_release3,
param_solo0, param_solo1, param_solo2, param_solo3,
param_effrelease0, param_effrelease1, param_effrelease2, param_effrelease3,
param_asc, param_asc_led, param_asc_coeff,
param_count };
PLUGIN_NAME_ID_LABEL("multiband_limiter", "multibandlimiter", "Multiband Limiter")
};
/// Markus's 5-band EQ - metadata
struct equalizer5band_metadata: public plugin_metadata<equalizer5band_metadata>
{
enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = false, require_midi = false, rt_capable = true };
enum { param_bypass, param_level_in, param_level_out, STEREO_VU_METER_PARAMS,
param_ls_active, param_ls_level, param_ls_freq,
param_hs_active, param_hs_level, param_hs_freq,
param_p1_active, param_p1_level, param_p1_freq, param_p1_q,
param_p2_active, param_p2_level, param_p2_freq, param_p2_q,
param_p3_active, param_p3_level, param_p3_freq, param_p3_q,
param_count };
// dummy parameter numbers, shouldn't be used EVER, they're only there to avoid pushing LP/HP filters to a separate class
// and potentially making inlining and optimization harder for the compiler
enum { param_lp_active = 0xDEADBEEF, param_hp_active, param_hp_mode, param_lp_mode, param_hp_freq, param_lp_freq };
enum { PeakBands = 3, first_graph_param = param_ls_active, last_graph_param = param_p3_q };
PLUGIN_NAME_ID_LABEL("equalizer5band", "eq5", "Equalizer 5 Band")
};
/// Markus's 8-band EQ - metadata
struct equalizer8band_metadata: public plugin_metadata<equalizer8band_metadata>
{
enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = false, require_midi = false, rt_capable = true };
enum { param_bypass, param_level_in, param_level_out,
STEREO_VU_METER_PARAMS,
param_hp_active, param_hp_freq, param_hp_mode,
param_lp_active, param_lp_freq, param_lp_mode,
param_ls_active, param_ls_level, param_ls_freq,
param_hs_active, param_hs_level, param_hs_freq,
param_p1_active, param_p1_level, param_p1_freq, param_p1_q,
param_p2_active, param_p2_level, param_p2_freq, param_p2_q,
param_p3_active, param_p3_level, param_p3_freq, param_p3_q,
param_p4_active, param_p4_level, param_p4_freq, param_p4_q,
param_count };
enum { PeakBands = 4, first_graph_param = param_hp_active, last_graph_param = param_p4_q };
PLUGIN_NAME_ID_LABEL("equalizer8band", "eq8", "Equalizer 8 Band")
};
/// Markus's 12-band EQ - metadata
struct equalizer12band_metadata: public plugin_metadata<equalizer12band_metadata>
{
enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = false, require_midi = false, rt_capable = true };
enum { param_bypass, param_level_in, param_level_out,
STEREO_VU_METER_PARAMS,
param_hp_active, param_hp_freq, param_hp_mode,
param_lp_active, param_lp_freq, param_lp_mode,
param_ls_active, param_ls_level, param_ls_freq,
param_hs_active, param_hs_level, param_hs_freq,
param_p1_active, param_p1_level, param_p1_freq, param_p1_q,
param_p2_active, param_p2_level, param_p2_freq, param_p2_q,
param_p3_active, param_p3_level, param_p3_freq, param_p3_q,
param_p4_active, param_p4_level, param_p4_freq, param_p4_q,
param_p5_active, param_p5_level, param_p5_freq, param_p5_q,
param_p6_active, param_p6_level, param_p6_freq, param_p6_q,
param_p7_active, param_p7_level, param_p7_freq, param_p7_q,
param_p8_active, param_p8_level, param_p8_freq, param_p8_q,
param_count };
enum { PeakBands = 8, first_graph_param = param_hp_active, last_graph_param = param_p8_q };
PLUGIN_NAME_ID_LABEL("equalizer12band", "eq12", "Equalizer 12 Band")
};
/// Markus's Pulsator - metadata
struct pulsator_metadata: public plugin_metadata<pulsator_metadata>
{
enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = false, require_midi = false, rt_capable = true };
enum { param_bypass, param_level_in, param_level_out, STEREO_VU_METER_PARAMS,
param_mode, param_freq, param_amount, param_offset, param_mono, param_reset, param_count };
PLUGIN_NAME_ID_LABEL("pulsator", "pulsator", "Pulsator")
};
/// Markus's Saturator - metadata
struct saturator_metadata: public plugin_metadata<saturator_metadata>
{
enum { in_count = 2, out_count = 2, ins_optional = 1, outs_optional = 1, support_midi = false, require_midi = false, rt_capable = true };
enum { param_bypass, param_level_in, param_level_out, param_mix, MONO_VU_METER_PARAMS, param_drive, param_blend, param_meter_drive,
param_lp_pre_freq, param_hp_pre_freq, param_lp_post_freq, param_hp_post_freq,
param_p_freq, param_p_level, param_p_q, param_count };
PLUGIN_NAME_ID_LABEL("saturator", "saturator", "Saturator")
};
/// Markus's Exciter - metadata
struct exciter_metadata: public plugin_metadata<exciter_metadata>
{
enum { in_count = 2, out_count = 2, ins_optional = 1, outs_optional = 1, support_midi = false, require_midi = false, rt_capable = true };
enum { param_bypass, param_level_in, param_level_out, param_amount, MONO_VU_METER_PARAMS, param_drive, param_blend, param_meter_drive,
param_freq, param_listen, param_ceil_active, param_ceil, param_count };
PLUGIN_NAME_ID_LABEL("exciter", "exciter", "Exciter")
};
/// Markus's Bass Enhancer - metadata
struct bassenhancer_metadata: public plugin_metadata<bassenhancer_metadata>
{
enum { in_count = 2, out_count = 2, ins_optional = 1, outs_optional = 1, support_midi = false, require_midi = false, rt_capable = true };
enum { param_bypass, param_level_in, param_level_out, param_amount, MONO_VU_METER_PARAMS, param_drive, param_blend, param_meter_drive,
param_freq, param_listen, param_floor_active, param_floor, param_count };
PLUGIN_NAME_ID_LABEL("bassenhancer", "bassenhancer", "Bass Enhancer")
};
/// Markus's Mono Module - metadata
struct stereo_metadata: public plugin_metadata<stereo_metadata>
{
enum { in_count = 2, out_count = 2, ins_optional = 1, outs_optional = 1, support_midi = false, require_midi = false, rt_capable = true };
enum { param_bypass, param_level_in, param_level_out,
STEREO_VU_METER_PARAMS, param_balance_in, param_balance_out, param_softclip,
param_mute_l, param_mute_r, param_phase_l, param_phase_r,
param_mode, param_slev, param_sbal, param_mlev, param_mpan,
param_widener, param_delay,
param_meter_phase,
param_count };
PLUGIN_NAME_ID_LABEL("stereo", "stereo", "Stereo Tools")
};
/// Markus's Mono Module - metadata
struct mono_metadata: public plugin_metadata<mono_metadata>
{
enum { in_count = 1, out_count = 2, ins_optional = 1, outs_optional = 1, support_midi = false, require_midi = false, rt_capable = true };
enum { param_bypass, param_level_in, param_level_out,
param_meter_in, param_meter_outL, param_meter_outR, param_clip_in,param_clip_outL, param_clip_outR,
param_balance_out, param_softclip,
param_mute_l, param_mute_r, param_phase_l, param_phase_r,
param_delay,
param_count };
PLUGIN_NAME_ID_LABEL("mono", "mono", "Mono Input")
};
/// Organ - enums for parameter IDs etc. (this mess is caused by organ split between plugin and generic class - which was
/// a bad design decision and should be sorted out some day) XXXKF @todo
struct organ_enums
{
enum {
par_drawbar1, par_drawbar2, par_drawbar3, par_drawbar4, par_drawbar5, par_drawbar6, par_drawbar7, par_drawbar8, par_drawbar9,
par_frequency1, par_frequency2, par_frequency3, par_frequency4, par_frequency5, par_frequency6, par_frequency7, par_frequency8, par_frequency9,
par_waveform1, par_waveform2, par_waveform3, par_waveform4, par_waveform5, par_waveform6, par_waveform7, par_waveform8, par_waveform9,
par_detune1, par_detune2, par_detune3, par_detune4, par_detune5, par_detune6, par_detune7, par_detune8, par_detune9,
par_phase1, par_phase2, par_phase3, par_phase4, par_phase5, par_phase6, par_phase7, par_phase8, par_phase9,
par_pan1, par_pan2, par_pan3, par_pan4, par_pan5, par_pan6, par_pan7, par_pan8, par_pan9,
par_routing1, par_routing2, par_routing3, par_routing4, par_routing5, par_routing6, par_routing7, par_routing8, par_routing9,
par_foldover,
par_percdecay, par_perclevel, par_percwave, par_percharm, par_percvel2amp,
par_percfmdecay, par_percfmdepth, par_percfmwave, par_percfmharm, par_percvel2fm,
par_perctrigger, par_percstereo,
par_filterchain,
par_filter1type,
par_master,
par_f1cutoff, par_f1res, par_f1env1, par_f1env2, par_f1env3, par_f1keyf,
par_f2cutoff, par_f2res, par_f2env1, par_f2env2, par_f2env3, par_f2keyf,
par_eg1attack, par_eg1decay, par_eg1sustain, par_eg1release, par_eg1velscl, par_eg1ampctl,
par_eg2attack, par_eg2decay, par_eg2sustain, par_eg2release, par_eg2velscl, par_eg2ampctl,
par_eg3attack, par_eg3decay, par_eg3sustain, par_eg3release, par_eg3velscl, par_eg3ampctl,
par_lforate, par_lfoamt, par_lfowet, par_lfophase, par_lfomode, par_lfotype,
par_transpose, par_detune,
par_polyphony,
par_quadenv,
par_pwhlrange,
par_bassfreq,
par_bassgain,
par_treblefreq,
par_treblegain,
param_count
};
enum organ_waveform {
wave_sine,
wave_sinepl1, wave_sinepl2, wave_sinepl3,
wave_ssaw, wave_ssqr, wave_spls, wave_saw, wave_sqr, wave_pulse, wave_sinepl05, wave_sqr05, wave_halfsin, wave_clvg, wave_bell, wave_bell2,
wave_w1, wave_w2, wave_w3, wave_w4, wave_w5, wave_w6, wave_w7, wave_w8, wave_w9,
wave_dsaw, wave_dsqr, wave_dpls,
wave_count_small,
wave_strings = wave_count_small,
wave_strings2,
wave_sinepad,
wave_bellpad,
wave_space,
wave_choir,
wave_choir2,
wave_choir3,
wave_count,
wave_count_big = wave_count - wave_count_small
};
enum {
ampctl_none,
ampctl_direct,
ampctl_f1,
ampctl_f2,
ampctl_all,
ampctl_count
};
enum {
lfotype_allpass = 0,
lfotype_cv1,
lfotype_cv2,
lfotype_cv3,
lfotype_cvfull,
lfotype_count
};
enum {
lfomode_off = 0,
lfomode_direct,
lfomode_filter1,
lfomode_filter2,
lfomode_voice,
lfomode_global,
lfomode_count
};
enum {
perctrig_first = 0,
perctrig_each,
perctrig_eachplus,
perctrig_polyphonic,
perctrig_count
};
};
/// Organ - metadata
struct organ_metadata: public organ_enums, public plugin_metadata<organ_metadata>
{
enum { in_count = 0, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = true, require_midi = true, rt_capable = true };
PLUGIN_NAME_ID_LABEL("organ", "organ", "Organ")
public:
plugin_command_info *get_commands();
const char *const *get_configure_vars() const;
};
/// FluidSynth - metadata
struct fluidsynth_metadata: public plugin_metadata<fluidsynth_metadata>
{
enum { par_master, par_interpolation, par_reverb, par_chorus, param_count };
enum { in_count = 0, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = true, require_midi = true, rt_capable = false };
PLUGIN_NAME_ID_LABEL("fluidsynth", "fluidsynth", "Fluidsynth")
public:
const char *const *get_configure_vars() const;
};
/// Wavetable - metadata
struct wavetable_metadata: public plugin_metadata<wavetable_metadata>
{
enum {
wt_fmshiny,
wt_fmshiny2,
wt_rezo,
wt_metal,
wt_bell,
wt_blah,
wt_pluck,
wt_stretch,
wt_stretch2,
wt_hardsync,
wt_hardsync2,
wt_softsync,
wt_bell2,
wt_bell3,
wt_tine,
wt_tine2,
wt_clav,
wt_clav2,
wt_gtr,
wt_gtr2,
wt_gtr3,
wt_gtr4,
wt_gtr5,
wt_reed,
wt_reed2,
wt_silver,
wt_brass,
wt_multi,
wt_multi2,
wt_count
};
enum {
modsrc_none,
modsrc_velocity,
modsrc_pressure,
modsrc_modwheel,
modsrc_env1,
modsrc_env2,
modsrc_env3,
modsrc_count,
};
enum {
moddest_none,
moddest_attenuation,
moddest_oscmix,
moddest_cutoff,
moddest_resonance,
moddest_o1shift,
moddest_o2shift,
moddest_o1detune,
moddest_o2detune,
moddest_count,
};
enum {
par_o1wave, par_o1offset, par_o1transpose, par_o1detune, par_o1level,
par_o2wave, par_o2offset, par_o2transpose, par_o2detune, par_o2level,
par_eg1attack, par_eg1decay, par_eg1sustain, par_eg1fade, par_eg1release, par_eg1velscl,
par_eg2attack, par_eg2decay, par_eg2sustain, par_eg2fade, par_eg2release, par_eg2velscl,
par_eg3attack, par_eg3decay, par_eg3sustain, par_eg3fade, par_eg3release, par_eg3velscl,
par_pwhlrange,
param_count };
enum { in_count = 0, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = true, require_midi = true, rt_capable = true };
enum { mod_matrix_slots = 10 };
enum { step_size = 64 };
PLUGIN_NAME_ID_LABEL("wavetable", "wavetable", "Wavetable")
mod_matrix_metadata mm_metadata;
wavetable_metadata();
/// Lookup of table edit interface
virtual const table_metadata_iface *get_table_metadata_iface(const char *key) const { if (!strcmp(key, "mod_matrix")) return &mm_metadata; else return NULL; }
};
};
#endif

View File

@@ -0,0 +1,125 @@
/* Calf DSP Library
* Modulation matrix boilerplate code.
*
* Copyright (C) 2009 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_MODMATRIX_H
#define __CALF_MODMATRIX_H
#include "giface.h"
#include <stdio.h>
namespace dsp {
/// Single entry in modulation matrix
struct modulation_entry
{
/// Mapped source
int src1;
/// Source mapping mode
calf_plugins::mod_matrix_metadata::mapping_mode mapping;
/// Unmapped modulating source
int src2;
/// Modulation amount
float amount;
/// Modulation destination
int dest;
modulation_entry() {
reset();
}
/// Reset the row to default
void reset() {
src1 = 0;
src2 = 0;
mapping = calf_plugins::mod_matrix_metadata::map_positive;
amount = 0.f;
dest = 0;
}
};
};
namespace calf_plugins {
class mod_matrix_impl
{
protected:
dsp::modulation_entry *matrix;
mod_matrix_metadata *metadata;
unsigned int matrix_rows;
/// Polynomials for different scaling modes (1, x, x^2)
static const float scaling_coeffs[calf_plugins::mod_matrix_metadata::map_type_count][3];
public:
mod_matrix_impl(dsp::modulation_entry *_matrix, calf_plugins::mod_matrix_metadata *_metadata);
/// Process modulation matrix, calculate outputs from inputs
inline void calculate_modmatrix(float *moddest, int moddest_count, float *modsrc)
{
for (int i = 0; i < moddest_count; i++)
moddest[i] = 0;
for (unsigned int i = 0; i < matrix_rows; i++)
{
dsp::modulation_entry &slot = matrix[i];
if (slot.dest) {
float value = modsrc[slot.src1];
const float *c = scaling_coeffs[slot.mapping];
value = c[0] + c[1] * value + c[2] * value * value;
moddest[slot.dest] += value * modsrc[slot.src2] * slot.amount;
}
}
}
void send_configures(send_configure_iface *);
char *configure(const char *key, const char *value);
/// Return a list of configure variables used by the modulation matrix
template<int rows>
static const char **get_configure_vars()
{
static std::vector<std::string> names_vector;
static const char *names[rows * 5 + 1];
if (names[0] == NULL)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < 5; j++)
{
char buf[40];
sprintf(buf, "mod_matrix:%d,%d", i, j);
names_vector.push_back(buf);
}
}
for (size_t i = 0; i < names_vector.size(); i++)
names[i] = names_vector[i].c_str();
names[names_vector.size()] = NULL;
}
return names;
}
private:
std::string get_cell(int row, int column) const;
void set_cell(int row, int column, const std::string &src, std::string &error);
};
};
#endif

View File

@@ -0,0 +1,35 @@
#ifdef PER_MODULE_ITEM
PER_MODULE_ITEM(filter, false, "filter")
PER_MODULE_ITEM(filterclavier, false, "filterclavier")
PER_MODULE_ITEM(flanger, false, "flanger")
PER_MODULE_ITEM(reverb, false, "reverb")
PER_MODULE_ITEM(monosynth, true, "monosynth")
PER_MODULE_ITEM(vintage_delay, false, "vintagedelay")
PER_MODULE_ITEM(organ, true, "organ")
PER_MODULE_ITEM(rotary_speaker, false, "rotaryspeaker")
PER_MODULE_ITEM(phaser, false, "phaser")
PER_MODULE_ITEM(multichorus, false, "multichorus")
PER_MODULE_ITEM(compressor, false, "compressor")
PER_MODULE_ITEM(sidechaincompressor, false, "sidechaincompressor")
PER_MODULE_ITEM(multibandcompressor, false, "multibandcompressor")
PER_MODULE_ITEM(deesser, false, "deesser")
PER_MODULE_ITEM(gate, false, "gate")
PER_MODULE_ITEM(sidechaingate, false, "sidechaingate")
PER_MODULE_ITEM(multibandgate, false, "multibandgate")
PER_MODULE_ITEM(limiter, false, "limiter")
PER_MODULE_ITEM(multibandlimiter, false, "multibandlimiter")
PER_MODULE_ITEM(pulsator, false, "pulsator")
PER_MODULE_ITEM(equalizer5band, false, "eq5")
PER_MODULE_ITEM(equalizer8band, false, "eq8")
PER_MODULE_ITEM(equalizer12band, false, "eq12")
PER_MODULE_ITEM(saturator, false, "saturator")
PER_MODULE_ITEM(exciter, false, "exciter")
PER_MODULE_ITEM(bassenhancer, false, "bassenhancer")
PER_MODULE_ITEM(mono, false, "mono")
PER_MODULE_ITEM(stereo, false, "stereo")
#ifdef ENABLE_EXPERIMENTAL
PER_MODULE_ITEM(fluidsynth, true, "fluidsynth")
PER_MODULE_ITEM(wavetable, true, "wavetable")
#endif
#undef PER_MODULE_ITEM
#endif

View File

@@ -0,0 +1,303 @@
/* Calf DSP plugin pack
* Assorted plugins
*
* Copyright (C) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen and others
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef CALF_MODULES_H
#define CALF_MODULES_H
#include <assert.h>
#include <limits.h>
#include "biquad.h"
#include "inertia.h"
#include "audio_fx.h"
#include "giface.h"
#include "metadata.h"
#include "loudness.h"
namespace calf_plugins {
struct ladspa_plugin_info;
class reverb_audio_module: public audio_module<reverb_metadata>
{
public:
dsp::reverb reverb;
dsp::simple_delay<16384, dsp::stereo_sample<float> > pre_delay;
dsp::onepole<float> left_lo, right_lo, left_hi, right_hi;
uint32_t srate;
dsp::gain_smoothing amount, dryamount;
int predelay_amt;
float meter_wet, meter_out;
uint32_t clip;
void params_changed();
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
void activate();
void set_sample_rate(uint32_t sr);
void deactivate();
};
class vintage_delay_audio_module: public audio_module<vintage_delay_metadata>
{
public:
// 1MB of delay memory per channel... uh, RAM is cheap
enum { MAX_DELAY = 262144, ADDR_MASK = MAX_DELAY - 1 };
enum { MIXMODE_STEREO, MIXMODE_PINGPONG, MIXMODE_LR, MIXMODE_RL };
float buffers[2][MAX_DELAY];
int bufptr, deltime_l, deltime_r, mixmode, medium, old_medium;
/// number of table entries written (value is only important when it is less than MAX_DELAY, which means that the buffer hasn't been totally filled yet)
int age;
dsp::gain_smoothing amt_left, amt_right, fb_left, fb_right, dry, chmix;
dsp::biquad_d2<float> biquad_left[2], biquad_right[2];
uint32_t srate;
vintage_delay_audio_module();
void params_changed();
void activate();
void deactivate();
void set_sample_rate(uint32_t sr);
void calc_filters();
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
};
template<typename FilterClass, typename Metadata>
class filter_module_with_inertia: public audio_module<Metadata>, public FilterClass
{
public:
/// These are pointers to the ins, outs, params arrays in the main class
typedef filter_module_with_inertia inertia_filter_module;
using audio_module<Metadata>::ins;
using audio_module<Metadata>::outs;
using audio_module<Metadata>::params;
dsp::inertia<dsp::exponential_ramp> inertia_cutoff, inertia_resonance, inertia_gain;
dsp::once_per_n timer;
bool is_active;
mutable volatile int last_generation, last_calculated_generation;
filter_module_with_inertia(float **ins, float **outs, float **params)
: inertia_cutoff(dsp::exponential_ramp(128), 20)
, inertia_resonance(dsp::exponential_ramp(128), 20)
, inertia_gain(dsp::exponential_ramp(128), 1.0)
, timer(128)
, is_active(false)
, last_generation(-1)
, last_calculated_generation(-2)
{}
void calculate_filter()
{
float freq = inertia_cutoff.get_last();
// printf("freq=%g inr.cnt=%d timer.left=%d\n", freq, inertia_cutoff.count, timer.left);
// XXXKF this is resonance of a single stage, obviously for three stages, resonant gain will be different
float q = inertia_resonance.get_last();
int mode = dsp::fastf2i_drm(*params[Metadata::par_mode]);
// printf("freq = %f q = %f mode = %d\n", freq, q, mode);
int inertia = dsp::fastf2i_drm(*params[Metadata::par_inertia]);
if (inertia != inertia_cutoff.ramp.length()) {
inertia_cutoff.ramp.set_length(inertia);
inertia_resonance.ramp.set_length(inertia);
inertia_gain.ramp.set_length(inertia);
}
FilterClass::calculate_filter(freq, q, mode, inertia_gain.get_last());
}
virtual void params_changed()
{
calculate_filter();
}
void on_timer()
{
int gen = last_generation;
inertia_cutoff.step();
inertia_resonance.step();
inertia_gain.step();
calculate_filter();
last_calculated_generation = gen;
}
void activate()
{
params_changed();
FilterClass::filter_activate();
timer = dsp::once_per_n(FilterClass::srate / 1000);
timer.start();
is_active = true;
}
void set_sample_rate(uint32_t sr)
{
FilterClass::srate = sr;
}
void deactivate()
{
is_active = false;
}
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask) {
// printf("sr=%d cutoff=%f res=%f mode=%f\n", FilterClass::srate, *params[Metadata::par_cutoff], *params[Metadata::par_resonance], *params[Metadata::par_mode]);
uint32_t ostate = 0;
numsamples += offset;
while(offset < numsamples) {
uint32_t numnow = numsamples - offset;
// if inertia's inactive, we can calculate the whole buffer at once
if (inertia_cutoff.active() || inertia_resonance.active() || inertia_gain.active())
numnow = timer.get(numnow);
if (outputs_mask & 1) {
ostate |= FilterClass::process_channel(0, ins[0] + offset, outs[0] + offset, numnow, inputs_mask & 1);
}
if (outputs_mask & 2) {
ostate |= FilterClass::process_channel(1, ins[1] + offset, outs[1] + offset, numnow, inputs_mask & 2);
}
if (timer.elapsed()) {
on_timer();
}
offset += numnow;
}
return ostate;
}
};
/// biquad filter module
class filter_audio_module:
public filter_module_with_inertia<dsp::biquad_filter_module, filter_metadata>,
public frequency_response_line_graph
{
mutable float old_cutoff, old_resonance, old_mode;
public:
filter_audio_module()
: filter_module_with_inertia<dsp::biquad_filter_module, filter_metadata>(ins, outs, params)
{
last_generation = 0;
old_mode = old_resonance = old_cutoff = -1;
}
void params_changed()
{
inertia_cutoff.set_inertia(*params[par_cutoff]);
inertia_resonance.set_inertia(*params[par_resonance]);
inertia_filter_module::params_changed();
}
bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const;
};
/// Filterclavier --- MIDI controlled filter by Hans Baier
class filterclavier_audio_module:
public filter_module_with_inertia<dsp::biquad_filter_module, filterclavier_metadata>,
public frequency_response_line_graph
{
using audio_module<filterclavier_metadata>::ins;
using audio_module<filterclavier_metadata>::outs;
using audio_module<filterclavier_metadata>::params;
const float min_gain;
const float max_gain;
int last_note;
int last_velocity;
public:
filterclavier_audio_module();
void params_changed();
void activate();
void set_sample_rate(uint32_t sr);
void deactivate();
/// MIDI control
virtual void note_on(int channel, int note, int vel);
virtual void note_off(int channel, int note, int vel);
bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
private:
void adjust_gain_according_to_filter_mode(int velocity);
};
#define MATH_E 2.718281828
class mono_audio_module:
public audio_module<mono_metadata>
{
typedef mono_audio_module AM;
uint32_t srate;
bool active;
uint32_t clip_in, clip_outL, clip_outR;
float meter_in, meter_outL, meter_outR;
float * buffer;
unsigned int pos;
unsigned int buffer_size;
void softclip(float &s) {
int ph = s / fabs(s);
s = s > 0.63 ? ((0.63 + 0.36) * ph * (1 - pow(MATH_E, (1.f / 3) * (0.63 + s * ph)))) : s;
}
public:
mono_audio_module();
void params_changed();
void activate();
void set_sample_rate(uint32_t sr);
void deactivate();
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
};
class stereo_audio_module:
public audio_module<stereo_metadata>
{
typedef stereo_audio_module AM;
float LL, LR, RL, RR;
uint32_t srate;
bool active;
uint32_t clip_inL, clip_inR, clip_outL, clip_outR;
float meter_inL, meter_inR, meter_outL, meter_outR, meter_phase;
float * buffer;
unsigned int pos;
unsigned int buffer_size;
void softclip(float &s) {
int ph = s / fabs(s);
s = s > 0.63 ? ((0.63 + 0.36) * ph * (1 - pow(MATH_E, (1.f / 3) * (0.63 + s * ph)))) : s;
}
public:
stereo_audio_module();
void params_changed();
void activate();
void set_sample_rate(uint32_t sr);
void deactivate();
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
};
};
#endif

View File

@@ -0,0 +1,334 @@
/* Calf DSP plugin pack
* Compression related plugins
*
* Copyright (C) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen and others
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef CALF_MODULES_COMP_H
#define CALF_MODULES_COMP_H
#include <assert.h>
#include <limits.h>
#include "biquad.h"
#include "inertia.h"
#include "audio_fx.h"
#include "giface.h"
#include "loudness.h"
#include "metadata.h"
#include "plugin_tools.h"
namespace calf_plugins {
/// Not a true _audio_module style class, just pretends to be one!
/// Main gain reduction routine by Thor called by various audio modules
class gain_reduction_audio_module
{
private:
float linSlope, detected, kneeSqrt, kneeStart, linKneeStart, kneeStop;
float compressedKneeStop, adjKneeStart, thres;
float attack, release, threshold, ratio, knee, makeup, detection, stereo_link, bypass, mute, meter_out, meter_comp;
mutable float old_threshold, old_ratio, old_knee, old_makeup, old_bypass, old_mute, old_detection, old_stereo_link;
mutable volatile int last_generation;
uint32_t srate;
bool is_active;
inline float output_level(float slope) const;
inline float output_gain(float linSlope, bool rms) const;
public:
gain_reduction_audio_module();
void set_params(float att, float rel, float thr, float rat, float kn, float mak, float det, float stl, float byp, float mu);
void update_curve();
void process(float &left, float &right, const float *det_left = NULL, const float *det_right = NULL);
void activate();
void deactivate();
int id;
void set_sample_rate(uint32_t sr);
float get_output_level();
float get_comp_level();
bool get_graph(int subindex, float *data, int points, cairo_iface *context) const;
bool get_dot(int subindex, float &x, float &y, int &size, cairo_iface *context) const;
bool get_gridline(int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
int get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const;
};
/// Not a true _audio_module style class, just pretends to be one!
/// Main gate routine by Damien called by various audio modules
class expander_audio_module {
private:
float linSlope, peak, detected, kneeSqrt, kneeStart, linKneeStart, kneeStop, linKneeStop;
float compressedKneeStop, adjKneeStart, range, thres, attack_coeff, release_coeff;
float attack, release, threshold, ratio, knee, makeup, detection, stereo_link, bypass, mute, meter_out, meter_gate;
mutable float old_threshold, old_ratio, old_knee, old_makeup, old_bypass, old_range, old_trigger, old_mute, old_detection, old_stereo_link;
mutable volatile int last_generation;
inline float output_level(float slope) const;
inline float output_gain(float linSlope, bool rms) const;
public:
uint32_t srate;
bool is_active;
expander_audio_module();
void set_params(float att, float rel, float thr, float rat, float kn, float mak, float det, float stl, float byp, float mu, float ran);
void update_curve();
void process(float &left, float &right, const float *det_left = NULL, const float *det_right = NULL);
void activate();
void deactivate();
int id;
void set_sample_rate(uint32_t sr);
float get_output_level();
float get_expander_level();
bool get_graph(int subindex, float *data, int points, cairo_iface *context) const;
bool get_dot(int subindex, float &x, float &y, int &size, cairo_iface *context) const;
bool get_gridline(int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
int get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const;
};
/// Compressor by Thor
class compressor_audio_module: public audio_module<compressor_metadata>, public line_graph_iface {
private:
typedef compressor_audio_module AM;
stereo_in_out_metering<compressor_metadata> meters;
gain_reduction_audio_module compressor;
public:
typedef std::complex<double> cfloat;
uint32_t srate;
bool is_active;
mutable volatile int last_generation, last_calculated_generation;
compressor_audio_module();
void activate();
void deactivate();
void params_changed();
void set_sample_rate(uint32_t sr);
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const;
bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const;
};
/// Sidecain Compressor by Markus Schmidt (based on Thor's compressor and Krzysztof's filters)
class sidechaincompressor_audio_module: public audio_module<sidechaincompressor_metadata>, public frequency_response_line_graph {
private:
typedef sidechaincompressor_audio_module AM;
enum CalfScModes {
WIDEBAND,
DEESSER_WIDE,
DEESSER_SPLIT,
DERUMBLER_WIDE,
DERUMBLER_SPLIT,
WEIGHTED_1,
WEIGHTED_2,
WEIGHTED_3,
BANDPASS_1,
BANDPASS_2
};
enum CalfScRoute {
STEREO,
RIGHT_LEFT,
LEFT_RIGHT
};
mutable float f1_freq_old, f2_freq_old, f1_level_old, f2_level_old;
mutable float f1_freq_old1, f2_freq_old1, f1_level_old1, f2_level_old1;
CalfScModes sc_mode;
mutable CalfScModes sc_mode_old, sc_mode_old1;
float f1_active, f2_active;
stereo_in_out_metering<sidechaincompressor_metadata> meters;
gain_reduction_audio_module compressor;
dsp::biquad_d2<float> f1L, f1R, f2L, f2R;
public:
typedef std::complex<double> cfloat;
uint32_t srate;
bool is_active;
mutable volatile int last_generation, last_calculated_generation;
sidechaincompressor_audio_module();
void activate();
void deactivate();
void params_changed();
cfloat h_z(const cfloat &z) const;
float freq_gain(int index, double freq, uint32_t sr) const;
void set_sample_rate(uint32_t sr);
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const;
bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const;
};
/// Multibandcompressor by Markus Schmidt (based on Thor's compressor and Krzysztof's filters)
class multibandcompressor_audio_module: public audio_module<multibandcompressor_metadata>, public line_graph_iface {
private:
typedef multibandcompressor_audio_module AM;
static const int strips = 4;
bool solo[strips];
bool no_solo;
uint32_t clip_inL, clip_inR, clip_outL, clip_outR;
float meter_inL, meter_inR, meter_outL, meter_outR;
gain_reduction_audio_module strip[strips];
dsp::biquad_d2<float> lpL[strips - 1][3], lpR[strips - 1][3], hpL[strips - 1][3], hpR[strips - 1][3];
float freq_old[strips - 1], sep_old[strips - 1], q_old[strips - 1];
int mode, mode_old;
public:
uint32_t srate;
bool is_active;
multibandcompressor_audio_module();
void activate();
void deactivate();
void params_changed();
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
void set_sample_rate(uint32_t sr);
const gain_reduction_audio_module *get_strip_by_param_index(int index) const;
virtual bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
virtual bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const;
virtual bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
virtual int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const;
};
/// Deesser by Markus Schmidt (based on Thor's compressor and Krzyexpander_audio_modulesztof's filters)
class deesser_audio_module: public audio_module<deesser_metadata>, public frequency_response_line_graph {
private:
enum CalfDeessModes {
WIDE,
SPLIT
};
mutable float f1_freq_old, f2_freq_old, f1_level_old, f2_level_old, f2_q_old;
mutable float f1_freq_old1, f2_freq_old1, f1_level_old1, f2_level_old1, f2_q_old1;
uint32_t detected_led;
float detected, clip_out;
uint32_t clip_led;
gain_reduction_audio_module compressor;
dsp::biquad_d2<float> hpL, hpR, lpL, lpR, pL, pR;
public:
uint32_t srate;
bool is_active;
mutable volatile int last_generation, last_calculated_generation;
deesser_audio_module();
void activate();
void deactivate();
void params_changed();
float freq_gain(int index, double freq, uint32_t sr) const
{
return hpL.freq_gain(freq, sr) * pL.freq_gain(freq, sr);
}
void set_sample_rate(uint32_t sr);
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const;
};
/// Gate by Damien
class gate_audio_module: public audio_module<gate_metadata>, public line_graph_iface {
private:
typedef gate_audio_module AM;
stereo_in_out_metering<gate_metadata> meters;
expander_audio_module gate;
public:
typedef std::complex<double> cfloat;
uint32_t srate;
bool is_active;
mutable volatile int last_generation, last_calculated_generation;
gate_audio_module();
void activate();
void deactivate();
void params_changed();
void set_sample_rate(uint32_t sr);
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const;
bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const;
};
/// Sidecain Gate by Markus Schmidt (based on Damiens's gate and Krzysztof's filters)
class sidechaingate_audio_module: public audio_module<sidechaingate_metadata>, public frequency_response_line_graph {
private:
typedef sidechaingate_audio_module AM;
enum CalfScModes {
WIDEBAND,
HIGHGATE_WIDE,
HIGHGATE_SPLIT,
LOWGATE_WIDE,
LOWGATE_SPLIT,
WEIGHTED_1,
WEIGHTED_2,
WEIGHTED_3,
BANDPASS_1,
BANDPASS_2
};
enum CalfScRoute {
STEREO,
RIGHT_LEFT,
LEFT_RIGHT
};
mutable float f1_freq_old, f2_freq_old, f1_level_old, f2_level_old;
mutable float f1_freq_old1, f2_freq_old1, f1_level_old1, f2_level_old1;
CalfScModes sc_mode;
mutable CalfScModes sc_mode_old, sc_mode_old1;
float f1_active, f2_active;
stereo_in_out_metering<sidechaingate_metadata> meters;
expander_audio_module gate;
dsp::biquad_d2<float> f1L, f1R, f2L, f2R;
public:
typedef std::complex<double> cfloat;
uint32_t srate;
bool is_active;
mutable volatile int last_generation, last_calculated_generation;
sidechaingate_audio_module();
void activate();
void deactivate();
void params_changed();
cfloat h_z(const cfloat &z) const;
float freq_gain(int index, double freq, uint32_t sr) const;
void set_sample_rate(uint32_t sr);
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const;
bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const;
};
/// Multibandgate by Markus Schmidt (based on Damiens's gate and Krzysztof's filters)
class multibandgate_audio_module: public audio_module<multibandgate_metadata>, public line_graph_iface {
private:
typedef multibandgate_audio_module AM;
static const int strips = 4;
bool solo[strips];
bool no_solo;
uint32_t clip_inL, clip_inR, clip_outL, clip_outR;
float meter_inL, meter_inR, meter_outL, meter_outR;
expander_audio_module gate[strips];
dsp::biquad_d2<float> lpL[strips - 1][3], lpR[strips - 1][3], hpL[strips - 1][3], hpR[strips - 1][3];
float freq_old[strips - 1], sep_old[strips - 1], q_old[strips - 1];
int mode, mode_old;
public:
uint32_t srate;
bool is_active;
multibandgate_audio_module();
void activate();
void deactivate();
void params_changed();
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
void set_sample_rate(uint32_t sr);
const expander_audio_module *get_strip_by_param_index(int index) const;
virtual bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
virtual bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const;
virtual bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
virtual int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const;
};
};
#endif

View File

@@ -0,0 +1,113 @@
/* Calf DSP Library
* Prototype audio modules
*
* Copyright (C) 2008 Thor Harald Johansen <thj@thj.no>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_MODULES_DEV_H
#define __CALF_MODULES_DEV_H
#include <calf/metadata.h>
#if ENABLE_EXPERIMENTAL
#include <fluidsynth.h>
#endif
namespace calf_plugins {
#if ENABLE_EXPERIMENTAL
/// Tiny wrapper for fluidsynth
class fluidsynth_audio_module: public audio_module<fluidsynth_metadata>
{
protected:
/// Current sample rate
uint32_t srate;
/// FluidSynth Settings object
fluid_settings_t *settings;
/// FluidSynth Synth object
fluid_synth_t *synth;
/// Soundfont filename
std::string soundfont;
/// Soundfont filename (as received from Fluidsynth)
std::string soundfont_name;
/// TAB-separated preset list (preset+128*bank TAB preset name LF)
std::string soundfont_preset_list;
/// FluidSynth assigned SoundFont ID
int sfid;
/// Map of preset+128*bank to preset name
std::map<uint32_t, std::string> sf_preset_names;
/// Last selected preset+128*bank
uint32_t last_selected_preset;
/// Serial number of status data
int status_serial;
/// Preset number to set on next process() call
volatile int set_preset;
/// Update last_selected_preset based on synth object state
void update_preset_num();
/// Create a fluidsynth object and load the current soundfont
fluid_synth_t *create_synth(int &new_sfid);
public:
/// Constructor to initialize handles to NULL
fluidsynth_audio_module();
void post_instantiate();
void set_sample_rate(uint32_t sr) { srate = sr; }
/// Handle MIDI Note On message (by sending it to fluidsynth)
void note_on(int channel, int note, int vel);
/// Handle MIDI Note Off message (by sending it to fluidsynth)
void note_off(int channel, int note, int vel);
/// Handle pitch bend message.
inline void pitch_bend(int channel, int value)
{
fluid_synth_pitch_bend(synth, 0, value + 0x2000);
}
/// Handle control change messages.
void control_change(int channel, int controller, int value);
/// Handle program change messages.
void program_change(int channel, int program);
/// Update variables from control ports.
void params_changed() {
}
void activate();
void deactivate();
/// No CV inputs for now
bool is_cv(int param_no) { return false; }
/// Practically all the stuff here is noisy... for now
bool is_noisy(int param_no) { return true; }
/// Main processing function
uint32_t process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask);
/// DSSI-style configure function for handling string port data
char *configure(const char *key, const char *value);
void send_configures(send_configure_iface *sci);
int send_status_updates(send_updates_iface *sui, int last_serial);
uint32_t message_run(const void *valid_inputs, void *output_ports) {
// silence a default printf (which is kind of a warning about unhandled message_run)
return 0;
}
~fluidsynth_audio_module();
};
#endif
};
#endif

View File

@@ -0,0 +1,101 @@
/* Calf DSP plugin pack
* Distortion related plugins
*
* Copyright (C) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen and others
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef CALF_MODULES_DIST_H
#define CALF_MODULES_DIST_H
#include <assert.h>
#include <limits.h>
#include "biquad.h"
#include "inertia.h"
#include "audio_fx.h"
#include "giface.h"
#include "metadata.h"
#include "plugin_tools.h"
namespace calf_plugins {
/// Saturator by Markus Schmidt (based on Krzysztof's filters and Tom's distortion algorythm)
class saturator_audio_module: public audio_module<saturator_metadata> {
private:
float hp_pre_freq_old, lp_pre_freq_old;
float hp_post_freq_old, lp_post_freq_old;
float p_level_old, p_freq_old, p_q_old;
stereo_in_out_metering<saturator_metadata> meters;
float meter_drive;
dsp::biquad_d2<float> lp[2][4], hp[2][4];
dsp::biquad_d2<float> p[2];
dsp::tap_distortion dist[2];
public:
uint32_t srate;
bool is_active;
saturator_audio_module();
void activate();
void deactivate();
void params_changed();
void set_sample_rate(uint32_t sr);
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
};
/// Exciter by Markus Schmidt (based on Krzysztof's filters and Tom's distortion algorythm)
class exciter_audio_module: public audio_module<exciter_metadata> {
private:
float freq_old, ceil_old;
bool ceil_active_old;
stereo_in_out_metering<exciter_metadata> meters;
float meter_drive;
dsp::biquad_d2<float> hp[2][4];
dsp::biquad_d2<float> lp[2][2];
dsp::tap_distortion dist[2];
public:
uint32_t srate;
bool is_active;
exciter_audio_module();
void activate();
void deactivate();
void params_changed();
void set_sample_rate(uint32_t sr);
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
};
/// Bass Enhancer by Markus Schmidt (based on Krzysztof's filters and Tom's distortion algorythm)
class bassenhancer_audio_module: public audio_module<bassenhancer_metadata> {
private:
float freq_old, floor_old;
bool floor_active_old;
stereo_in_out_metering<exciter_metadata> meters;
float meter_drive;
dsp::biquad_d2<float> lp[2][4];
dsp::biquad_d2<float> hp[2][2];
dsp::tap_distortion dist[2];
public:
uint32_t srate;
bool is_active;
bassenhancer_audio_module();
void activate();
void deactivate();
void params_changed();
void set_sample_rate(uint32_t sr);
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
};
};
#endif

View File

@@ -0,0 +1,90 @@
/* Calf DSP plugin pack
* Equalization related plugins
*
* Copyright (C) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen and others
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef CALF_MODULES_EQ_H
#define CALF_MODULES_EQ_H
#include <assert.h>
#include <limits.h>
#include "biquad.h"
#include "inertia.h"
#include "audio_fx.h"
#include "giface.h"
#include "metadata.h"
#include "plugin_tools.h"
namespace calf_plugins {
/// Equalizer N Band by Markus Schmidt (based on Krzysztof's filters)
template<class BaseClass, bool has_lphp>
class equalizerNband_audio_module: public audio_module<BaseClass>, public frequency_response_line_graph {
public:
typedef audio_module<BaseClass> AM;
using AM::ins;
using AM::outs;
using AM::params;
using AM::in_count;
using AM::out_count;
using AM::param_count;
using AM::PeakBands;
private:
enum { graph_param_count = BaseClass::last_graph_param - BaseClass::first_graph_param + 1, params_per_band = AM::param_p2_active - AM::param_p1_active };
float hp_mode_old, hp_freq_old;
float lp_mode_old, lp_freq_old;
float ls_level_old, ls_freq_old;
float hs_level_old, hs_freq_old;
float p_level_old[PeakBands], p_freq_old[PeakBands], p_q_old[PeakBands];
mutable float old_params_for_graph[graph_param_count];
dual_in_out_metering<BaseClass> meters;
CalfEqMode hp_mode, lp_mode;
dsp::biquad_d2<float> hp[3][2], lp[3][2];
dsp::biquad_d2<float> lsL, lsR, hsL, hsR;
dsp::biquad_d2<float> pL[PeakBands], pR[PeakBands];
inline void process_hplp(float &left, float &right);
public:
typedef std::complex<double> cfloat;
uint32_t srate;
bool is_active;
mutable volatile int last_generation, last_calculated_generation;
equalizerNband_audio_module();
void activate();
void deactivate();
void params_changed();
float freq_gain(int index, double freq, uint32_t sr) const;
void set_sample_rate(uint32_t sr)
{
srate = sr;
meters.set_sample_rate(sr);
}
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const;
};
typedef equalizerNband_audio_module<equalizer5band_metadata, false> equalizer5band_audio_module;
typedef equalizerNband_audio_module<equalizer8band_metadata, true> equalizer8band_audio_module;
typedef equalizerNband_audio_module<equalizer12band_metadata, true> equalizer12band_audio_module;
};
#endif

View File

@@ -0,0 +1,98 @@
/* Calf DSP plugin pack
* Limiter related plugins
*
* Copyright (C) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen and others
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef CALF_MODULES_LIMIT_H
#define CALF_MODULES_LIMIT_H
#include <assert.h>
#include <limits.h>
#include "biquad.h"
#include "inertia.h"
#include "audio_fx.h"
#include "giface.h"
#include "metadata.h"
#include "plugin_tools.h"
namespace calf_plugins {
/// Limiter by Markus Schmidt and Christian Holschuh
class limiter_audio_module: public audio_module<limiter_metadata>, public line_graph_iface {
private:
typedef limiter_audio_module AM;
uint32_t clip_inL, clip_inR, clip_outL, clip_outR, asc_led;
int mode, mode_old;
float meter_inL, meter_inR, meter_outL, meter_outR;
dsp::lookahead_limiter limiter;
public:
uint32_t srate;
bool is_active;
float limit_old;
bool asc_old;
float attack_old;
limiter_audio_module();
void activate();
void deactivate();
void params_changed();
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
void set_sample_rate(uint32_t sr);
};
/// Multiband Limiter by Markus Schmidt and Christian Holschuh
class multibandlimiter_audio_module: public audio_module<multibandlimiter_metadata>, public line_graph_iface {
private:
typedef multibandlimiter_audio_module AM;
static const int strips = 4;
uint32_t clip_inL, clip_inR, clip_outL, clip_outR, asc_led;
int mode, mode_old;
bool solo[strips];
bool no_solo;
float meter_inL, meter_inR, meter_outL, meter_outR;
dsp::lookahead_limiter strip[strips];
dsp::lookahead_limiter broadband;
dsp::biquad_d2<float> lpL[strips - 1][3], lpR[strips - 1][3], hpL[strips - 1][3], hpR[strips - 1][3];
float freq_old[strips - 1], sep_old[strips - 1], q_old[strips - 1];
unsigned int pos;
unsigned int buffer_size;
unsigned int overall_buffer_size;
float *buffer;
int channels;
float striprel[strips];
float weight[strips];
float weight_old[strips];
float limit_old;
bool asc_old;
float attack_old;
bool _sanitize;
public:
uint32_t srate;
bool is_active;
multibandlimiter_audio_module();
void activate();
void deactivate();
void params_changed();
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
void set_sample_rate(uint32_t sr);
bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
};
};
#endif

View File

@@ -0,0 +1,190 @@
/* Calf DSP plugin pack
* Modulation effect plugins
*
* Copyright (C) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen and others
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef CALF_MODULES_MOD_H
#define CALF_MODULES_MOD_H
#include <assert.h>
#include <limits.h>
#include "biquad.h"
#include "inertia.h"
#include "audio_fx.h"
#include "giface.h"
#include "metadata.h"
#include "multichorus.h"
namespace calf_plugins {
class flanger_audio_module: public audio_module<flanger_metadata>, public frequency_response_line_graph
{
public:
dsp::simple_flanger<float, 2048> left, right;
uint32_t srate;
bool clear_reset;
float last_r_phase;
bool is_active;
public:
flanger_audio_module() {
is_active = false;
}
void set_sample_rate(uint32_t sr);
void params_changed();
void params_reset();
void activate();
void deactivate();
uint32_t process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask) {
left.process(outs[0] + offset, ins[0] + offset, nsamples);
right.process(outs[1] + offset, ins[1] + offset, nsamples);
return outputs_mask; // XXXKF allow some delay after input going blank
}
bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
float freq_gain(int subindex, float freq, float srate) const;
};
class phaser_audio_module: public audio_module<phaser_metadata>, public frequency_response_line_graph
{
public:
enum { MaxStages = 12 };
uint32_t srate;
bool clear_reset;
float last_r_phase;
dsp::simple_phaser left, right;
float x1vals[2][MaxStages], y1vals[2][MaxStages];
bool is_active;
public:
phaser_audio_module();
void params_changed();
void params_reset();
void activate();
void set_sample_rate(uint32_t sr);
void deactivate();
uint32_t process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask) {
left.process(outs[0] + offset, ins[0] + offset, nsamples);
right.process(outs[1] + offset, ins[1] + offset, nsamples);
return outputs_mask; // XXXKF allow some delay after input going blank
}
bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
float freq_gain(int subindex, float freq, float srate) const;
};
class rotary_speaker_audio_module: public audio_module<rotary_speaker_metadata>
{
public:
/// Current phases and phase deltas for bass and treble rotors
uint32_t phase_l, dphase_l, phase_h, dphase_h;
dsp::simple_delay<1024, float> delay;
dsp::biquad_d2<float> crossover1l, crossover1r, crossover2l, crossover2r, damper1l, damper1r;
dsp::simple_delay<8, float> phaseshift;
uint32_t srate;
int vibrato_mode;
/// Current CC1 (Modulation) value, normalized to [0, 1]
float mwhl_value;
/// Current CC64 (Hold) value, normalized to [0, 1]
float hold_value;
/// Current rotation speed for bass rotor - automatic mode
float aspeed_l;
/// Current rotation speed for treble rotor - automatic mode
float aspeed_h;
/// Desired speed (0=slow, 1=fast) - automatic mode
float dspeed;
/// Current rotation speed for bass rotor - manual mode
float maspeed_l;
/// Current rotation speed for treble rotor - manual mode
float maspeed_h;
int meter_l, meter_h;
rotary_speaker_audio_module();
void set_sample_rate(uint32_t sr);
void setup();
void activate();
void deactivate();
void params_changed();
void set_vibrato();
/// Convert RPM speed to delta-phase
uint32_t rpm2dphase(float rpm);
/// Set delta-phase variables based on current calculated (and interpolated) RPM speed
void update_speed();
void update_speed_manual(float delta);
/// Increase or decrease aspeed towards raspeed, with required negative and positive rate
bool incr_towards(float &aspeed, float raspeed, float delta_decc, float delta_acc);
uint32_t process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask);
virtual void control_change(int channel, int ctl, int val);
};
/// A multitap stereo chorus thing
class multichorus_audio_module: public audio_module<multichorus_metadata>, public frequency_response_line_graph
{
public:
uint32_t srate;
dsp::multichorus<float, dsp::sine_multi_lfo<float, 8>, dsp::filter_sum<dsp::biquad_d2<>, dsp::biquad_d2<> >, 4096> left, right;
float last_r_phase;
float cutoff;
bool is_active;
public:
multichorus_audio_module();
void params_changed();
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
void activate();
void deactivate();
void set_sample_rate(uint32_t sr);
bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
float freq_gain(int subindex, float freq, float srate) const;
bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const;
bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
};
/// Pulsator by Markus Schmidt
class pulsator_audio_module: public audio_module<pulsator_metadata>, public frequency_response_line_graph {
private:
typedef pulsator_audio_module AM;
uint32_t clip_inL, clip_inR, clip_outL, clip_outR;
float meter_inL, meter_inR, meter_outL, meter_outR;
float offset_old;
int mode_old;
bool clear_reset;
dsp::simple_lfo lfoL, lfoR;
public:
uint32_t srate;
bool is_active;
pulsator_audio_module();
void activate();
void deactivate();
void params_changed();
void set_sample_rate(uint32_t sr);
void params_reset()
{
if (clear_reset) {
*params[param_reset] = 0.f;
clear_reset = false;
}
}
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const;
bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
};
};
#endif

View File

@@ -0,0 +1,193 @@
/* Calf DSP Library
* Audio modules - synthesizers
*
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_MODULES_SYNTHS_H
#define __CALF_MODULES_SYNTHS_H
#include "biquad.h"
#include "onepole.h"
#include "audio_fx.h"
#include "inertia.h"
#include "osc.h"
#include "synth.h"
#include "envelope.h"
#include "modmatrix.h"
#include "metadata.h"
namespace calf_plugins {
#define MONOSYNTH_WAVE_BITS 12
/// Monosynth-in-making. Parameters may change at any point, so don't make songs with it!
/// It lacks inertia for parameters, even for those that really need it.
class monosynth_audio_module: public audio_module<monosynth_metadata>, public line_graph_iface, public mod_matrix_impl
{
public:
uint32_t srate, crate;
static dsp::waveform_family<MONOSYNTH_WAVE_BITS> *waves;
dsp::waveform_oscillator<MONOSYNTH_WAVE_BITS> osc1, osc2;
dsp::triangle_lfo lfo1, lfo2;
dsp::biquad_d1_lerp<float> filter, filter2;
/// The step code is producing non-zero values
bool running;
/// This is the last non-zero buffer (set on calculate_step after fadeout is complete, the next calculate_step will zero running)
bool stopping;
/// A key is kept pressed
bool gate;
/// All notes off fadeout
bool force_fadeout;
/// Last triggered note
int last_key;
/// Output buffers, used to ensure updates are done every step_size regardless of process buffer size
float buffer[step_size], buffer2[step_size];
/// Read position within the buffers, on each '0' the buffers are being filled with new data by calculate_step
uint32_t output_pos;
/// Waveform number - OSC1
int wave1;
/// Waveform number - OSC2
int wave2;
/// Last used waveform number - OSC1
int prev_wave1;
/// Last used waveform number - OSC2
int prev_wave2;
/// Filter type
int filter_type;
/// Filter type on the last calculate_step
int last_filter_type;
float freq, start_freq, target_freq, cutoff, fgain, fgain_delta, separation;
float detune, xpose, xfade, ampctl, fltctl;
float odcr, porta_time, lfo_bend;
/// Modulation wheel position (0.f-1.f)
float modwheel_value;
/// Delay counter for LFOs
float lfo_clock;
/// Last value of phase shift for pulse width emulation for OSC1
int32_t last_pwshift1;
/// Last value of phase shift for pulse width emulation for OSC2
int32_t last_pwshift2;
/// Last value of stretch for osc sync emulation for OSC1
int32_t last_stretch1;
/// Next note to play on the next calculate_step
int queue_note_on;
/// Whether the queued note has been already released
bool queue_note_on_and_off;
/// Velocity of the next note to play
float queue_vel;
/// Integer value for modwheel (0-16383, read from CC1 - MSBs and CC33 - LSBs)
int modwheel_value_int;
/// Legato mode (bitmask)
int legato;
/// Envelope Generators
dsp::adsr envelope1, envelope2;
dsp::keystack stack;
/// Smoothing for master volume
dsp::gain_smoothing master;
/// Fadeout for buffer 1
dsp::fadeout fadeout;
/// Fadeout for buffer 2
dsp::fadeout fadeout2;
/// Smoothed cutoff value
dsp::inertia<dsp::exponential_ramp> inertia_cutoff;
/// Smoothed pitch bend value
dsp::inertia<dsp::exponential_ramp> inertia_pitchbend;
/// Smoothed channel pressure value
dsp::inertia<dsp::linear_ramp> inertia_pressure;
/// Rows of the modulation matrix
dsp::modulation_entry mod_matrix_data[mod_matrix_slots];
/// Currently used velocity
float velocity;
/// Last value of oscillator mix ratio
float last_xfade;
/// Current calculated mod matrix outputs
float moddest[moddest_count];
monosynth_audio_module();
static void precalculate_waves(progress_report_iface *reporter);
void set_sample_rate(uint32_t sr);
void delayed_note_on();
/// Release a note (physically), called from note-off handler or when note-off has been scheduled after note-on (very short queued note)
void end_note();
/// Handle MIDI Note On message (does not immediately trigger a note, as it must start on
/// boundary of step_size samples).
void note_on(int channel, int note, int vel);
/// Handle MIDI Note Off message
void note_off(int channel, int note, int vel);
/// Handle MIDI Channel Pressure
void channel_pressure(int channel, int value);
/// Handle pitch bend message.
inline void pitch_bend(int /*channel*/, int value)
{
inertia_pitchbend.set_inertia(pow(2.0, (value * *params[par_pwhlrange]) / (1200.0 * 8192.0)));
}
/// Update oscillator frequency based on base frequency, detune amount, pitch bend scaling factor and sample rate.
void set_frequency();
/// Handle control change messages.
void control_change(int channel, int controller, int value);
/// Update variables from control ports.
void params_changed();
void activate();
void deactivate();
void post_instantiate()
{
precalculate_waves(progress_report);
}
/// Set waveform addresses for oscillators
void lookup_waveforms();
/// Run oscillators
void calculate_buffer_oscs(float lfo);
/// Run two filters in series to produce mono output samples.
void calculate_buffer_ser();
/// Run one filter to produce mono output samples.
void calculate_buffer_single();
/// Run two filters (one per channel) to produce stereo output samples.
void calculate_buffer_stereo();
/// Retrieve filter graph (which is 'live' so it cannot be generated by get_static_graph), or fall back to get_static_graph.
bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
/// @retval true if the filter 1 is to be used for the left channel and filter 2 for the right channel
/// @retval false if filters are to be connected in series and sent (mono) to both channels
inline bool is_stereo_filter() const
{
return filter_type == flt_2lp12 || filter_type == flt_2bp6;
}
/// No CV inputs for now
bool is_cv(int param_no) const { return false; }
/// Practically all the stuff here is noisy
bool is_noisy(int param_no) const { return param_no != par_cutoff; }
/// Calculate control signals and produce step_size samples of output.
void calculate_step();
/// Apply anti-click'n'pop fadeout (used at the end of the sound)
void apply_fadeout();
/// Main processing function
uint32_t process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask);
/// Send all configure variables set within a plugin to given destination (which may be limited to only those that plugin understands)
virtual void send_configures(send_configure_iface *sci) { return mod_matrix_impl::send_configures(sci); }
virtual char *configure(const char *key, const char *value) { return mod_matrix_impl::configure(key, value); }
};
};
#if ENABLE_EXPERIMENTAL
#include "wavetable.h"
#endif
#endif

View File

@@ -0,0 +1,213 @@
/* Calf DSP Library
* Multitap chorus class.
*
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_MULTICHORUS_H
#define __CALF_MULTICHORUS_H
#include "audio_fx.h"
namespace dsp {
typedef fixed_point<unsigned int, 20> chorus_phase;
template<class T, uint32_t Voices>
class sine_multi_lfo
{
protected:
sine_table<int, 4096, 65535> sine;
public:
/// Current LFO phase
chorus_phase phase;
/// LFO phase increment
chorus_phase dphase;
/// LFO phase per-voice increment
chorus_phase vphase;
/// Current number of voices
uint32_t voices;
/// Current scale (output multiplier)
T scale;
/// Per-voice offset unit (the value that says how much the voices are offset with respect to each other in non-100% 'overlap' mode), scaled so that full range = 131072
int32_t voice_offset;
/// LFO Range scaling for non-100% overlap
uint32_t voice_depth;
public:
sine_multi_lfo()
{
phase = dphase = vphase = 0.0;
voice_offset = 0;
voice_depth = 1U << 31;
set_voices(Voices);
}
inline uint32_t get_voices() const
{
return voices;
}
inline void set_voices(uint32_t value)
{
voices = value;
// use sqrt, because some phases will cancel each other - so 1 / N is usually too low
scale = sqrt(1.0 / voices);
}
inline void set_overlap(float overlap)
{
// If we scale the delay amount so that full range of a single LFO is 0..1, all the overlapped LFOs will cover 0..range
// How it's calculated:
// 1. First voice is assumed to always cover the range of 0..1
// 2. Each remaining voice contributes an interval of a width = 1 - overlap, starting from the end of the interval of the previous voice
// Coverage = non-overlapped part of the LFO range in the 1st voice
float range = 1.f + (1.f - overlap) * (voices - 1);
float scaling = 1.f / range;
voice_offset = (int)(131072 * (1 - overlap) / range);
voice_depth = (unsigned int)((1U << 30) * 1.0 * scaling);
}
/// Get LFO value for given voice, returns a values in range of [-65536, 65535] (or close)
inline int get_value(uint32_t voice) const {
// find this voice's phase (= phase + voice * 360 degrees / number of voices)
chorus_phase voice_phase = phase + vphase * (int)voice;
// find table offset
unsigned int ipart = voice_phase.ipart();
// interpolate (use 14 bits of precision - because the table itself uses 17 bits and the result of multiplication must fit in int32_t)
// note, the result is still -65535 .. 65535, it's just interpolated
// it is never reaching -65536 - but that's acceptable
int intval = voice_phase.lerp_by_fract_int<int, 14, int>(sine.data[ipart], sine.data[ipart+1]);
// apply the voice offset/depth (rescale from -65535..65535 to appropriate voice's "band")
return -65535 + voice * voice_offset + ((voice_depth >> (30-13)) * (65536 + intval) >> 13);
}
inline void step() {
phase += dphase;
}
inline T get_scale() const {
return scale;
}
void reset() {
phase = 0.f;
}
};
/**
* Multi-tap chorus without feedback.
* Perhaps MaxDelay should be a bit longer!
*/
template<class T, class MultiLfo, class Postprocessor, int MaxDelay=4096>
class multichorus: public chorus_base
{
protected:
simple_delay<MaxDelay,T> delay;
public:
MultiLfo lfo;
Postprocessor post;
public:
multichorus() {
rate = 0.63f;
dry = 0.5f;
wet = 0.5f;
min_delay = 0.005f;
mod_depth = 0.0025f;
setup(44100);
}
void reset() {
delay.reset();
lfo.reset();
}
void set_rate(float rate) {
chorus_base::set_rate(rate);
lfo.dphase = dphase;
}
virtual void setup(int sample_rate) {
modulation_effect::setup(sample_rate);
delay.reset();
lfo.reset();
set_min_delay(get_min_delay());
set_mod_depth(get_mod_depth());
}
template<class OutIter, class InIter>
void process(OutIter buf_out, InIter buf_in, int nsamples) {
int mds = min_delay_samples + mod_depth_samples * 1024 + 2*65536;
int mdepth = mod_depth_samples;
// 1 sample peak-to-peak = mod_depth_samples of 32 (this scaling stuff is tricky and may - but shouldn't - be wrong)
// with 192 kHz sample rate, 1 ms = 192 samples, and the maximum 20 ms = 3840 samples (so, 4096 will be used)
// 3840 samples of mod depth = mdepth of 122880 (which multiplied by 65536 doesn't fit in int32_t)
// so, it will be right-shifted by 2, which gives it a safe range of 30720
// NB: calculation of mod_depth_samples (and multiply-by-32) is in chorus_base::set_mod_depth
mdepth = mdepth >> 2;
T scale = lfo.get_scale();
for (int i=0; i<nsamples; i++) {
phase += dphase;
float in = *buf_in++;
delay.put(in);
unsigned int nvoices = lfo.get_voices();
T out = 0.f;
// add up values from all voices, each voice tell its LFO phase and the buffer value is picked at that location
for (unsigned int v = 0; v < nvoices; v++)
{
int lfo_output = lfo.get_value(v);
// 3 = log2(32 >> 2) + 1 because the LFO value is in range of [-65535, 65535] (17 bits)
int dv = mds + (mdepth * lfo_output >> (3 + 1));
int ifv = dv >> 16;
T fd; // signal from delay's output
delay.get_interp(fd, ifv, (dv & 0xFFFF)*(1.0/65536.0));
out += fd;
}
// apply the post filter
out = post.process(out);
T sdry = in * gs_dry.get();
T swet = out * gs_wet.get() * scale;
*buf_out++ = sdry + swet;
lfo.step();
}
post.sanitize();
}
float freq_gain(float freq, float sr) const
{
typedef std::complex<double> cfloat;
freq *= 2.0 * M_PI / sr;
cfloat z = 1.0 / exp(cfloat(0.0, freq)); // z^-1
cfloat h = 0.0;
int mds = min_delay_samples + mod_depth_samples * 1024 + 2*65536;
int mdepth = mod_depth_samples;
mdepth = mdepth >> 2;
T scale = lfo.get_scale();
unsigned int nvoices = lfo.get_voices();
for (unsigned int v = 0; v < nvoices; v++)
{
int lfo_output = lfo.get_value(v);
// 3 = log2(32 >> 2) + 1 because the LFO value is in range of [-65535, 65535] (17 bits)
int dv = mds + (mdepth * lfo_output >> (3 + 1));
int fldp = dv >> 16;
cfloat zn = std::pow(z, fldp); // z^-N
h += zn + (zn * z - zn) * cfloat(dv / 65536.0 - fldp);
}
// apply the post filter
h *= post.h_z(z);
// mix with dry signal
float v = std::abs(cfloat(gs_dry.get_last()) + cfloat(scale * gs_wet.get_last()) * h);
return v;
}
};
};
#endif

View File

@@ -0,0 +1,192 @@
/* Calf DSP Library
* Basic one-pole one-zero filters based on bilinear transform.
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_ONEPOLE_H
#define __CALF_ONEPOLE_H
#include "primitives.h"
namespace dsp {
/**
* one-pole filter, for floating point values
* coefficient calculation is based on bilinear transform, and the code itself is based on my very old OneSignal lib
* lp and hp are *somewhat* tested, allpass is not tested at all
* don't use this for integers because it won't work
*/
template<class T = float, class Coeff = float>
class onepole
{
public:
typedef std::complex<double> cfloat;
T x1, y1;
Coeff a0, a1, b1;
onepole()
{
reset();
}
/// Set coefficients for a lowpass filter
void set_lp(float fc, float sr)
{
// x x
// x+1 x-1
Coeff x = tan (M_PI * fc / (2 * sr));
Coeff q = 1/(1+x);
a0 = a1 = x*q;
b1 = (x-1)*q;
}
/// Set coefficients for an allpass filter
void set_ap(float fc, float sr)
{
// x-1 x+1
// x+1 x-1
Coeff x = tan (M_PI * fc / (2 * sr));
Coeff q = 1/(1+x);
b1 = a0 = (x-1)*q;
a1 = 1;
}
/// Set coefficients for an allpass filter, using omega instead of fc and sr
/// omega = (PI / 2) * fc / sr
void set_ap_w(float w)
{
// x-1 x+1
// x+1 x-1
Coeff x = tan (w);
Coeff q = 1/(1+x);
b1 = a0 = (x-1)*q;
a1 = 1;
}
/// Set coefficients for a highpass filter
void set_hp(float fc, float sr)
{
// x -x
// x+1 x-1
Coeff x = tan (M_PI * fc / (2 * sr));
Coeff q = 1/(1+x);
a0 = q;
a1 = -a0;
b1 = (x-1)*q;
}
/// Process one sample
inline T process(T in)
{
T out = in * a0 + x1 * a1 - y1 * b1;
x1 = in;
y1 = out;
return out;
}
/// Process one sample, assuming it's a lowpass filter (optimized special case)
inline T process_lp(T in)
{
T out = (in + x1) * a0 - y1 * b1;
x1 = in;
y1 = out;
return out;
}
/// Process one sample, assuming it's a highpass filter (optimized special case)
inline T process_hp(T in)
{
T out = (in - x1) * a0 - y1 * b1;
x1 = in;
y1 = out;
return out;
}
/// Process one sample, assuming it's an allpass filter (optimized special case)
inline T process_ap(T in)
{
T out = (in - y1) * a0 + x1;
x1 = in;
y1 = out;
return out;
}
/// Process one sample using external state variables
inline T process_ap(T in, float &x1, float &y1)
{
T out = (in - y1) * a0 + x1;
x1 = in;
y1 = out;
return out;
}
/// Process one sample using external state variables, including filter coeff
inline T process_ap(T in, float &x1, float &y1, float a0)
{
T out = (in - y1) * a0 + x1;
x1 = in;
y1 = out;
return out;
}
inline bool empty() const {
return y1 == 0;
}
inline void sanitize()
{
dsp::sanitize(x1);
dsp::sanitize(y1);
}
inline void reset()
{
dsp::zero(x1);
dsp::zero(y1);
}
template<class U>
inline void copy_coeffs(const onepole<U> &src)
{
a0 = src.a0;
a1 = src.a1;
b1 = src.b1;
}
/// Return the filter's gain at frequency freq
/// @param freq Frequency to look up
/// @param sr Filter sample rate (used to convert frequency to angular frequency)
float freq_gain(float freq, float sr) const
{
freq *= 2.0 * M_PI / sr;
cfloat z = 1.0 / exp(cfloat(0.0, freq));
return std::abs(h_z(z));
}
/// Return H(z) the filter's gain at frequency freq
/// @param z Z variable (e^jw)
cfloat h_z(const cfloat &z) const
{
return (cfloat(a0) + double(a1) * z) / (cfloat(1.0) + double(b1) * z);
}
};
};
#endif

View File

@@ -0,0 +1,359 @@
/* Calf DSP Library
* Drawbar organ emulator.
*
* Copyright (C) 2007 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_ORGAN_H
#define __CALF_ORGAN_H
#include "audio_fx.h"
#include "envelope.h"
#include "metadata.h"
#include "osc.h"
#include "synth.h"
#define ORGAN_KEYTRACK_POINTS 4
namespace dsp
{
struct organ_parameters {
enum { FilterCount = 2, EnvCount = 3 };
struct organ_filter_parameters
{
float cutoff;
float resonance;
float envmod[organ_parameters::EnvCount];
float keyf;
};
struct organ_env_parameters
{
float attack, decay, sustain, release, velscale, ampctl;
};
//////////////////////////////////////////////////////////////////////////
// these parameters are binary-copied from control ports (order is important!)
float drawbars[9];
float harmonics[9];
float waveforms[9];
float detune[9];
float phase[9];
float pan[9];
float routing[9];
float foldover;
float percussion_time;
float percussion_level;
float percussion_wave;
float percussion_harmonic;
float percussion_vel2amp;
float percussion_fm_time;
float percussion_fm_depth;
float percussion_fm_wave;
float percussion_fm_harmonic;
float percussion_vel2fm;
float percussion_trigger;
float percussion_stereo;
float filter_chain;
float filter1_type;
float master;
organ_filter_parameters filters[organ_parameters::FilterCount];
organ_env_parameters envs[organ_parameters::EnvCount];
float lfo_rate;
float lfo_amt;
float lfo_wet;
float lfo_phase;
float lfo_mode;
float lfo_type;
float global_transpose;
float global_detune;
float polyphony;
float quad_env;
float pitch_bend_range;
float bass_freq;
float bass_gain;
float treble_freq;
float treble_gain;
float dummy_mapcurve;
//////////////////////////////////////////////////////////////////////////
// these parameters are calculated
double perc_decay_const, perc_fm_decay_const;
float multiplier[9];
int phaseshift[9];
float cutoff;
unsigned int foldvalue;
float pitch_bend;
float percussion_keytrack[ORGAN_KEYTRACK_POINTS][2];
organ_parameters() : pitch_bend(1.0f) {}
inline int get_percussion_wave() { return dsp::fastf2i_drm(percussion_wave); }
inline int get_percussion_fm_wave() { return dsp::fastf2i_drm(percussion_fm_wave); }
};
#define ORGAN_WAVE_BITS 12
#define ORGAN_WAVE_SIZE 4096
#define ORGAN_BIG_WAVE_BITS 17
#define ORGAN_BIG_WAVE_SIZE 131072
/// 2^ORGAN_BIG_WAVE_SHIFT = how many (quasi)periods per sample
#define ORGAN_BIG_WAVE_SHIFT 5
class organ_voice_base: public calf_plugins::organ_enums
{
public:
typedef waveform_family<ORGAN_WAVE_BITS> small_wave_family;
typedef waveform_family<ORGAN_BIG_WAVE_BITS> big_wave_family;
public:
organ_parameters *parameters;
protected:
static small_wave_family (*waves)[wave_count_small];
static big_wave_family (*big_waves)[wave_count_big];
int note;
dsp::decay amp;
/// percussion FM carrier amplitude envelope
dsp::decay pamp;
/// percussion FM modulator amplitude envelope
dsp::decay fm_amp;
dsp::fixed_point<int64_t, 20> pphase, dpphase;
dsp::fixed_point<int64_t, 20> modphase, moddphase;
float fm_keytrack;
int &sample_rate_ref;
bool &released_ref;
/// pamp per-sample (linear) step during release stage (calculated on release so that it will take 30ms for it to go from "current value at release point" to 0)
float rel_age_const;
organ_voice_base(organ_parameters *_parameters, int &_sample_rate_ref, bool &_released_ref);
inline float wave(float *data, dsp::fixed_point<int, 20> ph) {
return ph.lerp_table_lookup_float(data);
}
inline float big_wave(float *data, dsp::fixed_point<int64_t, 20> &ph) {
// wrap to fit within the wave
return ph.lerp_table_lookup_float_mask(data, ORGAN_BIG_WAVE_SIZE - 1);
}
public:
static inline small_wave_family &get_wave(int wave) {
return (*waves)[wave];
}
static inline big_wave_family &get_big_wave(int wave) {
return (*big_waves)[wave];
}
static void precalculate_waves(calf_plugins::progress_report_iface *reporter);
void update_pitch();
// this doesn't really have a voice interface
void render_percussion_to(float (*buf)[2], int nsamples);
void perc_note_on(int note, int vel);
void perc_note_off(int note, int vel);
void perc_reset();
};
/// A simple (and bad) simulation of scanner vibrato based on a series of modulated allpass filters
class organ_vibrato
{
protected:
enum { VibratoSize = 6 };
float vibrato_x1[VibratoSize][2], vibrato_y1[VibratoSize][2];
float lfo_phase;
dsp::onepole<float> vibrato[2];
public:
void reset();
void process(organ_parameters *parameters, float (*data)[2], unsigned int len, float sample_rate);
};
/// A more sophisticated simulation of scanner vibrato. Simulates a line box
/// and an interpolating scanner. The line box is a series of 18 2nd order
/// lowpass filters with cutoff frequency ~4kHz, with loss compensation.
/// The interpolating scanner uses linear interpolation to "slide" between
/// selected outputs of the line box.
///
/// @note
/// This is a true CPU hog, and it should be optimised some day.
/// @note
/// The line box is mono. 36 lowpass filters might be an overkill.
/// @note
/// See also: http://www.jhaible.de/interpolating_scanner_and_scanvib/jh_interpolating_scanner_and_scanvib.html
/// (though it's a very loose adaptation of that version)
class scanner_vibrato
{
protected:
enum { ScannerSize = 18 };
float lfo_phase;
dsp::biquad_d2<float> scanner[ScannerSize];
organ_vibrato legacy;
public:
void reset();
void process(organ_parameters *parameters, float (*data)[2], unsigned int len, float sample_rate);
};
class organ_voice: public dsp::voice, public organ_voice_base {
protected:
enum { Channels = 2, BlockSize = 64, EnvCount = organ_parameters::EnvCount, FilterCount = organ_parameters::FilterCount };
union {
float output_buffer[BlockSize][Channels];
float aux_buffers[3][BlockSize][Channels];
};
dsp::fixed_point<int64_t, 52> phase, dphase;
dsp::biquad_d1<float> filterL[2], filterR[2];
adsr envs[EnvCount];
dsp::inertia<dsp::linear_ramp> expression;
scanner_vibrato vibrato;
float velocity;
bool perc_released;
/// The envelopes have ended and the voice is in final fadeout stage
bool finishing;
dsp::inertia<dsp::exponential_ramp> inertia_pitchbend;
public:
organ_voice()
: organ_voice_base(NULL, sample_rate, perc_released)
, expression(dsp::linear_ramp(16))
, inertia_pitchbend(dsp::exponential_ramp(1))
{
inertia_pitchbend.set_now(1);
}
void reset();
void note_on(int note, int vel);
void note_off(int /* vel */);
virtual float get_priority() { return stolen ? 20000 : (perc_released ? 1 : (sostenuto ? 200 : 100)); }
virtual void steal();
void render_block();
virtual int get_current_note() {
return note;
}
virtual bool get_active() {
// printf("note %d getactive %d use_percussion %d pamp active %d\n", note, amp.get_active(), use_percussion(), pamp.get_active());
return (note != -1) && (amp.get_active() || (use_percussion() && pamp.get_active()));
}
void update_pitch();
inline bool use_percussion()
{
return dsp::fastf2i_drm(parameters->percussion_trigger) == perctrig_polyphonic && parameters->percussion_level > 0;
}
};
/// Not a true voice, just something with similar-ish interface.
class percussion_voice: public organ_voice_base {
public:
int sample_rate;
bool released;
percussion_voice(organ_parameters *_parameters)
: organ_voice_base(_parameters, sample_rate, released)
, released(false)
{
}
bool get_active() {
return (note != -1) && pamp.get_active();
}
bool get_noticable() {
return (note != -1) && (pamp.get() > 0.2 * parameters->percussion_level);
}
void setup(int sr) {
sample_rate = sr;
}
};
struct drawbar_organ: public dsp::basic_synth, public calf_plugins::organ_enums {
organ_parameters *parameters;
percussion_voice percussion;
scanner_vibrato global_vibrato;
two_band_eq eq_l, eq_r;
drawbar_organ(organ_parameters *_parameters)
: parameters(_parameters)
, percussion(_parameters) {
}
void render_separate(float *output[], int nsamples);
dsp::voice *alloc_voice();
virtual void percussion_note_on(int note, int vel);
virtual void params_changed() = 0;
virtual void setup(int sr);
void update_params();
void control_change(int controller, int value)
{
dsp::basic_synth::control_change(controller, value);
}
void pitch_bend(int amt);
virtual bool check_percussion();
};
};
namespace calf_plugins {
struct organ_audio_module: public audio_module<organ_metadata>, public dsp::drawbar_organ, public line_graph_iface
{
public:
using drawbar_organ::note_on;
using drawbar_organ::note_off;
using drawbar_organ::control_change;
enum { param_count = drawbar_organ::param_count};
dsp::organ_parameters par_values;
uint32_t srate;
bool panic_flag;
/// Value for configure variable map_curve
std::string var_map_curve;
organ_audio_module();
void post_instantiate();
void set_sample_rate(uint32_t sr) {
srate = sr;
}
void params_changed();
void activate();
void deactivate();
uint32_t process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask);
/// No CV inputs for now
bool is_cv(int param_no) { return false; }
/// Practically all the stuff here is noisy
bool is_noisy(int param_no) { return true; }
void execute(int cmd_no);
bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
char *configure(const char *key, const char *value);
void send_configures(send_configure_iface *);
uint32_t message_run(const void *valid_inputs, void *output_ports);
public:
// overrides
virtual void note_on(int /*channel*/, int note, int velocity) { dsp::drawbar_organ::note_on(note, velocity); }
virtual void note_off(int /*channel*/, int note, int velocity) { dsp::drawbar_organ::note_off(note, velocity); }
virtual void control_change(int /*channel*/, int controller, int value) { dsp::drawbar_organ::control_change(controller, value); }
virtual void pitch_bend(int /*channel*/, int value) { dsp::drawbar_organ::pitch_bend(value); }
};
};
#endif

View File

@@ -0,0 +1,336 @@
/* Calf DSP Library
* Oscillators
*
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef CALF_OSC_H
#define CALF_OSC_H
#include "fft.h"
#include <map>
namespace dsp
{
/** Very simple, non-bandlimited saw oscillator. Should not be used for anything
* else than testing/prototyping. Unless get() function is replaced with something
* with "proper" oscillator code, as the frequency setting function is fine.
*/
struct simple_oscillator
{
/// Phase (from 0 to 0xFFFFFFFF)
uint32_t phase;
/// Per-sample phase delta (phase increment), equal to 2^32*freq/sr.
uint32_t phasedelta;
/// Reset oscillator phase to zero.
void reset()
{
phase = 0;
}
/// Set phase delta based on oscillator frequency and sample rate.
void set_freq(float freq, float sr)
{
phasedelta = (int)(freq * 65536.0 * 256.0 * 16.0 / sr) << 4;
}
/// Set phase delta based on oscillator frequency and inverse of sample rate.
void set_freq_odsr(float freq, double odsr)
{
phasedelta = (int)(freq * 65536.0 * 256.0 * 16.0 * odsr) << 4;
}
inline float get()
{
float value = (phase >> 16 ) / 65535.0 - 0.5;
phase += phasedelta;
return value;
}
};
/**
* FFT-based bandlimiting helper class. Allows conversion between time and frequency domains and generating brickwall filtered
* versions of a waveform given a pre-computed spectrum.
* Waveform size must be a power of two, and template argument SIZE_BITS is log2 of waveform size.
*/
template<int SIZE_BITS>
struct bandlimiter
{
enum { SIZE = 1 << SIZE_BITS };
static dsp::fft<float, SIZE_BITS> &get_fft()
{
static dsp::fft<float, SIZE_BITS> fft;
return fft;
}
std::complex<float> spectrum[SIZE];
/// Import time domain waveform and calculate spectrum from it
void compute_spectrum(float input[SIZE])
{
dsp::fft<float, SIZE_BITS> &fft = get_fft();
std::complex<float> *data = new std::complex<float>[SIZE];
for (int i = 0; i < SIZE; i++)
data[i] = input[i];
fft.calculate(data, spectrum, false);
delete []data;
}
/// Generate the waveform from the contained spectrum.
void compute_waveform(float output[SIZE])
{
dsp::fft<float, SIZE_BITS> &fft = get_fft();
std::complex<float> *data = new std::complex<float>[SIZE];
fft.calculate(spectrum, data, true);
for (int i = 0; i < SIZE; i++)
output[i] = data[i].real();
delete []data;
}
/// remove DC offset of the spectrum (it usually does more harm than good!)
void remove_dc()
{
spectrum[0] = 0.f;
}
/// Very basic bandlimiting (brickwall filter)
/// might need to be improved much in future!
void make_waveform(float output[SIZE], int cutoff, bool foldover = false)
{
dsp::fft<float, SIZE_BITS> &fft = get_fft();
std::vector<std::complex<float> > new_spec, iffted;
new_spec.resize(SIZE);
iffted.resize(SIZE);
// Copy original harmonics up to cutoff point
new_spec[0] = spectrum[0];
for (int i = 1; i < cutoff; i++)
new_spec[i] = spectrum[i],
new_spec[SIZE - i] = spectrum[SIZE - i];
// Fill the rest with zeros, optionally folding over harmonics over the
// cutoff point into the lower octaves while halving the amplitude.
// (I think it is almost nice for bell type waveforms when the original
// waveform has few widely spread harmonics)
if (foldover)
{
std::complex<float> fatt(0.5);
cutoff /= 2;
if (cutoff < 2)
cutoff = 2;
for (int i = SIZE / 2; i >= cutoff; i--)
{
new_spec[i / 2] += new_spec[i] * fatt;
new_spec[SIZE - i / 2] += new_spec[SIZE - i] * fatt;
new_spec[i] = 0.f,
new_spec[SIZE - i] = 0.f;
}
}
else
{
if (cutoff < 1)
cutoff = 1;
for (int i = cutoff; i < SIZE / 2; i++)
new_spec[i] = 0.f,
new_spec[SIZE - i] = 0.f;
}
// convert back to time domain (IFFT) and extract only real part
fft.calculate(&new_spec.front(), &iffted.front(), true);
for (int i = 0; i < SIZE; i++)
output[i] = iffted[i].real();
}
};
/// Set of bandlimited wavetables
template<int SIZE_BITS>
struct waveform_family: public std::map<uint32_t, float *>
{
enum { SIZE = 1 << SIZE_BITS };
using std::map<uint32_t, float *>::iterator;
using std::map<uint32_t, float *>::end;
using std::map<uint32_t, float *>::lower_bound;
float original[SIZE];
/// Fill the family using specified bandlimiter and original waveform. Optionally apply foldover.
/// Does not produce harmonics over specified limit (limit = (SIZE / 2) / min_number_of_harmonics)
void make(bandlimiter<SIZE_BITS> &bl, float input[SIZE], bool foldover = false, uint32_t limit = SIZE / 2)
{
memcpy(original, input, sizeof(original));
bl.compute_spectrum(input);
make_from_spectrum(bl, foldover);
}
/// Fill the family using specified bandlimiter and spectrum contained within. Optionally apply foldover.
/// Does not produce harmonics over specified limit (limit = (SIZE / 2) / min_number_of_harmonics)
void make_from_spectrum(bandlimiter<SIZE_BITS> &bl, bool foldover = false, uint32_t limit = SIZE / 2)
{
bl.remove_dc();
uint32_t base = 1 << (32 - SIZE_BITS);
uint32_t cutoff = SIZE / 2, top = SIZE / 2;
float vmax = 0;
for (unsigned int i = 0; i < cutoff; i++)
vmax = std::max(vmax, abs(bl.spectrum[i]));
float vthres = vmax / 1024.0; // -60dB
float cumul = 0.f;
while(cutoff > (SIZE / limit)) {
if (!foldover)
{
// skip harmonics too quiet to be heard, but measure their loudness cumulatively,
// because even if a single harmonic is too quiet, a whole bunch of them may add up
// to considerable amount of space
cumul = 0.f;
while(cutoff > 1 && cumul + abs(bl.spectrum[cutoff - 1]) < vthres)
{
cumul += abs(bl.spectrum[cutoff - 1]);
cutoff--;
}
}
float *wf = new float[SIZE+1];
bl.make_waveform(wf, cutoff, foldover);
wf[SIZE] = wf[0];
(*this)[base * (top / cutoff)] = wf;
cutoff = (int)(0.75 * cutoff);
}
}
/// Retrieve waveform pointer suitable for specified phase_delta
inline float *get_level(uint32_t phase_delta)
{
iterator i = upper_bound(phase_delta);
if (i == end())
return NULL;
// printf("Level = %08x\n", i->first);
return i->second;
}
/// Destructor, deletes the waveforms and removes them from the map.
~waveform_family()
{
for (iterator i = begin(); i != end(); i++)
delete []i->second;
clear();
}
};
#if 0
// cubic interpolation
static inline float cerp(float pm1, float p0, float p1, float p2, float t)
{
return (-t*(t-1)*(t-2) * pm1 + 3*(t+1)*(t-1)*(t-2) * p0 - 3*(t+1)*t*(t-2) * p1 + (t+1)*t*(t-1) * p2) * (1.0 / 6.0);
}
#endif
/**
* Simple table-based lerping oscillator. Uses waveform of size 2^SIZE_BITS.
* Combine with waveform_family if bandlimited waveforms are needed. Because
* of linear interpolation, it's usually a good idea to use large tables
* (2048-4096 points), otherwise aliasing may be produced.
*/
template<int SIZE_BITS>
struct waveform_oscillator: public simple_oscillator
{
enum { SIZE = 1 << SIZE_BITS, MASK = SIZE - 1, SCALE = 1 << (32 - SIZE_BITS) };
float *waveform;
waveform_oscillator()
{
waveform = NULL;
}
/// Get the value from single oscillator at current position
inline float get()
{
uint32_t wpos = phase >> (32 - SIZE_BITS);
return dsp::lerp(waveform[wpos], waveform[(wpos + 1) & MASK], (phase & (SCALE - 1)) * (1.0f / SCALE));
}
/// Add/substract two phase-shifted values
inline float get_phaseshifted(uint32_t shift, float mix)
{
uint32_t wpos = phase >> (32 - SIZE_BITS);
float value1 = dsp::lerp(waveform[wpos], waveform[(wpos + 1) & MASK], (phase & (SCALE - 1)) * (1.0f / SCALE));
wpos = (phase + shift) >> (32 - SIZE_BITS);
float value2 = dsp::lerp(waveform[wpos], waveform[(wpos + 1) & MASK], ((phase + shift) & (SCALE - 1)) * (1.0f / SCALE));
return value1 + mix * value2;
}
/// Get the value of a hard synced osc (65536 = 1:1 ratio)
inline float get_phasedist(uint32_t sync, uint32_t shift, float mix)
{
uint32_t phase_mod = (uint64_t(phase) * sync >> 16);
uint32_t wpos = phase_mod >> (32 - SIZE_BITS);
float value1 = dsp::lerp(waveform[wpos], waveform[(wpos + 1) & MASK], (phase & (SCALE - 1)) * (1.0f / SCALE));
wpos = (phase_mod + shift) >> (32 - SIZE_BITS);
float value2 = dsp::lerp(waveform[wpos], waveform[(wpos + 1) & MASK], ((phase + shift) & (SCALE - 1)) * (1.0f / SCALE));
return value1 + mix * value2;
}
/// One step
inline void advance()
{
phase += phasedelta;
}
};
/**
* Simple triangle LFO without any smoothing or anything of this sort.
*/
struct triangle_lfo: public simple_oscillator
{
/// Previous value (not stored here, but may be used by calling code)
float last;
triangle_lfo()
{
reset();
}
void reset()
{
simple_oscillator::reset();
last = 0;
}
inline float get()
{
uint32_t phase2 = phase;
// start at 90 degrees point of the "/\" wave (-1 to +1)
phase2 += 1<<30;
// if in second half, invert the wave (so it falls back into 0..0x7FFFFFFF)
phase2 ^= ((int32_t)phase2)>>31;
float value = (phase2 >> 6) / 16777216.0 - 1.0;
phase += phasedelta;
return value;
}
};
/// Simple stupid inline function to normalize a waveform (by removing DC offset and ensuring max absolute value of 1).
static inline void normalize_waveform(float *table, unsigned int size)
{
float dc = 0;
for (unsigned int i = 0; i < size; i++)
dc += table[i];
dc /= size;
for (unsigned int i = 0; i < size; i++)
table[i] -= dc;
float thismax = 0;
for (unsigned int i = 0; i < size; i++)
thismax = std::max(thismax, fabsf(table[i]));
if (thismax < 0.000001f)
return;
double divv = 1.0 / thismax;
for (unsigned int i = 0; i < size; i++)
table[i] *= divv;
}
};
#endif

View File

@@ -0,0 +1,561 @@
/* Calf DSP Library
* Open Sound Control primitives
*
* Copyright (C) 2007 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_OSCTL_H
#define __CALF_OSCTL_H
#include <string>
#include <vector>
#include <string.h>
#include <errno.h>
#include <iostream>
#include <stdint.h>
namespace osctl
{
enum osc_type
{
osc_i32 = 'i',
osc_f32 = 'f',
osc_string = 's',
osc_blob = 'b',
// unsupported
osc_i64 = 'h',
osc_ts = 't',
osc_f64 = 'd',
osc_string_alt = 'S',
osc_char = 'c',
osc_rgba = 'r',
osc_midi = 'm',
osc_true = 'T',
osc_false = 'F',
osc_nil = 'N',
osc_inf = 'I',
osc_start_array = '[',
osc_end_array = ']'
};
extern const char *osc_type_name(osc_type type);
struct osc_exception: public std::exception
{
virtual const char *what() const throw() { return "OSC parsing error"; }
};
struct osc_read_exception: public std::exception
{
virtual const char *what() const throw() { return "OSC buffer underflow"; }
};
struct osc_write_exception: public std::exception
{
virtual const char *what() const throw() { return "OSC buffer overflow"; }
};
struct null_buffer
{
static bool read(uint8_t *dest, uint32_t bytes)
{
return false;
}
static bool write(uint8_t *dest, uint32_t bytes)
{
return true;
}
static void clear()
{
}
};
struct raw_buffer
{
uint8_t *ptr;
uint32_t pos, count, size;
raw_buffer()
{
ptr = NULL;
pos = count = size = 0;
}
raw_buffer(uint8_t *_ptr, uint32_t _count, uint32_t _size)
{
set(_ptr, _count, _size);
}
inline void set(uint8_t *_ptr, uint32_t _count, uint32_t _size)
{
ptr = _ptr;
pos = 0;
count = _count;
size = _size;
}
bool read(uint8_t *dest, uint32_t bytes)
{
if (pos + bytes > count)
return false;
memcpy(dest, ptr + pos, bytes);
pos += bytes;
return true;
}
bool write(const uint8_t *src, uint32_t bytes)
{
if (count + bytes > size)
return false;
memcpy(ptr + count, src, bytes);
count += bytes;
return true;
}
int read_left()
{
return count - pos;
}
int write_left()
{
return size - count;
}
inline int write_misalignment()
{
return 4 - (count & 3);
}
void clear()
{
pos = 0;
count = 0;
}
int tell()
{
return pos;
}
void seek(int _pos)
{
pos = _pos;
}
};
struct string_buffer
{
std::string data;
uint32_t pos, size;
string_buffer()
{
pos = 0;
size = 1048576;
}
string_buffer(std::string _data, int _size = 1048576)
{
data = _data;
pos = 0;
size = _size;
}
bool read(uint8_t *dest, uint32_t bytes)
{
if (pos + bytes > data.length())
return false;
memcpy(dest, &data[pos], bytes);
pos += bytes;
return true;
}
bool write(const uint8_t *src, uint32_t bytes)
{
if (data.length() + bytes > size)
return false;
uint32_t wpos = data.length();
data.resize(wpos + bytes);
memcpy(&data[wpos], src, bytes);
return true;
}
inline int read_left()
{
return data.length() - pos;
}
inline int write_left()
{
return size - data.length();
}
inline int write_misalignment()
{
return 4 - (data.length() & 3);
}
void clear()
{
data.clear();
pos = 0;
}
int tell()
{
return pos;
}
void seek(int _pos)
{
pos = _pos;
}
};
template<class Buffer, class TypeBuffer = null_buffer, bool Throw = true>
struct osc_stream
{
Buffer &buffer;
TypeBuffer *type_buffer;
bool error;
osc_stream(Buffer &_buffer) : buffer(_buffer), type_buffer(NULL), error(false) {}
osc_stream(Buffer &_buffer, TypeBuffer &_type_buffer) : buffer(_buffer), type_buffer(&_type_buffer), error(false) {}
inline void pad()
{
uint32_t zero = 0;
write(&zero, buffer.write_misalignment());
}
inline void read(void *dest, uint32_t bytes)
{
if (!buffer.read((uint8_t *)dest, bytes))
{
#if 0
if (Throw)
throw osc_read_exception();
else
#endif
{
error = true;
memset(dest, 0, bytes);
}
}
}
inline void write(const void *src, uint32_t bytes)
{
if (!buffer.write((const uint8_t *)src, bytes))
{
#if 0
if (Throw)
throw osc_write_exception();
else
#endif
error = true;
}
}
inline void clear()
{
buffer.clear();
if (type_buffer)
type_buffer->clear();
}
inline void write_type(char ch)
{
if (type_buffer)
type_buffer->write((uint8_t *)&ch, 1);
}
};
typedef osc_stream<string_buffer> osc_strstream;
typedef osc_stream<string_buffer, string_buffer> osc_typed_strstream;
struct osc_inline_strstream: public string_buffer, public osc_strstream
{
osc_inline_strstream()
: string_buffer(), osc_strstream(static_cast<string_buffer &>(*this))
{
}
};
struct osc_str_typed_buffer_pair
{
string_buffer buf_data, buf_types;
};
struct osc_inline_typed_strstream: public osc_str_typed_buffer_pair, public osc_typed_strstream
{
osc_inline_typed_strstream()
: osc_str_typed_buffer_pair(), osc_typed_strstream(buf_data, buf_types)
{
}
};
template<class Buffer, class TypeBuffer>
inline osc_stream<Buffer, TypeBuffer> &
operator <<(osc_stream<Buffer, TypeBuffer> &s, uint32_t val)
{
#if 0
val = htonl(val);
s.write(&val, 4);
s.write_type(osc_i32);
#endif
return s;
}
template<class Buffer, class TypeBuffer>
inline osc_stream<Buffer, TypeBuffer> &
operator >>(osc_stream<Buffer, TypeBuffer> &s, uint32_t &val)
{
#if 0
s.read(&val, 4);
val = htonl(val);
#endif
return s;
}
template<class Buffer, class TypeBuffer>
inline osc_stream<Buffer, TypeBuffer> &
operator >>(osc_stream<Buffer, TypeBuffer> &s, int32_t &val)
{
#if 0
s.read(&val, 4);
val = htonl(val);
#endif
return s;
}
template<class Buffer, class TypeBuffer>
inline osc_stream<Buffer, TypeBuffer> &
operator <<(osc_stream<Buffer, TypeBuffer> &s, float val)
{
union { float v; uint32_t i; } val2;
val2.v = val;
val2.i = htonl(val2.i);
s.write(&val2.i, 4);
s.write_type(osc_f32);
return s;
}
template<class Buffer, class TypeBuffer>
inline osc_stream<Buffer, TypeBuffer> &
operator >>(osc_stream<Buffer, TypeBuffer> &s, float &val)
{
union { float v; uint32_t i; } val2;
s.read(&val2.i, 4);
val2.i = htonl(val2.i);
val = val2.v;
return s;
}
template<class Buffer, class TypeBuffer>
inline osc_stream<Buffer, TypeBuffer> &
operator <<(osc_stream<Buffer, TypeBuffer> &s, const std::string &str)
{
s.write(&str[0], str.length());
s.pad();
s.write_type(osc_string);
return s;
}
template<class Buffer, class TypeBuffer>
inline osc_stream<Buffer, TypeBuffer> &
operator >>(osc_stream<Buffer, TypeBuffer> &s, std::string &str)
{
// inefficient...
char five[5];
five[4] = '\0';
str.resize(0);
while(1)
{
s.read(five, 4);
if (five[0] == '\0')
break;
str += five;
if (!five[1] || !five[2] || !five[3])
break;
}
return s;
}
template<class Buffer, class TypeBuffer, class DestBuffer>
inline osc_stream<Buffer, TypeBuffer> &
read_buffer_from_osc_stream(osc_stream<Buffer, TypeBuffer> &s, DestBuffer &buf)
{
#if 0
uint32_t nlen = 0;
s.read(&nlen, 4);
uint32_t len = htonl(nlen);
// write length in network order
for (uint32_t i = 0; i < len; i += 1024)
{
uint8_t tmp[1024];
uint32_t part = std::min((uint32_t)1024, len - i);
s.read(tmp, part);
buf.write(tmp, part);
}
// pad
s.read(&nlen, 4 - (len & 3));
#endif
return s;
}
template<class Buffer, class TypeBuffer, class SrcBuffer>
inline osc_stream<Buffer, TypeBuffer> &
write_buffer_to_osc_stream(osc_stream<Buffer, TypeBuffer> &s, SrcBuffer &buf)
{
#if 0
uint32_t len = buf.read_left();
uint32_t nlen = ntohl(len);
s.write(&nlen, 4);
// write length in network order
for (uint32_t i = 0; i < len; i += 1024)
{
uint8_t tmp[1024];
uint32_t part = std::min((uint32_t)1024, len - i);
buf.read(tmp, part);
s.write(tmp, part);
}
s.pad();
s.write_type(osc_blob);
#endif
return s;
}
template<class Buffer, class TypeBuffer>
inline osc_stream<Buffer, TypeBuffer> &
operator >>(osc_stream<Buffer, TypeBuffer> &s, raw_buffer &str)
{
return read_buffer_from_osc_stream(s, str);
}
template<class Buffer, class TypeBuffer>
inline osc_stream<Buffer, TypeBuffer> &
operator >>(osc_stream<Buffer, TypeBuffer> &s, string_buffer &str)
{
return read_buffer_from_osc_stream(s, str);
}
template<class Buffer, class TypeBuffer>
inline osc_stream<Buffer, TypeBuffer> &
operator <<(osc_stream<Buffer, TypeBuffer> &s, raw_buffer &str)
{
return write_buffer_to_osc_stream(s, str);
}
template<class Buffer, class TypeBuffer>
inline osc_stream<Buffer, TypeBuffer> &
operator <<(osc_stream<Buffer, TypeBuffer> &s, string_buffer &str)
{
return write_buffer_to_osc_stream(s, str);
}
// XXXKF: I don't support reading binary blobs yet
struct osc_net_bad_address: public std::exception
{
std::string addr, error_msg;
osc_net_bad_address(const char *_addr)
{
addr = _addr;
error_msg = "Incorrect OSC URI: " + addr;
}
virtual const char *what() const throw() { return error_msg.c_str(); }
virtual ~osc_net_bad_address() throw () {}
};
struct osc_net_exception: public std::exception
{
int net_errno;
std::string command, error_msg;
osc_net_exception(const char *cmd, int _errno = errno)
{
command = cmd;
net_errno = _errno;
error_msg = "OSC error in "+command+": "+strerror(_errno);
}
virtual const char *what() const throw() { return error_msg.c_str(); }
virtual ~osc_net_exception() throw () {}
};
struct osc_net_dns_exception: public std::exception
{
#if 0
int net_errno;
std::string command, error_msg;
osc_net_dns_exception(const char *cmd, int _errno = h_errno)
{
command = cmd;
net_errno = _errno;
error_msg = "OSC error in "+command+": "+hstrerror(_errno);
}
virtual const char *what() const throw() { return error_msg.c_str(); }
virtual ~osc_net_dns_exception() throw () {}
#endif
};
template<class OscStream>
struct osc_message_sink
{
virtual void receive_osc_message(std::string address, std::string type_tag, OscStream &buffer)=0;
virtual ~osc_message_sink() {}
};
template<class OscStream, class DumpStream>
struct osc_message_dump: public osc_message_sink<OscStream>
{
DumpStream &stream;
osc_message_dump(DumpStream &_stream) : stream(_stream) {}
virtual void receive_osc_message(std::string address, std::string type_tag, OscStream &buffer)
{
int pos = buffer.buffer.tell();
stream << "address: " << address << ", type tag: " << type_tag << std::endl;
for (unsigned int i = 0; i < type_tag.size(); i++)
{
stream << "Argument " << i << " is ";
switch(type_tag[i])
{
case 'i':
{
uint32_t val;
buffer >> val;
stream << val;
break;
}
case 'f':
{
float val;
buffer >> val;
stream << val;
break;
}
case 's':
{
std::string val;
buffer >> val;
stream << val;
break;
}
case 'b':
{
osctl::string_buffer val;
buffer >> val;
stream << "blob (" << val.data.length() << " bytes)";
break;
}
default:
{
stream << "unknown - cannot parse more arguments" << std::endl;
i = type_tag.size();
break;
}
}
stream << std::endl;
}
stream << std::flush;
buffer.buffer.seek(pos);
}
};
};
#endif

View File

@@ -0,0 +1,133 @@
/* Calf DSP plugin pack
* Tools to use in plugins
*
* Copyright (C) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen and others
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef CALF_PLUGIN_TOOLS_H
#define CALF_PLUGIN_TOOLS_H
#include <config.h>
#include "giface.h"
#include "vumeter.h"
namespace calf_plugins {
template<class Meter>
struct in_out_metering_base
{
typedef Meter meter;
meter vumeter_in, vumeter_out;
in_out_metering_base()
{
reset();
}
void reset()
{
vumeter_in.reset();
vumeter_out.reset();
}
void set_sample_rate(double sample_rate)
{
vumeter_in.set_falloff(0.f, sample_rate);
vumeter_out.copy_falloff(vumeter_in);
}
};
/// Universal single stereo level metering for a specific plugin
template<class Metadata>
class stereo_in_out_metering: public in_out_metering_base<dsp::vumeter>
{
public:
inline void process(float *const *params, const float *const *inputs, const float *const *outputs, unsigned int offset, unsigned int nsamples)
{
if (params[Metadata::param_meter_in] || params[Metadata::param_clip_in]) {
if (inputs)
vumeter_in.update_stereo(inputs[0] ? inputs[0] + offset : NULL, inputs[1] ? inputs[1] + offset : NULL, nsamples);
else
vumeter_in.update_zeros(nsamples);
if (params[Metadata::param_meter_in])
*params[Metadata::param_meter_in] = vumeter_in.level;
if (params[Metadata::param_clip_in])
*params[Metadata::param_clip_in] = vumeter_in.clip > 0 ? 1.f : 0.f;
}
if (params[Metadata::param_meter_out] || params[Metadata::param_clip_out]) {
if (outputs)
vumeter_out.update_stereo(outputs[0] ? outputs[0] + offset : NULL, outputs[1] ? outputs[1] + offset : NULL, nsamples);
else
vumeter_out.update_zeros(nsamples);
if (params[Metadata::param_meter_out])
*params[Metadata::param_meter_out] = vumeter_out.level;
if (params[Metadata::param_clip_out])
*params[Metadata::param_clip_out] = vumeter_out.clip > 0 ? 1.f : 0.f;
}
}
void bypassed(float *const *params, unsigned int nsamples)
{
reset();
process(params, NULL, NULL, 0, nsamples);
}
};
/// Universal dual level metering for a specific plugin
template<class Metadata>
class dual_in_out_metering: public in_out_metering_base<dsp::dual_vumeter>
{
public:
inline void process(float *const *params, const float *const *inputs, const float *const *outputs, unsigned int offset, unsigned int nsamples)
{
if (params[Metadata::param_meter_inL] || params[Metadata::param_clip_inL] || params[Metadata::param_meter_inR] || params[Metadata::param_clip_inR]) {
if (inputs)
vumeter_in.update_stereo(inputs[0] ? inputs[0] + offset : NULL, inputs[1] ? inputs[1] + offset : NULL, nsamples);
else
vumeter_in.update_zeros(nsamples);
if (params[Metadata::param_meter_inL])
*params[Metadata::param_meter_inL] = vumeter_in.left.level;
if (params[Metadata::param_meter_inR])
*params[Metadata::param_meter_inR] = vumeter_in.right.level;
if (params[Metadata::param_clip_inL])
*params[Metadata::param_clip_inL] = vumeter_in.left.clip > 0 ? 1.f : 0.f;
if (params[Metadata::param_clip_inR])
*params[Metadata::param_clip_inR] = vumeter_in.right.clip > 0 ? 1.f : 0.f;
}
if (params[Metadata::param_meter_outL] || params[Metadata::param_clip_outL] || params[Metadata::param_meter_outR] || params[Metadata::param_clip_outR]) {
if (outputs)
vumeter_out.update_stereo(outputs[0] ? outputs[0] + offset : NULL, outputs[1] ? outputs[1] + offset : NULL, nsamples);
else
vumeter_out.update_zeros(nsamples);
if (params[Metadata::param_meter_outL])
*params[Metadata::param_meter_outL] = vumeter_out.left.level;
if (params[Metadata::param_meter_outR])
*params[Metadata::param_meter_outR] = vumeter_out.right.level;
if (params[Metadata::param_clip_outL])
*params[Metadata::param_clip_outL] = vumeter_out.left.clip > 0 ? 1.f : 0.f;
if (params[Metadata::param_clip_outR])
*params[Metadata::param_clip_outR] = vumeter_out.right.clip > 0 ? 1.f : 0.f;
}
}
void bypassed(float *const *params, unsigned int nsamples)
{
reset();
process(params, NULL, NULL, 0, nsamples);
}
};
};
#endif

View File

@@ -0,0 +1,167 @@
/* Calf DSP Library
* Preset management
*
* Copyright (C) 2007 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_PRESET_H
#define __CALF_PRESET_H
#include <vector>
#include <string.h>
#include "utils.h"
namespace calf_plugins {
class plugin_ctl_iface;
/// Contents of single preset
struct plugin_preset
{
/// Bank the preset belongs to (not used yet)
int bank;
/// Program number of the preset (not used yet)
int program;
/// Name of the preset
std::string name;
/// Name of the plugin the preset is for
std::string plugin;
/// Names of parameters in values array (for each item in param_names there should be a counterpart in values)
std::vector<std::string> param_names;
/// Values of parameters
std::vector<float> values;
/// DSSI configure-style variables
std::map<std::string, std::string> variables;
plugin_preset() : bank(0), program(0) {}
/// Export preset as XML
std::string to_xml();
/// "Upload" preset content to the plugin
void activate(plugin_ctl_iface *plugin);
/// "Download" preset content from the plugin
void get_from(plugin_ctl_iface *plugin);
std::string get_safe_name();
};
/// Exception thrown by preset system
struct preset_exception
{
std::string message, param, fulltext;
int error;
preset_exception(const std::string &_message, const std::string &_param, int _error)
: message(_message), param(_param), error(_error)
{
}
const char *what() {
if (error)
fulltext = message + " " + param + " (" + strerror(error) + ")";
else
fulltext = message + " " + param;
return fulltext.c_str();
}
~preset_exception()
{
}
};
/// A vector of presets
typedef std::vector<plugin_preset> preset_vector;
/// A single list of presets (usually there are two - @see get_builtin_presets(), get_user_presets() )
struct preset_list
{
/// Plugin list item
struct plugin_snapshot
{
/// Preset offset
int preset_offset;
/// Plugin type
std::string type;
/// Instance name
std::string instance_name;
/// Index of the first input port
int input_index;
/// Index of the first output port
int output_index;
/// Index of the first MIDI port
int midi_index;
/// Reset to initial values
void reset();
};
/// Parser states
enum parser_state
{
START, ///< Beginning of parsing process (before root element)
LIST, ///< Inside root element
PRESET, ///< Inside preset definition
VALUE, ///< Inside (empty) param tag
VAR, ///< Inside (non-empty) var tag
PLUGIN, ///< Inside plugin element (calfjackhost snapshots only)
RACK, ///< Inside rack element (calfjackhost snapshots only)
} state;
/// Contained presets (usually for all plugins)
preset_vector presets;
/// Temporary preset used during parsing process
plugin_preset parser_preset;
/// Temporary plugin desc used during parsing process
plugin_snapshot parser_plugin;
/// Preset number counters for DSSI (currently broken)
std::map<std::string, int> last_preset_ids;
/// The key used in current <var name="key"> tag (for state == VAR)
std::string current_key;
/// The file is loaded in rack mode (and rack/plugin elements are expected)
bool rack_mode;
/// List of plugin states for rack mode
std::vector<plugin_snapshot> plugins;
/// Return the name of the built-in or user-defined preset file
static std::string get_preset_filename(bool builtin);
/// Load default preset list (built-in or user-defined)
bool load_defaults(bool builtin);
/// Load preset list from an in-memory XML string
void parse(const std::string &data, bool in_rack_mode);
/// Load preset list from XML file
void load(const char *filename, bool in_rack_mode);
/// Save preset list as XML file
void save(const char *filename);
/// Append or replace a preset (replaces a preset with the same plugin and preset name)
void add(const plugin_preset &sp);
/// Get a sublist of presets for a given plugin (those with plugin_preset::plugin == plugin)
void get_for_plugin(preset_vector &vec, const char *plugin);
protected:
/// Internal function: start element handler for expat
static void xml_start_element_handler(void *user_data, const char *name, const char *attrs[]);
/// Internal function: end element handler for expat
static void xml_end_element_handler(void *user_data, const char *name);
/// Internal function: character data (tag text content) handler for expat
static void xml_character_data_handler(void *user_data, const char *data, int len);
};
/// Return the current list of built-in (factory) presets (these are loaded from system-wide file)
extern preset_list &get_builtin_presets();
/// Return the current list of user-defined presets (these are loaded from ~/.calfpresets)
extern preset_list &get_user_presets();
};
#endif

View File

@@ -0,0 +1,533 @@
/* Calf DSP Library
* DSP primitives.
*
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_PRIMITIVES_H
#define __CALF_PRIMITIVES_H
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <cmath>
#include <cstdlib>
#include <map>
namespace dsp {
/// Set a float to zero
inline void zero(float &v) {
v = 0;
};
/// Set a double to zero
inline void zero(double &v) {
v = 0;
};
/// Set 64-bit unsigned integer value to zero
inline void zero(uint64_t &v) { v = 0; };
/// Set 32-bit unsigned integer value to zero
inline void zero(uint32_t &v) { v = 0; };
/// Set 16-bit unsigned integer value to zero
inline void zero(uint16_t &v) { v = 0; };
/// Set 8-bit unsigned integer value to zero
inline void zero(uint8_t &v) { v = 0; };
/// Set 64-bit signed integer value to zero
inline void zero(int64_t &v) { v = 0; };
/// Set 32-bit signed integer value to zero
inline void zero(int32_t &v) { v = 0; };
/// Set 16-bit signed integer value to zero
inline void zero(int16_t &v) { v = 0; };
/// Set 8-bit signed integer value to zero
inline void zero(int8_t &v) { v = 0; };
/// Set array (buffer or anything similar) to vector of zeroes
template<class T>
void zero(T *data, unsigned int size) {
T value;
dsp::zero(value);
for (unsigned int i=0; i<size; i++)
*data++ = value;
}
template<class T = float>struct stereo_sample {
T left;
T right;
/// default constructor - preserves T's semantics (ie. no implicit initialization to 0)
inline stereo_sample() {
}
inline stereo_sample(T _left, T _right) {
left = _left;
right = _right;
}
inline stereo_sample(T _both) {
left = right = _both;
}
template<typename U>
inline stereo_sample(const stereo_sample<U> &value) {
left = value.left;
right = value.right;
}
inline stereo_sample& operator=(const T &value) {
left = right = value;
return *this;
}
template<typename U>
inline stereo_sample& operator=(const stereo_sample<U> &value) {
left = value.left;
right = value.right;
return *this;
}
/*
inline operator T() const {
return (left+right)/2;
}
*/
inline stereo_sample& operator*=(const T &multiplier) {
left *= multiplier;
right *= multiplier;
return *this;
}
inline stereo_sample& operator+=(const stereo_sample<T> &value) {
left += value.left;
right += value.right;
return *this;
}
inline stereo_sample& operator-=(const stereo_sample<T> &value) {
left -= value.left;
right -= value.right;
return *this;
}
template<typename U> inline stereo_sample<U> operator*(const U &value) const {
return stereo_sample<U>(left*value, right*value);
}
/*inline stereo_sample<float> operator*(float value) const {
return stereo_sample<float>(left*value, right*value);
}
inline stereo_sample<double> operator*(double value) const {
return stereo_sample<double>(left*value, right*value);
}*/
inline stereo_sample<T> operator+(const stereo_sample<T> &value) {
return stereo_sample(left+value.left, right+value.right);
}
inline stereo_sample<T> operator-(const stereo_sample<T> &value) {
return stereo_sample(left-value.left, right-value.right);
}
inline stereo_sample<T> operator+(const T &value) {
return stereo_sample(left+value, right+value);
}
inline stereo_sample<T> operator-(const T &value) {
return stereo_sample(left-value, right-value);
}
inline stereo_sample<float> operator+(float value) {
return stereo_sample<float>(left+value, right+value);
}
inline stereo_sample<float> operator-(float value) {
return stereo_sample<float>(left-value, right-value);
}
inline stereo_sample<double> operator+(double value) {
return stereo_sample<double>(left+value, right+value);
}
inline stereo_sample<double> operator-(double value) {
return stereo_sample<double>(left-value, right-value);
}
};
/// Multiply constant by stereo_value
template<class T>
inline stereo_sample<T> operator*(const T &value, const stereo_sample<T> &value2) {
return stereo_sample<T>(value2.left*value, value2.right*value);
}
/// Add constant to stereo_value
template<class T>
inline stereo_sample<T> operator+(const T &value, const stereo_sample<T> &value2) {
return stereo_sample<T>(value2.left+value, value2.right+value);
}
/// Subtract stereo_value from constant (yields stereo_value of course)
template<class T>
inline stereo_sample<T> operator-(const T &value, const stereo_sample<T> &value2) {
return stereo_sample<T>(value-value2.left, value-value2.right);
}
/// Shift value right by 'bits' bits (multiply by 2^-bits)
template<typename T>
inline stereo_sample<T> shr(stereo_sample<T> v, int bits = 1) {
v.left = shr(v.left, bits);
v.right = shr(v.right, bits);
return v;
}
/// Set a stereo_sample<T> value to zero
template<typename T>
inline void zero(stereo_sample<T> &v) {
dsp::zero(v.left);
dsp::zero(v.right);
}
/// 'Small value' for integer and other types
template<typename T>
inline T small_value() {
return 0;
}
/// 'Small value' for floats (2^-24) - used for primitive underrun prevention. The value is pretty much arbitrary (allowing for 24-bit signals normalized to 1.0).
template<>
inline float small_value<float>() {
return (1.0/16777216.0); // allows for 2^-24, should be enough for 24-bit DACs at least :)
}
/// 'Small value' for doubles (2^-24) - used for primitive underrun prevention. The value is pretty much arbitrary.
template<>
inline double small_value<double>() {
return (1.0/16777216.0);
}
/// Convert a single value to single value = do nothing :) (but it's a generic with specialisation for stereo_sample)
template<typename T>
inline float mono(T v) {
return v;
}
/// Convert a stereo_sample to single value by averaging two channels
template<typename T>
inline T mono(stereo_sample<T> v) {
return shr(v.left+v.right);
}
/// Clip a value to [min, max]
template<typename T>
inline T clip(T value, T min, T max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
/// Clip a double to [-1.0, +1.0]
inline double clip11(double value) {
double a = fabs(value);
if (a<=1) return value;
return (value<0) ? -1.0 : 1.0;
}
/// Clip a float to [-1.0f, +1.0f]
inline float clip11(float value) {
float a = fabsf(value);
if (a<=1) return value;
return (value<0) ? -1.0f : 1.0f;
}
/// Clip a double to [0.0, +1.0]
inline double clip01(double value) {
double a = fabs(value-0.5);
if (a<=0.5) return value;
return (a<0) ? -0.0 : 1.0;
}
/// Clip a float to [0.0f, +1.0f]
inline float clip01(float value) {
float a = fabsf(value-0.5f);
if (a<=0.5f) return value;
return (value < 0) ? -0.0f : 1.0f;
}
// Linear interpolation (mix-way between v1 and v2).
template<typename T, typename U>
inline T lerp(T v1, T v2, U mix) {
return v1+(v2-v1)*mix;
}
// Linear interpolation for stereo values (mix-way between v1 and v2).
template<typename T>
inline stereo_sample<T> lerp(stereo_sample<T> &v1, stereo_sample<T> &v2, float mix) {
return stereo_sample<T>(v1.left+(v2.left-v1.left)*mix, v1.right+(v2.right-v1.right)*mix);
}
/**
* decay-only envelope (linear or exponential); deactivates itself when it goes below a set point (epsilon)
*/
class decay
{
double value, initial;
unsigned int age, mask;
bool active;
public:
decay() {
active = false;
mask = 127;
initial = value = 0.0;
}
inline bool get_active() {
return active;
}
inline double get() {
return active ? value : 0.0;
}
inline void set(double v) {
initial = value = v;
active = true;
age = 0;
}
/// reinitialise envelope (must be called if shape changes from linear to exponential or vice versa in the middle of envelope)
inline void reinit()
{
initial = value;
age = 1;
}
inline void add(double v) {
if (active)
value += v;
else
value = v;
initial = value;
age = 0;
active = true;
}
static inline double calc_exp_constant(double times, double cycles)
{
if (cycles < 1.0)
cycles = 1.0;
return pow(times, 1.0 / cycles);
}
inline void age_exp(double constant, double epsilon) {
if (active) {
if (!(age & mask))
value = initial * pow(constant, (double)age);
else
value *= constant;
if (value < epsilon)
active = false;
age++;
}
}
inline void age_lin(double constant, double epsilon) {
if (active) {
if (!(age & mask))
value = initial - constant * age;
else
value -= constant;
if (value < epsilon)
active = false;
age++;
}
}
inline void deactivate() {
active = false;
value = 0;
}
};
class scheduler;
class task {
public:
virtual void execute(scheduler *s)=0;
virtual void dispose() { delete this; }
virtual ~task() {}
};
/// this scheduler is based on std::multimap, so it isn't very fast, I guess
/// maybe some day it should be rewritten to use heapsort or something
/// work in progress, don't use!
class scheduler {
std::multimap<unsigned int, task *> timeline;
unsigned int time, next_task;
bool eob;
class end_buf_task: public task {
public:
scheduler *p;
end_buf_task(scheduler *_p) : p(_p) {}
virtual void execute(scheduler *s) { p->eob = true; }
virtual void dispose() { }
} eobt;
public:
scheduler()
: time(0)
, next_task((unsigned)-1)
, eob(true)
, eobt (this)
{
time = 0;
next_task = (unsigned)-1;
eob = false;
}
inline bool is_next_tick() {
if (time < next_task)
return true;
do_tasks();
}
inline void next_tick() {
time++;
}
void set(int pos, task *t) {
timeline.insert(std::pair<unsigned int, task *>(time+pos, t));
next_task = timeline.begin()->first;
}
void do_tasks() {
std::multimap<unsigned int, task *>::iterator i = timeline.begin();
while(i != timeline.end() && i->first == time) {
i->second->execute(this);
i->second->dispose();
timeline.erase(i);
}
}
bool is_eob() {
return eob;
}
void set_buffer_size(int count) {
set(count, &eobt);
}
};
/**
* Force "small enough" float value to zero
*/
inline void sanitize(float &value)
{
if (std::abs(value) < small_value<float>())
value = 0.f;
}
/**
* Force already-denormal float value to zero
*/
inline void sanitize_denormal(float& value)
{
if (((*(unsigned int *) &value) & 0x7f800000) == 0) {
value = 0;
}
}
/**
* Force "small enough" double value to zero
*/
inline void sanitize(double &value)
{
if (std::abs(value) < small_value<double>())
value = 0.f;
}
/**
* Force "small enough" stereo value to zero
*/
template<class T>
inline void sanitize(stereo_sample<T> &value)
{
sanitize(value.left);
sanitize(value.right);
}
inline float fract16(unsigned int value)
{
return (value & 0xFFFF) * (1.0 / 65536.0);
}
/**
* typical precalculated sine table
*/
template<class T, int N, int Multiplier>
class sine_table
{
public:
static bool initialized;
static T data[N+1];
sine_table() {
if (initialized)
return;
initialized = true;
for (int i=0; i<N+1; i++)
data[i] = (T)(Multiplier*sin(i*2*M_PI*(1.0/N)));
}
};
template<class T, int N, int Multiplier>
bool sine_table<T,N,Multiplier>::initialized = false;
template<class T, int N, int Multiplier>
T sine_table<T,N,Multiplier>::data[N+1];
/// fast float to int conversion using default rounding mode
inline int fastf2i_drm(float f)
{
#ifdef __X86__
volatile int v;
__asm ( "flds %1; fistpl %0" : "=m"(v) : "m"(f));
return v;
#else
return (int)nearbyintf(f);
#endif
}
/// Convert MIDI note to frequency in Hz.
inline float note_to_hz(double note, double detune_cents = 0.0)
{
return 440 * pow(2.0, (note - 69 + detune_cents/100.0) / 12.0);
}
/// Hermite interpolation between two points and slopes in normalized range (written after Wikipedia article)
/// @arg t normalized x coordinate (0-1 over the interval in question)
/// @arg p0 first point
/// @arg p1 second point
/// @arg m0 first slope (multiply by interval width when using over non-1-wide interval)
/// @arg m1 second slope (multiply by interval width when using over non-1-wide interval)
inline float normalized_hermite(float t, float p0, float p1, float m0, float m1)
{
float t2 = t*t;
float t3 = t2*t;
return (2*t3 - 3*t2 + 1) * p0 + (t3 - 2*t2 + t) * m0 + (-2*t3 + 3*t2) * p1 + (t3-t2) * m1;
}
/// Hermite interpolation between two points and slopes
/// @arg x point within interval (x0 <= x <= x1)
/// @arg x0 interval start
/// @arg x1 interval end
/// @arg p0 value at x0
/// @arg p1 value at x1
/// @arg m0 slope (steepness, tangent) at x0
/// @arg m1 slope at x1
inline float hermite_interpolation(float x, float x0, float x1, float p0, float p1, float m0, float m1)
{
float width = x1 - x0;
float t = (x - x0) / width;
m0 *= width;
m1 *= width;
float t2 = t*t;
float t3 = t2*t;
float ct0 = p0;
float ct1 = m0;
float ct2 = -3 * p0 - 2 * m0 + 3 * p1 - m1;
float ct3 = 2 * p0 + m0 - 2 * p1 + m1;
return ct3 * t3 + ct2 * t2 + ct1 * t + ct0;
//return (2*t3 - 3*t2 + 1) * p0 + (t3 - 2*t2 + t) * m0 + (-2*t3 + 3*t2) * p1 + (t3-t2) * m1;
}
/// convert amplitude value to dB
inline float amp2dB(float amp)
{
return 6.0 * log(amp) / log(2);
}
};
#endif

View File

@@ -0,0 +1,230 @@
/* Calf DSP Library
* Framework for synthesizer-like plugins. This is based
* on my earlier work on Drawbar electric organ emulator.
*
* Copyright (C) 2007 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef CALF_SYNTH_H
#define CALF_SYNTH_H
#include <assert.h>
#include <math.h>
#include <memory.h>
#include <stdint.h>
#include <bitset>
#include <list>
#include <stack>
namespace dsp {
/**
* A kind of set with fast non-ordered iteration, used for storing lists of pressed keys.
*/
class keystack {
private:
int dcount;
uint8_t active[128];
uint8_t states[128];
public:
keystack() {
memset(states, 0xFF, sizeof(states));
dcount = 0;
}
void clear() {
for (int i=0; i<dcount; i++)
states[active[i]] = 0xFF;
dcount = 0;
}
bool push(int key) {
assert(key >= 0 && key <= 127);
if (states[key] != 0xFF) {
return true;
}
states[key] = dcount;
active[dcount++] = key;
return false;
}
bool pop(int key) {
if (states[key] == 0xFF)
return false;
int pos = states[key];
if (pos != dcount-1) {
// reuse the popped item's stack position for stack top
int last = active[dcount-1];
active[pos] = last;
// mark that position's new place on stack
states[last] = pos;
}
states[key] = 0xFF;
dcount--;
return true;
}
inline bool has(int key) {
return states[key] != 0xFF;
}
inline int count() {
return dcount;
}
inline bool empty() {
return (dcount == 0);
}
inline int nth(int n) {
return active[n];
}
};
/**
* Convert MIDI note number to normalized UINT phase (where 1<<32 is full cycle).
* @param MIDI note number
* @param cents detune in cents (1/100 of a semitone)
* @param sr sample rate
*/
inline unsigned int midi_note_to_phase(int note, double cents, int sr) {
double incphase = 440*pow(2.0, (note-69)/12.0 + cents/1200.0)/sr;
if (incphase >= 1.0) incphase = fmod(incphase, 1.0);
incphase *= 65536.0*65536.0;
return (unsigned int)incphase;
}
// Base class for all voice objects
class voice {
public:
int sample_rate;
bool released, sostenuto, stolen;
voice() : sample_rate(-1), released(false), sostenuto(false), stolen(false) {}
/// reset voice to default state (used when a voice is to be reused)
virtual void setup(int sr) { sample_rate = sr; }
/// reset voice to default state (used when a voice is to be reused)
virtual void reset()=0;
/// a note was pressed
virtual void note_on(int note, int vel)=0;
/// a note was released
virtual void note_off(int vel)=0;
/// check if voice can be removed from active voice list
virtual bool get_active()=0;
/// render voice data to buffer
virtual void render_to(float (*buf)[2], int nsamples)=0;
/// very fast note off
virtual void steal()=0;
/// return the note used by this voice
virtual int get_current_note()=0;
virtual float get_priority() { return stolen ? 20000 : (released ? 1 : (sostenuto ? 200 : 100)); }
/// empty virtual destructor
virtual ~voice() {}
};
/// An "optimized" voice class using fixed-size processing units
/// and fixed number of channels. The drawback is that voice
/// control is not sample-accurate, and no modulation input
/// is possible, but it should be good enough for most cases
/// (like Calf Organ).
template<class Base>
class block_voice: public Base {
public:
// derived from Base
// enum { Channels = 2 };
using Base::Channels;
// enum { BlockSize = 16 };
using Base::BlockSize;
// float output_buffer[BlockSize][Channels];
using Base::output_buffer;
// void render_block();
using Base::render_block;
unsigned int read_ptr;
block_voice()
{
read_ptr = BlockSize;
}
virtual void reset()
{
Base::reset();
read_ptr = BlockSize;
}
virtual void render_to(float (*buf)[2], int nsamples)
{
int p = 0;
while(p < nsamples)
{
if (read_ptr == BlockSize)
{
render_block();
read_ptr = 0;
}
int ncopy = std::min<int>(BlockSize - read_ptr, nsamples - p);
for (int i = 0; i < ncopy; i++)
for (int c = 0; c < Channels; c++)
buf[p + i][c] += output_buffer[read_ptr + i][c];
p += ncopy;
read_ptr += ncopy;
}
}
};
/// Base class for all kinds of polyphonic instruments, provides
/// somewhat reasonable voice management, pedal support - and
/// little else. It's implemented as a base class with virtual
/// functions, so there's some performance loss, but it shouldn't
/// be horrible.
/// @todo it would make sense to support all notes off controller too
struct basic_synth {
protected:
/// Current sample rate
int sample_rate;
/// Hold pedal state
bool hold;
/// Sostenuto pedal state
bool sostenuto;
/// Voices currently playing
std::list<dsp::voice *> active_voices;
/// Voices allocated, but not used
std::stack<dsp::voice *> unused_voices;
/// Gate values for all 128 MIDI notes
std::bitset<128> gate;
/// Maximum allocated number of channels
unsigned int polyphony_limit;
void kill_note(int note, int vel, bool just_one);
public:
virtual void setup(int sr) {
sample_rate = sr;
hold = false;
sostenuto = false;
polyphony_limit = (unsigned)-1;
}
virtual void trim_voices();
virtual dsp::voice *give_voice();
virtual dsp::voice *alloc_voice()=0;
virtual dsp::voice *steal_voice();
virtual void render_to(float (*output)[2], int nsamples);
virtual void note_on(int note, int vel);
virtual void percussion_note_on(int note, int vel) {}
virtual void control_change(int ctl, int val);
virtual void note_off(int note, int vel);
/// amt = -8192 to 8191
virtual void pitch_bend(int amt) {}
virtual void on_pedal_release();
virtual bool check_percussion() { return active_voices.empty(); }
virtual ~basic_synth();
};
}
#endif

View File

@@ -0,0 +1,191 @@
/* Calf DSP Library
* Utilities
*
* Copyright (C) 2008 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_UTILS_H
#define __CALF_UTILS_H
#include <errno.h>
#include <pthread.h>
#include <map>
#include <string>
namespace calf_utils
{
/// Pthreads based mutex class
class ptmutex
{
public:
pthread_mutex_t pm;
ptmutex(int type = PTHREAD_MUTEX_RECURSIVE)
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, type);
pthread_mutex_init(&pm, &attr);
pthread_mutexattr_destroy(&attr);
}
bool lock()
{
return pthread_mutex_lock(&pm) == 0;
}
bool trylock()
{
return pthread_mutex_trylock(&pm) == 0;
}
void unlock()
{
pthread_mutex_unlock(&pm);
}
~ptmutex()
{
pthread_mutex_destroy(&pm);
}
};
class ptlock_base
{
protected:
ptmutex &mutex;
bool locked;
ptlock_base(ptmutex &_m)
: mutex(_m)
, locked(false)
{
}
public:
bool is_locked()
{
return locked;
}
void unlock()
{
mutex.unlock();
locked = false;
}
void unlocked()
{
locked = false;
}
~ptlock_base()
{
if (locked)
mutex.unlock();
}
};
/// Exception-safe mutex lock
class ptlock: public ptlock_base
{
public:
ptlock(ptmutex &_m) : ptlock_base(_m)
{
locked = mutex.lock();
}
};
/// Exception-safe polling mutex lock
class pttrylock: public ptlock_base
{
public:
pttrylock(ptmutex &_m) : ptlock_base(_m)
{
locked = mutex.trylock();
}
};
/// Exception-safe temporary assignment
template<class T, class Tref = T&>
class scope_assign
{
Tref data;
T old_value;
public:
scope_assign(Tref _data, T new_value)
: data(_data), old_value(_data)
{
data = new_value;
}
~scope_assign()
{
data = old_value;
}
};
struct text_exception: public std::exception
{
const char *text;
std::string container;
public:
text_exception(const std::string &t) : container(t) { text = container.c_str(); }
virtual const char *what() const throw () { return text; }
virtual ~text_exception() throw () {}
};
struct file_exception: public std::exception
{
const char *text;
std::string message, filename, container;
public:
file_exception(const std::string &f);
file_exception(const std::string &f, const std::string &t);
virtual const char *what() const throw () { return text; }
virtual ~file_exception() throw () {}
};
/// String-to-string mapping
typedef std::map<std::string, std::string> dictionary;
/// Serialize a dictonary to a string
extern std::string encode_map(const dictionary &data);
/// Deserialize a dictonary from a string
extern void decode_map(dictionary &data, const std::string &src);
/// int-to-string
extern std::string i2s(int value);
/// float-to-string
extern std::string f2s(double value);
/// float-to-string-that-doesn't-resemble-an-int
extern std::string ff2s(double value);
/// Encode a key-value pair as XML attribute
std::string to_xml_attr(const std::string &key, const std::string &value);
/// Escape a string to be used in XML file
std::string xml_escape(const std::string &src);
/// Load file from disk into a std::string blob, or throw file_exception
std::string load_file(const std::string &src);
/// Indent a string by another string (prefix each line)
std::string indent(const std::string &src, const std::string &indent);
};
#endif

View File

@@ -0,0 +1,147 @@
/* Calf DSP Library
* Peak metering facilities.
*
* Copyright (C) 2007 Krzysztof Foltman
*
* 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, 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; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#ifndef __CALF_VUMETER_H
#define __CALF_VUMETER_H
#include <math.h>
namespace dsp {
/// Peak meter class
struct vumeter
{
/// Measured signal level
float level;
/// Falloff of signal level (b1 coefficient of a 1-pole filter)
float falloff;
/// Clip indicator (set to 1 when |value| >= 1, fading otherwise)
float clip;
/// Falloff of clip indicator (b1 coefficient of a 1-pole filter); set to 1 if no falloff is required (manual reset of clip indicator)
float clip_falloff;
vumeter()
{
falloff = 0.999f;
clip_falloff = 0.999f;
reset();
}
void reset()
{
level = 0;
clip = 0;
}
/// Set falloff so that the meter falls 20dB in time_20dB seconds, assuming sample rate of sample_rate
/// @arg time_20dB time for the meter to move by 20dB (default 300ms if <= 0)
void set_falloff(double time_20dB, double sample_rate)
{
if (time_20dB <= 0)
time_20dB = 0.3;
// 20dB = 10x +/- --> 0.1 = pow(falloff, sample_rate * time_20dB) = exp(sample_rate * ln(falloff))
// ln(0.1) = sample_rate * ln(falloff)
falloff = pow(0.1, 1 / (sample_rate * time_20dB));
clip_falloff = falloff;
}
/// Copy falloff from another object
void copy_falloff(const vumeter &src)
{
falloff = src.falloff;
clip_falloff = src.clip_falloff;
}
/// Update peak meter based on input signal
inline void update(const float *src, unsigned int len)
{
update_stereo(src, NULL, len);
}
/// Update peak meter based on louder of two input signals
inline void update_stereo(const float *src1, const float *src2, unsigned int len)
{
// "Age" the old level by falloff^length
level *= pow(falloff, len);
// Same for clip level (using different fade constant)
clip *= pow(clip_falloff, len);
dsp::sanitize(level);
dsp::sanitize(clip);
// Process input samples - to get peak value, take a max of all values in the input signal and "aged" old peak
// Clip is set to 1 if any sample is out-of-range, if no clip occurs, the "aged" value is assumed
if (src1)
run_sample_loop(src1, len);
if (src2)
run_sample_loop(src2, len);
}
inline void run_sample_loop(const float *src, unsigned int len)
{
float tmp = level;
for (unsigned int i = 0; i < len; i++) {
float sig = fabs(src[i]);
tmp = std::max(tmp, sig);
if (sig >= 1.f)
clip = 1.f;
}
level = tmp;
}
/// Update clip meter as if update was called with all-zero input signal
inline void update_zeros(unsigned int len)
{
level *= pow((double)falloff, (double)len);
clip *= pow((double)clip_falloff, (double)len);
dsp::sanitize(level);
dsp::sanitize(clip);
}
};
struct dual_vumeter
{
vumeter left, right;
inline void update_stereo(const float *src1, const float *src2, unsigned int len)
{
left.update_stereo(src1, NULL, len);
right.update_stereo(NULL, src2, len);
}
inline void update_zeros(unsigned int len)
{
left.update_zeros(len);
right.update_zeros(len);
}
inline void reset()
{
left.reset();
right.reset();
}
inline void set_falloff(double time_20dB, double sample_rate)
{
left.set_falloff(time_20dB, sample_rate);
right.copy_falloff(left);
}
inline void copy_falloff(const dual_vumeter &src)
{
left.copy_falloff(src.left);
right.copy_falloff(src.right);
}
};
};
#endif

View File

@@ -0,0 +1,37 @@
/* Calf DSP Library
* Placeholder for waveshaping classes
*
* Copyright (C) 2001-2009 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02111-1307, USA.
*/
#ifndef __CALF_WAVESHAPING_H
#define __CALF_WAVESHAPING_H
/// This will be a waveshaper... when I'll code it (-:
/// (or get Tom Szlagyi's permission to use his own)
class waveshaper {
public:
waveshaper();
void activate() {}
void deactivate() {}
void set_params(float blend, float drive) {}
void set_sample_rate(uint32_t sr) {}
float process(float in) { return in; }
float get_distortion_level() { return 1; }
};
#endif

View File

@@ -0,0 +1,167 @@
#ifndef __CALF_WAVETABLE_H
#define __CALF_WAVETABLE_H
#include <assert.h>
#include "biquad.h"
#include "onepole.h"
#include "audio_fx.h"
#include "inertia.h"
#include "osc.h"
#include "synth.h"
#include "envelope.h"
#include "modmatrix.h"
namespace calf_plugins {
#define WAVETABLE_WAVE_BITS 8
class wavetable_audio_module;
struct wavetable_oscillator: public dsp::simple_oscillator
{
enum { SIZE = 1 << 8, MASK = SIZE - 1, SCALE = 1 << (32 - 8) };
int16_t (*tables)[256];
inline float get(uint16_t slice)
{
float fracslice = (slice & 255) * (1.0 / 256.0);
slice = slice >> 8;
int16_t *waveform = tables[slice];
int16_t *waveform2 = tables[slice + 1];
float value1 = 0.f, value2 = 0.f;
uint32_t cphase = phase, cphasedelta = phasedelta >> 3;
for (int j = 0; j < 8; j++)
{
uint32_t wpos = cphase >> (32 - 8);
uint32_t wpos2 = (wpos + 1) & MASK;
float frac = (cphase & (SCALE - 1)) * (1.0f / SCALE);
value1 += dsp::lerp((float)waveform[wpos], (float)waveform[wpos2], frac);
value2 += dsp::lerp((float)waveform2[wpos], (float)waveform2[wpos2], frac);
cphase += cphasedelta;
}
phase += phasedelta;
return dsp::lerp(value1, value2, fracslice) * (1.0 / 8.0) * (1.0 / 32768.0);;
}
};
class wavetable_voice: public dsp::voice
{
public:
enum { Channels = 2, BlockSize = 64, EnvCount = 3, OscCount = 2 };
float output_buffer[BlockSize][Channels];
protected:
int note;
wavetable_audio_module *parent;
float **params;
dsp::decay amp;
wavetable_oscillator oscs[OscCount];
dsp::adsr envs[EnvCount];
/// Current MIDI velocity
float velocity;
/// Current calculated mod matrix outputs
float moddest[wavetable_metadata::moddest_count];
/// Last oscillator shift (wavetable index) of each oscillator
float last_oscshift[OscCount];
/// Last oscillator amplitude of each oscillator
float last_oscamp[OscCount];
/// Current osc amplitude
float cur_oscamp[OscCount];
public:
wavetable_voice();
void set_params_ptr(wavetable_audio_module *_parent, int _srate);
void reset();
void note_on(int note, int vel);
void note_off(int /* vel */);
void channel_pressure(int value);
void steal();
void render_block();
virtual int get_current_note() {
return note;
}
virtual bool get_active() {
// printf("note %d getactive %d use_percussion %d pamp active %d\n", note, amp.get_active(), use_percussion(), pamp.get_active());
return (note != -1) && (amp.get_active()) && !envs[0].stopped();
}
inline void calc_derived_dests() {
float cv = dsp::clip<float>(0.5f + moddest[wavetable_metadata::moddest_oscmix], 0.f, 1.f);
cur_oscamp[0] = (cv) * *params[wavetable_metadata::par_o1level];
cur_oscamp[1] = (1 - cv) * *params[wavetable_metadata::par_o2level];
}
};
class wavetable_audio_module: public audio_module<wavetable_metadata>, public dsp::basic_synth, public mod_matrix_impl
{
public:
using dsp::basic_synth::note_on;
using dsp::basic_synth::note_off;
using dsp::basic_synth::control_change;
using dsp::basic_synth::pitch_bend;
protected:
uint32_t crate;
bool panic_flag;
public:
int16_t tables[wt_count][129][256]; // one dummy level for interpolation
/// Rows of the modulation matrix
dsp::modulation_entry mod_matrix_data[mod_matrix_slots];
/// Smoothed cutoff value
dsp::inertia<dsp::exponential_ramp> inertia_cutoff;
/// Smoothed pitch bend value
dsp::inertia<dsp::exponential_ramp> inertia_pitchbend;
/// Smoothed channel pressure value
dsp::inertia<dsp::linear_ramp> inertia_pressure;
/// Unsmoothed mod wheel value
float modwheel_value;
public:
wavetable_audio_module();
dsp::voice *alloc_voice() {
dsp::block_voice<wavetable_voice> *v = new dsp::block_voice<wavetable_voice>();
v->set_params_ptr(this, sample_rate);
return v;
}
/// process function copied from Organ (will probably need some adjustments as well as implementing the panic flag elsewhere
uint32_t process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask) {
float *o[2] = { outs[0] + offset, outs[1] + offset };
if (panic_flag)
{
control_change(120, 0); // stop all sounds
control_change(121, 0); // reset all controllers
panic_flag = false;
}
float buf[4096][2];
dsp::zero(&buf[0][0], 2 * nsamples);
basic_synth::render_to(buf, nsamples);
float gain = 1.0f;
for (uint32_t i=0; i<nsamples; i++) {
o[0][i] = gain*buf[i][0];
o[1][i] = gain*buf[i][1];
}
return 3;
}
void set_sample_rate(uint32_t sr) {
setup(sr);
crate = sample_rate / wavetable_voice::BlockSize;
inertia_cutoff.ramp.set_length(crate / 30); // 1/30s
inertia_pitchbend.ramp.set_length(crate / 30); // 1/30s
inertia_pressure.ramp.set_length(crate / 30); // 1/30s - XXXKF monosynth needs that too
}
virtual void note_on(int /*channel*/, int note, int velocity) { dsp::basic_synth::note_on(note, velocity); }
virtual void note_off(int /*channel*/, int note, int velocity) { dsp::basic_synth::note_off(note, velocity); }
virtual void control_change(int /*channel*/, int controller, int value) { dsp::basic_synth::control_change(controller, value); }
/// Handle MIDI Channel Pressure
virtual void channel_pressure(int channel, int value);
/// Handle pitch bend message.
virtual void pitch_bend(int channel, int value)
{
inertia_pitchbend.set_inertia(pow(2.0, (value * *params[par_pwhlrange]) / (1200.0 * 8192.0)));
}
};
};
#endif

View File

@@ -0,0 +1 @@
#define USE_LADSPA 1

View File

@@ -0,0 +1,397 @@
/* Calf DSP Library
* Implementation of various helpers for the plugin interface.
*
* Copyright (C) 2001-2010 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include <config.h>
#include <limits.h>
#include <calf/giface.h>
#include <calf/utils.h>
using namespace std;
using namespace calf_utils;
using namespace calf_plugins;
float parameter_properties::from_01(double value01) const
{
double value = dsp::clip(value01, 0., 1.);
switch(flags & PF_SCALEMASK)
{
case PF_SCALE_DEFAULT:
case PF_SCALE_LINEAR:
case PF_SCALE_PERC:
default:
value = min + (max - min) * value01;
break;
case PF_SCALE_QUAD:
value = min + (max - min) * value01 * value01;
break;
case PF_SCALE_LOG:
value = min * pow(double(max / min), value01);
break;
case PF_SCALE_GAIN:
if (value01 < 0.00001)
value = min;
else {
float rmin = std::max(1.0f / 1024.0f, min);
value = rmin * pow(double(max / rmin), value01);
}
break;
case PF_SCALE_LOG_INF:
assert(step);
if (value01 > (step - 1.0) / step)
value = FAKE_INFINITY;
else
value = min * pow(double(max / min), value01 * step / (step - 1.0));
break;
}
switch(flags & PF_TYPEMASK)
{
case PF_INT:
case PF_BOOL:
case PF_ENUM:
case PF_ENUM_MULTI:
if (value > 0)
value = (int)(value + 0.5);
else
value = (int)(value - 0.5);
break;
}
return value;
}
double parameter_properties::to_01(float value) const
{
switch(flags & PF_SCALEMASK)
{
case PF_SCALE_DEFAULT:
case PF_SCALE_LINEAR:
case PF_SCALE_PERC:
default:
return double(value - min) / (max - min);
case PF_SCALE_QUAD:
return sqrt(double(value - min) / (max - min));
case PF_SCALE_LOG:
value /= min;
return log((double)value) / log((double)max / min);
case PF_SCALE_LOG_INF:
if (IS_FAKE_INFINITY(value))
return max;
value /= min;
assert(step);
return (step - 1.0) * log((double)value) / (step * log((double)max / min));
case PF_SCALE_GAIN:
if (value < 1.0 / 1024.0) // new bottom limit - 60 dB
return 0;
double rmin = std::max(1.0f / 1024.0f, min);
value /= rmin;
return log((double)value) / log(max / rmin);
}
}
float parameter_properties::get_increment() const
{
float increment = 0.01;
if (step > 1)
increment = 1.0 / (step - 1);
else
if (step > 0 && step < 1)
increment = step;
else
if ((flags & PF_TYPEMASK) != PF_FLOAT)
increment = 1.0 / (max - min);
return increment;
}
int parameter_properties::get_char_count() const
{
if ((flags & PF_SCALEMASK) == PF_SCALE_PERC)
return 6;
if ((flags & PF_SCALEMASK) == PF_SCALE_GAIN) {
char buf[256];
size_t len = 0;
sprintf(buf, "%0.0f dB", 6.0 * log(min) / log(2));
len = strlen(buf);
sprintf(buf, "%0.0f dB", 6.0 * log(max) / log(2));
len = std::max(len, strlen(buf)) + 2;
return (int)len;
}
return std::max(to_string(min).length(), std::max(to_string(max).length(), to_string(min + (max-min) * 0.987654).length()));
}
std::string parameter_properties::to_string(float value) const
{
char buf[32];
if ((flags & PF_SCALEMASK) == PF_SCALE_PERC) {
sprintf(buf, "%0.f%%", 100.0 * value);
return string(buf);
}
if ((flags & PF_SCALEMASK) == PF_SCALE_GAIN) {
if (value < 1.0 / 1024.0) // new bottom limit - 60 dB
return "-inf dB"; // XXXKF change to utf-8 infinity
sprintf(buf, "%0.1f dB", 6.0 * log(value) / log(2));
return string(buf);
}
switch(flags & PF_TYPEMASK)
{
case PF_INT:
case PF_BOOL:
case PF_ENUM:
case PF_ENUM_MULTI:
value = (int)value;
break;
}
if ((flags & PF_SCALEMASK) == PF_SCALE_LOG_INF && IS_FAKE_INFINITY(value))
sprintf(buf, "+inf"); // XXXKF change to utf-8 infinity
else
sprintf(buf, "%g", value);
switch(flags & PF_UNITMASK) {
case PF_UNIT_DB: return string(buf) + " dB";
case PF_UNIT_HZ: return string(buf) + " Hz";
case PF_UNIT_SEC: return string(buf) + " s";
case PF_UNIT_MSEC: return string(buf) + " ms";
case PF_UNIT_CENTS: return string(buf) + " ct";
case PF_UNIT_SEMITONES: return string(buf) + "#";
case PF_UNIT_BPM: return string(buf) + " bpm";
case PF_UNIT_RPM: return string(buf) + " rpm";
case PF_UNIT_DEG: return string(buf) + " deg";
case PF_UNIT_NOTE:
{
static const char *notes = "C C#D D#E F F#G G#A A#B ";
int note = (int)value;
if (note < 0 || note > 127)
return "---";
return string(notes + 2 * (note % 12), 2) + i2s(note / 12 - 2);
}
}
return string(buf);
}
void calf_plugins::plugin_ctl_iface::clear_preset() {
int param_count = get_metadata_iface()->get_param_count();
for (int i = 0; i < param_count; i++)
{
const parameter_properties &pp = *get_metadata_iface()->get_param_props(i);
set_param_value(i, pp.def_value);
}
const char *const *vars = get_metadata_iface()->get_configure_vars();
if (vars)
{
for (int i = 0; vars[i]; i++)
configure(vars[i], NULL);
}
}
const char *calf_plugins::load_gui_xml(const std::string &plugin_id)
{
#if 0
try {
return strdup(calf_utils::load_file((std::string(PKGLIBDIR) + "/gui-" + plugin_id + ".xml").c_str()).c_str());
}
catch(file_exception e)
#endif
{
return NULL;
}
}
bool calf_plugins::get_freq_gridline(int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context, bool use_frequencies, float res, float ofs)
{
if (subindex < 0 )
return false;
if (use_frequencies)
{
if (subindex < 28)
{
vertical = true;
if (subindex == 9) legend = "100 Hz";
if (subindex == 18) legend = "1 kHz";
if (subindex == 27) legend = "10 kHz";
float freq = 100;
if (subindex < 9)
freq = 10 * (subindex + 1);
else if (subindex < 18)
freq = 100 * (subindex - 9 + 1);
else if (subindex < 27)
freq = 1000 * (subindex - 18 + 1);
else
freq = 10000 * (subindex - 27 + 1);
pos = log(freq / 20.0) / log(1000);
if (!legend.empty())
context->set_source_rgba(0, 0, 0, 0.2);
else
context->set_source_rgba(0, 0, 0, 0.1);
return true;
}
subindex -= 28;
}
if (subindex >= 32)
return false;
float gain = 16.0 / (1 << subindex);
pos = dB_grid(gain, res, ofs);
if (pos < -1)
return false;
if (subindex != 4)
context->set_source_rgba(0, 0, 0, subindex & 1 ? 0.1 : 0.2);
if (!(subindex & 1))
{
std::stringstream ss;
ss << (24 - 6 * subindex) << " dB";
legend = ss.str();
}
vertical = false;
return true;
}
void calf_plugins::set_channel_color(cairo_iface *context, int channel)
{
if (channel & 1)
context->set_source_rgba(0.35, 0.4, 0.2, 1);
else
context->set_source_rgba(0.35, 0.4, 0.2, 0.5);
context->set_line_width(1.5);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool frequency_response_line_graph::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const
{
return get_freq_gridline(subindex, pos, vertical, legend, context);
}
int frequency_response_line_graph::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const
{
subindex_graph = 0;
subindex_dot = 0;
subindex_gridline = generation ? INT_MAX : 0;
return 1;
}
///////////////////////////////////////////////////////////////////////////////////////
calf_plugins::plugin_registry &calf_plugins::plugin_registry::instance()
{
static calf_plugins::plugin_registry registry;
return registry;
}
const plugin_metadata_iface *calf_plugins::plugin_registry::get_by_uri(const char *plugin_uri)
{
static const char prefix[] = "http://calf.sourceforge.net/plugins/";
if (strncmp(plugin_uri, prefix, sizeof(prefix) - 1))
return NULL;
const char *label = plugin_uri + sizeof(prefix) - 1;
for (unsigned int i = 0; i < plugins.size(); i++)
{
if (!strcmp(plugins[i]->get_plugin_info().label, label))
return plugins[i];
}
return NULL;
}
const plugin_metadata_iface *calf_plugins::plugin_registry::get_by_id(const char *id, bool case_sensitive)
{
typedef int (*comparator)(const char *, const char *);
comparator comp = case_sensitive ? strcmp : strcasecmp;
for (unsigned int i = 0; i < plugins.size(); i++)
{
if (!comp(plugins[i]->get_id(), id))
return plugins[i];
}
return NULL;
}
///////////////////////////////////////////////////////////////////////////////////////
bool calf_plugins::parse_table_key(const char *key, const char *prefix, bool &is_rows, int &row, int &column)
{
is_rows = false;
row = -1;
column = -1;
if (0 != strncmp(key, prefix, strlen(prefix)))
return false;
key += strlen(prefix);
if (!strcmp(key, "rows"))
{
is_rows = true;
return true;
}
const char *comma = strchr(key, ',');
if (comma)
{
row = atoi(string(key, comma - key).c_str());
column = atoi(comma + 1);
return true;
}
printf("Unknown key %s under prefix %s", key, prefix);
return false;
}
///////////////////////////////////////////////////////////////////////////////////////
const char *mod_mapping_names[] = { "0..1", "-1..1", "-1..0", "x^2", "2x^2-1", "ASqr", "ASqrBip", "Para", NULL };
mod_matrix_metadata::mod_matrix_metadata(unsigned int _rows, const char **_src_names, const char **_dest_names)
: mod_src_names(_src_names)
, mod_dest_names(_dest_names)
, matrix_rows(_rows)
{
table_column_info tci[6] = {
{ "Source", TCT_ENUM, 0, 0, 0, mod_src_names },
{ "Mapping", TCT_ENUM, 0, 0, 0, mod_mapping_names },
{ "Modulator", TCT_ENUM, 0, 0, 0, mod_src_names },
{ "Amount", TCT_FLOAT, 0, 1, 1, NULL},
{ "Destination", TCT_ENUM, 0, 0, 0, mod_dest_names },
{ NULL }
};
assert(sizeof(table_columns) == sizeof(tci));
memcpy(table_columns, tci, sizeof(table_columns));
}
const table_column_info *mod_matrix_metadata::get_table_columns() const
{
return table_columns;
}
uint32_t mod_matrix_metadata::get_table_rows() const
{
return matrix_rows;
}
///////////////////////////////////////////////////////////////////////////////////////
#if USE_EXEC_GUI
table_via_configure::table_via_configure()
{
rows = 0;
}
table_via_configure::~table_via_configure()
{
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,153 @@
/* Calf DSP Library
* Modulation matrix boilerplate code.
*
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include <calf/modmatrix.h>
#include <calf/utils.h>
#include <memory.h>
#include <sstream>
using namespace std;
using namespace dsp;
using namespace calf_plugins;
using namespace calf_utils;
mod_matrix_impl::mod_matrix_impl(dsp::modulation_entry *_matrix, mod_matrix_metadata *_metadata)
: matrix(_matrix)
, metadata(_metadata)
{
matrix_rows = metadata->get_table_rows();
for (unsigned int i = 0; i < matrix_rows; i++)
matrix[i].reset();
}
const float mod_matrix_impl::scaling_coeffs[mod_matrix_metadata::map_type_count][3] = {
{ 0, 1, 0 },
{ -1, 2, 0 },
{ -1, 1, 0 },
{ 0, 0, 1 },
{ -1, 0, 1 },
{ 0, 2, -1 },
{ -1, 4, -2 },
{ 0, 4, -4 },
};
std::string mod_matrix_impl::get_cell(int row, int column) const
{
assert(row >= 0 && row < (int)matrix_rows);
modulation_entry &slot = matrix[row];
const char **arr = metadata->get_table_columns()[column].values;
switch(column) {
case 0: // source 1
return arr[slot.src1];
case 1: // mapping mode
return arr[slot.mapping];
case 2: // source 2
return arr[slot.src2];
case 3: // amount
return calf_utils::f2s(slot.amount);
case 4: // destination
return arr[slot.dest];
default:
assert(0);
return "";
}
}
void mod_matrix_impl::set_cell(int row, int column, const std::string &src, std::string &error)
{
assert(row >= 0 && row < (int)matrix_rows);
modulation_entry &slot = matrix[row];
const char **arr = metadata->get_table_columns()[column].values;
switch(column) {
case 0:
case 1:
case 2:
case 4:
{
for (int i = 0; arr[i]; i++)
{
if (src == arr[i])
{
if (column == 0)
slot.src1 = i;
else if (column == 1)
slot.mapping = (mod_matrix_metadata::mapping_mode)i;
else if (column == 2)
slot.src2 = i;
else if (column == 4)
slot.dest = i;
error.clear();
return;
}
}
error = "Invalid name: " + src;
return;
}
case 3:
{
stringstream ss(src);
ss >> slot.amount;
error.clear();
return;
}
}
}
void mod_matrix_impl::send_configures(send_configure_iface *sci)
{
for (int i = 0; i < (int)matrix_rows; i++)
{
for (int j = 0; j < 5; j++)
{
string key = "mod_matrix:" + i2s(i) + "," + i2s(j);
sci->send_configure(key.c_str(), get_cell(i, j).c_str());
}
}
}
char *mod_matrix_impl::configure(const char *key, const char *value)
{
bool is_rows;
int row, column;
if (!parse_table_key(key, "mod_matrix:", is_rows, row, column))
return NULL;
if (is_rows)
return strdup("Unexpected key");
if (row != -1 && column != -1)
{
string error;
string value_text;
if (value == NULL)
{
const table_column_info &ci = metadata->get_table_columns()[column];
if (ci.type == TCT_ENUM)
value_text = ci.values[(int)ci.def_value];
else
if (ci.type == TCT_FLOAT)
value_text = f2s(ci.def_value);
value = value_text.c_str();
}
set_cell(row, column, value, error);
if (!error.empty())
return strdup(error.c_str());
}
return NULL;
}

View File

@@ -0,0 +1,797 @@
/* Calf DSP plugin pack
* Assorted plugins
*
* Copyright (C) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include <limits.h>
#include <memory.h>
#include <calf/giface.h>
#include <calf/modules.h>
#include <calf/modules_dev.h>
using namespace dsp;
using namespace calf_plugins;
#define SET_IF_CONNECTED(name) if (params[AM::param_##name] != NULL) *params[AM::param_##name] = name;
///////////////////////////////////////////////////////////////////////////////////////////////
void reverb_audio_module::activate()
{
reverb.reset();
}
void reverb_audio_module::deactivate()
{
}
void reverb_audio_module::set_sample_rate(uint32_t sr)
{
srate = sr;
reverb.setup(sr);
amount.set_sample_rate(sr);
}
void reverb_audio_module::params_changed()
{
reverb.set_type_and_diffusion(fastf2i_drm(*params[par_roomsize]), *params[par_diffusion]);
reverb.set_time(*params[par_decay]);
reverb.set_cutoff(*params[par_hfdamp]);
amount.set_inertia(*params[par_amount]);
dryamount.set_inertia(*params[par_dry]);
left_lo.set_lp(dsp::clip(*params[par_treblecut], 20.f, (float)(srate * 0.49f)), srate);
left_hi.set_hp(dsp::clip(*params[par_basscut], 20.f, (float)(srate * 0.49f)), srate);
right_lo.copy_coeffs(left_lo);
right_hi.copy_coeffs(left_hi);
predelay_amt = (int) (srate * (*params[par_predelay]) * (1.0f / 1000.0f) + 1);
}
uint32_t reverb_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
{
numsamples += offset;
clip -= std::min(clip, numsamples);
for (uint32_t i = offset; i < numsamples; i++) {
float dry = dryamount.get();
float wet = amount.get();
stereo_sample<float> s(ins[0][i], ins[1][i]);
stereo_sample<float> s2 = pre_delay.process(s, predelay_amt);
float rl = s2.left, rr = s2.right;
rl = left_lo.process(left_hi.process(rl));
rr = right_lo.process(right_hi.process(rr));
reverb.process(rl, rr);
outs[0][i] = dry*s.left + wet*rl;
outs[1][i] = dry*s.right + wet*rr;
meter_wet = std::max(fabs(wet*rl), fabs(wet*rr));
meter_out = std::max(fabs(outs[0][i]), fabs(outs[1][i]));
if(outs[0][i] > 1.f or outs[1][i] > 1.f) {
clip = srate >> 3;
}
}
reverb.extra_sanitize();
left_lo.sanitize();
left_hi.sanitize();
right_lo.sanitize();
right_hi.sanitize();
if(params[par_meter_wet] != NULL) {
*params[par_meter_wet] = meter_wet;
}
if(params[par_meter_out] != NULL) {
*params[par_meter_out] = meter_out;
}
if(params[par_clip] != NULL) {
*params[par_clip] = clip;
}
return outputs_mask;
}
///////////////////////////////////////////////////////////////////////////////////////////////
vintage_delay_audio_module::vintage_delay_audio_module()
{
old_medium = -1;
for (int i = 0; i < MAX_DELAY; i++) {
buffers[0][i] = 0.f;
buffers[1][i] = 0.f;
}
}
void vintage_delay_audio_module::params_changed()
{
float unit = 60.0 * srate / (*params[par_bpm] * *params[par_divide]);
deltime_l = dsp::fastf2i_drm(unit * *params[par_time_l]);
deltime_r = dsp::fastf2i_drm(unit * *params[par_time_r]);
int deltime_fb = deltime_l + deltime_r;
float fb = *params[par_feedback];
dry.set_inertia(*params[par_dryamount]);
mixmode = dsp::fastf2i_drm(*params[par_mixmode]);
medium = dsp::fastf2i_drm(*params[par_medium]);
switch(mixmode)
{
case MIXMODE_STEREO:
fb_left.set_inertia(fb);
fb_right.set_inertia(pow(fb, *params[par_time_r] / *params[par_time_l]));
amt_left.set_inertia(*params[par_amount]);
amt_right.set_inertia(*params[par_amount]);
break;
case MIXMODE_PINGPONG:
fb_left.set_inertia(fb);
fb_right.set_inertia(fb);
amt_left.set_inertia(*params[par_amount]);
amt_right.set_inertia(*params[par_amount]);
break;
case MIXMODE_LR:
fb_left.set_inertia(fb);
fb_right.set_inertia(fb);
amt_left.set_inertia(*params[par_amount]); // L is straight 'amount'
amt_right.set_inertia(*params[par_amount] * pow(fb, 1.0 * deltime_r / deltime_fb)); // R is amount with feedback based dampening as if it ran through R/FB*100% of delay line's dampening
// deltime_l <<< deltime_r -> pow() = fb -> full delay line worth of dampening
// deltime_l >>> deltime_r -> pow() = 1 -> no dampening
break;
case MIXMODE_RL:
fb_left.set_inertia(fb);
fb_right.set_inertia(fb);
amt_left.set_inertia(*params[par_amount] * pow(fb, 1.0 * deltime_l / deltime_fb));
amt_right.set_inertia(*params[par_amount]);
break;
}
chmix.set_inertia((1 - *params[par_width]) * 0.5);
if (medium != old_medium)
calc_filters();
}
void vintage_delay_audio_module::activate()
{
bufptr = 0;
age = 0;
}
void vintage_delay_audio_module::deactivate()
{
}
void vintage_delay_audio_module::set_sample_rate(uint32_t sr)
{
srate = sr;
old_medium = -1;
amt_left.set_sample_rate(sr); amt_right.set_sample_rate(sr);
fb_left.set_sample_rate(sr); fb_right.set_sample_rate(sr);
}
void vintage_delay_audio_module::calc_filters()
{
// parameters are heavily influenced by gordonjcp and his tape delay unit
// although, don't blame him if it sounds bad - I've messed with them too :)
biquad_left[0].set_lp_rbj(6000, 0.707, srate);
biquad_left[1].set_bp_rbj(4500, 0.250, srate);
biquad_right[0].copy_coeffs(biquad_left[0]);
biquad_right[1].copy_coeffs(biquad_left[1]);
}
/// Single delay line with feedback at the same tap
static inline void delayline_impl(int age, int deltime, float dry_value, const float &delayed_value, float &out, float &del, gain_smoothing &amt, gain_smoothing &fb)
{
// if the buffer hasn't been cleared yet (after activation), pretend we've read zeros
if (age <= deltime) {
out = 0;
del = dry_value;
amt.step();
fb.step();
}
else
{
float delayed = delayed_value; // avoid dereferencing the pointer in 'then' branch of the if()
dsp::sanitize(delayed);
out = delayed * amt.get();
del = dry_value + delayed * fb.get();
}
}
/// Single delay line with tap output
static inline void delayline2_impl(int age, int deltime, float dry_value, const float &delayed_value, const float &delayed_value_for_fb, float &out, float &del, gain_smoothing &amt, gain_smoothing &fb)
{
if (age <= deltime) {
out = 0;
del = dry_value;
amt.step();
fb.step();
}
else
{
out = delayed_value * amt.get();
del = dry_value + delayed_value_for_fb * fb.get();
dsp::sanitize(out);
dsp::sanitize(del);
}
}
static inline void delay_mix(float dry_left, float dry_right, float &out_left, float &out_right, float dry, float chmix)
{
float tmp_left = lerp(out_left, out_right, chmix);
float tmp_right = lerp(out_right, out_left, chmix);
out_left = dry_left * dry + tmp_left;
out_right = dry_right * dry + tmp_right;
}
uint32_t vintage_delay_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
{
uint32_t ostate = 3; // XXXKF optimize!
uint32_t end = offset + numsamples;
int orig_bufptr = bufptr;
float out_left, out_right, del_left, del_right;
switch(mixmode)
{
case MIXMODE_STEREO:
case MIXMODE_PINGPONG:
{
int v = mixmode == MIXMODE_PINGPONG ? 1 : 0;
for(uint32_t i = offset; i < end; i++)
{
delayline_impl(age, deltime_l, ins[0][i], buffers[v][(bufptr - deltime_l) & ADDR_MASK], out_left, del_left, amt_left, fb_left);
delayline_impl(age, deltime_r, ins[1][i], buffers[1 - v][(bufptr - deltime_r) & ADDR_MASK], out_right, del_right, amt_right, fb_right);
delay_mix(ins[0][i], ins[1][i], out_left, out_right, dry.get(), chmix.get());
age++;
outs[0][i] = out_left; outs[1][i] = out_right; buffers[0][bufptr] = del_left; buffers[1][bufptr] = del_right;
bufptr = (bufptr + 1) & (MAX_DELAY - 1);
}
}
break;
case MIXMODE_LR:
case MIXMODE_RL:
{
int v = mixmode == MIXMODE_RL ? 1 : 0;
int deltime_fb = deltime_l + deltime_r;
int deltime_l_corr = mixmode == MIXMODE_RL ? deltime_fb : deltime_l;
int deltime_r_corr = mixmode == MIXMODE_LR ? deltime_fb : deltime_r;
for(uint32_t i = offset; i < end; i++)
{
delayline2_impl(age, deltime_l, ins[0][i], buffers[v][(bufptr - deltime_l_corr) & ADDR_MASK], buffers[v][(bufptr - deltime_fb) & ADDR_MASK], out_left, del_left, amt_left, fb_left);
delayline2_impl(age, deltime_r, ins[1][i], buffers[1 - v][(bufptr - deltime_r_corr) & ADDR_MASK], buffers[1-v][(bufptr - deltime_fb) & ADDR_MASK], out_right, del_right, amt_right, fb_right);
delay_mix(ins[0][i], ins[1][i], out_left, out_right, dry.get(), chmix.get());
age++;
outs[0][i] = out_left; outs[1][i] = out_right; buffers[0][bufptr] = del_left; buffers[1][bufptr] = del_right;
bufptr = (bufptr + 1) & (MAX_DELAY - 1);
}
}
}
if (age >= MAX_DELAY)
age = MAX_DELAY;
if (medium > 0) {
bufptr = orig_bufptr;
if (medium == 2)
{
for(uint32_t i = offset; i < end; i++)
{
buffers[0][bufptr] = biquad_left[0].process_lp(biquad_left[1].process(buffers[0][bufptr]));
buffers[1][bufptr] = biquad_right[0].process_lp(biquad_right[1].process(buffers[1][bufptr]));
bufptr = (bufptr + 1) & (MAX_DELAY - 1);
}
biquad_left[0].sanitize();biquad_right[0].sanitize();
} else {
for(uint32_t i = offset; i < end; i++)
{
buffers[0][bufptr] = biquad_left[1].process(buffers[0][bufptr]);
buffers[1][bufptr] = biquad_right[1].process(buffers[1][bufptr]);
bufptr = (bufptr + 1) & (MAX_DELAY - 1);
}
}
biquad_left[1].sanitize();biquad_right[1].sanitize();
}
return ostate;
}
///////////////////////////////////////////////////////////////////////////////////////////////
bool filter_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const
{
if (!is_active)
return false;
if (index == par_cutoff && !subindex) {
context->set_line_width(1.5);
return ::get_graph(*this, subindex, data, points);
}
return false;
}
int filter_audio_module::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const
{
if (fabs(inertia_cutoff.get_last() - old_cutoff) + 100 * fabs(inertia_resonance.get_last() - old_resonance) + fabs(*params[par_mode] - old_mode) > 0.1f)
{
old_cutoff = inertia_cutoff.get_last();
old_resonance = inertia_resonance.get_last();
old_mode = *params[par_mode];
last_generation++;
subindex_graph = 0;
subindex_dot = INT_MAX;
subindex_gridline = INT_MAX;
}
else {
subindex_graph = 0;
subindex_dot = subindex_gridline = generation ? INT_MAX : 0;
}
if (generation == last_calculated_generation)
subindex_graph = INT_MAX;
return last_generation;
}
///////////////////////////////////////////////////////////////////////////////////////////////
filterclavier_audio_module::filterclavier_audio_module()
: filter_module_with_inertia<biquad_filter_module, filterclavier_metadata>(ins, outs, params)
, min_gain(1.0)
, max_gain(32.0)
, last_note(-1)
, last_velocity(-1)
{
}
void filterclavier_audio_module::params_changed()
{
inertia_filter_module::inertia_cutoff.set_inertia(
note_to_hz(last_note + *params[par_transpose], *params[par_detune]));
float min_resonance = param_props[par_max_resonance].min;
inertia_filter_module::inertia_resonance.set_inertia(
(float(last_velocity) / 127.0)
// 0.001: see below
* (*params[par_max_resonance] - min_resonance + 0.001)
+ min_resonance);
adjust_gain_according_to_filter_mode(last_velocity);
inertia_filter_module::calculate_filter();
}
void filterclavier_audio_module::activate()
{
inertia_filter_module::activate();
}
void filterclavier_audio_module::set_sample_rate(uint32_t sr)
{
inertia_filter_module::set_sample_rate(sr);
}
void filterclavier_audio_module::deactivate()
{
inertia_filter_module::deactivate();
}
void filterclavier_audio_module::note_on(int channel, int note, int vel)
{
last_note = note;
last_velocity = vel;
inertia_filter_module::inertia_cutoff.set_inertia(
note_to_hz(note + *params[par_transpose], *params[par_detune]));
float min_resonance = param_props[par_max_resonance].min;
inertia_filter_module::inertia_resonance.set_inertia(
(float(vel) / 127.0)
// 0.001: if the difference is equal to zero (which happens
// when the max_resonance knom is at minimum position
// then the filter gain doesnt seem to snap to zero on most note offs
* (*params[par_max_resonance] - min_resonance + 0.001)
+ min_resonance);
adjust_gain_according_to_filter_mode(vel);
inertia_filter_module::calculate_filter();
}
void filterclavier_audio_module::note_off(int channel, int note, int vel)
{
if (note == last_note) {
inertia_filter_module::inertia_resonance.set_inertia(param_props[par_max_resonance].min);
inertia_filter_module::inertia_gain.set_inertia(min_gain);
inertia_filter_module::calculate_filter();
last_velocity = 0;
}
}
void filterclavier_audio_module::adjust_gain_according_to_filter_mode(int velocity)
{
int mode = dsp::fastf2i_drm(*params[par_mode]);
// for bandpasses: boost gain for velocities > 0
if ( (mode_6db_bp <= mode) && (mode <= mode_18db_bp) ) {
// gain for velocity 0: 1.0
// gain for velocity 127: 32.0
float mode_max_gain = max_gain;
// max_gain is right for mode_6db_bp
if (mode == mode_12db_bp)
mode_max_gain /= 6.0;
if (mode == mode_18db_bp)
mode_max_gain /= 10.5;
inertia_filter_module::inertia_gain.set_now(
(float(velocity) / 127.0) * (mode_max_gain - min_gain) + min_gain);
} else {
inertia_filter_module::inertia_gain.set_now(min_gain);
}
}
bool filterclavier_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const
{
if (!is_active || index != par_mode) {
return false;
}
if (!subindex) {
context->set_line_width(1.5);
return ::get_graph(*this, subindex, data, points);
}
return false;
}
///////////////////////////////////////////////////////////////////////////////////////////////
stereo_audio_module::stereo_audio_module() {
active = false;
clip_inL = 0.f;
clip_inR = 0.f;
clip_outL = 0.f;
clip_outR = 0.f;
meter_inL = 0.f;
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
}
void stereo_audio_module::activate() {
active = true;
}
void stereo_audio_module::deactivate() {
active = false;
}
void stereo_audio_module::params_changed() {
float slev = 2 * *params[param_slev]; // stereo level ( -2 -> 2 )
float sbal = 1 + *params[param_sbal]; // stereo balance ( 0 -> 2 )
float mlev = 2 * *params[param_mlev]; // mono level ( -2 -> 2 )
float mpan = 1 + *params[param_mpan]; // mono pan ( 0 -> 2 )
switch((int)*params[param_mode])
{
case 0:
default:
//LR->LR
LL = (mlev * (2.f - mpan) + slev * (2.f - sbal));
LR = (mlev * mpan - slev * sbal);
RL = (mlev * (2.f - mpan) - slev * (2.f - sbal));
RR = (mlev * mpan + slev * sbal);
break;
case 1:
//LR->MS
LL = (2.f - mpan) * (2.f - sbal);
LR = mpan * (2.f - sbal) * -1;
RL = (2.f - mpan) * sbal;
RR = mpan * sbal;
break;
case 2:
//MS->LR
LL = mlev * (2.f - sbal);
LR = mlev * mpan;
RL = slev * (2.f - sbal);
RR = slev * sbal * -1;
break;
case 3:
case 4:
case 5:
case 6:
//LR->LL
LL = 0.f;
LR = 0.f;
RL = 0.f;
RR = 0.f;
break;
}
}
uint32_t stereo_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask) {
for(uint32_t i = offset; i < offset + numsamples; i++) {
if(*params[param_bypass] > 0.5) {
outs[0][i] = ins[0][i];
outs[1][i] = ins[1][i];
clip_inL = 0.f;
clip_inR = 0.f;
clip_outL = 0.f;
clip_outR = 0.f;
meter_inL = 0.f;
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
} else {
// let meters fall a bit
clip_inL -= std::min(clip_inL, numsamples);
clip_inR -= std::min(clip_inR, numsamples);
clip_outL -= std::min(clip_outL, numsamples);
clip_outR -= std::min(clip_outR, numsamples);
meter_inL = 0.f;
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
float L = ins[0][i];
float R = ins[1][i];
// levels in
L *= *params[param_level_in];
R *= *params[param_level_in];
// balance in
L *= (1.f - std::max(0.f, *params[param_balance_in]));
R *= (1.f + std::min(0.f, *params[param_balance_in]));
// copy / flip / mono ...
switch((int)*params[param_mode])
{
case 0:
default:
// LR > LR
break;
case 1:
// LR > MS
break;
case 2:
// MS > LR
break;
case 3:
// LR > LL
R = L;
break;
case 4:
// LR > RR
L = R;
break;
case 5:
// LR > L+R
L = (L + R) / 2;
R = L;
break;
case 6:
// LR > RL
float tmp = L;
L = R;
R = tmp;
break;
}
// softclip
if(*params[param_softclip]) {
int ph;
ph = L / fabs(L);
L = L > 0.63 ? ph * (0.63 + 0.36 * (1 - pow(MATH_E, (1.f / 3) * (0.63 + L * ph)))) : L;
ph = R / fabs(R);
R = R > 0.63 ? ph * (0.63 + 0.36 * (1 - pow(MATH_E, (1.f / 3) * (0.63 + R * ph)))) : R;
}
// GUI stuff
if(L > meter_inL) meter_inL = L;
if(R > meter_inR) meter_inR = R;
if(L > 1.f) clip_inL = srate >> 3;
if(R > 1.f) clip_inR = srate >> 3;
// mute
L *= (1 - floor(*params[param_mute_l] + 0.5));
R *= (1 - floor(*params[param_mute_r] + 0.5));
// phase
L *= (2 * (1 - floor(*params[param_phase_l] + 0.5))) - 1;
R *= (2 * (1 - floor(*params[param_phase_r] + 0.5))) - 1;
// LR/MS
L += LL*L + RL*R;
R += RR*R + LR*L;
// widener
L += *params[param_widener] * R * -1;
R += *params[param_widener] * L * -1;
// delay
buffer[pos] = L;
buffer[pos + 1] = R;
int nbuf = srate * (fabs(*params[param_delay]) / 1000.f);
nbuf -= nbuf % 2;
if(*params[param_delay] > 0.f) {
R = buffer[(pos - (int)nbuf + 1 + buffer_size) % buffer_size];
} else if (*params[param_delay] < 0.f) {
L = buffer[(pos - (int)nbuf + buffer_size) % buffer_size];
}
pos = (pos + 2) % buffer_size;
// balance out
L *= (1.f - std::max(0.f, *params[param_balance_out]));
R *= (1.f + std::min(0.f, *params[param_balance_out]));
// level
L *= *params[param_level_out];
R *= *params[param_level_out];
//output
outs[0][i] = L;
outs[1][i] = R;
// clip LED's
if(L > 1.f) clip_outL = srate >> 3;
if(R > 1.f) clip_outR = srate >> 3;
if(L > meter_outL) meter_outL = L;
if(R > meter_outR) meter_outR = R;
// phase meter
if(fabs(L) > 0.001 and fabs(R) > 0.001) {
meter_phase = fabs(fabs(L+R) > 0.000000001 ? sin(fabs((L-R)/(L+R))) : 0.f);
} else {
meter_phase = 0.f;
}
}
}
// draw meters
SET_IF_CONNECTED(clip_inL);
SET_IF_CONNECTED(clip_inR);
SET_IF_CONNECTED(clip_outL);
SET_IF_CONNECTED(clip_outR);
SET_IF_CONNECTED(meter_inL);
SET_IF_CONNECTED(meter_inR);
SET_IF_CONNECTED(meter_outL);
SET_IF_CONNECTED(meter_outR);
SET_IF_CONNECTED(meter_phase);
return outputs_mask;
}
void stereo_audio_module::set_sample_rate(uint32_t sr)
{
srate = sr;
// rebuild buffer
buffer_size = (int)(srate * 0.05 * 2.f); // buffer size attack rate multiplied by 2 channels
buffer = (float*) calloc(buffer_size, sizeof(float));
memset(buffer, 0, buffer_size * sizeof(float)); // reset buffer to zero
pos = 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////
mono_audio_module::mono_audio_module() {
active = false;
clip_in = 0.f;
clip_outL = 0.f;
clip_outR = 0.f;
meter_in = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
}
void mono_audio_module::activate() {
active = true;
}
void mono_audio_module::deactivate() {
active = false;
}
void mono_audio_module::params_changed() {
}
uint32_t mono_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask) {
for(uint32_t i = offset; i < offset + numsamples; i++) {
if(*params[param_bypass] > 0.5) {
outs[0][i] = ins[0][i];
outs[1][i] = ins[0][i];
clip_in = 0.f;
clip_outL = 0.f;
clip_outR = 0.f;
meter_in = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
} else {
// let meters fall a bit
clip_in -= std::min(clip_in, numsamples);
clip_outL -= std::min(clip_outL, numsamples);
clip_outR -= std::min(clip_outR, numsamples);
meter_in = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
float L = ins[0][i];
// levels in
L *= *params[param_level_in];
// softclip
if(*params[param_softclip]) {
int ph = L / fabs(L);
L = L > 0.63 ? ph * (0.63 + 0.36 * (1 - pow(MATH_E, (1.f / 3) * (0.63 + L * ph)))) : L;
}
// GUI stuff
if(L > meter_in) meter_in = L;
if(L > 1.f) clip_in = srate >> 3;
float R = L;
// mute
L *= (1 - floor(*params[param_mute_l] + 0.5));
R *= (1 - floor(*params[param_mute_r] + 0.5));
// phase
L *= (2 * (1 - floor(*params[param_phase_l] + 0.5))) - 1;
R *= (2 * (1 - floor(*params[param_phase_r] + 0.5))) - 1;
// delay
buffer[pos] = L;
buffer[pos + 1] = R;
int nbuf = srate * (fabs(*params[param_delay]) / 1000.f);
nbuf -= nbuf % 2;
if(*params[param_delay] > 0.f) {
R = buffer[(pos - (int)nbuf + 1 + buffer_size) % buffer_size];
} else if (*params[param_delay] < 0.f) {
L = buffer[(pos - (int)nbuf + buffer_size) % buffer_size];
}
pos = (pos + 2) % buffer_size;
// balance out
L *= (1.f - std::max(0.f, *params[param_balance_out]));
R *= (1.f + std::min(0.f, *params[param_balance_out]));
// level
L *= *params[param_level_out];
R *= *params[param_level_out];
//output
outs[0][i] = L;
outs[1][i] = R;
// clip LED's
if(L > 1.f) clip_outL = srate >> 3;
if(R > 1.f) clip_outR = srate >> 3;
if(L > meter_outL) meter_outL = L;
if(R > meter_outR) meter_outR = R;
}
}
// draw meters
SET_IF_CONNECTED(clip_in);
SET_IF_CONNECTED(clip_outL);
SET_IF_CONNECTED(clip_outR);
SET_IF_CONNECTED(meter_in);
SET_IF_CONNECTED(meter_outL);
SET_IF_CONNECTED(meter_outR);
return outputs_mask;
}
void mono_audio_module::set_sample_rate(uint32_t sr)
{
srate = sr;
// rebuild buffer
buffer_size = (int)srate * 0.05 * 2; // delay buffer size multiplied by 2 channels
buffer = (float*) calloc(buffer_size, sizeof(float));
memset(buffer, 0, buffer_size * sizeof(float)); // reset buffer to zero
pos = 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,644 @@
/* Calf DSP plugin pack
* Distortion related plugins
*
* Copyright (C) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen and others
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include <limits.h>
#include <memory.h>
#include <calf/giface.h>
#include <calf/modules_dist.h>
using namespace dsp;
using namespace calf_plugins;
/// Saturator Band by Markus Schmidt
///
/// This module is based on Krzysztof's filters and Tom Szilagyi's distortion routine.
/// It provides a blendable saturation stage followed by a highpass, a lowpass and a peak filter
///////////////////////////////////////////////////////////////////////////////////////////////
saturator_audio_module::saturator_audio_module()
{
is_active = false;
srate = 0;
meter_drive = 0.f;
lp_pre_freq_old = -1;
hp_pre_freq_old = -1;
lp_post_freq_old = -1;
hp_post_freq_old = -1;
p_freq_old = -1;
p_level_old = -1;
}
void saturator_audio_module::activate()
{
is_active = true;
// set all filters
params_changed();
meters.reset();
meter_drive = 0.f;
}
void saturator_audio_module::deactivate()
{
is_active = false;
}
void saturator_audio_module::params_changed()
{
// set the params of all filters
if(*params[param_lp_pre_freq] != lp_pre_freq_old) {
lp[0][0].set_lp_rbj(*params[param_lp_pre_freq], 0.707, (float)srate);
if(in_count > 1 && out_count > 1)
lp[1][0].copy_coeffs(lp[0][0]);
lp[0][1].copy_coeffs(lp[0][0]);
if(in_count > 1 && out_count > 1)
lp[1][1].copy_coeffs(lp[0][0]);
lp_pre_freq_old = *params[param_lp_pre_freq];
}
if(*params[param_hp_pre_freq] != hp_pre_freq_old) {
hp[0][0].set_hp_rbj(*params[param_hp_pre_freq], 0.707, (float)srate);
if(in_count > 1 && out_count > 1)
hp[1][0].copy_coeffs(hp[0][0]);
hp[0][1].copy_coeffs(hp[0][0]);
if(in_count > 1 && out_count > 1)
hp[1][1].copy_coeffs(hp[0][0]);
hp_pre_freq_old = *params[param_hp_pre_freq];
}
if(*params[param_lp_post_freq] != lp_post_freq_old) {
lp[0][2].set_lp_rbj(*params[param_lp_post_freq], 0.707, (float)srate);
if(in_count > 1 && out_count > 1)
lp[1][2].copy_coeffs(lp[0][2]);
lp[0][3].copy_coeffs(lp[0][2]);
if(in_count > 1 && out_count > 1)
lp[1][3].copy_coeffs(lp[0][2]);
lp_post_freq_old = *params[param_lp_post_freq];
}
if(*params[param_hp_post_freq] != hp_post_freq_old) {
hp[0][2].set_hp_rbj(*params[param_hp_post_freq], 0.707, (float)srate);
if(in_count > 1 && out_count > 1)
hp[1][2].copy_coeffs(hp[0][2]);
hp[0][3].copy_coeffs(hp[0][2]);
if(in_count > 1 && out_count > 1)
hp[1][3].copy_coeffs(hp[0][2]);
hp_post_freq_old = *params[param_hp_post_freq];
}
if(*params[param_p_freq] != p_freq_old or *params[param_p_level] != p_level_old or *params[param_p_q] != p_q_old) {
p[0].set_peakeq_rbj((float)*params[param_p_freq], (float)*params[param_p_q], (float)*params[param_p_level], (float)srate);
if(in_count > 1 && out_count > 1)
p[1].copy_coeffs(p[0]);
p_freq_old = *params[param_p_freq];
p_level_old = *params[param_p_level];
p_q_old = *params[param_p_q];
}
// set distortion
dist[0].set_params(*params[param_blend], *params[param_drive]);
if(in_count > 1 && out_count > 1)
dist[1].set_params(*params[param_blend], *params[param_drive]);
}
void saturator_audio_module::set_sample_rate(uint32_t sr)
{
srate = sr;
dist[0].set_sample_rate(sr);
if(in_count > 1 && out_count > 1)
dist[1].set_sample_rate(sr);
meters.set_sample_rate(srate);
}
uint32_t saturator_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
{
bool bypass = *params[param_bypass] > 0.5f;
uint32_t orig_offset = offset;
uint32_t orig_numsamples = numsamples;
numsamples += offset;
if(bypass) {
// everything bypassed
while(offset < numsamples) {
if(in_count > 1 && out_count > 1) {
outs[0][offset] = ins[0][offset];
outs[1][offset] = ins[1][offset];
} else if(in_count > 1) {
outs[0][offset] = (ins[0][offset] + ins[1][offset]) / 2;
} else if(out_count > 1) {
outs[0][offset] = ins[0][offset];
outs[1][offset] = ins[0][offset];
} else {
outs[0][offset] = ins[0][offset];
}
++offset;
}
meters.bypassed(params, orig_numsamples);
} else {
meter_drive = 0.f;
float in_avg[2] = {0.f, 0.f};
float out_avg[2] = {0.f, 0.f};
float tube_avg = 0.f;
// process
while(offset < numsamples) {
// cycle through samples
float out[2], in[2] = {0.f, 0.f};
int c = 0;
if(in_count > 1 && out_count > 1) {
// stereo in/stereo out
// handle full stereo
in[0] = ins[0][offset];
in[1] = ins[1][offset];
c = 2;
} else {
// in and/or out mono
// handle mono
in[0] = ins[0][offset];
in[1] = in[0];
c = 1;
}
float proc[2];
proc[0] = in[0] * *params[param_level_in];
proc[1] = in[1] * *params[param_level_in];
float onedivlevelin = 1.0 / *params[param_level_in];
for (int i = 0; i < c; ++i) {
// all pre filters in chain
proc[i] = lp[i][1].process(lp[i][0].process(proc[i]));
proc[i] = hp[i][1].process(hp[i][0].process(proc[i]));
// get average for display purposes before...
in_avg[i] += fabs(pow(proc[i], 2.f));
// ...saturate...
proc[i] = dist[i].process(proc[i]);
// ...and get average after...
out_avg[i] += fabs(pow(proc[i], 2.f));
// tone control
proc[i] = p[i].process(proc[i]);
// all post filters in chain
proc[i] = lp[i][2].process(lp[i][3].process(proc[i]));
proc[i] = hp[i][2].process(hp[i][3].process(proc[i]));
//subtract gain
proc[i] *= onedivlevelin;
}
if(in_count > 1 && out_count > 1) {
// full stereo
out[0] = ((proc[0] * *params[param_mix]) + in[0] * (1 - *params[param_mix])) * *params[param_level_out];
outs[0][offset] = out[0];
out[1] = ((proc[1] * *params[param_mix]) + in[1] * (1 - *params[param_mix])) * *params[param_level_out];
outs[1][offset] = out[1];
} else if(out_count > 1) {
// mono -> pseudo stereo
out[0] = ((proc[0] * *params[param_mix]) + in[0] * (1 - *params[param_mix])) * *params[param_level_out];
outs[0][offset] = out[0];
out[1] = out[0];
outs[1][offset] = out[1];
} else {
// stereo -> mono
// or full mono
out[0] = ((proc[0] * *params[param_mix]) + in[0] * (1 - *params[param_mix])) * *params[param_level_out];
outs[0][offset] = out[0];
}
// next sample
++offset;
} // cycle trough samples
meters.process(params, ins, outs, orig_offset, orig_numsamples);
tube_avg = (sqrt(std::max(out_avg[0], out_avg[1])) / numsamples) - (sqrt(std::max(in_avg[0], in_avg[1])) / numsamples);
meter_drive = (5.0f * fabs(tube_avg) * (float(*params[param_blend]) + 30.0f));
// printf("out:%.6f in: %.6f avg: %.6f drv: %.3f\n", sqrt(std::max(out_avg[0], out_avg[1])) / numsamples, sqrt(std::max(in_avg[0], in_avg[1])) / numsamples, tube_avg, meter_drive);
// clean up
lp[0][0].sanitize();
lp[1][0].sanitize();
lp[0][1].sanitize();
lp[1][1].sanitize();
lp[0][2].sanitize();
lp[1][2].sanitize();
lp[0][3].sanitize();
lp[1][3].sanitize();
hp[0][0].sanitize();
hp[1][0].sanitize();
hp[0][1].sanitize();
hp[1][1].sanitize();
hp[0][2].sanitize();
hp[1][2].sanitize();
hp[0][3].sanitize();
hp[1][3].sanitize();
p[0].sanitize();
p[1].sanitize();
}
// draw meters
if(params[param_meter_drive] != NULL) {
*params[param_meter_drive] = meter_drive;
}
// whatever has to be returned x)
return outputs_mask;
}
/// Exciter by Markus Schmidt
///
/// This module is based on Krzysztof's filters and Tom Szilagyi's distortion routine.
/// It provides a blendable saturation stage followed by a highpass, a lowpass and a peak filter
///////////////////////////////////////////////////////////////////////////////////////////////
exciter_audio_module::exciter_audio_module()
{
is_active = false;
srate = 0;
meter_drive = 0.f;
}
void exciter_audio_module::activate()
{
is_active = true;
// set all filters
params_changed();
}
void exciter_audio_module::deactivate()
{
is_active = false;
}
void exciter_audio_module::params_changed()
{
// set the params of all filters
if(*params[param_freq] != freq_old) {
hp[0][0].set_hp_rbj(*params[param_freq], 0.707, (float)srate);
hp[0][1].copy_coeffs(hp[0][0]);
hp[0][2].copy_coeffs(hp[0][0]);
hp[0][3].copy_coeffs(hp[0][0]);
if(in_count > 1 && out_count > 1) {
hp[1][0].copy_coeffs(hp[0][0]);
hp[1][1].copy_coeffs(hp[0][0]);
hp[1][2].copy_coeffs(hp[0][0]);
hp[1][3].copy_coeffs(hp[0][0]);
}
freq_old = *params[param_freq];
}
// set the params of all filters
if(*params[param_ceil] != ceil_old or *params[param_ceil_active] != ceil_active_old) {
lp[0][0].set_lp_rbj(*params[param_ceil], 0.707, (float)srate);
lp[0][1].copy_coeffs(lp[0][0]);
lp[1][0].copy_coeffs(lp[0][0]);
lp[1][1].copy_coeffs(lp[0][0]);
ceil_old = *params[param_ceil];
ceil_active_old = *params[param_ceil_active];
}
// set distortion
dist[0].set_params(*params[param_blend], *params[param_drive]);
if(in_count > 1 && out_count > 1)
dist[1].set_params(*params[param_blend], *params[param_drive]);
}
void exciter_audio_module::set_sample_rate(uint32_t sr)
{
srate = sr;
dist[0].set_sample_rate(sr);
if(in_count > 1 && out_count > 1)
dist[1].set_sample_rate(sr);
meters.set_sample_rate(srate);
}
uint32_t exciter_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
{
uint32_t orig_offset = offset;
uint32_t orig_numsamples = numsamples;
bool bypass = *params[param_bypass] > 0.5f;
numsamples += offset;
if(bypass) {
// everything bypassed
while(offset < numsamples) {
if(in_count > 1 && out_count > 1) {
outs[0][offset] = ins[0][offset];
outs[1][offset] = ins[1][offset];
} else if(in_count > 1) {
outs[0][offset] = (ins[0][offset] + ins[1][offset]) / 2;
} else if(out_count > 1) {
outs[0][offset] = ins[0][offset];
outs[1][offset] = ins[0][offset];
} else {
outs[0][offset] = ins[0][offset];
}
++offset;
}
meters.bypassed(params, orig_numsamples);
// displays, too
meter_drive = 0.f;
} else {
meter_drive = 0.f;
float in2out = *params[param_listen] > 0.f ? 0.f : 1.f;
// process
while(offset < numsamples) {
// cycle through samples
float out[2], in[2] = {0.f, 0.f};
float maxDrive = 0.f;
int c = 0;
if(in_count > 1 && out_count > 1) {
// stereo in/stereo out
// handle full stereo
in[0] = ins[0][offset];
in[0] *= *params[param_level_in];
in[1] = ins[1][offset];
in[1] *= *params[param_level_in];
c = 2;
} else {
// in and/or out mono
// handle mono
in[0] = ins[0][offset];
in[0] *= *params[param_level_in];
in[1] = in[0];
c = 1;
}
float proc[2];
proc[0] = in[0];
proc[1] = in[1];
for (int i = 0; i < c; ++i) {
// all pre filters in chain
proc[i] = hp[i][1].process(hp[i][0].process(proc[i]));
// saturate
proc[i] = dist[i].process(proc[i]);
// all post filters in chain
proc[i] = hp[i][2].process(hp[i][3].process(proc[i]));
if(*params[param_ceil_active] > 0.5f) {
// all H/P post filters in chain
proc[i] = lp[i][0].process(lp[i][1].process(proc[i]));
}
}
maxDrive = dist[0].get_distortion_level() * *params[param_amount];
if(in_count > 1 && out_count > 1) {
maxDrive = std::max(maxDrive, dist[1].get_distortion_level() * *params[param_amount]);
// full stereo
out[0] = (proc[0] * *params[param_amount] + in2out * in[0]) * *params[param_level_out];
out[1] = (proc[1] * *params[param_amount] + in2out * in[1]) * *params[param_level_out];
outs[0][offset] = out[0];
outs[1][offset] = out[1];
} else if(out_count > 1) {
// mono -> pseudo stereo
out[1] = out[0] = (proc[0] * *params[param_amount] + in2out * in[0]) * *params[param_level_out];
outs[0][offset] = out[0];
outs[1][offset] = out[1];
} else {
// stereo -> mono
// or full mono
out[0] = (proc[0] * *params[param_amount] + in2out * in[0]) * *params[param_level_out];
outs[0][offset] = out[0];
}
// set up in / out meters
if(maxDrive > meter_drive) {
meter_drive = maxDrive;
}
// next sample
++offset;
} // cycle trough samples
meters.process(params, ins, outs, orig_offset, orig_numsamples);
// clean up
hp[0][0].sanitize();
hp[1][0].sanitize();
hp[0][1].sanitize();
hp[1][1].sanitize();
hp[0][2].sanitize();
hp[1][2].sanitize();
hp[0][3].sanitize();
hp[1][3].sanitize();
}
// draw meters
if(params[param_meter_drive] != NULL) {
*params[param_meter_drive] = meter_drive;
}
// whatever has to be returned x)
return outputs_mask;
}
/// Bass Enhancer by Markus Schmidt
///
/// This module is based on Krzysztof's filters and Tom's distortion routine.
/// It sends the signal through a lowpass, saturates it and sends it through a lowpass again
///////////////////////////////////////////////////////////////////////////////////////////////
bassenhancer_audio_module::bassenhancer_audio_module()
{
is_active = false;
srate = 0;
meters.reset();
meter_drive = 0.f;
}
void bassenhancer_audio_module::activate()
{
is_active = true;
meters.reset();
// set all filters
params_changed();
}
void bassenhancer_audio_module::deactivate()
{
is_active = false;
}
void bassenhancer_audio_module::params_changed()
{
// set the params of all filters
if(*params[param_freq] != freq_old) {
lp[0][0].set_lp_rbj(*params[param_freq], 0.707, (float)srate);
lp[0][1].copy_coeffs(lp[0][0]);
lp[0][2].copy_coeffs(lp[0][0]);
lp[0][3].copy_coeffs(lp[0][0]);
if(in_count > 1 && out_count > 1) {
lp[1][0].copy_coeffs(lp[0][0]);
lp[1][1].copy_coeffs(lp[0][0]);
lp[1][2].copy_coeffs(lp[0][0]);
lp[1][3].copy_coeffs(lp[0][0]);
}
freq_old = *params[param_freq];
}
// set the params of all filters
if(*params[param_floor] != floor_old or *params[param_floor_active] != floor_active_old) {
hp[0][0].set_hp_rbj(*params[param_floor], 0.707, (float)srate);
hp[0][1].copy_coeffs(hp[0][0]);
hp[1][0].copy_coeffs(hp[0][0]);
hp[1][1].copy_coeffs(hp[0][0]);
floor_old = *params[param_floor];
floor_active_old = *params[param_floor_active];
}
// set distortion
dist[0].set_params(*params[param_blend], *params[param_drive]);
if(in_count > 1 && out_count > 1)
dist[1].set_params(*params[param_blend], *params[param_drive]);
}
void bassenhancer_audio_module::set_sample_rate(uint32_t sr)
{
srate = sr;
dist[0].set_sample_rate(sr);
if(in_count > 1 && out_count > 1)
dist[1].set_sample_rate(sr);
meters.set_sample_rate(srate);
}
uint32_t bassenhancer_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
{
bool bypass = *params[param_bypass] > 0.5f;
uint32_t orig_offset = offset;
uint32_t orig_numsamples = numsamples;
numsamples += offset;
if(bypass) {
// everything bypassed
while(offset < numsamples) {
if(in_count > 1 && out_count > 1) {
outs[0][offset] = ins[0][offset];
outs[1][offset] = ins[1][offset];
} else if(in_count > 1) {
outs[0][offset] = (ins[0][offset] + ins[1][offset]) / 2;
} else if(out_count > 1) {
outs[0][offset] = ins[0][offset];
outs[1][offset] = ins[0][offset];
} else {
outs[0][offset] = ins[0][offset];
}
++offset;
}
// displays, too
meters.bypassed(params, orig_numsamples);
meter_drive = 0.f;
} else {
meter_drive = 0.f;
// process
while(offset < numsamples) {
// cycle through samples
float out[2], in[2] = {0.f, 0.f};
float maxDrive = 0.f;
int c = 0;
if(in_count > 1 && out_count > 1) {
// stereo in/stereo out
// handle full stereo
in[0] = ins[0][offset];
in[0] *= *params[param_level_in];
in[1] = ins[1][offset];
in[1] *= *params[param_level_in];
c = 2;
} else {
// in and/or out mono
// handle mono
in[0] = ins[0][offset];
in[0] *= *params[param_level_in];
in[1] = in[0];
c = 1;
}
float proc[2];
proc[0] = in[0];
proc[1] = in[1];
for (int i = 0; i < c; ++i) {
// all pre filters in chain
proc[i] = lp[i][1].process(lp[i][0].process(proc[i]));
// saturate
proc[i] = dist[i].process(proc[i]);
// all post filters in chain
proc[i] = lp[i][2].process(lp[i][3].process(proc[i]));
if(*params[param_floor_active] > 0.5f) {
// all H/P post filters in chain
proc[i] = hp[i][0].process(hp[i][1].process(proc[i]));
}
}
if(in_count > 1 && out_count > 1) {
// full stereo
if(*params[param_listen] > 0.f)
out[0] = proc[0] * *params[param_amount] * *params[param_level_out];
else
out[0] = (proc[0] * *params[param_amount] + in[0]) * *params[param_level_out];
outs[0][offset] = out[0];
if(*params[param_listen] > 0.f)
out[1] = proc[1] * *params[param_amount] * *params[param_level_out];
else
out[1] = (proc[1] * *params[param_amount] + in[1]) * *params[param_level_out];
outs[1][offset] = out[1];
maxDrive = std::max(dist[0].get_distortion_level() * *params[param_amount],
dist[1].get_distortion_level() * *params[param_amount]);
} else if(out_count > 1) {
// mono -> pseudo stereo
if(*params[param_listen] > 0.f)
out[0] = proc[0] * *params[param_amount] * *params[param_level_out];
else
out[0] = (proc[0] * *params[param_amount] + in[0]) * *params[param_level_out];
outs[0][offset] = out[0];
out[1] = out[0];
outs[1][offset] = out[1];
maxDrive = dist[0].get_distortion_level() * *params[param_amount];
} else {
// stereo -> mono
// or full mono
if(*params[param_listen] > 0.f)
out[0] = proc[0] * *params[param_amount] * *params[param_level_out];
else
out[0] = (proc[0] * *params[param_amount] + in[0]) * *params[param_level_out];
outs[0][offset] = out[0];
maxDrive = dist[0].get_distortion_level() * *params[param_amount];
}
// set up in / out meters
if(maxDrive > meter_drive) {
meter_drive = maxDrive;
}
// next sample
++offset;
} // cycle trough samples
meters.process(params, ins, outs, orig_offset, orig_numsamples);
// clean up
lp[0][0].sanitize();
lp[1][0].sanitize();
lp[0][1].sanitize();
lp[1][1].sanitize();
lp[0][2].sanitize();
lp[1][2].sanitize();
lp[0][3].sanitize();
lp[1][3].sanitize();
}
// draw meters
if(params[param_meter_drive] != NULL) {
*params[param_meter_drive] = meter_drive;
}
// whatever has to be returned x)
return outputs_mask;
}

View File

@@ -0,0 +1,343 @@
/* Calf DSP plugin pack
* Equalization related plugins
*
* Copyright (C) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen and others
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include <limits.h>
#include <memory.h>
#include <calf/giface.h>
#include <calf/modules_eq.h>
using namespace dsp;
using namespace calf_plugins;
/// Equalizer 12 Band by Markus Schmidt
///
/// This module is based on Krzysztof's filters. It provides a couple
/// of different chained filters.
///////////////////////////////////////////////////////////////////////////////////////////////
template<class BaseClass, bool has_lphp>
equalizerNband_audio_module<BaseClass, has_lphp>::equalizerNband_audio_module()
{
is_active = false;
srate = 0;
last_generation = 0;
hp_freq_old = lp_freq_old = 0;
hs_freq_old = ls_freq_old = 0;
hs_level_old = ls_level_old = 0;
for (int i = 0; i < AM::PeakBands; i++)
{
p_freq_old[i] = 0;
p_level_old[i] = 0;
p_q_old[i] = 0;
}
}
template<class BaseClass, bool has_lphp>
void equalizerNband_audio_module<BaseClass, has_lphp>::activate()
{
is_active = true;
// set all filters
params_changed();
meters.reset();
}
template<class BaseClass, bool has_lphp>
void equalizerNband_audio_module<BaseClass, has_lphp>::deactivate()
{
is_active = false;
}
static inline void copy_lphp(biquad_d2<float> filters[3][2])
{
for (int i = 0; i < 3; i++)
for (int j = 0; j < 2; j++)
if (i || j)
filters[i][j].copy_coeffs(filters[0][0]);
}
template<class BaseClass, bool has_lphp>
void equalizerNband_audio_module<BaseClass, has_lphp>::params_changed()
{
// set the params of all filters
// lp/hp first (if available)
if (has_lphp)
{
hp_mode = (CalfEqMode)(int)*params[AM::param_hp_mode];
lp_mode = (CalfEqMode)(int)*params[AM::param_lp_mode];
float hpfreq = *params[AM::param_hp_freq], lpfreq = *params[AM::param_lp_freq];
if(hpfreq != hp_freq_old) {
hp[0][0].set_hp_rbj(hpfreq, 0.707, (float)srate, 1.0);
copy_lphp(hp);
hp_freq_old = hpfreq;
}
if(lpfreq != lp_freq_old) {
lp[0][0].set_lp_rbj(lpfreq, 0.707, (float)srate, 1.0);
copy_lphp(lp);
lp_freq_old = lpfreq;
}
}
// then shelves
float hsfreq = *params[AM::param_hs_freq], hslevel = *params[AM::param_hs_level];
float lsfreq = *params[AM::param_ls_freq], lslevel = *params[AM::param_ls_level];
if(lsfreq != ls_freq_old or lslevel != ls_level_old) {
lsL.set_lowshelf_rbj(lsfreq, 0.707, lslevel, (float)srate);
lsR.copy_coeffs(lsL);
ls_level_old = lslevel;
ls_freq_old = lsfreq;
}
if(hsfreq != hs_freq_old or hslevel != hs_level_old) {
hsL.set_highshelf_rbj(hsfreq, 0.707, hslevel, (float)srate);
hsR.copy_coeffs(hsL);
hs_level_old = hslevel;
hs_freq_old = hsfreq;
}
for (int i = 0; i < AM::PeakBands; i++)
{
int offset = i * params_per_band;
float freq = *params[AM::param_p1_freq + offset];
float level = *params[AM::param_p1_level + offset];
float q = *params[AM::param_p1_q + offset];
if(freq != p_freq_old[i] or level != p_level_old[i] or q != p_q_old[i]) {
pL[i].set_peakeq_rbj(freq, q, level, (float)srate);
pR[i].copy_coeffs(pL[i]);
p_freq_old[i] = freq;
p_level_old[i] = level;
p_q_old[i] = q;
}
}
}
template<class BaseClass, bool has_lphp>
inline void equalizerNband_audio_module<BaseClass, has_lphp>::process_hplp(float &left, float &right)
{
if (!has_lphp)
return;
if (*params[AM::param_lp_active] > 0.f)
{
switch(lp_mode)
{
case MODE12DB:
left = lp[0][0].process(left);
right = lp[0][1].process(right);
break;
case MODE24DB:
left = lp[1][0].process(lp[0][0].process(left));
right = lp[1][1].process(lp[0][1].process(right));
break;
case MODE36DB:
left = lp[2][0].process(lp[1][0].process(lp[0][0].process(left)));
right = lp[2][1].process(lp[1][1].process(lp[0][1].process(right)));
break;
}
}
if (*params[AM::param_hp_active] > 0.f)
{
switch(hp_mode)
{
case MODE12DB:
left = hp[0][0].process(left);
right = hp[0][1].process(right);
break;
case MODE24DB:
left = hp[1][0].process(hp[0][0].process(left));
right = hp[1][1].process(hp[0][1].process(right));
break;
case MODE36DB:
left = hp[2][0].process(hp[1][0].process(hp[0][0].process(left)));
right = hp[2][1].process(hp[1][1].process(hp[0][1].process(right)));
break;
}
}
}
template<class BaseClass, bool has_lphp>
uint32_t equalizerNband_audio_module<BaseClass, has_lphp>::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
{
bool bypass = *params[AM::param_bypass] > 0.f;
uint32_t orig_offset = offset;
uint32_t orig_numsamples = numsamples;
numsamples += offset;
if(bypass) {
// everything bypassed
while(offset < numsamples) {
outs[0][offset] = ins[0][offset];
outs[1][offset] = ins[1][offset];
++offset;
}
// displays, too
meters.bypassed(params, orig_numsamples);
} else {
// process
while(offset < numsamples) {
// cycle through samples
float outL = 0.f;
float outR = 0.f;
float inL = ins[0][offset];
float inR = ins[1][offset];
// in level
inR *= *params[AM::param_level_in];
inL *= *params[AM::param_level_in];
float procL = inL;
float procR = inR;
// all filters in chain
process_hplp(procL, procR);
if(*params[AM::param_ls_active] > 0.f) {
procL = lsL.process(procL);
procR = lsR.process(procR);
}
if(*params[AM::param_hs_active] > 0.f) {
procL = hsL.process(procL);
procR = hsR.process(procR);
}
for (int i = 0; i < AM::PeakBands; i++)
{
if(*params[AM::param_p1_active + i * params_per_band] > 0.f) {
procL = pL[i].process(procL);
procR = pR[i].process(procR);
}
}
outL = procL * *params[AM::param_level_out];
outR = procR * *params[AM::param_level_out];
// send to output
outs[0][offset] = outL;
outs[1][offset] = outR;
// next sample
++offset;
} // cycle trough samples
meters.process(params, ins, outs, orig_offset, orig_numsamples);
// clean up
for(int i = 0; i < 3; ++i) {
hp[i][0].sanitize();
hp[i][1].sanitize();
lp[i][0].sanitize();
lp[i][1].sanitize();
}
lsL.sanitize();
hsR.sanitize();
for(int i = 0; i < AM::PeakBands; ++i) {
pL[i].sanitize();
pR[i].sanitize();
}
}
// whatever has to be returned x)
return outputs_mask;
}
template<class BaseClass, bool has_lphp>
bool equalizerNband_audio_module<BaseClass, has_lphp>::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const
{
if (!is_active)
return false;
if (index == AM::param_p1_freq && !subindex) {
context->set_line_width(1.5);
return ::get_graph(*this, subindex, data, points, 32, 0);
}
return false;
}
template<class BaseClass, bool has_lphp>
bool equalizerNband_audio_module<BaseClass, has_lphp>::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const
{
if (!is_active) {
return false;
} else {
return get_freq_gridline(subindex, pos, vertical, legend, context, true, 32, 0);
}
}
template<class BaseClass, bool has_lphp>
int equalizerNband_audio_module<BaseClass, has_lphp>::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const
{
if (!is_active) {
return false;
} else {
bool changed = false;
for (int i = 0; i < graph_param_count && !changed; i++)
{
if (*params[AM::first_graph_param + i] != old_params_for_graph[i])
changed = true;
}
if (changed)
{
for (int i = 0; i < graph_param_count; i++)
old_params_for_graph[i] = *params[AM::first_graph_param + i];
last_generation++;
subindex_graph = 0;
subindex_dot = INT_MAX;
subindex_gridline = INT_MAX;
}
else {
subindex_graph = 0;
subindex_dot = subindex_gridline = generation ? INT_MAX : 0;
}
if (generation == last_calculated_generation)
subindex_graph = INT_MAX;
return last_generation;
}
return false;
}
static inline float adjusted_lphp_gain(const float *const *params, int param_active, int param_mode, const biquad_d2<float> &filter, float freq, float srate)
{
if(*params[param_active] > 0.f) {
float gain = filter.freq_gain(freq, srate);
switch((int)*params[param_mode]) {
case MODE12DB:
return gain;
case MODE24DB:
return gain * gain;
case MODE36DB:
return gain * gain * gain;
}
}
return 1;
}
template<class BaseClass, bool use_hplp>
float equalizerNband_audio_module<BaseClass, use_hplp>::freq_gain(int index, double freq, uint32_t sr) const
{
float ret = 1.f;
if (use_hplp)
{
ret *= adjusted_lphp_gain(params, AM::param_hp_active, AM::param_hp_mode, hp[0][0], freq, (float)sr);
ret *= adjusted_lphp_gain(params, AM::param_lp_active, AM::param_lp_mode, lp[0][0], freq, (float)sr);
}
ret *= (*params[AM::param_ls_active] > 0.f) ? lsL.freq_gain(freq, sr) : 1;
ret *= (*params[AM::param_hs_active] > 0.f) ? hsL.freq_gain(freq, sr) : 1;
for (int i = 0; i < PeakBands; i++)
ret *= (*params[AM::param_p1_active + i * params_per_band] > 0.f) ? pL[i].freq_gain(freq, (float)sr) : 1;
return ret;
}
template class equalizerNband_audio_module<equalizer5band_metadata, false>;
template class equalizerNband_audio_module<equalizer8band_metadata, true>;
template class equalizerNband_audio_module<equalizer12band_metadata, true>;

View File

@@ -0,0 +1,695 @@
/* Calf DSP plugin pack
* Limiter related plugins
*
* Copyright (C) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen and others
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include <limits.h>
#include <memory.h>
#include <calf/giface.h>
#include <calf/modules_limit.h>
using namespace dsp;
using namespace calf_plugins;
#define SET_IF_CONNECTED(name) if (params[AM::param_##name] != NULL) *params[AM::param_##name] = name;
/// Limiter by Markus Schmidt and Christian Holschuh
///
/// This module provides the lookahead limiter as a simple audio module
/// with choosable lookahead and release time.
///////////////////////////////////////////////////////////////////////////////////////////////
limiter_audio_module::limiter_audio_module()
{
is_active = false;
srate = 0;
// zero all displays
clip_inL = 0.f;
clip_inR = 0.f;
clip_outL = 0.f;
clip_outR = 0.f;
meter_inL = 0.f;
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
asc_led = 0.f;
attack_old = -1.f;
limit_old = -1.f;
asc_old = true;
}
void limiter_audio_module::activate()
{
is_active = true;
// set all filters and strips
params_changed();
limiter.activate();
}
void limiter_audio_module::deactivate()
{
is_active = false;
limiter.deactivate();
}
void limiter_audio_module::params_changed()
{
limiter.set_params(*params[param_limit], *params[param_attack], *params[param_release], 1.f, *params[param_asc], pow(0.5, (*params[param_asc_coeff] - 0.5) * 2 * -1), true);
if( *params[param_attack] != attack_old) {
attack_old = *params[param_attack];
limiter.reset();
}
if(*params[param_limit] != limit_old or *params[param_asc] != asc_old) {
asc_old = *params[param_asc];
limit_old = *params[param_limit];
limiter.reset_asc();
}
}
void limiter_audio_module::set_sample_rate(uint32_t sr)
{
srate = sr;
limiter.set_sample_rate(srate);
}
uint32_t limiter_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
{
bool bypass = *params[param_bypass] > 0.5f;
numsamples += offset;
if(bypass) {
// everything bypassed
while(offset < numsamples) {
outs[0][offset] = ins[0][offset];
outs[1][offset] = ins[1][offset];
++offset;
}
// displays, too
clip_inL = 0.f;
clip_inR = 0.f;
clip_outL = 0.f;
clip_outR = 0.f;
meter_inL = 0.f;
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
asc_led = 0.f;
} else {
// let meters fall a bit
clip_inL -= std::min(clip_inL, numsamples);
clip_inR -= std::min(clip_inR, numsamples);
clip_outL -= std::min(clip_outL, numsamples);
clip_outR -= std::min(clip_outR, numsamples);
meter_inL = 0.f;
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
asc_led -= std::min(asc_led, numsamples);
while(offset < numsamples) {
// cycle through samples
float inL = ins[0][offset];
float inR = ins[1][offset];
// in level
inR *= *params[param_level_in];
inL *= *params[param_level_in];
// out vars
float outL = inL;
float outR = inR;
// process gain reduction
float fickdich[0];
limiter.process(outL, outR, fickdich);
if(limiter.get_asc())
asc_led = srate >> 3;
// should never be used. but hackers are paranoid by default.
// so we make shure NOTHING is above limit
outL = std::max(outL, -*params[param_limit]);
outL = std::min(outL, *params[param_limit]);
outR = std::max(outR, -*params[param_limit]);
outR = std::min(outR, *params[param_limit]);
// autolevel
outL /= *params[param_limit];
outR /= *params[param_limit];
// out level
outL *= *params[param_level_out];
outR *= *params[param_level_out];
// send to output
outs[0][offset] = outL;
outs[1][offset] = outR;
// clip LED's
if(inL > 1.f) {
clip_inL = srate >> 3;
}
if(inR > 1.f) {
clip_inR = srate >> 3;
}
if(outL > 1.f) {
clip_outL = srate >> 3;
}
if(outR > 1.f) {
clip_outR = srate >> 3;
}
// set up in / out meters
if(inL > meter_inL) {
meter_inL = inL;
}
if(inR > meter_inR) {
meter_inR = inR;
}
if(outL > meter_outL) {
meter_outL = outL;
}
if(outR > meter_outR) {
meter_outR = outR;
}
// next sample
++offset;
} // cycle trough samples
} // process (no bypass)
// draw meters
SET_IF_CONNECTED(clip_inL);
SET_IF_CONNECTED(clip_inR);
SET_IF_CONNECTED(clip_outL);
SET_IF_CONNECTED(clip_outR);
SET_IF_CONNECTED(meter_inL);
SET_IF_CONNECTED(meter_inR);
SET_IF_CONNECTED(meter_outL);
SET_IF_CONNECTED(meter_outR);
if (params[param_asc_led] != NULL) *params[param_asc_led] = asc_led;
if (*params[param_att]) {
if(bypass)
*params[param_att] = 1.f;
else
*params[param_att] = limiter.get_attenuation();
}
// whatever has to be returned x)
return outputs_mask;
}
/// Multiband Limiter by Markus Schmidt and Christian Holschuh
///
/// This module splits the signal in four different bands
/// and sends them through multiple filters (implemented by
/// Krzysztof). They are processed by a lookahead limiter module afterwards
/// and summed up to the final output again.
///////////////////////////////////////////////////////////////////////////////////////////////
multibandlimiter_audio_module::multibandlimiter_audio_module()
{
is_active = false;
srate = 0;
// zero all displays
clip_inL = 0.f;
clip_inR = 0.f;
clip_outL = 0.f;
clip_outR = 0.f;
meter_inL = 0.f;
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
asc_led = 0.f;
attack_old = -1.f;
channels = 2;
buffer_size = 0;
overall_buffer_size = 0;
_sanitize = false;
for(int i = 0; i < strips - 1; i ++) {
freq_old[i] = -1;
sep_old[i] = -1;
q_old[i] = -1;
}
mode_old = 0;
for(int i = 0; i < strips; i ++) {
weight_old[i] = -1.f;
}
attack_old = -1.f;
limit_old = -1.f;
asc_old = true;
}
void multibandlimiter_audio_module::activate()
{
is_active = true;
// set all filters and strips
params_changed();
// activate all strips
for (int j = 0; j < strips; j ++) {
strip[j].activate();
strip[j].set_multi(true);
strip[j].id = j;
}
broadband.activate();
pos = 0;
}
void multibandlimiter_audio_module::deactivate()
{
is_active = false;
// deactivate all strips
for (int j = 0; j < strips; j ++) {
strip[j].deactivate();
}
broadband.deactivate();
}
void multibandlimiter_audio_module::params_changed()
{
// determine mute/solo states
solo[0] = *params[param_solo0] > 0.f ? true : false;
solo[1] = *params[param_solo1] > 0.f ? true : false;
solo[2] = *params[param_solo2] > 0.f ? true : false;
solo[3] = *params[param_solo3] > 0.f ? true : false;
no_solo = (*params[param_solo0] > 0.f ||
*params[param_solo1] > 0.f ||
*params[param_solo2] > 0.f ||
*params[param_solo3] > 0.f) ? false : true;
mode_old = mode;
mode = *params[param_mode];
int i;
int j1;
switch(mode) {
case 0:
default:
j1 = 0;
break;
case 1:
j1 = 2;
break;
}
// set the params of all filters
if(*params[param_freq0] != freq_old[0] or *params[param_sep0] != sep_old[0] or *params[param_q0] != q_old[0] or *params[param_mode] != mode_old) {
lpL[0][0].set_lp_rbj((float)(*params[param_freq0] * (1 - *params[param_sep0])), *params[param_q0], (float)srate);
hpL[0][0].set_hp_rbj((float)(*params[param_freq0] * (1 + *params[param_sep0])), *params[param_q0], (float)srate);
lpR[0][0].copy_coeffs(lpL[0][0]);
hpR[0][0].copy_coeffs(hpL[0][0]);
for(i = 1; i <= j1; i++) {
lpL[0][i].copy_coeffs(lpL[0][0]);
hpL[0][i].copy_coeffs(hpL[0][0]);
lpR[0][i].copy_coeffs(lpL[0][0]);
hpR[0][i].copy_coeffs(hpL[0][0]);
}
freq_old[0] = *params[param_freq0];
sep_old[0] = *params[param_sep0];
q_old[0] = *params[param_q0];
}
if(*params[param_freq1] != freq_old[1] or *params[param_sep1] != sep_old[1] or *params[param_q1] != q_old[1] or *params[param_mode] != mode_old) {
lpL[1][0].set_lp_rbj((float)(*params[param_freq1] * (1 - *params[param_sep1])), *params[param_q1], (float)srate);
hpL[1][0].set_hp_rbj((float)(*params[param_freq1] * (1 + *params[param_sep1])), *params[param_q1], (float)srate);
lpR[1][0].copy_coeffs(lpL[1][0]);
hpR[1][0].copy_coeffs(hpL[1][0]);
for(i = 1; i <= j1; i++) {
lpL[1][i].copy_coeffs(lpL[1][0]);
hpL[1][i].copy_coeffs(hpL[1][0]);
lpR[1][i].copy_coeffs(lpL[1][0]);
hpR[1][i].copy_coeffs(hpL[1][0]);
}
freq_old[1] = *params[param_freq1];
sep_old[1] = *params[param_sep1];
q_old[1] = *params[param_q1];
}
if(*params[param_freq2] != freq_old[2] or *params[param_sep2] != sep_old[2] or *params[param_q2] != q_old[2] or *params[param_mode] != mode_old) {
lpL[2][0].set_lp_rbj((float)(*params[param_freq2] * (1 - *params[param_sep2])), *params[param_q2], (float)srate);
hpL[2][0].set_hp_rbj((float)(*params[param_freq2] * (1 + *params[param_sep2])), *params[param_q2], (float)srate);
lpR[2][0].copy_coeffs(lpL[2][0]);
hpR[2][0].copy_coeffs(hpL[2][0]);
for(i = 1; i <= j1; i++) {
lpL[2][i].copy_coeffs(lpL[2][0]);
hpL[2][i].copy_coeffs(hpL[2][0]);
lpR[2][i].copy_coeffs(lpL[2][0]);
hpR[2][i].copy_coeffs(hpL[2][0]);
}
freq_old[2] = *params[param_freq2];
sep_old[2] = *params[param_sep2];
q_old[2] = *params[param_q2];
}
// set the params of all strips
float rel;
rel = *params[param_release] * pow(0.25, *params[param_release0] * -1);
rel = (*params[param_minrel] > 0.5) ? std::max(2500 * (1.f / 30), rel) : rel;
weight[0] = pow(0.25, *params[param_weight0] * -1);
strip[0].set_params(*params[param_limit], *params[param_attack], rel, weight[0], *params[param_asc], pow(0.5, (*params[param_asc_coeff] - 0.5) * 2 * -1), true);
*params[param_effrelease0] = rel;
rel = *params[param_release] * pow(0.25, *params[param_release1] * -1);
rel = (*params[param_minrel] > 0.5) ? std::max(2500 * (1.f / *params[param_freq0]), rel) : rel;
weight[1] = pow(0.25, *params[param_weight1] * -1);
strip[1].set_params(*params[param_limit], *params[param_attack], rel, weight[1], *params[param_asc], pow(0.5, (*params[param_asc_coeff] - 0.5) * 2 * -1));
*params[param_effrelease1] = rel;
rel = *params[param_release] * pow(0.25, *params[param_release2] * -1);
rel = (*params[param_minrel] > 0.5) ? std::max(2500 * (1.f / *params[param_freq1]), rel) : rel;
weight[2] = pow(0.25, *params[param_weight2] * -1);
strip[2].set_params(*params[param_limit], *params[param_attack], rel, weight[2], *params[param_asc], pow(0.5, (*params[param_asc_coeff] - 0.5) * 2 * -1));
*params[param_effrelease2] = rel;
rel = *params[param_release] * pow(0.25, *params[param_release3] * -1);
rel = (*params[param_minrel] > 0.5) ? std::max(2500 * (1.f / *params[param_freq2]), rel) : rel;
weight[3] = pow(0.25, *params[param_weight3] * -1);
strip[3].set_params(*params[param_limit], *params[param_attack], rel, weight[3], *params[param_asc], pow(0.5, (*params[param_asc_coeff] - 0.5) * 2 * -1));
*params[param_effrelease3] = rel;
broadband.set_params(*params[param_limit], *params[param_attack], rel, 1.f, *params[param_asc], pow(0.5, (*params[param_asc_coeff] - 0.5) * 2 * -1));
// rebuild multiband buffer
if( *params[param_attack] != attack_old) {
int bs = (int)(srate * (*params[param_attack] / 1000.f) * channels);
buffer_size = bs - bs % channels; // buffer size attack rate
attack_old = *params[param_attack];
_sanitize = true;
pos = 0;
for (int j = 0; j < strips; j ++) {
strip[j].reset();
}
broadband.reset();
}
if(*params[param_limit] != limit_old or *params[param_asc] != asc_old or *params[param_weight0] != weight_old[0] or *params[param_weight1] != weight_old[1] or *params[param_weight2] != weight_old[2] or *params[param_weight3] != weight_old[3] ) {
asc_old = *params[param_asc];
limit_old = *params[param_limit];
weight_old[0] = *params[param_weight0];
weight_old[1] = *params[param_weight1];
weight_old[2] = *params[param_weight2];
weight_old[3] = *params[param_weight3];
for (int j = 0; j < strips; j ++) {
strip[j].reset_asc();
}
broadband.reset_asc();
}
}
void multibandlimiter_audio_module::set_sample_rate(uint32_t sr)
{
srate = sr;
// rebuild buffer
overall_buffer_size = (int)(srate * (100.f / 1000.f) * channels) + channels; // buffer size max attack rate
buffer = (float*) calloc(overall_buffer_size, sizeof(float));
memset(buffer, 0, overall_buffer_size * sizeof(float)); // reset buffer to zero
pos = 0;
// set srate of all strips
for (int j = 0; j < strips; j ++) {
strip[j].set_sample_rate(srate);
}
broadband.set_sample_rate(srate);
}
#define BYPASSED_COMPRESSION(index) \
if(params[param_att##index] != NULL) \
*params[param_att##index] = 1.0; \
#define ACTIVE_COMPRESSION(index) \
if(params[param_att##index] != NULL) \
*params[param_att##index] = strip[index].get_attenuation(); \
uint32_t multibandlimiter_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
{
bool bypass = *params[param_bypass] > 0.5f;
numsamples += offset;
float batt = 0.f;
if(bypass) {
// everything bypassed
while(offset < numsamples) {
outs[0][offset] = ins[0][offset];
outs[1][offset] = ins[1][offset];
++offset;
}
// displays, too
clip_inL = 0.f;
clip_inR = 0.f;
clip_outL = 0.f;
clip_outR = 0.f;
meter_inL = 0.f;
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
asc_led = 0.f;
} else {
// process all strips
// let meters fall a bit
clip_inL -= std::min(clip_inL, numsamples);
clip_inR -= std::min(clip_inR, numsamples);
clip_outL -= std::min(clip_outL, numsamples);
clip_outR -= std::min(clip_outR, numsamples);
asc_led -= std::min(asc_led, numsamples);
meter_inL = 0.f;
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
float _tmpL[channels];
float _tmpR[channels];
while(offset < numsamples) {
float inL = 0.f;
float inR = 0.f;
// cycle through samples
if(!_sanitize) {
inL = ins[0][offset];
inR = ins[1][offset];
}
// in level
inR *= *params[param_level_in];
inL *= *params[param_level_in];
// even out filters gain reduction
// 3dB - levelled manually (based on default sep and q settings)
switch(mode) {
case 0:
inL *= 1.414213562;
inR *= 1.414213562;
break;
case 1:
inL *= 0.88;
inR *= 0.88;
break;
}
// out vars
float outL = 0.f;
float outR = 0.f;
int j1;
float left;
float right;
float sum_left = 0.f;
float sum_right = 0.f;
bool asc_active = false;
for (int i = 0; i < strips; i++) {
left = inL;
right = inR;
// send trough filters
switch(mode) {
// how many filter passes? (12/36dB)
case 0:
default:
j1 = 0;
break;
case 1:
j1 = 2;
break;
}
for (int j = 0; j <= j1; j++){
if(i + 1 < strips) {
// do lowpass on all bands except most high
left = lpL[i][j].process(left);
right = lpR[i][j].process(right);
lpL[i][j].sanitize();
lpR[i][j].sanitize();
}
if(i - 1 >= 0) {
// do highpass on all bands except most low
left = hpL[i - 1][j].process(left);
right = hpR[i - 1][j].process(right);
hpL[i - 1][j].sanitize();
hpR[i - 1][j].sanitize();
}
}
// remember filtered values for limiting
// (we need multiband_coeff before we can call the limiter bands)
_tmpL[i] = left;
_tmpR[i] = right;
// sum up for multiband coefficient
sum_left += ((fabs(left) > *params[param_limit]) ? *params[param_limit] * (fabs(left) / left) : left) * weight[i];
sum_right += ((fabs(right) > *params[param_limit]) ? *params[param_limit] * (fabs(right) / right) : right) * weight[i];
} // process single strip with filter
// write multiband coefficient to buffer
buffer[pos] = std::min(*params[param_limit] / std::max(fabs(sum_left), fabs(sum_right)), 1.0);
for (int i = 0; i < strips; i++) {
// process gain reduction
strip[i].process(_tmpL[i], _tmpR[i], buffer);
// sum up output of limiters
if (solo[i] || no_solo) {
outL += _tmpL[i];
outR += _tmpR[i];
}
asc_active = asc_active || strip[i].get_asc();
} // process single strip again for limiter
float fickdich[0];
broadband.process(outL, outR, fickdich);
asc_active = asc_active || broadband.get_asc();
// should never be used. but hackers are paranoid by default.
// so we make shure NOTHING is above limit
outL = std::max(outL, -*params[param_limit]);
outL = std::min(outL, *params[param_limit]);
outR = std::max(outR, -*params[param_limit]);
outR = std::min(outR, *params[param_limit]);
batt = broadband.get_attenuation();
// autolevel
outL /= *params[param_limit];
outR /= *params[param_limit];
// out level
outL *= *params[param_level_out];
outR *= *params[param_level_out];
// send to output
outs[0][offset] = outL;
outs[1][offset] = outR;
// clip LED's
if(ins[0][offset] * *params[param_level_in] > 1.f) {
clip_inL = srate >> 3;
}
if(ins[1][offset] * *params[param_level_in] > 1.f) {
clip_inR = srate >> 3;
}
if(outL > 1.f) {
clip_outL = srate >> 3;
}
if(outR > 1.f) {
clip_outR = srate >> 3;
}
// set up in / out meters
if(ins[0][offset] * *params[param_level_in] > meter_inL) {
meter_inL = ins[0][offset] * *params[param_level_in];
}
if(ins[1][offset] * *params[param_level_in] > meter_inR) {
meter_inR = ins[1][offset] * *params[param_level_in];
}
if(outL > meter_outL) {
meter_outL = outL;
}
if(outR > meter_outR) {
meter_outR = outR;
}
if(asc_active) {
asc_led = srate >> 3;
}
// next sample
++offset;
pos = (pos + channels) % buffer_size;
if(pos == 0) _sanitize = false;
} // cycle trough samples
} // process all strips (no bypass)
// draw meters
SET_IF_CONNECTED(clip_inL);
SET_IF_CONNECTED(clip_inR);
SET_IF_CONNECTED(clip_outL);
SET_IF_CONNECTED(clip_outR);
SET_IF_CONNECTED(meter_inL);
SET_IF_CONNECTED(meter_inR);
SET_IF_CONNECTED(meter_outL);
SET_IF_CONNECTED(meter_outR);
if (params[param_asc_led] != NULL) *params[param_asc_led] = asc_led;
// draw strip meters
if(bypass > 0.5f) {
if(params[param_att0] != NULL) *params[param_att0] = 1.0;
if(params[param_att1] != NULL) *params[param_att1] = 1.0;
if(params[param_att2] != NULL) *params[param_att2] = 1.0;
if(params[param_att3] != NULL) *params[param_att3] = 1.0;
} else {
if(params[param_att0] != NULL) *params[param_att0] = strip[0].get_attenuation() * batt;
if(params[param_att1] != NULL) *params[param_att1] = strip[1].get_attenuation() * batt;
if(params[param_att2] != NULL) *params[param_att2] = strip[2].get_attenuation() * batt;
if(params[param_att3] != NULL) *params[param_att3] = strip[3].get_attenuation() * batt;
}
// whatever has to be returned x)
return outputs_mask;
}
bool multibandlimiter_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const
{
if (!is_active or subindex > 3)
return false;
float ret;
double freq;
int j1;
for (int i = 0; i < points; i++)
{
ret = 1.f;
freq = 20.0 * pow (20000.0 / 20.0, i * 1.0 / points);
switch(mode) {
case 0:
default:
j1 = 0;
break;
case 1:
j1 = 2;
break;
}
for(int j = 0; j <= j1; j ++) {
switch(subindex) {
case 0:
ret *= lpL[0][j].freq_gain(freq, (float)srate);
break;
case 1:
ret *= hpL[0][j].freq_gain(freq, (float)srate);
ret *= lpL[1][j].freq_gain(freq, (float)srate);
break;
case 2:
ret *= hpL[1][j].freq_gain(freq, (float)srate);
ret *= lpL[2][j].freq_gain(freq, (float)srate);
break;
case 3:
ret *= hpL[2][j].freq_gain(freq, (float)srate);
break;
}
}
data[i] = dB_grid(ret);
}
if (*params[param_bypass] > 0.5f)
context->set_source_rgba(0.35, 0.4, 0.2, 0.3);
else {
context->set_source_rgba(0.35, 0.4, 0.2, 1);
context->set_line_width(1.5);
}
return true;
}
bool multibandlimiter_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const
{
if (!is_active) {
return false;
} else {
vertical = (subindex & 1) != 0;
return get_freq_gridline(subindex, pos, vertical, legend, context);
}
}

View File

@@ -0,0 +1,753 @@
/* Calf DSP plugin pack
* Modulation effect plugins
*
* Copyright (C) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen and others
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include <limits.h>
#include <memory.h>
#include <calf/giface.h>
#include <calf/modules_mod.h>
using namespace dsp;
using namespace calf_plugins;
#define SET_IF_CONNECTED(name) if (params[AM::param_##name] != NULL) *params[AM::param_##name] = name;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void flanger_audio_module::activate() {
left.reset();
right.reset();
last_r_phase = *params[par_stereo] * (1.f / 360.f);
left.reset_phase(0.f);
right.reset_phase(last_r_phase);
is_active = true;
}
void flanger_audio_module::set_sample_rate(uint32_t sr) {
srate = sr;
left.setup(sr);
right.setup(sr);
}
void flanger_audio_module::deactivate() {
is_active = false;
}
bool flanger_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const
{
if (!is_active)
return false;
if (index == par_delay && subindex < 2)
{
set_channel_color(context, subindex);
return ::get_graph(*this, subindex, data, points);
}
return false;
}
float flanger_audio_module::freq_gain(int subindex, float freq, float srate) const
{
return (subindex ? right : left).freq_gain(freq, srate);
}
void flanger_audio_module::params_changed()
{
float dry = *params[par_dryamount];
float wet = *params[par_amount];
float rate = *params[par_rate]; // 0.01*pow(1000.0f,*params[par_rate]);
float min_delay = *params[par_delay] / 1000.0;
float mod_depth = *params[par_depth] / 1000.0;
float fb = *params[par_fb];
left.set_dry(dry); right.set_dry(dry);
left.set_wet(wet); right.set_wet(wet);
left.set_rate(rate); right.set_rate(rate);
left.set_min_delay(min_delay); right.set_min_delay(min_delay);
left.set_mod_depth(mod_depth); right.set_mod_depth(mod_depth);
left.set_fb(fb); right.set_fb(fb);
float r_phase = *params[par_stereo] * (1.f / 360.f);
clear_reset = false;
if (*params[par_reset] >= 0.5) {
clear_reset = true;
left.reset_phase(0.f);
right.reset_phase(r_phase);
} else {
if (fabs(r_phase - last_r_phase) > 0.0001f) {
right.phase = left.phase;
right.inc_phase(r_phase);
last_r_phase = r_phase;
}
}
}
void flanger_audio_module::params_reset()
{
if (clear_reset) {
*params[par_reset] = 0.f;
clear_reset = false;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
phaser_audio_module::phaser_audio_module()
: left(MaxStages, x1vals[0], y1vals[0])
, right(MaxStages, x1vals[1], y1vals[1])
{
is_active = false;
}
void phaser_audio_module::set_sample_rate(uint32_t sr)
{
srate = sr;
left.setup(sr);
right.setup(sr);
}
void phaser_audio_module::activate()
{
is_active = true;
left.reset();
right.reset();
last_r_phase = *params[par_stereo] * (1.f / 360.f);
left.reset_phase(0.f);
right.reset_phase(last_r_phase);
}
void phaser_audio_module::deactivate()
{
is_active = false;
}
bool phaser_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const
{
if (!is_active)
return false;
if (subindex < 2)
{
set_channel_color(context, subindex);
return ::get_graph(*this, subindex, data, points);
}
return false;
}
float phaser_audio_module::freq_gain(int subindex, float freq, float srate) const
{
return (subindex ? right : left).freq_gain(freq, srate);
}
bool phaser_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const
{
return get_freq_gridline(subindex, pos, vertical, legend, context);
}
void phaser_audio_module::params_changed()
{
float dry = *params[par_dryamount];
float wet = *params[par_amount];
float rate = *params[par_rate]; // 0.01*pow(1000.0f,*params[par_rate]);
float base_frq = *params[par_freq];
float mod_depth = *params[par_depth];
float fb = *params[par_fb];
int stages = (int)*params[par_stages];
left.set_dry(dry); right.set_dry(dry);
left.set_wet(wet); right.set_wet(wet);
left.set_rate(rate); right.set_rate(rate);
left.set_base_frq(base_frq); right.set_base_frq(base_frq);
left.set_mod_depth(mod_depth); right.set_mod_depth(mod_depth);
left.set_fb(fb); right.set_fb(fb);
left.set_stages(stages); right.set_stages(stages);
float r_phase = *params[par_stereo] * (1.f / 360.f);
clear_reset = false;
if (*params[par_reset] >= 0.5) {
clear_reset = true;
left.reset_phase(0.f);
right.reset_phase(r_phase);
} else {
if (fabs(r_phase - last_r_phase) > 0.0001f) {
right.phase = left.phase;
right.inc_phase(r_phase);
last_r_phase = r_phase;
}
}
}
void phaser_audio_module::params_reset()
{
if (clear_reset) {
*params[par_reset] = 0.f;
clear_reset = false;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
rotary_speaker_audio_module::rotary_speaker_audio_module()
{
mwhl_value = hold_value = 0.f;
phase_h = phase_l = 0.f;
aspeed_l = 1.f;
aspeed_h = 1.f;
dspeed = 0.f;
}
void rotary_speaker_audio_module::set_sample_rate(uint32_t sr)
{
srate = sr;
setup();
}
void rotary_speaker_audio_module::setup()
{
crossover1l.set_lp_rbj(800.f, 0.7, (float)srate);
crossover1r.set_lp_rbj(800.f, 0.7, (float)srate);
crossover2l.set_hp_rbj(800.f, 0.7, (float)srate);
crossover2r.set_hp_rbj(800.f, 0.7, (float)srate);
}
void rotary_speaker_audio_module::activate()
{
phase_h = phase_l = 0.f;
maspeed_h = maspeed_l = 0.f;
setup();
}
void rotary_speaker_audio_module::deactivate()
{
}
void rotary_speaker_audio_module::control_change(int /*channel*/, int ctl, int val)
{
if (vibrato_mode == 3 && ctl == 64)
{
hold_value = val / 127.f;
set_vibrato();
return;
}
if (vibrato_mode == 4 && ctl == 1)
{
mwhl_value = val / 127.f;
set_vibrato();
return;
}
}
void rotary_speaker_audio_module::params_changed()
{
set_vibrato();
}
void rotary_speaker_audio_module::set_vibrato()
{
vibrato_mode = fastf2i_drm(*params[par_speed]);
// manual vibrato - do not recalculate speeds as they're not used anyway
if (vibrato_mode == 5)
return;
if (!vibrato_mode)
dspeed = -1;
else {
float speed = vibrato_mode - 1;
if (vibrato_mode == 3)
speed = hold_value;
if (vibrato_mode == 4)
speed = mwhl_value;
dspeed = (speed < 0.5f) ? 0 : 1;
}
update_speed();
}
/// Convert RPM speed to delta-phase
uint32_t rotary_speaker_audio_module::rpm2dphase(float rpm)
{
return (uint32_t)((rpm / (60.0 * srate)) * (1 << 30)) << 2;
}
/// Set delta-phase variables based on current calculated (and interpolated) RPM speed
void rotary_speaker_audio_module::update_speed()
{
float speed_h = aspeed_h >= 0 ? (48 + (400-48) * aspeed_h) : (48 * (1 + aspeed_h));
float speed_l = aspeed_l >= 0 ? 40 + (342-40) * aspeed_l : (40 * (1 + aspeed_l));
dphase_h = rpm2dphase(speed_h);
dphase_l = rpm2dphase(speed_l);
}
void rotary_speaker_audio_module::update_speed_manual(float delta)
{
float ts = *params[par_treblespeed];
float bs = *params[par_bassspeed];
incr_towards(maspeed_h, ts, delta * 200, delta * 200);
incr_towards(maspeed_l, bs, delta * 200, delta * 200);
dphase_h = rpm2dphase(maspeed_h);
dphase_l = rpm2dphase(maspeed_l);
}
/// map a ramp [int] to a sinusoid-like function [0, 65536]
static inline int pseudo_sine_scl(int counter)
{
// premature optimization is a root of all evil; it can be done with integers only - but later :)
double v = counter * (1.0 / (65536.0 * 32768.0));
return (int) (32768 + 32768 * (v - v*v*v) * (1.0 / 0.3849));
}
/// Increase or decrease aspeed towards raspeed, with required negative and positive rate
inline bool rotary_speaker_audio_module::incr_towards(float &aspeed, float raspeed, float delta_decc, float delta_acc)
{
if (aspeed < raspeed) {
aspeed = std::min(raspeed, aspeed + delta_acc);
return true;
}
else if (aspeed > raspeed)
{
aspeed = std::max(raspeed, aspeed - delta_decc);
return true;
}
return false;
}
uint32_t rotary_speaker_audio_module::process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask)
{
if (true)
{
crossover2l.set_bp_rbj(2000.f, 0.7, (float)srate);
crossover2r.copy_coeffs(crossover2l);
damper1l.set_bp_rbj(1000.f*pow(4.0, *params[par_test]), 0.7, (float)srate);
damper1r.copy_coeffs(damper1l);
}
else
{
crossover2l.set_hp_rbj(800.f, 0.7, (float)srate);
crossover2r.copy_coeffs(crossover2l);
}
int shift = (int)(300000 * (*params[par_shift])), pdelta = (int)(300000 * (*params[par_spacing]));
int md = (int)(100 * (*params[par_moddepth]));
float mix = 0.5 * (1.0 - *params[par_micdistance]);
float mix2 = *params[par_reflection];
float mix3 = mix2 * mix2;
float am_depth = *params[par_am_depth];
for (unsigned int i = 0; i < nsamples; i++) {
float in_l = ins[0][i + offset], in_r = ins[1][i + offset];
float in_mono = atan(0.5f * (in_l + in_r));
int xl = pseudo_sine_scl(phase_l), yl = pseudo_sine_scl(phase_l + 0x40000000);
int xh = pseudo_sine_scl(phase_h), yh = pseudo_sine_scl(phase_h + 0x40000000);
// printf("%d %d %d\n", shift, pdelta, shift + pdelta + 20 * xl);
meter_l = xl;
meter_h = xh;
// float out_hi_l = in_mono - delay.get_interp_1616(shift + md * xh) + delay.get_interp_1616(shift + md * 65536 + pdelta - md * yh) - delay.get_interp_1616(shift + md * 65536 + pdelta + pdelta - md * xh);
// float out_hi_r = in_mono + delay.get_interp_1616(shift + md * 65536 - md * yh) - delay.get_interp_1616(shift + pdelta + md * xh) + delay.get_interp_1616(shift + pdelta + pdelta + md * yh);
float fm_hi_l = delay.get_interp_1616(shift + md * xh) - mix2 * delay.get_interp_1616(shift + md * 65536 + pdelta - md * yh) + mix3 * delay.get_interp_1616(shift + md * 65536 + pdelta + pdelta - md * xh);
float fm_hi_r = delay.get_interp_1616(shift + md * 65536 - md * yh) - mix2 * delay.get_interp_1616(shift + pdelta + md * xh) + mix3 * delay.get_interp_1616(shift + pdelta + pdelta + md * yh);
float out_hi_l = lerp(in_mono, damper1l.process(fm_hi_l), lerp(0.5, xh * 1.0 / 65536.0, am_depth));
float out_hi_r = lerp(in_mono, damper1r.process(fm_hi_r), lerp(0.5, yh * 1.0 / 65536.0, am_depth));
float out_lo_l = lerp(in_mono, delay.get_interp_1616(shift + (md * xl >> 2)), lerp(0.5, yl * 1.0 / 65536.0, am_depth)); // + delay.get_interp_1616(shift + md * 65536 + pdelta - md * yl);
float out_lo_r = lerp(in_mono, delay.get_interp_1616(shift + (md * yl >> 2)), lerp(0.5, xl * 1.0 / 65536.0, am_depth)); // + delay.get_interp_1616(shift + md * 65536 + pdelta - md * yl);
out_hi_l = crossover2l.process(out_hi_l); // sanitize(out_hi_l);
out_hi_r = crossover2r.process(out_hi_r); // sanitize(out_hi_r);
out_lo_l = crossover1l.process(out_lo_l); // sanitize(out_lo_l);
out_lo_r = crossover1r.process(out_lo_r); // sanitize(out_lo_r);
float out_l = out_hi_l + out_lo_l;
float out_r = out_hi_r + out_lo_r;
float mic_l = out_l + mix * (out_r - out_l);
float mic_r = out_r + mix * (out_l - out_r);
outs[0][i + offset] = mic_l;
outs[1][i + offset] = mic_r;
delay.put(in_mono);
phase_l += dphase_l;
phase_h += dphase_h;
}
crossover1l.sanitize();
crossover1r.sanitize();
crossover2l.sanitize();
crossover2r.sanitize();
damper1l.sanitize();
damper1r.sanitize();
float delta = nsamples * 1.0 / srate;
if (vibrato_mode == 5)
update_speed_manual(delta);
else
{
bool u1 = incr_towards(aspeed_l, dspeed, delta * 0.2, delta * 0.14);
bool u2 = incr_towards(aspeed_h, dspeed, delta, delta * 0.5);
if (u1 || u2)
set_vibrato();
}
if(params[par_meter_l] != NULL) {
*params[par_meter_l] = (float)meter_l / 65536.0;
}
if(params[par_meter_h] != NULL) {
*params[par_meter_h] = (float)meter_h / 65536.0;
}
return outputs_mask;
}
///////////////////////////////////////////////////////////////////////////////////////////////
multichorus_audio_module::multichorus_audio_module()
{
is_active = false;
last_r_phase = -1;
}
void multichorus_audio_module::activate()
{
is_active = true;
params_changed();
}
void multichorus_audio_module::deactivate()
{
is_active = false;
}
void multichorus_audio_module::set_sample_rate(uint32_t sr) {
srate = sr;
left.setup(sr);
right.setup(sr);
}
bool multichorus_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const
{
if (!is_active)
return false;
int nvoices = (int)*params[par_voices];
if (index == par_delay && subindex < 3)
{
if (subindex < 2)
set_channel_color(context, subindex);
else {
context->set_source_rgba(0.35, 0.4, 0.2);
context->set_line_width(1.0);
}
return ::get_graph(*this, subindex, data, points);
}
if (index == par_rate && subindex < nvoices) {
const sine_multi_lfo<float, 8> &lfo = left.lfo;
for (int i = 0; i < points; i++) {
float phase = i * 2 * M_PI / points;
// original -65536 to 65535 value
float orig = subindex * lfo.voice_offset + ((lfo.voice_depth >> (30-13)) * 65536.0 * (0.95 * sin(phase) + 1)/ 8192.0) - 65536;
// scale to -1..1
data[i] = orig / 65536.0;
}
return true;
}
return false;
}
bool multichorus_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const
{
int voice = subindex >> 1;
int nvoices = (int)*params[par_voices];
if ((index != par_rate && index != par_depth) || voice >= nvoices)
return false;
float unit = (1 - *params[par_overlap]);
float scw = 1 + unit * (nvoices - 1);
set_channel_color(context, subindex);
const sine_multi_lfo<float, 8> &lfo = (subindex & 1 ? right : left).lfo;
if (index == par_rate)
{
x = (double)(lfo.phase + lfo.vphase * voice) / 4096.0;
y = 0.95 * sin(x * 2 * M_PI);
y = (voice * unit + (y + 1) / 2) / scw * 2 - 1;
}
else
{
double ph = (double)(lfo.phase + lfo.vphase * voice) / 4096.0;
x = 0.5 + 0.5 * sin(ph * 2 * M_PI);
y = subindex & 1 ? -0.75 : 0.75;
x = (voice * unit + x) / scw;
}
return true;
}
bool multichorus_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const
{
if (index == par_rate && !subindex)
{
pos = 0;
vertical = false;
return true;
}
if (index == par_delay)
return get_freq_gridline(subindex, pos, vertical, legend, context);
return false;
}
float multichorus_audio_module::freq_gain(int subindex, float freq, float srate) const
{
if (subindex == 2)
return *params[par_amount] * left.post.freq_gain(freq, srate);
return (subindex ? right : left).freq_gain(freq, srate);
}
void multichorus_audio_module::params_changed()
{
// delicious copy-pasta from flanger module - it'd be better to keep it common or something
float dry = *params[par_dryamount];
float wet = *params[par_amount];
float rate = *params[par_rate];
float min_delay = *params[par_delay] / 1000.0;
float mod_depth = *params[par_depth] / 1000.0;
float overlap = *params[par_overlap];
left.set_dry(dry); right.set_dry(dry);
left.set_wet(wet); right.set_wet(wet);
left.set_rate(rate); right.set_rate(rate);
left.set_min_delay(min_delay); right.set_min_delay(min_delay);
left.set_mod_depth(mod_depth); right.set_mod_depth(mod_depth);
int voices = (int)*params[par_voices];
left.lfo.set_voices(voices); right.lfo.set_voices(voices);
left.lfo.set_overlap(overlap);right.lfo.set_overlap(overlap);
float vphase = *params[par_vphase] * (1.f / 360.f);
left.lfo.vphase = right.lfo.vphase = vphase * (4096 / std::max(voices - 1, 1));
float r_phase = *params[par_stereo] * (1.f / 360.f);
if (fabs(r_phase - last_r_phase) > 0.0001f) {
right.lfo.phase = left.lfo.phase;
right.lfo.phase += chorus_phase(r_phase * 4096);
last_r_phase = r_phase;
}
left.post.f1.set_bp_rbj(*params[par_freq], *params[par_q], srate);
left.post.f2.set_bp_rbj(*params[par_freq2], *params[par_q], srate);
right.post.f1.copy_coeffs(left.post.f1);
right.post.f2.copy_coeffs(left.post.f2);
}
uint32_t multichorus_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
{
left.process(outs[0] + offset, ins[0] + offset, numsamples);
right.process(outs[1] + offset, ins[1] + offset, numsamples);
return outputs_mask; // XXXKF allow some delay after input going blank
}
/// Pulsator by Markus Schmidt
///
/// This module provides a couple
/// of different LFO's for modulating the level of a signal.
///////////////////////////////////////////////////////////////////////////////////////////////
pulsator_audio_module::pulsator_audio_module()
{
is_active = false;
srate = 0;
// last_generation = 0;
clip_inL = 0.f;
clip_inR = 0.f;
clip_outL = 0.f;
clip_outR = 0.f;
meter_inL = 0.f;
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
}
void pulsator_audio_module::activate()
{
is_active = true;
lfoL.activate();
lfoR.activate();
params_changed();
}
void pulsator_audio_module::deactivate()
{
is_active = false;
lfoL.deactivate();
lfoR.deactivate();
}
void pulsator_audio_module::params_changed()
{
lfoL.set_params(*params[param_freq], *params[param_mode], 0.f, srate, *params[param_amount]);
lfoR.set_params(*params[param_freq], *params[param_mode], *params[param_offset], srate, *params[param_amount]);
clear_reset = false;
if (*params[param_reset] >= 0.5) {
clear_reset = true;
lfoL.set_phase(0.f);
lfoR.set_phase(0.f);
}
}
void pulsator_audio_module::set_sample_rate(uint32_t sr)
{
srate = sr;
}
uint32_t pulsator_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
{
bool bypass = *params[param_bypass] > 0.5f;
uint32_t samples = numsamples + offset;
if(bypass) {
// everything bypassed
while(offset < samples) {
outs[0][offset] = ins[0][offset];
outs[1][offset] = ins[1][offset];
++offset;
}
// displays, too
clip_inL = 0.f;
clip_inR = 0.f;
clip_outL = 0.f;
clip_outR = 0.f;
meter_inL = 0.f;
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
// LFO's should go on
lfoL.advance(numsamples);
lfoR.advance(numsamples);
} else {
clip_inL -= std::min(clip_inL, numsamples);
clip_inR -= std::min(clip_inR, numsamples);
clip_outL -= std::min(clip_outL, numsamples);
clip_outR -= std::min(clip_outR, numsamples);
meter_inL = 0.f;
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
// process
while(offset < samples) {
// cycle through samples
float outL = 0.f;
float outR = 0.f;
float inL = ins[0][offset];
float inR = ins[1][offset];
// in level
inR *= *params[param_level_in];
inL *= *params[param_level_in];
if(*params[param_mono] > 0.5) {
inL = (inL + inR) * 0.5;
inR = inL;
}
float procL = inL;
float procR = inR;
procL *= (lfoL.get_value() * 0.5 + *params[param_amount] / 2);
procR *= (lfoR.get_value() * 0.5 + *params[param_amount] / 2);
outL = procL + inL * (1 - *params[param_amount]);
outR = procR + inR * (1 - *params[param_amount]);
outL *= *params[param_level_out];
outR *= *params[param_level_out];
// send to output
outs[0][offset] = outL;
outs[1][offset] = outR;
// clip LED's
if(inL > 1.f) {
clip_inL = srate >> 3;
}
if(inR > 1.f) {
clip_inR = srate >> 3;
}
if(outL > 1.f) {
clip_outL = srate >> 3;
}
if(outR > 1.f) {
clip_outR = srate >> 3;
}
// set up in / out meters
if(inL > meter_inL) {
meter_inL = inL;
}
if(inR > meter_inR) {
meter_inR = inR;
}
if(outL > meter_outL) {
meter_outL = outL;
}
if(outR > meter_outR) {
meter_outR = outR;
}
// next sample
++offset;
// advance lfo's
lfoL.advance(1);
lfoR.advance(1);
} // cycle trough samples
}
// draw meters
SET_IF_CONNECTED(clip_inL)
SET_IF_CONNECTED(clip_inR)
SET_IF_CONNECTED(clip_outL)
SET_IF_CONNECTED(clip_outR)
SET_IF_CONNECTED(meter_inL)
SET_IF_CONNECTED(meter_inR)
SET_IF_CONNECTED(meter_outL)
SET_IF_CONNECTED(meter_outR)
// whatever has to be returned x)
return outputs_mask;
}
bool pulsator_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const
{
if (!is_active) {
return false;
} else if(index == param_freq) {
if(subindex == 0) {
context->set_source_rgba(0.35, 0.4, 0.2, 1);
return lfoL.get_graph(data, points, context);
}
if(subindex == 1) {
context->set_source_rgba(0.35, 0.4, 0.2, 0.5);
return lfoR.get_graph(data, points, context);
}
}
return false;
}
bool pulsator_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const
{
if (!is_active) {
return false;
} else if(index == param_freq) {
if(subindex == 0) {
context->set_source_rgba(0.35, 0.4, 0.2, 1);
return lfoL.get_dot(x, y, size, context);
}
if(subindex == 1) {
context->set_source_rgba(0.35, 0.4, 0.2, 0.5);
return lfoR.get_dot(x, y, size, context);
}
return false;
}
return false;
}
bool pulsator_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const
{
if (index == param_freq && !subindex)
{
pos = 0;
vertical = false;
return true;
}
return false;
}

View File

@@ -0,0 +1,802 @@
/* Calf DSP Library
* Example audio modules - monosynth
*
* Copyright (C) 2001-2007 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include <calf/giface.h>
#include <calf/modules_synths.h>
using namespace dsp;
using namespace calf_plugins;
using namespace std;
float silence[4097];
monosynth_audio_module::monosynth_audio_module()
: mod_matrix_impl(mod_matrix_data, &mm_metadata)
, inertia_cutoff(1)
, inertia_pitchbend(1)
, inertia_pressure(64)
{
}
void monosynth_audio_module::activate() {
running = false;
output_pos = 0;
queue_note_on = -1;
inertia_pitchbend.set_now(1.f);
lfo_bend = 1.0;
modwheel_value = 0.f;
modwheel_value_int = 0;
inertia_cutoff.set_now(*params[par_cutoff]);
inertia_pressure.set_now(0);
filter.reset();
filter2.reset();
stack.clear();
last_pwshift1 = last_pwshift2 = 0;
last_stretch1 = 65536;
queue_note_on_and_off = false;
prev_wave1 = -1;
prev_wave2 = -1;
wave1 = -1;
wave2 = -1;
queue_note_on = -1;
last_filter_type = -1;
}
waveform_family<MONOSYNTH_WAVE_BITS> *monosynth_audio_module::waves;
void monosynth_audio_module::precalculate_waves(progress_report_iface *reporter)
{
float data[1 << MONOSYNTH_WAVE_BITS];
bandlimiter<MONOSYNTH_WAVE_BITS> bl;
if (waves)
return;
static waveform_family<MONOSYNTH_WAVE_BITS> waves_data[wave_count];
waves = waves_data;
enum { S = 1 << MONOSYNTH_WAVE_BITS, HS = S / 2, QS = S / 4, QS3 = 3 * QS };
float iQS = 1.0 / QS;
if (reporter)
reporter->report_progress(0, "Precalculating waveforms");
// yes these waves don't have really perfect 1/x spectrum because of aliasing
// (so what?)
for (int i = 0 ; i < HS; i++)
data[i] = (float)(i * 1.0 / HS),
data[i + HS] = (float)(i * 1.0 / HS - 1.0f);
waves[wave_saw].make(bl, data);
// this one is dummy, fake and sham, we're using a difference of two sawtooths for square wave due to PWM
for (int i = 0 ; i < S; i++)
data[i] = (float)(i < HS ? -1.f : 1.f);
waves[wave_sqr].make(bl, data, 4);
for (int i = 0 ; i < S; i++)
data[i] = (float)(i < (64 * S / 2048)? -1.f : 1.f);
waves[wave_pulse].make(bl, data);
for (int i = 0 ; i < S; i++)
data[i] = (float)sin(i * M_PI / HS);
waves[wave_sine].make(bl, data);
for (int i = 0 ; i < QS; i++) {
data[i] = i * iQS,
data[i + QS] = 1 - i * iQS,
data[i + HS] = - i * iQS,
data[i + QS3] = -1 + i * iQS;
}
waves[wave_triangle].make(bl, data);
for (int i = 0, j = 1; i < S; i++) {
data[i] = -1 + j * 1.0 / HS;
if (i == j)
j *= 2;
}
waves[wave_varistep].make(bl, data);
for (int i = 0; i < S; i++) {
data[i] = (min(1.f, (float)(i / 64.f))) * (1.0 - i * 1.0 / S) * (-1 + fmod (i * i * 8/ (S * S * 1.0), 2.0));
}
waves[wave_skewsaw].make(bl, data);
for (int i = 0; i < S; i++) {
data[i] = (min(1.f, (float)(i / 64.f))) * (1.0 - i * 1.0 / S) * (fmod (i * i * 8/ (S * S * 1.0), 2.0) < 1.0 ? -1.0 : +1.0);
}
waves[wave_skewsqr].make(bl, data);
if (reporter)
reporter->report_progress(50, "Precalculating waveforms");
for (int i = 0; i < S; i++) {
if (i < QS3) {
float p = i * 1.0 / QS3;
data[i] = sin(M_PI * p * p * p);
} else {
float p = (i - QS3 * 1.0) / QS;
data[i] = -0.5 * sin(3 * M_PI * p * p);
}
}
waves[wave_test1].make(bl, data);
for (int i = 0; i < S; i++) {
data[i] = exp(-i * 1.0 / HS) * sin(i * M_PI / HS) * cos(2 * M_PI * i / HS);
}
normalize_waveform(data, S);
waves[wave_test2].make(bl, data);
for (int i = 0; i < S; i++) {
//int ii = (i < HS) ? i : S - i;
int ii = HS;
data[i] = (ii * 1.0 / HS) * sin(i * 3 * M_PI / HS + 2 * M_PI * sin(M_PI / 4 + i * 4 * M_PI / HS)) * sin(i * 5 * M_PI / HS + 2 * M_PI * sin(M_PI / 8 + i * 6 * M_PI / HS));
}
waves[wave_test3].make(bl, data);
for (int i = 0; i < S; i++) {
data[i] = sin(i * 2 * M_PI / HS + sin(i * 2 * M_PI / HS + 0.5 * M_PI * sin(i * 18 * M_PI / HS)) * sin(i * 1 * M_PI / HS + 0.5 * M_PI * sin(i * 11 * M_PI / HS)));
}
waves[wave_test4].make(bl, data);
for (int i = 0; i < S; i++) {
data[i] = sin(i * 2 * M_PI / HS + 0.2 * M_PI * sin(i * 13 * M_PI / HS) + 0.1 * M_PI * sin(i * 37 * M_PI / HS)) * sin(i * M_PI / HS + 0.2 * M_PI * sin(i * 15 * M_PI / HS));
}
waves[wave_test5].make(bl, data);
for (int i = 0; i < S; i++) {
if (i < HS)
data[i] = sin(i * 2 * M_PI / HS);
else
if (i < 3 * S / 4)
data[i] = sin(i * 4 * M_PI / HS);
else
if (i < 7 * S / 8)
data[i] = sin(i * 8 * M_PI / HS);
else
data[i] = sin(i * 8 * M_PI / HS) * (S - i) / (S / 8);
}
waves[wave_test6].make(bl, data);
for (int i = 0; i < S; i++) {
int j = i >> (MONOSYNTH_WAVE_BITS - 11);
data[i] = (j ^ 0x1D0) * 1.0 / HS - 1;
}
waves[wave_test7].make(bl, data);
for (int i = 0; i < S; i++) {
int j = i >> (MONOSYNTH_WAVE_BITS - 11);
data[i] = -1 + 0.66 * (3 & ((j >> 8) ^ (j >> 10) ^ (j >> 6)));
}
waves[wave_test8].make(bl, data);
if (reporter)
reporter->report_progress(100, "");
}
bool monosynth_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const
{
monosynth_audio_module::precalculate_waves(NULL);
// printf("get_graph %d %p %d wave1=%d wave2=%d\n", index, data, points, wave1, wave2);
if (index == par_wave1 || index == par_wave2) {
if (subindex)
return false;
enum { S = 1 << MONOSYNTH_WAVE_BITS };
float value = *params[index];
int wave = dsp::clip(dsp::fastf2i_drm(value), 0, (int)wave_count - 1);
uint32_t shift = index == par_wave1 ? last_pwshift1 : last_pwshift2;
if (!running)
shift = (int32_t)(0x78000000 * (*params[index == par_wave1 ? par_pw1 : par_pw2]));
int flag = (wave == wave_sqr);
shift = (flag ? S/2 : 0) + (shift >> (32 - MONOSYNTH_WAVE_BITS));
int sign = flag ? -1 : 1;
if (wave == wave_sqr)
wave = wave_saw;
float *waveform = waves[wave].original;
float rnd_start = 1 - *params[par_window1] * 0.5f;
float scl = rnd_start < 1.0 ? 1.f / (1 - rnd_start) : 0.f;
for (int i = 0; i < points; i++)
{
int pos = i * S / points;
float r = 1;
if (index == par_wave1)
{
float ph = i * 1.0 / points;
if (ph < 0.5f)
ph = 1.f - ph;
ph = (ph - rnd_start) * scl;
if (ph < 0)
ph = 0;
r = 1.0 - ph * ph;
pos = int(pos * 1.0 * last_stretch1 / 65536.0 ) % S;
}
data[i] = r * (sign * waveform[pos] + waveform[(pos + shift) & (S - 1)]) / (sign == -1 ? 1 : 2);
}
return true;
}
if (index == par_filtertype) {
if (!running)
return false;
if (subindex > (is_stereo_filter() ? 1 : 0))
return false;
for (int i = 0; i < points; i++)
{
double freq = 20.0 * pow (20000.0 / 20.0, i * 1.0 / points);
const dsp::biquad_d1_lerp<float> &f = subindex ? filter2 : filter;
float level = f.freq_gain(freq, srate);
if (!is_stereo_filter())
level *= filter2.freq_gain(freq, srate);
level *= fgain;
data[i] = log(level) / log(1024.0) + 0.5;
}
return true;
}
return get_static_graph(index, subindex, *params[index], data, points, context);
}
void monosynth_audio_module::calculate_buffer_oscs(float lfo1)
{
int flag1 = (wave1 == wave_sqr);
int flag2 = (wave2 == wave_sqr);
int32_t shift1 = last_pwshift1;
int32_t shift2 = last_pwshift2;
int32_t stretch1 = last_stretch1;
int32_t shift_target1 = (int32_t)(0x78000000 * dsp::clip11(*params[par_pw1] + lfo1 * *params[par_lfopw] + 0.01f * moddest[moddest_o1pw]));
int32_t shift_target2 = (int32_t)(0x78000000 * dsp::clip11(*params[par_pw2] + lfo1 * *params[par_lfopw] + 0.01f * moddest[moddest_o2pw]));
int32_t stretch_target1 = (int32_t)(65536 * dsp::clip(*params[par_stretch1] + 0.01f * moddest[moddest_o1stretch], 1.f, 16.f));
int32_t shift_delta1 = ((shift_target1 >> 1) - (last_pwshift1 >> 1)) >> (step_shift - 1);
int32_t shift_delta2 = ((shift_target2 >> 1) - (last_pwshift2 >> 1)) >> (step_shift - 1);
int32_t stretch_delta1 = ((stretch_target1 >> 1) - (last_stretch1 >> 1)) >> (step_shift - 1);
last_pwshift1 = shift_target1;
last_pwshift2 = shift_target2;
last_stretch1 = stretch_target1;
lookup_waveforms();
shift1 += (flag1 << 31);
shift2 += (flag2 << 31);
float mix1 = 1 - 2 * flag1, mix2 = 1 - 2 * flag2;
float new_xfade = dsp::clip<float>(xfade + 0.01f * moddest[moddest_oscmix], 0.f, 1.f);
float cur_xfade = last_xfade;
float xfade_step = (new_xfade - cur_xfade) * (1.0 / step_size);
float rnd_start = 1 - *params[par_window1] * 0.5f;
float scl = rnd_start < 1.0 ? 1.f / (1 - rnd_start) : 0.f;
for (uint32_t i = 0; i < step_size; i++)
{
//buffer[i] = lerp(osc1.get_phaseshifted(shift1, mix1), osc2.get_phaseshifted(shift2, mix2), cur_xfade);
float o1phase = osc1.phase / (65536.0 * 65536.0);
if (o1phase < 0.5)
o1phase = 1 - o1phase;
o1phase = (o1phase - rnd_start) * scl;
if (o1phase < 0)
o1phase = 0;
float r = 1.0 - o1phase * o1phase;
buffer[i] = lerp(r * osc1.get_phasedist(stretch1, shift1, mix1), osc2.get_phaseshifted(shift2, mix2), cur_xfade);
osc1.advance();
osc2.advance();
shift1 += shift_delta1;
shift2 += shift_delta2;
stretch1 += stretch_delta1;
cur_xfade += xfade_step;
}
last_xfade = new_xfade;
}
void monosynth_audio_module::calculate_buffer_ser()
{
filter.big_step(1.0 / step_size);
filter2.big_step(1.0 / step_size);
for (uint32_t i = 0; i < step_size; i++)
{
float wave = buffer[i] * fgain;
wave = filter.process(wave);
wave = filter2.process(wave);
buffer[i] = wave;
fgain += fgain_delta;
}
}
void monosynth_audio_module::calculate_buffer_single()
{
filter.big_step(1.0 / step_size);
for (uint32_t i = 0; i < step_size; i++)
{
float wave = buffer[i] * fgain;
wave = filter.process(wave);
buffer[i] = wave;
fgain += fgain_delta;
}
}
void monosynth_audio_module::calculate_buffer_stereo()
{
filter.big_step(1.0 / step_size);
filter2.big_step(1.0 / step_size);
for (uint32_t i = 0; i < step_size; i++)
{
float wave1 = buffer[i] * fgain;
buffer[i] = fgain * filter.process(wave1);
buffer2[i] = fgain * filter2.process(wave1);
fgain += fgain_delta;
}
}
void monosynth_audio_module::lookup_waveforms()
{
osc1.waveform = waves[wave1 == wave_sqr ? wave_saw : wave1].get_level((uint32_t)(((uint64_t)osc1.phasedelta) * last_stretch1 >> 16));
osc2.waveform = waves[wave2 == wave_sqr ? wave_saw : wave2].get_level(osc2.phasedelta);
if (!osc1.waveform) osc1.waveform = silence;
if (!osc2.waveform) osc2.waveform = silence;
prev_wave1 = wave1;
prev_wave2 = wave2;
}
void monosynth_audio_module::delayed_note_on()
{
force_fadeout = false;
fadeout.reset_soft();
fadeout2.reset_soft();
porta_time = 0.f;
start_freq = freq;
target_freq = freq = 440 * pow(2.0, (queue_note_on - 69) / 12.0);
velocity = queue_vel;
ampctl = 1.0 + (queue_vel - 1.0) * *params[par_vel2amp];
fltctl = 1.0 + (queue_vel - 1.0) * *params[par_vel2filter];
bool starting = false;
if (!running)
{
starting = true;
if (legato >= 2)
porta_time = -1.f;
last_xfade = xfade;
osc1.reset();
osc2.reset();
filter.reset();
filter2.reset();
if (*params[par_lfo1trig] <= 0)
lfo1.reset();
if (*params[par_lfo2trig] <= 0)
lfo2.reset();
switch((int)*params[par_oscmode])
{
case 1:
osc2.phase = 0x80000000;
break;
case 2:
osc2.phase = 0x40000000;
break;
case 3:
osc1.phase = osc2.phase = 0x40000000;
break;
case 4:
osc1.phase = 0x40000000;
osc2.phase = 0xC0000000;
break;
case 5:
// rand() is crap, but I don't have any better RNG in Calf yet
osc1.phase = rand() << 16;
osc2.phase = rand() << 16;
break;
default:
break;
}
running = true;
}
if (legato >= 2 && !gate)
porta_time = -1.f;
gate = true;
stopping = false;
if (starting || !(legato & 1) || envelope1.released())
envelope1.note_on();
if (starting || !(legato & 1) || envelope2.released())
envelope2.note_on();
envelope1.advance();
envelope2.advance();
queue_note_on = -1;
float modsrc[modsrc_count] = {
1,
velocity,
inertia_pressure.get_last(),
modwheel_value,
(float)envelope1.value,
(float)envelope2.value,
(float)(0.5+0.5*lfo1.last),
(float)(0.5+0.5*lfo2.last)
};
calculate_modmatrix(moddest, moddest_count, modsrc);
set_frequency();
lookup_waveforms();
if (queue_note_on_and_off)
{
end_note();
queue_note_on_and_off = false;
}
}
void monosynth_audio_module::set_sample_rate(uint32_t sr) {
srate = sr;
crate = sr / step_size;
odcr = (float)(1.0 / crate);
fgain = 0.f;
fgain_delta = 0.f;
inertia_cutoff.ramp.set_length(crate / 30); // 1/30s
inertia_pitchbend.ramp.set_length(crate / 30); // 1/30s
master.set_sample_rate(sr);
}
void monosynth_audio_module::calculate_step()
{
if (queue_note_on != -1)
delayed_note_on();
else
if (stopping || !running)
{
running = false;
envelope1.advance();
envelope2.advance();
lfo1.get();
lfo2.get();
float modsrc[modsrc_count] = {
1,
velocity,
inertia_pressure.get_last(),
modwheel_value,
(float)envelope1.value,
(float)envelope2.value,
(float)(0.5+0.5*lfo1.last),
(float)(0.5+0.5*lfo2.last)
};
calculate_modmatrix(moddest, moddest_count, modsrc);
last_stretch1 = (int32_t)(65536 * dsp::clip(*params[par_stretch1] + 0.01f * moddest[moddest_o1stretch], 1.f, 16.f));
return;
}
lfo1.set_freq(*params[par_lforate], crate);
lfo2.set_freq(*params[par_lfo2rate], crate);
float porta_total_time = *params[par_portamento] * 0.001f;
if (porta_total_time >= 0.00101f && porta_time >= 0) {
// XXXKF this is criminal, optimize!
float point = porta_time / porta_total_time;
if (point >= 1.0f) {
freq = target_freq;
porta_time = -1;
} else {
freq = start_freq + (target_freq - start_freq) * point;
// freq = start_freq * pow(target_freq / start_freq, point);
porta_time += odcr;
}
}
float lfov1 = lfo1.get() * std::min(1.0f, lfo_clock / *params[par_lfodelay]);
lfov1 = lfov1 * dsp::lerp(1.f, modwheel_value, *params[par_mwhl_lfo]);
float lfov2 = lfo2.get() * std::min(1.0f, lfo_clock / *params[par_lfo2delay]);
lfo_clock += odcr;
if (fabs(*params[par_lfopitch]) > small_value<float>())
lfo_bend = pow(2.0f, *params[par_lfopitch] * lfov1 * (1.f / 1200.0f));
inertia_pitchbend.step();
envelope1.advance();
envelope2.advance();
float env1 = envelope1.value, env2 = envelope2.value;
float aenv1 = envelope1.get_amp_value(), aenv2 = envelope2.get_amp_value();
// mod matrix
// this should be optimized heavily; I think I'll do it when MIDI in Ardour 3 gets stable :>
float modsrc[modsrc_count] = {
1,
velocity,
inertia_pressure.get(),
modwheel_value,
(float)env1,
(float)env2,
(float)(0.5+0.5*lfov1),
(float)(0.5+0.5*lfov2)
};
calculate_modmatrix(moddest, moddest_count, modsrc);
set_frequency();
inertia_cutoff.set_inertia(*params[par_cutoff]);
cutoff = inertia_cutoff.get() * pow(2.0f, (lfov1 * *params[par_lfofilter] + env1 * fltctl * *params[par_env1tocutoff] + env2 * fltctl * *params[par_env2tocutoff] + moddest[moddest_cutoff]) * (1.f / 1200.f));
if (*params[par_keyfollow] > 0.01f)
cutoff *= pow(freq / 264.f, *params[par_keyfollow]);
cutoff = dsp::clip(cutoff , 10.f, 18000.f);
float resonance = *params[par_resonance];
float e2r1 = *params[par_env1tores];
resonance = resonance * (1 - e2r1) + (0.7 + (resonance - 0.7) * env1 * env1) * e2r1;
float e2r2 = *params[par_env2tores];
resonance = resonance * (1 - e2r2) + (0.7 + (resonance - 0.7) * env2 * env2) * e2r2 + moddest[moddest_resonance];
float cutoff2 = dsp::clip(cutoff * separation, 10.f, 18000.f);
float newfgain = 0.f;
if (filter_type != last_filter_type)
{
filter.y2 = filter.y1 = filter.x2 = filter.x1 = filter.y1;
filter2.y2 = filter2.y1 = filter2.x2 = filter2.x1 = filter2.y1;
last_filter_type = filter_type;
}
switch(filter_type)
{
case flt_lp12:
filter.set_lp_rbj(cutoff, resonance, srate);
filter2.set_null();
newfgain = min(0.7f, 0.7f / resonance) * ampctl;
break;
case flt_hp12:
filter.set_hp_rbj(cutoff, resonance, srate);
filter2.set_null();
newfgain = min(0.7f, 0.7f / resonance) * ampctl;
break;
case flt_lp24:
filter.set_lp_rbj(cutoff, resonance, srate);
filter2.set_lp_rbj(cutoff2, resonance, srate);
newfgain = min(0.5f, 0.5f / resonance) * ampctl;
break;
case flt_lpbr:
filter.set_lp_rbj(cutoff, resonance, srate);
filter2.set_br_rbj(cutoff2, 1.0 / resonance, srate);
newfgain = min(0.5f, 0.5f / resonance) * ampctl;
break;
case flt_hpbr:
filter.set_hp_rbj(cutoff, resonance, srate);
filter2.set_br_rbj(cutoff2, 1.0 / resonance, srate);
newfgain = min(0.5f, 0.5f / resonance) * ampctl;
break;
case flt_2lp12:
filter.set_lp_rbj(cutoff, resonance, srate);
filter2.set_lp_rbj(cutoff2, resonance, srate);
newfgain = min(0.7f, 0.7f / resonance) * ampctl;
break;
case flt_bp6:
filter.set_bp_rbj(cutoff, resonance, srate);
filter2.set_null();
newfgain = ampctl;
break;
case flt_2bp6:
filter.set_bp_rbj(cutoff, resonance, srate);
filter2.set_bp_rbj(cutoff2, resonance, srate);
newfgain = ampctl;
break;
}
float e2a1 = *params[par_env1toamp];
float e2a2 = *params[par_env2toamp];
if (e2a1 > 0.f)
newfgain *= aenv1;
if (e2a2 > 0.f)
newfgain *= aenv2;
if (moddest[moddest_attenuation] != 0.f)
newfgain *= dsp::clip<float>(1 - moddest[moddest_attenuation] * moddest[moddest_attenuation], 0.f, 1.f);
fgain_delta = (newfgain - fgain) * (1.0 / step_size);
calculate_buffer_oscs(lfov1);
lfo1.last = lfov1;
lfo2.last = lfov2;
switch(filter_type)
{
case flt_lp24:
case flt_lpbr:
case flt_hpbr: // Oomek's wish
calculate_buffer_ser();
break;
case flt_lp12:
case flt_hp12:
case flt_bp6:
calculate_buffer_single();
break;
case flt_2lp12:
case flt_2bp6:
calculate_buffer_stereo();
break;
}
apply_fadeout();
}
void monosynth_audio_module::apply_fadeout()
{
if (fadeout.undoing)
{
fadeout.process(buffer2, step_size);
if (is_stereo_filter())
fadeout2.process(buffer2, step_size);
}
else
{
// stop the sound if the amplitude envelope is not running (if there's any)
bool aenv1_on = *params[par_env1toamp] > 0.f, aenv2_on = *params[par_env2toamp] > 0.f;
bool do_fadeout = force_fadeout;
// if there's no amplitude envelope at all, the fadeout starts at key release
if (!aenv1_on && !aenv2_on && !gate)
do_fadeout = true;
// if ENV1 modulates amplitude, the fadeout will start on ENV1 end too
if (aenv1_on && envelope1.state == adsr::STOP)
do_fadeout = true;
// if ENV2 modulates amplitude, the fadeout will start on ENV2 end too
if (aenv2_on && envelope2.state == adsr::STOP)
do_fadeout = true;
if (do_fadeout || fadeout.undoing || fadeout2.undoing)
{
fadeout.process(buffer, step_size);
if (is_stereo_filter())
fadeout2.process(buffer2, step_size);
if (fadeout.done)
stopping = true;
}
}
}
void monosynth_audio_module::note_on(int /*channel*/, int note, int vel)
{
queue_note_on = note;
queue_note_on_and_off = false;
last_key = note;
queue_vel = vel / 127.f;
stack.push(note);
}
void monosynth_audio_module::note_off(int /*channel*/, int note, int vel)
{
stack.pop(note);
if (note == queue_note_on)
{
queue_note_on_and_off = true;
return;
}
// If releasing the currently played note, try to get another one from note stack.
if (note == last_key) {
end_note();
}
}
void monosynth_audio_module::end_note()
{
if (stack.count())
{
int note;
last_key = note = stack.nth(stack.count() - 1);
start_freq = freq;
target_freq = freq = dsp::note_to_hz(note);
porta_time = 0;
set_frequency();
if (!(legato & 1)) {
envelope1.note_on();
envelope2.note_on();
stopping = false;
running = true;
}
return;
}
gate = false;
envelope1.note_off();
envelope2.note_off();
}
void monosynth_audio_module::channel_pressure(int /*channel*/, int value)
{
inertia_pressure.set_inertia(value * (1.0 / 127.0));
}
void monosynth_audio_module::control_change(int /*channel*/, int controller, int value)
{
switch(controller)
{
case 1:
modwheel_value_int = (modwheel_value_int & 127) | (value << 7);
modwheel_value = modwheel_value_int / 16383.0;
break;
case 33:
modwheel_value_int = (modwheel_value_int & (127 << 7)) | value;
modwheel_value = modwheel_value_int / 16383.0;
break;
case 120: // all sounds off
force_fadeout = true;
// fall through
case 123: // all notes off
gate = false;
queue_note_on = -1;
envelope1.note_off();
envelope2.note_off();
stack.clear();
break;
}
}
void monosynth_audio_module::deactivate()
{
gate = false;
running = false;
stopping = false;
envelope1.reset();
envelope2.reset();
stack.clear();
}
void monosynth_audio_module::set_frequency()
{
float detune_scaled = (detune - 1); // * log(freq / 440);
if (*params[par_scaledetune] > 0)
detune_scaled *= pow(20.0 / freq, (double)*params[par_scaledetune]);
float p1 = 1, p2 = 1;
if (moddest[moddest_o1detune] != 0)
p1 = pow(2.0, moddest[moddest_o1detune] * (1.0 / 1200.0));
if (moddest[moddest_o2detune] != 0)
p2 = pow(2.0, moddest[moddest_o2detune] * (1.0 / 1200.0));
osc1.set_freq(freq * (1 - detune_scaled) * p1 * inertia_pitchbend.get_last() * lfo_bend, srate);
osc2.set_freq(freq * (1 + detune_scaled) * p2 * inertia_pitchbend.get_last() * lfo_bend * xpose, srate);
}
void monosynth_audio_module::params_changed()
{
float sf = 0.001f;
envelope1.set(*params[par_env1attack] * sf, *params[par_env1decay] * sf, std::min(0.999f, *params[par_env1sustain]), *params[par_env1release] * sf, srate / step_size, *params[par_env1fade] * sf);
envelope2.set(*params[par_env2attack] * sf, *params[par_env2decay] * sf, std::min(0.999f, *params[par_env2sustain]), *params[par_env2release] * sf, srate / step_size, *params[par_env2fade] * sf);
filter_type = dsp::fastf2i_drm(*params[par_filtertype]);
separation = pow(2.0, *params[par_cutoffsep] / 1200.0);
wave1 = dsp::clip(dsp::fastf2i_drm(*params[par_wave1]), 0, (int)wave_count - 1);
wave2 = dsp::clip(dsp::fastf2i_drm(*params[par_wave2]), 0, (int)wave_count - 1);
detune = pow(2.0, *params[par_detune] / 1200.0);
xpose = pow(2.0, *params[par_osc2xpose] / 12.0);
xfade = *params[par_oscmix];
legato = dsp::fastf2i_drm(*params[par_legato]);
master.set_inertia(*params[par_master]);
if (running)
set_frequency();
if (wave1 != prev_wave1 || wave2 != prev_wave2)
lookup_waveforms();
}
uint32_t monosynth_audio_module::process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask)
{
uint32_t op = offset;
uint32_t op_end = offset + nsamples;
int had_data = 0;
while(op < op_end) {
if (output_pos == 0)
calculate_step();
if(op < op_end) {
uint32_t ip = output_pos;
uint32_t len = std::min(step_size - output_pos, op_end - op);
if (running)
{
had_data = 3;
if (is_stereo_filter())
for(uint32_t i = 0 ; i < len; i++) {
float vol = master.get();
outs[0][op + i] = buffer[ip + i] * vol;
outs[1][op + i] = buffer2[ip + i] * vol;
}
else
for(uint32_t i = 0 ; i < len; i++)
outs[0][op + i] = outs[1][op + i] = buffer[ip + i] * master.get();
}
else
{
dsp::zero(&outs[0][op], len);
dsp::zero(&outs[1][op], len);
}
op += len;
output_pos += len;
if (output_pos == step_size)
output_pos = 0;
}
}
return had_data;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,538 @@
/* Calf DSP plugin pack
* LADSPA/DSSI/LV2 wrapper instantiation for all plugins
*
* Copyright (C) 2001-2010 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include <config.h>
#include <calf/ladspa_wrap.h>
#include <calf/lv2wrap.h>
#include <calf/modules.h>
#include <calf/modules_comp.h>
#include <calf/modules_limit.h>
#include <calf/modules_dev.h>
#include <calf/modules_dist.h>
#include <calf/modules_eq.h>
#include <calf/modules_mod.h>
#include <calf/modules_synths.h>
#include <calf/organ.h>
using namespace calf_plugins;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if USE_LADSPA
ladspa_instance::ladspa_instance(audio_module_iface *_module, ladspa_plugin_metadata_set *_ladspa, int sample_rate)
{
module = _module;
metadata = module->get_metadata_iface();
ladspa = _ladspa;
module->get_port_arrays(ins, outs, params);
activate_flag = true;
#if USE_DSSI
feedback_sender = NULL;
#endif
module->set_sample_rate(sample_rate);
module->post_instantiate();
}
float ladspa_instance::get_param_value(int param_no)
{
// XXXKF hack
if (param_no >= ladspa->param_count)
return 0;
return *params[param_no];
}
void ladspa_instance::set_param_value(int param_no, float value)
{
// XXXKF hack
if (param_no >= ladspa->param_count)
return;
*params[param_no] = value;
}
bool ladspa_instance::activate_preset(int bank, int program)
{
return false;
}
/// LADSPA run function - does set sample rate / activate logic when it's run first time after activation
void ladspa_instance::run(unsigned long SampleCount)
{
if (activate_flag)
{
module->activate();
activate_flag = false;
}
module->params_changed();
module->process_slice(0, SampleCount);
}
#if USE_DSSI
void ladspa_instance::run_synth(unsigned long SampleCount, snd_seq_event_t *Events, unsigned long EventCount)
{
if (activate_flag)
{
module->activate();
activate_flag = false;
}
module->params_changed();
uint32_t offset = 0;
for (uint32_t e = 0; e < EventCount; e++)
{
uint32_t timestamp = Events[e].time.tick;
if (timestamp != offset)
module->process_slice(offset, timestamp);
process_dssi_event(Events[e]);
offset = timestamp;
}
if (offset != SampleCount)
module->process_slice(offset, SampleCount);
}
#endif
char *ladspa_instance::configure(const char *key, const char *value)
{
#if USE_DSSI_GUI
if (!strcmp(key, "OSC:FEEDBACK_URI"))
{
const line_graph_iface *lgi = dynamic_cast<const line_graph_iface *>(metadata);
//if (!lgi)
// return NULL;
if (*value)
{
if (feedback_sender) {
delete feedback_sender;
feedback_sender = NULL;
}
feedback_sender = new dssi_feedback_sender(value, lgi);
feedback_sender->add_graphs(metadata->get_param_props(0), metadata->get_param_count());
}
else
{
if (feedback_sender) {
delete feedback_sender;
feedback_sender = NULL;
}
}
return NULL;
}
else
if (!strcmp(key, "OSC:UPDATE"))
{
if (feedback_sender)
feedback_sender->update();
return NULL;
}
else
if (!strcmp(key, "OSC:SEND_STATUS"))
{
if (!feedback_sender)
return NULL;
struct status_gatherer: public send_updates_iface
{
osc_inline_typed_strstream str;
void send_status(const char *key, const char *value)
{
str << key << value;
}
} sg;
int serial = atoi(value);
serial = module->send_status_updates(&sg, serial);
sg.str << (uint32_t)serial;
feedback_sender->client->send("/status_data", sg.str);
return NULL;
}
else
#endif
if (!strcmp(key, "ExecCommand"))
{
if (*value)
{
execute(atoi(value));
}
return NULL;
}
return module->configure(key, value);
}
template<class Module>
ladspa_plugin_metadata_set ladspa_wrapper<Module>::output;
#if USE_DSSI
/// Utility function: handle MIDI event (only handles a subset in this version)
void ladspa_instance::process_dssi_event(snd_seq_event_t &event)
{
switch(event.type) {
case SND_SEQ_EVENT_NOTEON:
module->note_on(event.data.note.channel, event.data.note.note, event.data.note.velocity);
break;
case SND_SEQ_EVENT_NOTEOFF:
module->note_off(event.data.note.channel, event.data.note.note, event.data.note.velocity);
break;
case SND_SEQ_EVENT_PGMCHANGE:
module->program_change(event.data.control.channel, event.data.control.value);
break;
case SND_SEQ_EVENT_CONTROLLER:
module->control_change(event.data.control.channel, event.data.control.param, event.data.control.value);
break;
case SND_SEQ_EVENT_PITCHBEND:
module->pitch_bend(event.data.control.channel, event.data.control.value);
break;
case SND_SEQ_EVENT_CHANPRESS:
module->channel_pressure(event.data.control.channel, event.data.control.value);
break;
}
}
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// LADSPA callbacks
/// LADSPA activate function (note that at this moment the ports are not set)
static void cb_activate(LADSPA_Handle Instance)
{
((ladspa_instance *)(Instance))->activate_flag = true;
}
/// LADSPA run function - does set sample rate / activate logic when it's run first time after activation
static void cb_run(LADSPA_Handle Instance, unsigned long SampleCount) {
((ladspa_instance *)(Instance))->run(SampleCount);
}
/// LADSPA port connection function
static void cb_connect(LADSPA_Handle Instance, unsigned long port, LADSPA_Data *DataLocation)
{
ladspa_instance *const mod = (ladspa_instance *)Instance;
int first_out = mod->ladspa->input_count;
int first_param = first_out + mod->ladspa->output_count;
int ladspa_port_count = first_param + mod->ladspa->param_count;
if ((int)port < first_out)
mod->ins[port] = DataLocation;
else if ((int)port < first_param)
mod->outs[port - first_out] = DataLocation;
else if ((int)port < ladspa_port_count) {
int i = port - first_param;
mod->params[i] = DataLocation;
*mod->params[i] = mod->metadata->get_param_props(i)->def_value;
}
}
/// LADSPA deactivate function
static void cb_deactivate(LADSPA_Handle Instance) {
((ladspa_instance *)(Instance))->module->deactivate();
}
/// LADSPA cleanup (delete instance) function
static void cb_cleanup(LADSPA_Handle Instance) {
delete ((ladspa_instance *)(Instance));
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// DSSI callbacks
#if USE_DSSI
/// DSSI "run synth" function, same as run() except it allows for event delivery
static void cb_run_synth(LADSPA_Handle Instance, unsigned long SampleCount,
snd_seq_event_t *Events, unsigned long EventCount) {
((ladspa_instance *)(Instance))->run_synth(SampleCount, Events, EventCount);
}
/// DSSI configure function (named properties)
static char *cb_configure(LADSPA_Handle Instance,
const char *Key,
const char *Value)
{
return ((ladspa_instance *)(Instance))->configure(Key, Value);
}
/// DSSI get program descriptor function; for 0, it returns the default program (from parameter properties table), for others, it uses global or user preset
static const DSSI_Program_Descriptor *cb_get_program(LADSPA_Handle Instance, unsigned long index)
{
ladspa_plugin_metadata_set *ladspa = ((ladspa_instance *)(Instance))->ladspa;
if (index > ladspa->presets->size())
return NULL;
if (index)
return &(*ladspa->preset_descs)[index - 1];
return &ladspa->dssi_default_program;
}
/// DSSI select program function; for 0, it sets the defaults, for others, it sets global or user preset
static void cb_select_program(LADSPA_Handle Instance, unsigned long Bank, unsigned long Program)
{
ladspa_instance *mod = (ladspa_instance *)Instance;
ladspa_plugin_metadata_set *ladspa = mod->ladspa;
unsigned int no = (Bank << 7) + Program - 1;
// printf("no = %d presets = %p:%d\n", no, presets, presets->size());
if (no == -1U) {
int rpc = ladspa->param_count;
for (int i =0 ; i < rpc; i++)
*mod->params[i] = mod->metadata->get_param_props(i)->def_value;
return;
}
if (no >= ladspa->presets->size())
return;
plugin_preset &p = (*ladspa->presets)[no];
// printf("activating preset %s\n", p.name.c_str());
p.activate(mod);
}
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
ladspa_plugin_metadata_set::ladspa_plugin_metadata_set()
{
metadata = NULL;
memset(&descriptor, 0, sizeof(descriptor));
#if USE_DSSI
presets = NULL;
preset_descs = NULL;
memset(&descriptor_for_dssi, 0, sizeof(descriptor_for_dssi));
memset(&dssi_descriptor, 0, sizeof(dssi_descriptor));
#endif
}
void ladspa_plugin_metadata_set::prepare(const plugin_metadata_iface *md, LADSPA_Handle (*cb_instantiate)(const struct _LADSPA_Descriptor * Descriptor, unsigned long sample_rate))
{
metadata = md;
input_count = md->get_input_count();
output_count = md->get_output_count();
param_count = md->get_param_count(); // XXXKF ladspa_instance<Module>::real_param_count();
const ladspa_plugin_info &plugin_info = md->get_plugin_info();
descriptor.UniqueID = plugin_info.unique_id;
descriptor.Label = plugin_info.label;
descriptor.Name = strdup((std::string(plugin_info.name) + " LADSPA").c_str());
descriptor.Maker = plugin_info.maker;
descriptor.Copyright = plugin_info.copyright;
descriptor.Properties = md->is_rt_capable() ? LADSPA_PROPERTY_HARD_RT_CAPABLE : 0;
descriptor.PortCount = input_count + output_count + param_count;
descriptor.PortNames = new char *[descriptor.PortCount];
descriptor.PortDescriptors = new LADSPA_PortDescriptor[descriptor.PortCount];
descriptor.PortRangeHints = new LADSPA_PortRangeHint[descriptor.PortCount];
int i;
for (i = 0; i < input_count + output_count; i++)
{
LADSPA_PortRangeHint &prh = ((LADSPA_PortRangeHint *)descriptor.PortRangeHints)[i];
((int *)descriptor.PortDescriptors)[i] = i < input_count ? LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO
: LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO;
prh.HintDescriptor = 0;
((const char **)descriptor.PortNames)[i] = md->get_port_names()[i];
}
for (; i < input_count + output_count + param_count; i++)
{
LADSPA_PortRangeHint &prh = ((LADSPA_PortRangeHint *)descriptor.PortRangeHints)[i];
const parameter_properties &pp = *md->get_param_props(i - input_count - output_count);
((int *)descriptor.PortDescriptors)[i] =
LADSPA_PORT_CONTROL | (pp.flags & PF_PROP_OUTPUT ? LADSPA_PORT_OUTPUT : LADSPA_PORT_INPUT);
prh.HintDescriptor = LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW;
((const char **)descriptor.PortNames)[i] = pp.name;
prh.LowerBound = pp.min;
prh.UpperBound = pp.max;
switch(pp.flags & PF_TYPEMASK) {
case PF_BOOL:
prh.HintDescriptor |= LADSPA_HINT_TOGGLED;
prh.HintDescriptor &= ~(LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW);
break;
case PF_INT:
case PF_ENUM:
prh.HintDescriptor |= LADSPA_HINT_INTEGER;
break;
default: {
int defpt = (int)(100 * (pp.def_value - pp.min) / (pp.max - pp.min));
if ((pp.flags & PF_SCALEMASK) == PF_SCALE_LOG)
defpt = (int)(100 * log(pp.def_value / pp.min) / log(pp.max / pp.min));
if (defpt < 12)
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_MINIMUM;
else if (defpt < 37)
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_LOW;
else if (defpt < 63)
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_MIDDLE;
else if (defpt < 88)
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_HIGH;
else
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_MAXIMUM;
}
}
if (pp.def_value == 0 || pp.def_value == 1 || pp.def_value == 100 || pp.def_value == 440 ) {
prh.HintDescriptor &= ~LADSPA_HINT_DEFAULT_MASK;
if (pp.def_value == 1)
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_1;
else if (pp.def_value == 100)
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_100;
else if (pp.def_value == 440)
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_440;
else
prh.HintDescriptor |= LADSPA_HINT_DEFAULT_0;
}
switch(pp.flags & PF_SCALEMASK) {
case PF_SCALE_LOG:
prh.HintDescriptor |= LADSPA_HINT_LOGARITHMIC;
break;
}
}
descriptor.ImplementationData = this;
descriptor.instantiate = cb_instantiate;
descriptor.connect_port = cb_connect;
descriptor.activate = cb_activate;
descriptor.run = cb_run;
descriptor.run_adding = NULL;
descriptor.set_run_adding_gain = NULL;
descriptor.deactivate = cb_deactivate;
descriptor.cleanup = cb_cleanup;
prepare_dssi();
}
void ladspa_plugin_metadata_set::prepare_dssi()
{
#if USE_DSSI
const ladspa_plugin_info &plugin_info = metadata->get_plugin_info();
memcpy(&descriptor_for_dssi, &descriptor, sizeof(descriptor));
descriptor_for_dssi.Name = strdup((std::string(plugin_info.name) + " DSSI").c_str());
memset(&dssi_descriptor, 0, sizeof(dssi_descriptor));
dssi_descriptor.DSSI_API_Version = 1;
dssi_descriptor.LADSPA_Plugin = &descriptor_for_dssi;
dssi_descriptor.configure = cb_configure;
dssi_descriptor.get_program = cb_get_program;
dssi_descriptor.select_program = cb_select_program;
if (metadata->get_midi())
dssi_descriptor.run_synth = cb_run_synth;
presets = new std::vector<plugin_preset>;
preset_descs = new std::vector<DSSI_Program_Descriptor>;
preset_list plist_tmp, plist;
plist.load_defaults(true);
plist_tmp.load_defaults(false);
plist.presets.insert(plist.presets.end(), plist_tmp.presets.begin(), plist_tmp.presets.end());
// XXXKF this assumes that plugin name in preset is case-insensitive equal to plugin label
// if I forget about this, I'll be in a deep trouble
dssi_default_program.Bank = 0;
dssi_default_program.Program = 0;
dssi_default_program.Name = "default";
int pos = 1;
for (unsigned int i = 0; i < plist.presets.size(); i++)
{
plugin_preset &pp = plist.presets[i];
if (strcasecmp(pp.plugin.c_str(), descriptor.Label))
continue;
DSSI_Program_Descriptor pd;
pd.Bank = pos >> 7;
pd.Program = pos++;
pd.Name = pp.name.c_str();
preset_descs->push_back(pd);
presets->push_back(pp);
}
#endif
}
ladspa_plugin_metadata_set::~ladspa_plugin_metadata_set()
{
delete []descriptor.PortNames;
delete []descriptor.PortDescriptors;
delete []descriptor.PortRangeHints;
#if USE_DSSI
if (presets)
presets->clear();
if (preset_descs)
preset_descs->clear();
delete presets;
delete preset_descs;
#endif
}
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if USE_LV2
// instantiate descriptor templates
template<class Module> LV2_Descriptor calf_plugins::lv2_wrapper<Module>::descriptor;
template<class Module> LV2_Calf_Descriptor calf_plugins::lv2_wrapper<Module>::calf_descriptor;
template<class Module> LV2_State_Interface calf_plugins::lv2_wrapper<Module>::state_iface;
extern "C" {
const LV2_Descriptor *lv2_descriptor(uint32_t index)
{
#define PER_MODULE_ITEM(name, isSynth, jackname) if (!(index--)) return &lv2_wrapper<name##_audio_module>::get().descriptor;
#include <calf/modulelist.h>
return NULL;
}
};
#endif
#if USE_LADSPA
extern "C" {
const LADSPA_Descriptor *ladspa_descriptor(unsigned long Index)
{
#define PER_MODULE_ITEM(name, isSynth, jackname) if (!isSynth && !(Index--)) return &ladspa_wrapper<name##_audio_module>::get().descriptor;
#include <calf/modulelist.h>
return NULL;
}
};
#if USE_DSSI
extern "C" {
const DSSI_Descriptor *dssi_descriptor(unsigned long Index)
{
#define PER_MODULE_ITEM(name, isSynth, jackname) if (!(Index--)) return &calf_plugins::ladspa_wrapper<name##_audio_module>::get().dssi_descriptor;
#include <calf/modulelist.h>
return NULL;
}
};
#endif
#endif
#if USE_JACK
extern "C" {
audio_module_iface *create_calf_plugin_by_name(const char *effect_name)
{
#define PER_MODULE_ITEM(name, isSynth, jackname) if (!strcasecmp(effect_name, jackname)) return new name##_audio_module;
#include <calf/modulelist.h>
return NULL;
}
}
#endif

View File

@@ -0,0 +1,220 @@
/* Calf DSP Library
* Generic polyphonic synthesizer framework.
*
* Copyright (C) 2007 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include <calf/synth.h>
using namespace dsp;
using namespace std;
void basic_synth::kill_note(int note, int vel, bool just_one)
{
for (list<dsp::voice *>::iterator it = active_voices.begin(); it != active_voices.end(); it++) {
// preserve sostenuto notes
if ((*it)->get_current_note() == note && !(sostenuto && (*it)->sostenuto)) {
(*it)->note_off(vel);
if (just_one)
return;
}
}
}
dsp::voice *basic_synth::give_voice()
{
if (active_voices.size() >= polyphony_limit)
{
dsp::voice *stolen = steal_voice();
if (stolen)
return stolen;
}
if (unused_voices.empty())
return alloc_voice();
else {
dsp::voice *v = unused_voices.top();
unused_voices.pop();
v->reset();
return v;
}
}
dsp::voice *basic_synth::steal_voice()
{
std::list<dsp::voice *>::iterator found = active_voices.end();
float priority = 10000;
//int idx = 0;
for(std::list<dsp::voice *>::iterator i = active_voices.begin(); i != active_voices.end(); i++)
{
//printf("Voice %d priority %f at %p\n", idx++, (*i)->get_priority(), *i);
if ((*i)->get_priority() < priority)
{
priority = (*i)->get_priority();
found = i;
}
}
//printf("Found: %p\n\n", *found);
if (found == active_voices.end())
return NULL;
(*found)->steal();
return NULL;
}
void basic_synth::trim_voices()
{
// count stealable voices
unsigned int count = 0;
for(std::list<dsp::voice *>::iterator i = active_voices.begin(); i != active_voices.end(); i++)
{
if ((*i)->get_priority() < 10000)
count++;
}
// printf("Count=%d limit=%d\n", count, polyphony_limit);
// steal any voices above polyphony limit
if (count > polyphony_limit) {
for (unsigned int i = 0; i < count - polyphony_limit; i++)
steal_voice();
}
}
void basic_synth::note_on(int note, int vel)
{
if (!vel) {
note_off(note, 0);
return;
}
bool perc = check_percussion();
dsp::voice *v = give_voice();
v->setup(sample_rate);
v->released = false;
v->sostenuto = false;
gate.set(note);
v->note_on(note, vel);
active_voices.push_back(v);
if (perc) {
percussion_note_on(note, vel);
}
}
void basic_synth::note_off(int note, int vel)
{
gate.reset(note);
if (!hold)
kill_note(note, vel, false);
}
#define for_all_voices(iter) for (std::list<dsp::voice *>::iterator iter = active_voices.begin(); iter != active_voices.end(); iter++)
void basic_synth::on_pedal_release()
{
for_all_voices(i)
{
int note = (*i)->get_current_note();
if (note < 0 || note > 127)
continue;
bool still_held = gate[note];
// sostenuto pedal released
if ((*i)->sostenuto && !sostenuto)
{
// mark note as non-sostenuto
(*i)->sostenuto = false;
// if key still pressed or hold pedal used, hold the note (as non-sostenuto so it can be released later by releasing the key or pedal)
// if key has been released and hold pedal is not depressed, release the note
if (!still_held && !hold)
(*i)->note_off(127);
}
else if (!hold && !still_held && !(*i)->released)
{
(*i)->released = true;
(*i)->note_off(127);
}
}
}
void basic_synth::control_change(int ctl, int val)
{
if (ctl == 64) { // HOLD controller
bool prev = hold;
hold = (val >= 64);
if (!hold && prev && !sostenuto) {
on_pedal_release();
}
}
if (ctl == 66) { // SOSTENUTO controller
bool prev = sostenuto;
sostenuto = (val >= 64);
if (sostenuto && !prev) {
// SOSTENUTO was pressed - move all notes onto sustain stack
for_all_voices(i) {
(*i)->sostenuto = true;
}
}
if (!sostenuto && prev) {
// SOSTENUTO was released - release all keys which were previously held
on_pedal_release();
}
}
if (ctl == 123 || ctl == 120) { // all notes off, all sounds off
if (ctl == 120) { // for "all sounds off", automatically release hold and sostenuto pedal
control_change(66, 0);
control_change(64, 0);
}
for_all_voices(i)
{
if (ctl == 123)
(*i)->note_off(127);
else
(*i)->steal();
}
}
if (ctl == 121) {
control_change(1, 0);
control_change(7, 100);
control_change(10, 64);
control_change(11, 127);
// release hold..hold2
for (int i = 64; i <= 69; i++)
control_change(i, 0);
}
}
void basic_synth::render_to(float (*output)[2], int nsamples)
{
// render voices, eliminate ones that aren't sounding anymore
for (list<dsp::voice *>::iterator i = active_voices.begin(); i != active_voices.end();) {
dsp::voice *v = *i;
v->render_to(output, nsamples);
if (!v->get_active()) {
i = active_voices.erase(i);
unused_voices.push(v);
continue;
}
i++;
}
}
basic_synth::~basic_synth()
{
while(!unused_voices.empty()) {
delete unused_voices.top();
unused_voices.pop();
}
for (list<voice *>::iterator i = active_voices.begin(); i != active_voices.end(); i++)
delete *i;
}

View File

@@ -0,0 +1,156 @@
/* Calf DSP Library
* Various utility functions.
* Copyright (C) 2007 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include <config.h>
#include <calf/osctl.h>
#include <calf/utils.h>
#include <stdio.h>
#include <sstream>
using namespace std;
using namespace osctl;
namespace calf_utils {
string encode_map(const dictionary &data)
{
osctl::string_buffer sb;
osc_stream<osctl::string_buffer> str(sb);
str << (uint32_t)data.size();
for(dictionary::const_iterator i = data.begin(); i != data.end(); i++)
{
str << i->first << i->second;
}
return sb.data;
}
void decode_map(dictionary &data, const string &src)
{
osctl::string_buffer sb(src);
osc_stream<osctl::string_buffer> str(sb);
uint32_t count = 0;
str >> count;
string tmp, tmp2;
data.clear();
for (uint32_t i = 0; i < count; i++)
{
str >> tmp;
str >> tmp2;
data[tmp] = tmp2;
}
}
std::string xml_escape(const std::string &src)
{
string dest;
for (size_t i = 0; i < src.length(); i++) {
// XXXKF take care of string encoding
if (src[i] < 0 || src[i] == '"' || src[i] == '<' || src[i] == '>' || src[i] == '&')
dest += "&"+i2s((uint8_t)src[i])+";";
else
dest += src[i];
}
return dest;
}
std::string to_xml_attr(const std::string &key, const std::string &value)
{
return " " + key + "=\"" + xml_escape(value) + "\"";
}
std::string load_file(const std::string &src)
{
std::string str;
FILE *f = fopen(src.c_str(), "rb");
#if 0
if (!f)
throw file_exception(src);
#endif
while(!feof(f))
{
char buffer[1024];
size_t len = fread(buffer, 1, sizeof(buffer), f);
#if 0
if (len < 0)
throw file_exception(src);
#endif
str += string(buffer, len);
}
fclose(f);
return str;
}
std::string i2s(int value)
{
char buf[32];
sprintf(buf, "%d", value);
return std::string(buf);
}
std::string f2s(double value)
{
stringstream ss;
ss << value;
return ss.str();
}
std::string ff2s(double value)
{
string s = f2s(value);
if (s.find('.') == string::npos)
s += ".0";
return s;
}
std::string indent(const std::string &src, const std::string &indent)
{
std::string dest;
size_t pos = 0;
do {
size_t epos = src.find("\n", pos);
if (epos == string::npos)
break;
dest += indent + src.substr(pos, epos - pos) + "\n";
pos = epos + 1;
} while(pos < src.length());
if (pos < src.length())
dest += indent + src.substr(pos);
return dest;
}
//////////////////////////////////////////////////////////////////////////////////
file_exception::file_exception(const std::string &f)
: message(strerror(errno))
, filename(f)
, container(filename + ":" + message)
{
text = container.c_str();
}
file_exception::file_exception(const std::string &f, const std::string &t)
: message(t)
, filename(f)
, container(filename + ":" + message)
{
text = container.c_str();
}
}

View File

@@ -0,0 +1,552 @@
/* Calf DSP Library
* Example audio modules - wavetable synthesizer
*
* Copyright (C) 2009 Krzysztof Foltman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include <config.h>
#if ENABLE_EXPERIMENTAL
#include <calf/giface.h>
#include <calf/modules_synths.h>
#include <iostream>
using namespace dsp;
using namespace calf_plugins;
wavetable_voice::wavetable_voice()
{
sample_rate = -1;
}
void wavetable_voice::set_params_ptr(wavetable_audio_module *_parent, int _srate)
{
parent = _parent;
params = parent->params;
sample_rate = _srate;
}
void wavetable_voice::reset()
{
note = -1;
}
void wavetable_voice::note_on(int note, int vel)
{
typedef wavetable_metadata md;
this->note = note;
velocity = vel / 127.0;
amp.set(1.0);
for (int i = 0; i < OscCount; i++) {
oscs[i].reset();
oscs[i].set_freq(note_to_hz(note, 0), sample_rate);
last_oscshift[i] = 0;
}
int cr = sample_rate / BlockSize;
for (int i = 0; i < EnvCount; i++) {
envs[i].set(0.01, 0.1, 0.5, 1, cr);
envs[i].note_on();
}
float modsrc[wavetable_metadata::modsrc_count] = { 1, velocity, parent->inertia_pressure.get_last(), parent->modwheel_value, envs[0].value, envs[1].value, envs[2].value};
parent->calculate_modmatrix(moddest, md::moddest_count, modsrc);
calc_derived_dests();
float oscshift[2] = { moddest[md::moddest_o1shift], moddest[md::moddest_o2shift] };
memcpy(last_oscshift, oscshift, sizeof(oscshift));
memcpy(last_oscamp, cur_oscamp, sizeof(cur_oscamp));
}
void wavetable_voice::note_off(int vel)
{
for (int i = 0; i < EnvCount; i++)
envs[i].note_off();
}
void wavetable_voice::steal()
{
}
void wavetable_voice::render_block()
{
typedef wavetable_metadata md;
const float step = 1.f / BlockSize;
float s = 0.001;
float scl[EnvCount];
int espc = md::par_eg2attack - md::par_eg1attack;
for (int j = 0; j < EnvCount; j++) {
int o = j*espc;
envs[j].set(*params[md::par_eg1attack + o] * s, *params[md::par_eg1decay + o] * s, *params[md::par_eg1sustain + o], *params[md::par_eg1release + o] * s, sample_rate / BlockSize, *params[md::par_eg1fade + o] * s);
scl[j] = dsp::lerp(1.f, velocity, *params[md::par_eg1velscl + o]);;
}
for (int i = 0; i < EnvCount; i++)
envs[i].advance();
float modsrc[wavetable_metadata::modsrc_count] = { 1, velocity, parent->inertia_pressure.get_last(), parent->modwheel_value, envs[0].value, envs[1].value, envs[2].value};
parent->calculate_modmatrix(moddest, md::moddest_count, modsrc);
calc_derived_dests();
int ospc = md::par_o2level - md::par_o1level;
for (int j = 0; j < OscCount; j++) {
oscs[j].tables = parent->tables[(int)*params[md::par_o1wave + j * ospc]];
oscs[j].set_freq(note_to_hz(note, *params[md::par_o1transpose + j * ospc] * 100+ *params[md::par_o1detune + j * ospc] + moddest[md::moddest_o1detune]), sample_rate);
}
float oscshift[2] = { moddest[md::moddest_o1shift], moddest[md::moddest_o2shift] };
float osstep[2] = { (oscshift[0] - last_oscshift[0]) * step, (oscshift[1] - last_oscshift[1]) * step };
float oastep[2] = { (cur_oscamp[0] - last_oscamp[0]) * step, (cur_oscamp[1] - last_oscamp[1]) * step };
for (int i = 0; i < BlockSize; i++) {
float value = 0.f;
for (int j = 0; j < OscCount; j++) {
float o = last_oscshift[j] * 0.01;
value += last_oscamp[j] * oscs[j].get(dsp::clip(fastf2i_drm((o + *params[md::par_o1offset + j * ospc]) * 127.0 * 256), 0, 127 * 256));
last_oscshift[j] += osstep[j];
last_oscamp[j] += oastep[j];
}
output_buffer[i][0] = output_buffer[i][1] = value;
}
if (envs[0].stopped())
released = true;
memcpy(last_oscshift, oscshift, sizeof(oscshift));
memcpy(last_oscamp, cur_oscamp, sizeof(cur_oscamp));
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
static inline float sincl(float x, float clip)
{
if (fabs(x) > clip)
return 0;
return sin(M_PI * x);
}
static inline float blip(float x, float center, float range)
{
if (x < center - range || x > center + range)
return 0;
return 1 - fabs(x - center)/range;
}
static void interpolate_wt(int16_t table[129][256], int step)
{
for (int i = 0; i < 128; i++)
{
if (!(i % step))
continue;
int prev = i - i % step;
int next = prev + step;
for (int j = 0; j < 256; j++)
{
table[i][j] = table[prev][j] + (i - prev) * (table[next][j] - table[prev][j]) / step;
}
}
}
wavetable_audio_module::wavetable_audio_module()
: mod_matrix_impl(mod_matrix_data, &mm_metadata)
, inertia_cutoff(1)
, inertia_pitchbend(1)
, inertia_pressure(64)
{
panic_flag = false;
modwheel_value = 0.;
for (int i = 0; i < 129; i += 8)
{
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
int harm = 1 + 2 * (i / 8);
float ii = i / 128.0;
float rezo1 = sin(harm * ph) * sin(ph);
float rezo2 = sin((harm+1) * ph) * sin(ph * 2);
float rezo3 = sin((harm+3) * ph) * sin(ph * 4);
float rezo = (rezo1 + rezo2 + rezo3) / 3;
float v = (sin (ph) + ii * ii * rezo) / 2;
tables[0][i][j] = 32767 * v;
}
}
interpolate_wt(tables[0], 8);
for (int i = 0; i < 129; i += 4)
{
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
int harm = 1 + (i / 4);
float ii = i / 128.0;
float h = sin(harm * ph);
float rezo1 = h * sin(ph);
float rezo2 = h * sin(ph * 2)/2;
float rezo3 = h * sin(ph * 3)/3;
float rezo4 = h * sin(ph * 4)/4;
float rezo5 = h * sin(ph * 5)/5;
float rezo = (rezo1 + rezo2 + rezo3 + rezo4 + rezo5) / 3;
float v = sin (ph + ii * rezo);
tables[1][i][j] = 32767 * v;
}
}
interpolate_wt(tables[1], 4);
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
float ii = (i & ~3) / 128.0;
float ii2 = ((i & ~3) + 4) / 128.0;
float peak = (32 * ii);
float rezo1 = sin(floor(peak) * ph);
float rezo2 = sin(floor(peak + 1) * ph);
float widener = (0.5 + 0.3 * sin(ph) + 0.2 * sin (3 * ph));
float v1 = 0.5 * sin (ph) + 0.5 * ii * ii * rezo1 * widener;
float v2 = 0.5 * sin (ph) + 0.5 * ii2 * ii2 * rezo2 * widener;
tables[wavetable_metadata::wt_rezo][i][j] = 32767 * lerp(v1, v2, (i & 3) / 4.0);
}
}
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
float ii = i / 128.0;
float v = (sin(ph) + ii * sin(ph + 2 * ii * sin(ph)) + ii * ii * sin(ph + 6 * ii * ii * sin(6 * ph)) + ii * ii * ii * ii * sin(ph + 11 * ii * ii * ii * ii * sin(11 * ph))) / 4;
tables[wavetable_metadata::wt_metal][i][j] = 32767 * v;
}
}
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
float ii = i / 128.0;
float v = (sin(ph) + ii * sin(ph - 3 * ii * sin(ph)) + ii * ii * sin(5 * ph - 5 * ii * ii * ii * ii * sin(11 * ph))) / 3;
tables[wavetable_metadata::wt_bell][i][j] = 32767 * v;
}
}
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
float ii = i / 128.0;
//float v = (sin(ph) + ii * sin(ph + 2 * ii * sin(ph)) + ii * ii * sin(ph + 3 * ii * ii * sin(3 * ph)) + ii * ii * ii * sin(ph + 5 * ii * ii * ii * sin(5 * ph))) / 4;
float v = (sin(ph) + sin(ph - 3 * sin(ii * 5 - 2) * sin(ph)) + sin(ii * 4 - 1.3) * sin(5 * ph + 3 * ii * ii * sin(6 * ph))) / 3;
tables[wavetable_metadata::wt_blah][i][j] = 32767 * v;
}
}
for (int i = 0; i < 256; i++)
{
tables[wavetable_metadata::wt_pluck][128][i] = (i < 128) ? 32000 * fabs(sin(i / 32.0 * M_PI) * sin(i / 13.0 * M_PI) * sin(i / 19.0 * M_PI)) : 0;
}
for (int i = 127; i >= 0; i--)
{
int16_t *parent = tables[wavetable_metadata::wt_pluck][i + 1];
float damp = 0.05;
for (int j = 0; j < 256; j++)
{
tables[wavetable_metadata::wt_pluck][i][j] = (1 - 2*damp) * parent[j] + damp * parent[(j+1)&255] + damp * parent[(j+2)&255];// + 0.1 * parent[(j-1)&255]+ 0.1 * parent[(j-2)&255];
}
}
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j / 128.0 - 1.0;
float ii = i / 128.0;
float v = sincl(ph * (1 + 15 * ii), 1);
tables[wavetable_metadata::wt_stretch][i][j] = 32767 * v;
}
}
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j / 128.0 - 1.0;
float ii = i / 128.0;
float v = sincl(ph * (1 + 15 * ii), 4) * sincl(j / 256.0, 1);
tables[wavetable_metadata::wt_stretch2][i][j] = 32000 * v;
}
}
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j / 128.0 - 1.0;
float ii = i / 128.0;
float w = sincl(ph * (1 + 15 * ii), 4);
float v = pow(w, 9) * sincl(j / 256.0, 1);
tables[wavetable_metadata::wt_hardsync][i][j] = 32000 * v;
}
}
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j / 128.0 - 1.0;
float ii = i / 128.0;
float w = sincl(ph * (1 + 31 * ii), 3);
float v = pow(w, 5) * sincl(j / 256.0, 1);
tables[wavetable_metadata::wt_hardsync2][i][j] = 32000 * v;
}
}
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j / 128.0 - 1.0;
float ii = i / 128.0;
float w = sincl(ph * ph * (1 + 15 * ii), 2);
float v = pow(w, 4) * sincl(j / 256.0, 1);
tables[wavetable_metadata::wt_softsync][i][j] = 32000 * v;
}
}
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
float ii = i / 128.0;
float v = (sin(ph) + ii * sin(ph - 3 * ii * sin(ph)) + ii * ii * ii * sin(7 * ph - 2 * ii * ii * ii * ii * sin(13 * ph))) / 3;
tables[wavetable_metadata::wt_bell2][i][j] = 32767 * v;
}
}
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
float ii = i / 128.0;
float v = (sin(ph) + ii * sin(ph - 3 * ii * sin(ph)) + ii * ii * ii * sin(9 * ph - ii * ii * sin(11 * ph))) / 3;
tables[wavetable_metadata::wt_bell3][i][j] = 32767 * v;
}
}
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
float ii = i / 128.0;
float v = (sin(ph + ii * sin(ph - 3 * ii * sin(ph) + ii * ii * ii * sin(5 * ph - ii * ii * sin(7 * ph)))));
tables[wavetable_metadata::wt_tine][i][j] = 32767 * v;
}
}
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
float ii = i / 128.0;
float v = (sin(ph + ii * sin(ph - 2 * ii * sin(ph) + ii * ii * ii * sin(3 * ph - ii * ii * sin(4 * ph)))));
tables[wavetable_metadata::wt_tine2][i][j] = 32767 * v;
}
}
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
float ph2 = j / 128.0 - 1;
float ii = i / 128.0;
float w = sincl(ph2 * (1 + 7 * ii * ii), 4) * pow(sincl(j / 256.0, 1), 2);
float v = sin(ph + ii * sin(ph - 2 * ii * w));
tables[wavetable_metadata::wt_clav][i][j] = 32767 * v;
}
}
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
float ph2 = j / 128.0 - 1;
float ii = i / 128.0;
float w = sincl(ph2 * (1 + 7 * ii * ii), 6) * sincl(j / 256.0, 1);
float v = sin(ph + ii * sin(3 * ph - 2 * ii * w));
tables[wavetable_metadata::wt_clav2][i][j] = 32767 * v;
}
}
/*
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
float ph2 = j / 128.0 - 1;
float ii = i / 128.0;
float w = sincl(ph2 * (1 + 7 * ii * ii), 6) * pow(sincl(j / 256.0, 1), 1);
float v = sin(ph + ii * ii * ii * sin(3 * ph - ii * ii * ii * w));
tables[wavetable_metadata::wt_gtr][i][j] = 32767 * v;
}
}
*/
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
float ii = i / 128.0;
float ii2 = ii;
float w = pow(sincl(j / 256.0, 1), 1);
float v = sin(ph + ii2 * ii2 * ii2 * sin(3 * ph - ii2 * ii2 * ii2 * w * sin(ph + sin(3 * ph) + ii * sin(11 * ph) + ii * ii * sin(25 * ph))));
tables[wavetable_metadata::wt_gtr][i][j] = 32767 * v;
}
}
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
float ii = i / 128.0;
float ii2 = dsp::clip(ii - 0.5, 0.0, 1.0);
float w = pow(sincl(j / 256.0, 1), 1);
float v = sin(ph + ii * ii * ii * sin(3 * ph - ii * ii * ii * w * sin(ph + sin(3 * ph + ii2 * sin(13 * ph)))));
tables[wavetable_metadata::wt_gtr2][i][j] = 32767 * v;
}
}
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
float ii = i / 128.0;
float ii2 = dsp::clip(2 * (ii - 0.5), 0.0, 1.0);
//float w = sincl(ph2 * (1 + 15 * ii2 * ii2), 4) * pow(sincl(j / 256.0, 1), 1);
float w = pow(sincl(j / 256.0, 1), 1);
float v = sin(ph + ii * sin(3 * ph - ii * w * sin(ph + sin(3 * ph + 0.5 * ii2 * sin(13 * ph + 0.5 * sin(4 * ph))))));
tables[wavetable_metadata::wt_gtr3][i][j] = 32767 * v;
}
}
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
float ii = i / 128.0;
float ii2 = dsp::clip(2 * (ii - 0.5), 0.0, 1.0);
//float w = sincl(ph2 * (1 + 15 * ii2 * ii2), 4) * pow(sincl(j / 256.0, 1), 1);
float w = pow(sincl(j / 256.0, 1), 1);
float v = sin(ph + ii * sin(3 * ph - ii * w * sin(2 * ph + sin(5 * ph + 0.5 * ii2 * sin(13 * ph + 0.5 * sin(4 * ph))))));
tables[wavetable_metadata::wt_gtr4][i][j] = 32767 * v;
}
}
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
float ii = i / 128.0;
float ii2 = dsp::clip((ii - 0.25)/0.75, 0.0, 1.0);
//float w = sincl(ph2 * (1 + 15 * ii2 * ii2), 4) * pow(sincl(j / 256.0, 1), 1);
float w = pow(sincl(j / 256.0, 1), 3);
float v = sin(ph + (ii + 0.05) * sin(3 * ph - 2 * ii * w * sin(5 * ph + sin(7 * ph + 0.5 * ii2 * sin(13 * ph + 0.5 * sin(11 * ph))))));
tables[wavetable_metadata::wt_gtr5][i][j] = 32767 * v;
}
}
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
float ii = i / 128.0;
float w = pow(sincl(2 * (j / 256.0), 2), 3);
float v = sin(ph + (ii + 0.05) * sin(7 * ph - 2 * ii * w * sin(11 * ph)));
tables[wavetable_metadata::wt_reed][i][j] = 32767 * v;
}
}
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
float ii = i / 128.0;
float ii2 = dsp::clip((ii - 0.25)/0.75, 0.0, 1.0);
float ii3 = dsp::clip((ii - 0.5)/0.5, 0.0, 1.0);
float v = sin(ph + (ii + 0.05) * sin(ii * sin(2 * ph) - 2 * ii2 * sin(2 * ph + ii2 * sin(3 * ph)) + 3 * ii3 * sin(3 * ph)));
tables[wavetable_metadata::wt_reed2][i][j] = 32767 * v;
}
}
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
float ii = i / 128.0;
float mod = 0;
for (int k = 0; k < 13; k++)
{
mod += blip(i, k * 10, 30) * sin (ph * (5 + 3 * k) + ii * cos(ph * (2 + 2 * k)));
}
float v = sin(ph + ii * mod);
tables[wavetable_metadata::wt_silver][i][j] = 32767 * v;
}
}
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
float ii = i / 128.0;
float mod = 0;
for (int k = 0; k < 16; k++)
{
mod += 2 * blip(i, k * 8, k * 4 + 10) * cos (ph * (k + 1));
}
float v = sin(ph + ii * mod);
tables[wavetable_metadata::wt_brass][i][j] = 32767 * v;
}
}
for (int i = 0; i < 129; i++)
{
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
float ii = i / 128.0;
float mod = 0;
for (int k = 0; k < 16; k++)
{
mod += 2 * blip(i, k * 8, 16) * cos (ph * (2 * k + 1));
}
float v = (sin(ph + ii * mod) + ii * sin(2 * ph + ii * mod)) / 2;
tables[wavetable_metadata::wt_multi][i][j] = 32767 * v;
}
}
for (int i = 0; i < 129; i ++)
{
float h = 1 + i / 16.0;
for (int j = 0; j < 256; j++)
{
float ph = j * 2 * M_PI / 256;
float v = sin(ph), tv = 1;
for (int k = 1; k < 24; k++) {
float amp = blip(i, k * 6, 20) / k;
v += amp * sin((k + 1) * ph + h * sin(ph));
tv += amp;
}
tables[wavetable_metadata::wt_multi2][i][j] = 32767 * v / tv;
}
}
}
void wavetable_audio_module::channel_pressure(int /*channel*/, int value)
{
inertia_pressure.set_inertia(value * (1.0 / 127.0));
}
#endif

View File

@@ -0,0 +1,637 @@
/*
Amp.cc
Copyright 2003-7
Tim Goetze <tim@quitte.de>
David Yeh <dtyeh@ccrma.stanford.edu> (Tone Stack in TS models)
http://quitte.de/dsp/
Tube amplifier models
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
#include "basics.h"
#include <stdio.h>
#include "Amp.h"
#include "Descriptor.h"
void
AmpStub::init (bool adjust_downsampler)
{
dc_blocker.set_f (10. / fs);
/* going a bit lower than nominal with fc */
double f = .7 * M_PI / OVERSAMPLE;
/* construct the upsampler filter kernel */
DSP::sinc (f, up.c, FIR_SIZE);
DSP::kaiser<DSP::apply_window> (up.c, FIR_SIZE, 6.4);
/* copy upsampler filter kernel for downsampler, make sum */
double s = 0;
for (int i = 0; i < up.n; ++i)
down.c[i] = up.c[i],
s += up.c[i];
s = 1 / s;
/* scale downsampler kernel for unity gain + correction for transfer */
double t = adjust_downsampler ?
s / max (fabs (tube.clip[0].value), fabs (tube.clip[1].value)) : s;
for (int i = 0; i < down.n; ++i)
down.c[i] *= t;
/* scale upsampler kernel for unity gain */
s *= OVERSAMPLE;
for (int i = 0; i < up.n; ++i)
up.c[i] *= s;
}
/* //////////////////////////////////////////////////////////////////////// */
void
AmpIII::init()
{
this->AmpStub::init (false);
/* need to filter out dc before the power amp stage, which is running at
* the oversampled rate */
dc_blocker.set_f (10. / (fs * OVERSAMPLE));
DSP::RBJ::LoShelve (200 / fs, .2, -3, filter.a, filter.b);
}
template <sample_func_t F, int OVERSAMPLE>
void
AmpIII::one_cycle (int frames)
{
sample_t * s = ports[0];
sample_t gain = getport(1);
sample_t temp = getport(2) * tube.scale;
drive = getport(3) * .5;
i_drive = 1 / (1 - drive);
sample_t * d = ports[4];
*ports[5] = OVERSAMPLE;
double g = current.g;
current.g = max (gain < 1 ? gain : exp2 (gain - 1), .000001);
current.g *= tube.scale / fabs (tube.transfer (temp));
/* recursive fade to prevent zipper noise from the 'gain' knob */
if (g == 0) g = current.g;
double one_over_n = frames > 0 ? 1. / frames : 1;
double gf = pow (current.g / g, one_over_n);
for (int i = 0; i < frames; ++i)
{
register sample_t a = s[i];
a = g * tube.transfer (a * temp);
a = filter.process (a + normal);
a = tube.transfer_clip (up.upsample (a));
a = power_transfer (dc_blocker.process (a));
a = down.process (a);
for (int o = 1; o < OVERSAMPLE; ++o)
down.store (
power_transfer (
dc_blocker.process (
normal + tube.transfer_clip (up.pad (o)))));
F (d, i, a, adding_gain);
g *= gf;
}
current.g = g;
}
/* //////////////////////////////////////////////////////////////////////// */
PortInfo
AmpIII::port_info [] =
{
{
"in",
INPUT | AUDIO,
{BOUNDED, -1, 1}
}, {
"gain",
INPUT | CONTROL,
{BOUNDED | DEFAULT_1, 0, 10}
}, {
"temperature",
INPUT | CONTROL,
{BOUNDED | DEFAULT_MID, 0.005, 1}
}, {
"drive",
INPUT | CONTROL,
{BOUNDED | DEFAULT_MAX, 0.0001, 1} /* ^2 gives the nice drive */
}, {
"out",
OUTPUT | AUDIO,
{0}
}, {
"latency",
OUTPUT | CONTROL,
{0}
}
};
template <> void
Descriptor<AmpIII>::setup()
{
UniqueID = 1786;
Label = "AmpIII";
Properties = HARD_RT;
Name = CAPS "AmpIII - Tube amp";
Maker = "Tim Goetze <tim@quitte.de>";
Copyright = "GPL, 2002-7";
/* fill port info and vtable */
autogen();
}
/* //////////////////////////////////////////////////////////////////////// */
void
AmpIV::init()
{
this->AmpStub::init (false);
/* need to filter out dc before the power amp stage, which is running at
* the oversampled rate */
dc_blocker.set_f (10. / (fs * OVERSAMPLE));
tone.init (fs);
}
template <sample_func_t F, int OVERSAMPLE>
void
AmpIV::one_cycle (int frames)
{
double one_over_n = frames > 0 ? 1. / frames : 1;
sample_t * s = ports[0];
sample_t gain = getport(1);
sample_t temp = getport(2) * tube.scale;
tone.start_cycle (ports + 3, one_over_n);
drive = getport(7) * .5;
i_drive = 1 / (1 - drive);
sample_t * d = ports[8];
*ports[9] = OVERSAMPLE;
double g = current.g;
current.g = max (gain < 1 ? gain : exp2 (gain - 1), .000001);
current.g *= tube.scale / fabs (tube.transfer (temp));
/* recursive fade to prevent zipper noise from the 'gain' knob */
if (g == 0) g = current.g;
double gf = pow (current.g / g, one_over_n);
for (int i = 0; i < frames; ++i)
{
register sample_t a = s[i] + normal;
a = g * tube.transfer (a * temp);
a = tone.process (a);
a = tube.transfer_clip (up.upsample (a));
a = power_transfer (dc_blocker.process (a));
a = down.process (a);
for (int o = 1; o < OVERSAMPLE; ++o)
down.store (
power_transfer (
dc_blocker.process (
normal + tube.transfer_clip (up.pad (o)))));
F (d, i, a, adding_gain);
g *= gf;
}
current.g = g;
}
/* //////////////////////////////////////////////////////////////////////// */
PortInfo
AmpIV::port_info [] =
{
{
"in",
INPUT | AUDIO,
{BOUNDED, -1, 1}
}, {
"gain",
INPUT | CONTROL,
{BOUNDED | DEFAULT_1, 0, 10}
}, {
"temperature",
INPUT | CONTROL,
{BOUNDED | DEFAULT_MID, 0.005, 1}
}, {
"bass",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -20, 20}
}, {
"mid",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -20, 20}
}, {
"treble",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -20, 20}
}, {
"hi",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -20, 20}
}, {
"drive",
INPUT | CONTROL,
{BOUNDED | DEFAULT_MAX, 0.0001, 1} /* ^2 gives the nice drive */
}, {
"out",
OUTPUT | AUDIO,
{0}
}, {
"latency",
OUTPUT | CONTROL,
{0}
}
};
template <> void
Descriptor<AmpIV>::setup()
{
UniqueID = 1794;
Label = "AmpIV";
Properties = HARD_RT;
Name = CAPS "AmpIV - Tube amp + tone controls";
Maker = "Tim Goetze <tim@quitte.de>";
Copyright = "GPL, 2002-7";
/* fill port info and vtable */
autogen();
}
/* //////////////////////////////////////////////////////////////////////// */
void
AmpV::init()
{
this->AmpStub::init (false);
/* need to filter out dc before the power amp stage, which is running at
* the oversampled rate */
dc_blocker.set_f (10. / (fs * OVERSAMPLE));
DSP::RBJ::LoShelve (210. / fs, .2, -1, filter[0].a, filter[0].b);
DSP::RBJ::LoShelve (4200. / fs, 1.2, +6, filter[1].a, filter[1].b);
DSP::RBJ::LoShelve (420. / fs, .2, +2, filter[2].a, filter[2].b);
/* power supply cap */
for (int i = 0; i < 2; ++i)
DSP::RBJ::LP (10. / fs, .3, power_cap[i].a, power_cap[i].b);
}
template <sample_func_t F, int OVERSAMPLE>
void
AmpV::one_cycle (int frames)
{
sample_t * s = ports[0];
sample_t gain = getport(1);
if (*ports[2] != cut)
{
cut = getport(2);
DSP::RBJ::LoShelve (210. / fs, .2, cut, filter[0].a, filter[0].b);
}
if (*ports[3] != tone)
{
tone = getport(3);
double f = tone * tone * 8400 + 420;
double q = tone * .4 + .2;
double db = tone * 2 + 2;
DSP::RBJ::LoShelve (f / fs, q, db, filter[2].a, filter[2].b);
}
drive = getport(4) * .5;
i_drive = 1 / (1 - drive);
#define MAX_WATTS port_info[5].range.UpperBound
sample_t sag = (MAX_WATTS - getport(5)) / MAX_WATTS;
sag = .6 * sag * sag;
sample_t * d = ports[6];
*ports[7] = OVERSAMPLE;
double g = current.g;
current.g = max (gain < 1 ? gain : pow (20, gain - 1), .000001);
#if 0
if (++_turn & 127) == 0)
fprintf (stderr, "supply = %.3f sag = %.3f\n", supply, sag);
#endif
if (g == 0) g = current.g;
/* recursive fade to prevent zipper noise from the 'gain' knob */
double one_over_n = frames > 0 ? 1. / frames : 1;
double gf = pow (current.g / g, one_over_n);
for (int i = 0; i < frames; ++i)
{
register sample_t a = s[i];
register sample_t v = 3 - supply;
/* alternative curve: v = v * v * .1 + .1; */
v = v * v * .06 + .46;
a = filter[0].process (a + normal);
if (0)
a = filter[2].process (a);
a = g * (a + supply * .001);
a = v * tube.transfer_clip (up.upsample (a));
a = power_transfer (dc_blocker.process (a));
a = down.process (a);
a = filter[1].process (a - normal);
if (1)
a = filter[2].process (a + normal);
{
for (int o = 1; o < OVERSAMPLE; ++o)
down.store (
power_transfer (
dc_blocker.process (
normal + tube.transfer_clip (
up.pad (o)))));
}
F (d, i, a, adding_gain);
/* integrate for an approximation of cumulative output power */
supply += sag * fabs (a) + normal;
/* filter integrated power consumption */
for (int j = 0; j < 2; ++j)
supply = 0.9 * (power_cap[j].process (supply));
g *= gf;
normal = -normal;
}
current.g = g;
}
/* //////////////////////////////////////////////////////////////////////// */
PortInfo
AmpV::port_info [] =
{
{
"in",
INPUT | AUDIO,
{BOUNDED, -1, 1}
}, {
"gain",
INPUT | CONTROL,
{BOUNDED | DEFAULT_1, 0, 3}
}, {
"bass",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -9, 9}
}, {
"tone",
INPUT | CONTROL,
{BOUNDED | DEFAULT_MIN, 0, 1}
}, {
"drive",
INPUT | CONTROL,
{BOUNDED | DEFAULT_HIGH, 0.0001, 1} /* ^2 gives the nice drive */
}, {
"watts",
INPUT | CONTROL,
{BOUNDED | DEFAULT_MID, 5, 150}
}, {
"out",
OUTPUT | AUDIO,
{0}
}, {
"latency",
OUTPUT | CONTROL,
{0}
}
};
template <> void
Descriptor<AmpV>::setup()
{
UniqueID = 2587;
Label = "AmpV";
Properties = HARD_RT;
Name = CAPS "AmpV - Tube amp";
Maker = "Tim Goetze <tim@quitte.de>";
Copyright = "GPL, 2002-7";
/* fill port info and vtable */
autogen();
}
/* //////////////////////////////////////////////////////////////////////// */
void
AmpVTS::init()
{
this->AmpStub::init (false);
/* need to filter out dc before the power amp stage, which is running at
* the oversampled rate */
dc_blocker.set_f (10. / (fs * OVERSAMPLE));
/* power supply capacitance */
for (int i = 0; i < 2; ++i)
DSP::RBJ::LP (10. / fs, .3, power_cap[i].a, power_cap[i].b);
tonestack.init (fs);
}
template <sample_func_t F, int OVERSAMPLE>
void
AmpVTS::one_cycle (int frames)
{
sample_t * s = ports[0];
tonestack.start_cycle (ports + 1, 2);
sample_t gain = getport(2);
drive = getport(6) * .5;
i_drive = 1 / (1 - drive);
sample_t sag = 1 - max (0.0001, min (1, getport(7)));
sag = .6 * sag * sag; /* map to log space makes slider better */
sample_t * d = ports[8];
*ports[9] = OVERSAMPLE;
double g = current.g;
if (gain < 1)
current.g = max (gain, .001);
else
{
gain -= 1;
gain *= gain;
current.g = pow (10, gain);
}
/* recursive fade to prevent zipper noise from the 'gain' knob */
double one_over_n = frames > 0 ? 1. / frames : 1;
double gf = pow (current.g / g, one_over_n);
for (int i = 0; i < frames; ++i)
{
register double a = s[i];
register double v = 3 - supply;
v = v * v * .06 + .46;
a = tube.transfer (a);
a = tonestack.process (a + normal);
a = g * (a + supply * .001);
a = v * tube.transfer_clip (up.upsample (a));
a = power_transfer (dc_blocker.process (a));
a = down.process (a);
{
for (int o = 1; o < OVERSAMPLE; ++o)
down.store (
power_transfer (
dc_blocker.process (
normal + tube.transfer_clip (
up.pad (o)))));
}
F (d, i, a, adding_gain);
/* integrate for an approximation of cumulative output power */
supply += sag * fabs (a) + normal;
/* filter integrated power consumption */
for (int j = 0; j < 2; ++j)
supply = 0.9 * (power_cap[j].process (supply + normal));
g *= gf;
normal = -normal;
}
current.g = g;
}
/* //////////////////////////////////////////////////////////////////////// */
PortInfo
AmpVTS::port_info [] =
{
{
"in",
INPUT | AUDIO,
{BOUNDED, -1, 1}
}, {
"model",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0 | INTEGER, 0, 5} /* no way to set dyn at compile t */
}, {
"gain",
INPUT | CONTROL,
{BOUNDED | DEFAULT_HIGH, 0, 3}
}, {
"bass",
INPUT | CONTROL,
{BOUNDED | DEFAULT_MID, 0, 1}
}, {
"mid",
INPUT | CONTROL,
{BOUNDED | DEFAULT_1, 0, 1}
}, {
"treble",
INPUT | CONTROL,
{BOUNDED | DEFAULT_HIGH, 0, 1}
}, {
"drive",
INPUT | CONTROL,
{BOUNDED | DEFAULT_LOW, 0.0001, 1}
}, {
"watts",
INPUT | CONTROL,
{BOUNDED | DEFAULT_HIGH, 0.0001, 1}
}, {
"out",
OUTPUT | AUDIO,
{0}
}, {
"latency",
OUTPUT | CONTROL,
{0}
}
};
template <> void
Descriptor<AmpVTS>::setup()
{
UniqueID = 2592;
Label = "AmpVTS";
Properties = HARD_RT;
Name = CAPS "AmpVTS - Tube amp + Tone stack";
Maker = "David Yeh <dtyeh@ccrma.stanford.edu> & Tim Goetze <tim@quitte.de>";
Copyright = "GPL, 2002-7";
/* fill port info and vtable */
autogen();
}

View File

@@ -0,0 +1,377 @@
/*
Amp.h
Copyright 2002-9 Tim Goetze <tim@quitte.de>
http://quitte.de/dsp/
Oversampled tube amplifier emulation.
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
#ifndef _AMP_H_
#define _AMP_H_
#include "dsp/util.h"
#include "dsp/OnePole.h"
#include "dsp/BiQuad.h"
#include "dsp/TwelveAX7.h"
#include "dsp/Roessler.h"
#include "dsp/FIR.h"
#include "dsp/sinc.h"
#include "dsp/windows.h"
#include "dsp/RBJ.h"
#include "dsp/Eq.h"
#include "dsp/ToneStack.h"
class AmpStub
: public Plugin
{
public:
DSP::TwelveAX7_3 tube;
sample_t drive, i_drive;
struct {
/* gain (remember current setting and fade to port setting in run) */
double g;
/* should also do this for temperature to remove another potential
* source of zippering, but that would be overkill, at the cost of
* at least one pow() per block. */
} current;
/* input is hipass-filtered first */
DSP::OnePoleHP dc_blocker;
enum {
OVERSAMPLE = 8,
FIR_SIZE = 64,
};
/* antialias filters */
DSP::FIRUpsampler up;
DSP::FIR down;
AmpStub()
: up (FIR_SIZE, OVERSAMPLE),
down (FIR_SIZE, up.c)
{ }
void init (bool adjust_downsampler = false);
inline sample_t power_transfer (sample_t a)
{
return i_drive * (a - drive * fabs (a) * a);
}
};
/* /////////////////////////////////////////////////////////////////////// */
class PreampIII
: public AmpStub
{
public:
template <sample_func_t F, int OVERSAMPLE>
void one_cycle (int frames);
DSP::BiQuad filter;
public:
static PortInfo port_info[];
sample_t adding_gain;
void init();
void activate()
{
current.g = 1;
filter.reset();
up.reset();
down.reset();
dc_blocker.reset();
}
void run (int n)
{
one_cycle<store_func, OVERSAMPLE> (n);
}
void run_adding (int n)
{
one_cycle<adding_func, OVERSAMPLE> (n);
}
};
/* /////////////////////////////////////////////////////////////////////// */
class AmpIII
: public AmpStub
{
public:
template <sample_func_t F, int OVERSAMPLE>
void one_cycle (int frames);
DSP::BiQuad filter;
public:
static PortInfo port_info[];
sample_t adding_gain;
void init();
void activate()
{
current.g = 1;
up.reset();
down.reset();
dc_blocker.reset();
filter.reset();
}
void run (int n)
{
one_cycle<store_func, OVERSAMPLE> (n);
}
void run_adding (int n)
{
one_cycle<adding_func, OVERSAMPLE> (n);
}
};
/* /////////////////////////////////////////////////////////////////////// */
typedef struct
{float center, Q, adjust;}
PreampBand;
class ToneControls
{
public:
sample_t eq_gain[4];
DSP::Eq<4> eq;
static PreampBand bands[4];
public:
void init (double _fs);
void activate (sample_t **);
inline void
start_cycle (sample_t ** ports, double one_over_n)
{
for (int i = 0; i < 4; ++i)
{
if (*ports[i] == eq_gain[i])
{
eq.gf[i] = 1;
continue;
}
eq_gain[i] = *ports [i];
double want = get_band_gain (i, eq_gain[i]);
eq.gf[i] = pow (want / eq.gain[i], one_over_n);
}
}
double get_band_gain (int i, double g);
void set_band_gain (int i, float g);
inline sample_t process (sample_t x)
{
return eq.process (x);
}
};
/* /////////////////////////////////////////////////////////////////////// */
class PreampIV
: public PreampIII
{
public:
ToneControls tone;
template <sample_func_t F, int OVERSAMPLE>
void one_cycle (int frames);
public:
static PortInfo port_info[];
sample_t adding_gain;
void init();
void activate();
void run (int n)
{
one_cycle<store_func, OVERSAMPLE> (n);
}
void run_adding (int n)
{
one_cycle<adding_func, OVERSAMPLE> (n);
}
};
/* /////////////////////////////////////////////////////////////////////// */
class AmpIV
: public AmpStub
{
public:
ToneControls tone;
template <sample_func_t F, int OVERSAMPLE>
void one_cycle (int frames);
public:
static PortInfo port_info[];
sample_t adding_gain;
void init();
void activate()
{
current.g = 1;
tone.activate (ports + 3);
up.reset();
down.reset();
dc_blocker.reset();
}
void run (int n)
{
one_cycle<store_func, OVERSAMPLE> (n);
}
void run_adding (int n)
{
one_cycle<adding_func, OVERSAMPLE> (n);
}
};
/* /////////////////////////////////////////////////////////////////////// */
class AmpV
: public AmpStub
{
public:
template <sample_func_t F, int OVERSAMPLE>
void one_cycle (int frames);
DSP::BiQuad filter[3];
sample_t cut, tone;
/* supply voltage sag */
sample_t supply;
DSP::BiQuad power_cap[2];
public:
static PortInfo port_info[];
sample_t adding_gain;
void init();
void activate()
{
current.g = 1;
for (int i = 0; i < 2; ++i)
filter[i].reset(),
power_cap[i].reset();
up.reset();
down.reset();
dc_blocker.reset();
cut = 2;
supply = 0.;
tone = -1; /* causes initialisation of the filter at first cycle */
}
void run (int n)
{
one_cycle<store_func, OVERSAMPLE> (n);
}
void run_adding (int n)
{
one_cycle<adding_func, OVERSAMPLE> (n);
}
};
/* /////////////////////////////////////////////////////////////////////// */
class AmpVTS
: public AmpStub
{
public:
DSP::ToneStack tonestack;
template <sample_func_t F, int OVERSAMPLE>
void one_cycle (int frames);
sample_t cut, tone;
/* supply voltage sag */
sample_t supply;
DSP::BiQuad power_cap[2];
public:
static PortInfo port_info[];
sample_t adding_gain;
void init();
void activate()
{
current.g = 1;
for (int i = 0; i < 2; ++i)
power_cap[i].reset();
up.reset();
down.reset();
dc_blocker.reset();
cut = 2;
supply = 0.;
}
void run (int n)
{
one_cycle<store_func, OVERSAMPLE> (n);
}
void run_adding (int n)
{
one_cycle<adding_func, OVERSAMPLE> (n);
}
};
#endif /* _AMP_H_ */

View File

@@ -0,0 +1,206 @@
0.4.5
* Narrower plugin added
* fixed 'configure.py' to work with python3
* fixed Sin, Roessler and Lorenz gain smoothing on activation
0.4.4
0.4.3
* basics.h cleanup / comments
* minor Makefile cleanup
* comment cosmetics
* Eq and Eq2x2 per-band Q changed to 1.414 (= 1 octave)
* Eq lowest band default value fixed to read 0
* Niclas' fix for the bessel function implemented
* uninitialised plugin states eliminated thanks to Damon
* linker options for OSX added to the Makefile
0.4.2
* fixed the 'model' port index for AmpVTS in the RDF generator
0.4.1
* cleaned up Eq.h and Eq.cc (many g++ versions choke on the unused code
there)
* changed -O3 to -O2 in the g++ invocation
0.4.0
* ToneStack plugins, by David Yeh
* AmpV + Tone stack plugin, employing David Yeh's fine work
* comment cosmetics
* Amp* denormal protection fixed (or is it, Dave? ;)
* minor code cleanup in Amp.cc
* caps.rdf updated with plugin categories (thanks to Paul Winkler)
* caps.rdf Cabinet* RDF preset labels renamed
* AutoWah plugin
* DSP::RMS reworked, may affect Compress plugin
* DSP::Eq reworked for double precision and denormal protection
* ./configure.py checks SSE availability
* in case of SSE math denormal flush to zero activated for all plugins
* all plugins renamed C* .. instead of CAPS: ..
* Eq modified to play nice with ardour
* Eq2x2
* introduced the Plugin base class, collecting common traits (normal etc)
* getport() -- read access to control ports which is clamped to port bounds
and maps inf and nan to 0 as well
* all LADSPA_HINT_SAMPLE_RATE ports changed to *_LOGARITHMIC because
of broken implementations (no surprise given the vagueness of ladspa.h
regarding this matter) -- this means changed default parameters of the
affected ports, too
* VCO* "latency" output ports removed
* actual activate() call is deferred to first run() after activate()
in order to prevent inadvertent parameter smoothing sweeps during the first
block of audio after activation, this should fix all problems with ardour
(except those caused by denormals or invalid audio input)
* caps.rdf installed by 'make install'
* fixed a bug in tools/make-ps.py that caused the spectrum plots to
be inaccurate for multi-channel plugins
0.3.0
* TwelveAX7_3 changed to clip slightly early in the upper lobe
* Scape plugin added
* plugin names rewritten, prefixed with "CAPS:"
* new ChorusII, StereoChorusII plugins
* Chorus, StereoChorus relabeled, appended 'I' suffix
* new PhaserII plugin (great stuff if I may say so)
* Phaser relabeled, appended 'I' suffix
* new AmpV plugin, based on AmpIII, emulates compression and distortion
modulation through power supply shortcomings, plus lots of fine-tuning
and an additional biquad. We're getting there!
* all Preamp and Amp models fitted with a new 12AX7 model, linear
interpolation of a sample table obtained from spice simulation
0.2.4
* feedback default reverted to 0 for the Chorus units
* fixed Cabinet to switch to correct gain at 'model' control change
* fixed 'model' control in Cabinet to work with a broader range of hosts
* Cabinet name changed to CabinetI
* CabinetII plugin: Cabinet with 32nd order IIR filters, more fidelity
to the original frequency responses, supplied coefficients for 4 of the
most used sample rates
* applied the gcc-4 enabling patch
* SweepVF renamed to SweepVFI
* new SweepVFII plugin, variant of SweepVFI with Q modulated by a
second Lorenz fractal
* dsp/exp2 dumped in favour of libm's exp2(3)
0.2.3
* StereoChorus denormal protection made functional
(Thanks again to S. Savolainen)
* Phaser denormal protected
0.2.2
* Build was _not_ fixed for g++-4.0.
* AmpIV gain control restored to operate as expected
* Chorus/StereoChorus denormal protection (thanks to S. Savolainen)
* a few cosmetic changes elsewhere
0.2.1
* Build fixed for g++-4.0, PPC and AMD64
(Thanks to Niklas Werner, Andreas Jochens and Mario Lang)
* Reverb.* cosmetics
* AmpIV tone controls moved to after initial tube transfer
0.2.0
* denormal protection for Preamp*, Amp*
* Capitalized plugin Names
* PDF now lists audio in- and outputs as well as control inputs, only
gives average CPU rating
* AmpIV: PreampIV + power amp stage
* Plate2x2: Plate with 2-in, 2-out audio routing
* Plate damping and bandwidth controls changed to map to filter fc, fixes
behaviour in hosts that handle the log hint incorrectly
0.1.13
* AmpIII activate() resets the boost filter
0.1.12
* PreampIV band controls fixed to operate as expected
0.1.11
* amps changed back to old tube model :) but new temp & gain behaviour stays
* SweepVF, AmpIII default value adjustments
0.1.10
* HRTF recursion runs in doubles
* Cabinet recursion runs in doubles for much clearer sound
* all amps fitted with a common tube voltage mapping, dsp/TwelveAX7.h
* all amps: temperature and gain controls changed slightly
* all amps declared in one common Amp.h
* Pan echo fixed to be filtered independent of sample rate
* Cabinet cosmetics and activate() from port values fix
* SweepVF fixed to activate() from the current control settings
* rid all *amp* plugins of the initial hi-pass, not needed anymore
* PreampIII and AmpIII more authentic with an rbj lo-shelve, +6 dB > 1.2 kHz
as hinted by circuit analysis
* something_random() removed, stdlib for random generation
0.1.9
* Pan plugin
* 'make depend' instead of 'make dep', uses $(CC) -MM instead of 'makedepend'
* *Chorus, AmpIII, Plate defaults changed
* *Chorus optimizations, reintroduces funny zipper noise when 'feedback' is
non-zero and 't' is changed
* experimental HRTF plugin
* Plate 'blend' goes all the way to wet output only
* dsp/White offers a get_31() method for reduced number of bitshifts needed
* *Chorus delay line tapping changed to employ cubic interpolation, sounds
better
* SweepVF modulation mix algorithm changed to clamp if over-fed, makes
for wider sweeps
0.1.8
* all oversampling plugins use Kaiser windows instead of Blackman-Harris,
for much better performance
* SweepVF modulation range slightly increased
* Cabinet filter loop cosmetics (slight speedup)
* new AmpIII Plugin: Preamp plus power amp emulation
* lowered NOISE_FLOOR (equals 'renormal' number)
0.1.7
* connect ports to lower bound on instantiate()
* Plate delay line lengths raised, sound changed
* Eq activate() fixed to initialize from the current settings
* Preamp* cutoff reverted to 0.1.3 setting, thanks to Ben Saylor for
testing
* old IIR-based Preamp cleaned from the sources
* zipper-noise in *Chorus units for t changes with feedback > 0 eliminated
* all plugin constructor code moved to init() calls
0.1.6
* SweepVF modulation mix algorithm changed to maintain proportion, not
absolute value if x + y + z > 1, for better control
* create $(DEST) directory on make install, pointed out by Daniel James
0.1.5
* fixed delay line length miscalculation in ModLattice
0.1.4
* SweepVF modulation source can be mixed now
* latency port for VCO*
* Lorenz and Roessler get x, y, z mixing knobs
* PreampIV eq bands slightly tuned and coefficients moved into common struct
* Preamp*, VCO* downsampler filter cutoff lowered
* Clip downsampler filter cutoff lowered
* nonsensical audio output bounds removed
* simplified VCO* implementation
* JVRev rewritten for code clarity (funny enough, it also got quicker)
* fixed JVRev to reset its history on activate()
* added purpose, copyright and licensing information to all (i think) files.
* HACKING file
* CHANGES file
0.1.3
* fixed all compilation problems with gcc 3.3, with the patient help
of the lad mailing list community
* dsp/Eq.h SSE assembler code had to go (gcc > 3 doesn't like multi-line
asm, and efficiency and even reliability go down if we allow gcc to
intersperse its 'optimization' code with our asm)
0.1.2
* fixed more compilation problems with gcc >= 3.0
0.1.1
* tried to (but didn't really) fix compilation problem with ladspa.h
0.1.0
* initial release

View File

@@ -0,0 +1,21 @@
INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/include")
FILE(GLOB SOURCES *.cc)
ADD_LIBRARY(caps MODULE ${SOURCES})
INSTALL(TARGETS caps LIBRARY DESTINATION "${PLUGIN_DIR}/ladspa")
IF(LMMS_BUILD_WIN64)
ADD_DEFINITIONS(-DLMMS_BUILD_WIN64)
ENDIF(LMMS_BUILD_WIN64)
SET_TARGET_PROPERTIES(caps PROPERTIES PREFIX "")
SET_TARGET_PROPERTIES(caps PROPERTIES COMPILE_FLAGS "-O2 -funroll-loops -Wno-write-strings")
IF(LMMS_BUILD_WIN32)
ADD_CUSTOM_COMMAND(TARGET caps POST_BUILD COMMAND "${STRIP}" "\"${CMAKE_CURRENT_BINARY_DIR}/caps.dll\"")
ENDIF(LMMS_BUILD_WIN32)
IF(NOT LMMS_BUILD_APPLE)
SET_TARGET_PROPERTIES(caps PROPERTIES LINK_FLAGS "${LINK_FLAGS} -shared -Wl,-no-undefined")
ENDIF(NOT LMMS_BUILD_APPLE)
IF(LMMS_BUILD_LINUX)
SET_TARGET_PROPERTIES(caps PROPERTIES LINK_FLAGS "${LINK_FLAGS} -nostartfiles")
ENDIF(LMMS_BUILD_LINUX)

View File

@@ -0,0 +1,235 @@
/*
Cabinet-Models32.h
Copyright 2005 Tim Goetze <tim@quitte.de>
http://quitte.de/dsp/
Coefficients for 32nd order IIR filters modeling the frequency
responses of a few select instrument amplifier speaker boxes.
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
Model32
CabinetII::models44100 [] = {
{
1, /* identity */
{1},
{0},
0.2500
},
{
32, /* matchless_off */
{0.751133877447, 0.476665652568, 0.232649157621, 0.073490164573, -0.0520549100755, -0.121257530713, -0.100197130001, -0.0412212505538, 0.0304188276576, 0.0756296082021, 0.0339966964433, -0.0188218345374, -0.0138445066369, 0.0213454687998, 0.0509702380835, 0.0734535180348, 0.0423999627069, 0.0153810625747, 0.0251173605116, 0.0231247294211, 0.0118254107717, 0.0397159715652, 0.0509631315658, 0.0289647310658, -0.0151415828019, -0.0678734405803, -0.093244908543, -0.066522401653, -0.0390777734265, -0.0206522167345, -0.0140462622686, -0.000367155236373},
{0.0, 0.582601584085, 0.179375009603, -0.0310635155569, -0.115346798496, -0.0929627102659, -0.00905181962865, 0.0529760132518, 0.0660791287878, 0.0264313110774, -0.0527906709163, -0.0832644831913, -0.047761487157, -0.00525089678838, 0.00920853658047, 0.00160221988302, -0.036921883774, -0.0508637181262, -0.0369139574948, -0.0297884797081, -0.0131892593004, 0.0328205903451, 0.0532522735819, 0.0434869096278, 0.0171966303505, -0.0117058173799, -0.0222005420711, -0.00885326183421, -0.00467956865215, -0.0017726627279, -0.00860012741033, -0.046231460458},
0.1088
},
{
32, /* matchless_on */
{0.737841828858, 0.460204992853, 0.280744637086, 0.0935206597339, -0.0705817082991, -0.156854249167, -0.144106172504, -0.0685499381158, 0.00620304241081, 0.0610934722291, 0.0465273895752, -0.0126452119054, -0.0649026246806, -0.0713209874063, -0.0596110923629, -0.0415373874755, -0.0389721881465, -0.0284327312853, -0.0109349222879, 0.0222033554026, 0.0552410801, 0.0896637673103, 0.106570641198, 0.122226072209, 0.115856033255, 0.0801435470104, 0.0100505511461, -0.0489360304782, -0.0829690212741, -0.0887089006669, -0.0886065785655, -0.0469811200837},
{0.0, 0.611100232423, 0.191219480026, -0.0831423854754, -0.193756321128, -0.162103938394, -0.0633420329893, 0.0168905353821, 0.0319336623225, 0.00544046343502, -0.0450052997134, -0.0655650754908, -0.0385274002732, 0.0140346387674, 0.0381359817338, 0.0270741530904, -0.00985978537582, -0.0339616898786, -0.039510862758, -0.0234251294011, -0.00577196250278, 0.00602171273173, -0.0012115688049, -0.00850899153196, -0.0195544970682, -0.0235059343611, -0.0181958065493, 0.00619896262657, 0.0223536984999, 0.0126908937718, -0.0260364491855, -0.048766780312},
0.1398
},
{
32, /* superchamp */
{0.764583545976, 0.538738620869, 0.179425189777, -0.0185116138637, -0.155373227876, -0.162444114313, -0.0955812737534, -0.0527583372679, 0.0109899803101, 0.027210394773, -0.0177057767782, -0.03205717291, -0.0483168565776, -0.00731185525588, 0.0552452873245, 0.075576957847, 0.0442457880974, -0.0118721835978, -0.0177888197761, 0.0351775943888, 0.0348792107809, 0.0127082443141, -0.0055147874283, -0.025538492041, -0.0266140723472, -0.00351312980821, -0.00705954101235, -0.0071756277097, -0.0073123807337, 0.00388589850911, 0.00920641143732, 0.00675123073573},
{0.0, 0.60762891993, 0.0804538514268, -0.139045714146, -0.188318237239, -0.102850850916, -0.014327283229, 0.0095126479685, 0.00675439266545, -0.0369566468812, -0.0735379197498, -0.0464884257152, -0.00652653570483, 0.0460655599383, 0.0503985731174, -5.63018161884e-05, -0.0506993010893, -0.0519236737825, 0.00601034157191, 0.0565585753845, 0.0271235886591, -0.0124334855533, -0.0275977294885, -0.0178167665585, 0.0128169339437, 0.0370635121992, 0.0234037648427, 0.00539790260771, -0.0105614779717, -0.00455010484908, 0.0101491481873, 0.0220600470141},
0.1771
},
{
32, /* fender_68 */
{0.952578805555, 0.423653323267, -0.299325586902, -0.181365977098, -0.00917621972835, -0.106211820702, -0.144312857058, -0.0664525836173, -0.0308777018884, -0.0289677657295, 0.06172127279, 0.0700934900581, 0.00309908907112, -0.00891983996273, -0.0687746247517, -0.0146407179784, 0.0264920122813, 0.0134951652977, 0.0480259959669, 0.021152058087, -0.0229395195318, 0.00306616207055, 0.015377268642, -0.0166969251715, -0.0472474005373, -0.0257631159809, -0.0298364749235, -0.0359529903186, -0.0228763062676, -0.0190698276841, -0.0481625561196, -0.024778494842},
{0.0, 0.373287747089, -0.379113471957, -0.162638822705, -0.0309789534425, -0.113942084113, -0.0746182251996, 0.00371088736605, -0.0311272559258, -0.0641468114835, -0.01820597504, -0.0106553201186, -0.0025754233332, 0.00779983208005, -0.0133002473609, 0.0474101909397, 0.0302769494631, -0.0206219756872, 0.00091004302278, 0.00989883124397, -0.00095818764248, -0.00710510898183, -0.0103772680579, 0.00636775505307, 0.0172954284337, 0.00560785607672, -0.00550757886611, 0.0349170637518, 0.033667260855, 0.0263631580776, 0.110816169944, 0.0551965257177},
0.5267
},
{
32, /* marshall */
{0.632136921276, 0.487520501766, 0.267166936676, 0.0328329910029, -0.124746548218, -0.175922403211, -0.134056005594, -0.0275494615733, 0.0707349518645, 0.112423870345, 0.0758114749041, 0.0139250983739, -0.0210142361937, -0.00193163235992, 0.0197248569676, 0.0206461311409, -0.00897578938148, -0.0306233925441, -0.041776582616, -0.0398244883075, -0.0328395317644, -0.0101508675516, 0.00636535089023, 0.0217430701187, 0.0223553033142, 0.0237782685683, 0.0278909244449, 0.0469073547267, 0.0509466858461, 0.0336174039295, 0.0028829896058, 0.0114245696423},
{0.0, 0.618938053184, 0.180052941443, -0.1028119852, -0.19233134457, -0.14303971742, -0.0450998045919, 0.0341843500162, 0.0465086637485, 0.0071435730282, -0.0461779135605, -0.059013077843, -0.0284397368519, 0.0187689538251, 0.0341298347311, 0.0244703904826, 0.00720524642447, 0.00991307420138, 0.018738094495, 0.0275442994126, 0.0299092523536, 0.0339721143791, 0.0270175082759, 0.0151058732685, -0.00751060215331, -0.0236467101096, -0.0272347092017, -0.00812409176413, 0.0141531255726, 0.0270163512016, 0.00208457244433, -0.0784208903527},
0.1132
},
{
32, /* mesa */
{0.462494284717, 0.393585742058, 0.303328992303, 0.181503108654, 0.0629173541037, -0.020681232006, -0.0571885284376, -0.0467796891544, -0.0276756897046, -0.00892844543715, 0.00204563746045, 0.0115134879366, 0.00106846505647, -0.0149439761237, -0.0330954690659, -0.0337794782369, -0.0276991983671, -0.0190072253549, -0.0324525139664, -0.0630355306227, -0.0999209480709, -0.111933999507, -0.110645947594, -0.092891889065, -0.0692697576821, -0.0450776510962, -0.0392851148349, -0.035324392499, -0.035403719608, -0.0376283525137, -0.0612971281858, -0.0900100571873},
{0.0, 0.56035020932, 0.29343466934, 0.0772363317707, -0.0530744622425, -0.0885361452358, -0.0589180077997, -0.00257855932105, 0.0334935643243, 0.0442328268628, 0.0294583386742, 0.00717959220156, -0.0175320543891, -0.0208128084726, -0.00373790944406, 0.0334761867029, 0.0627006023834, 0.0700861323961, 0.0416727224516, -0.000637024095321, -0.0357094622237, -0.0404012923223, -0.0285442599547, -0.00354190734239, 0.0184796761008, 0.0306524756002, 0.0181509660649, -0.00687913029057, -0.0393242377083, -0.0550286859069, -0.0375235630652, 0.0471541139774},
0.2500
},
{
32, /* pro_jr */
{0.734249445521, 0.491537503216, 0.337459965314, 0.176462147927, 0.0276390917692, -0.0644515999868, -0.0972401016684, -0.0803985996088, -0.0428685363211, -0.0140919517701, -0.0337409328059, -0.0478897322188, -0.0362288411405, -0.0130597902066, -0.00582049410513, 0.0022469159791, -0.00964075940401, -0.0380828738621, -0.0763568061844, -0.089961400489, -0.0952731732008, -0.0895604973654, -0.101346765163, -0.119569610891, -0.128233125318, -0.109253322153, -0.0821678710293, -0.041620049917, -0.0245690004857, -0.0219634796685, -0.0419014341928, -0.0651369005252},
{0.0, 0.663188793345, 0.270439727768, 0.0190336508265, -0.0946822703696, -0.0888520332756, -0.0267575060227, 0.03588910806, 0.0599594346375, 0.041850528867, -0.00632721029526, -0.0244115687714, -0.0103162286821, 0.0119858957112, 0.0197992179523, 0.0238625339809, 0.0142267453341, 0.00296440492823, -0.00543873217766, -0.00014858213959, -0.00196572770138, -0.00750279035903, -0.0243069229182, -0.0285801812939, -0.0100437887585, 0.0259127459737, 0.0475587872734, 0.0497287926166, 0.0189008750066, -0.0156259358581, -0.0395277285226, -0.0325403937133},
0.2500
},
};
Model32
CabinetII::models48000 [] = {
{
1, /* identity */
{1},
{0},
0.2500
},
{
32, /* matchless_off */
{0.751616859768, 0.455477674067, 0.236301148417, 0.0831041136969, -0.0271403780165, -0.100856772665, -0.105648770896, -0.0532306291781, 0.00532168244437, 0.0769562829624, 0.0998339309568, 0.0558038693299, -0.00395884124382, -0.0114310936324, -0.00035476064153, 0.0174339944264, 0.0326924241347, 0.0270873548662, -0.00429020715999, 0.00156163927912, 0.0110729521074, -0.00355947960002, -0.0197465530354, -0.00226491671692, 0.000562585645118, -0.0113374627036, -0.0395530054938, -0.0773637715714, -0.110300349197, -0.102242460538, -0.0802708342222, -0.0368542661019},
{0.0, 0.573363683971, 0.199471497847, -0.0100987131104, -0.10105055289, -0.104899707204, -0.0453437303875, 0.0227558858103, 0.0494210617945, 0.0469948522053, -0.000869801014908, -0.060632162889, -0.078080719406, -0.036500327, 0.00169111404144, 0.0161416041893, 0.00446588199593, -0.0256497606161, -0.052007235054, -0.0329954828689, -0.00846672300472, 0.00503287700528, 0.0225460696732, 0.0488769816297, 0.0448900330347, 0.0265213842101, 0.00867720983291, 0.00309440217249, 0.0106055108636, 0.0271743998844, 0.00790666559882, -0.0494793375024},
0.0972
},
{
32, /* matchless_on */
{0.736752793274, 0.42789796886, 0.276149514408, 0.115820415827, -0.0320628697966, -0.121795204879, -0.142828258933, -0.0859958693575, -0.0176210080708, 0.0507262077785, 0.0760681727953, 0.0516030677135, -0.0156690595908, -0.0681826247693, -0.0944163538419, -0.0902627133447, -0.0823076517005, -0.0734934090705, -0.0692943287136, -0.0513490885216, -0.0305887683924, 0.00285704997857, 0.0369925466712, 0.0764037770343, 0.103822670992, 0.130505051247, 0.126189167018, 0.0870288516942, 0.00607091602923, -0.0716539223465, -0.122336223951, -0.107802803871},
{0.0, 0.60867772822, 0.226828570484, -0.0393069664502, -0.171393086666, -0.174192716258, -0.103361484389, -0.0132853346158, 0.028065787663, 0.0276791758057, -0.0093177316362, -0.0469821632045, -0.0621330892865, -0.0326281391326, 0.00952584471493, 0.0391305970452, 0.0343547401694, 0.0103991548433, -0.0167038490391, -0.0239538249183, -0.0200522931237, -0.00619037145571, 0.00112030880301, 0.00379111038519, -0.00425737141588, -0.00510101082859, -0.00618382168232, 0.00222622566288, 0.012234432496, 0.0240659559777, 0.00806553945799, -0.0500627523652},
0.1292
},
{
32, /* superchamp */
{0.736144077022, 0.533697136818, 0.208223243775, 0.0105446749467, -0.115112852124, -0.167784888349, -0.118309369108, -0.0686796829989, -0.0320689348914, 0.0293577338735, 0.0228074058112, -0.00942321170451, -0.0247236206138, -0.0382275338893, -0.0220962904251, 0.0369265390908, 0.0652234781224, 0.0625494411772, 0.0203218939615, -0.0130049534934, 0.00776964355966, 0.0408524993227, 0.0187403995074, -0.00323676614764, -0.011729909669, -0.0146182789026, -0.00520348014418, 0.0251718299467, 0.0292350516935, 0.0265773340322, 0.0181154887123, 0.00421057480694},
{0.0, 0.610899704338, 0.116614493347, -0.107102211172, -0.17045907835, -0.125900190491, -0.0364778545046, 0.00268191730596, 0.00393059621292, -0.00442930148683, -0.0496456084728, -0.0641110080937, -0.0380451267687, -0.00269939732468, 0.0336320714582, 0.0473822399663, 0.00672982206906, -0.0376868259805, -0.0574242984978, -0.0235397064668, 0.0375124576452, 0.0561991090538, 0.0137707473486, -0.019313535496, -0.0308393757193, -0.0231851781013, -0.000359955230619, 0.0224262684571, 0.0140137052508, 0.000784652753983, -0.00886431320574, 0.00520310404507},
0.1627
},
{
32, /* fender_68 */
{0.940499002611, 0.474406048513, -0.234027864276, -0.245068743132, -0.0275886925241, -0.0375353084644, -0.116020540053, -0.0788142311793, -0.0192628993487, -0.0208031874862, -0.036274591111, 0.0274443468203, 0.0248807593189, -0.00647753415313, 0.0123263590476, -0.0207149994271, -0.0169433327912, 0.0367748568855, 0.0157152144192, 0.0219763333967, 0.0220500063178, -0.0236599648747, -0.028051497911, 0.0300390624893, 0.0503387731847, 0.0200944005064, -0.0199651438354, -0.0189512878551, -0.0341641453641, -0.0429368448753, -0.00172415323758, 0.0187773541719},
{0.0, 0.438339721191, -0.314663980515, -0.200197131305, -0.0199447108206, -0.0806444850114, -0.112178302737, -0.0407327776523, -0.0162859630428, -0.04823028022, -0.0411406583855, 0.0181605305425, 0.0182296926714, 0.0118833391549, 0.00393162532115, -0.0208012980893, 0.0236552029021, 0.0440436249244, -0.015760708804, -0.00800215008051, 0.0264390446392, 0.0224755303713, -0.00488388416495, -0.0302927869543, -0.0389708286652, -0.0213138828272, -0.0196593434643, -0.0250837259433, -0.0171306364105, -0.00247139702561, -0.0442683480699, 0.00104076341183},
0.4976
},
{
32, /* marshall */
{0.632432814914, 0.466340577796, 0.289928964438, 0.0873773815151, -0.0637714745665, -0.134210611966, -0.135503699906, -0.0734327294692, 0.00792058229369, 0.0760927054439, 0.0890306791735, 0.0595619591479, 0.00922265898468, -0.00784921065447, 0.00721708132165, 0.0350086110935, 0.0376247852781, 0.0202910905225, -0.00870656585895, -0.0201433438279, -0.0279367587019, -0.0245180701261, -0.0152282108474, 0.00828831561931, 0.026894267099, 0.0460507228179, 0.0479724909309, 0.0409446830643, 0.0201085872414, 0.00561387003945, -0.00753169056058, 0.000986659484038},
{0.0, 0.594136919296, 0.201493775156, -0.0716153113375, -0.180702317671, -0.160390621032, -0.0826806167453, 0.000697581661664, 0.0397029332137, 0.0299147550153, -0.0177304803895, -0.0570701545448, -0.0652412298089, -0.0298128518083, 0.0124560081161, 0.0355161884622, 0.0255626596849, 0.00762986123778, -0.00431800264305, 0.00420452150537, 0.0150803653129, 0.0291131391488, 0.0359312991789, 0.0364212054952, 0.0203516393338, 0.0022141464642, -0.0149317465975, -0.0121651677123, 0.00704936522966, 0.0353605810957, 0.0322793623201, -0.0307481566657},
0.0996
},
{
32, /* mesa */
{0.495930895017, 0.376476419856, 0.303586589503, 0.197845120631, 0.0899987258287, 0.00535662962566, -0.0475751298521, -0.0519576953746, -0.0376515865627, -0.0168652310379, -0.00508935920432, 0.00888673824726, 0.0109356809664, 0.00654236486877, -0.0122417367265, -0.0265484743045, -0.0339837870474, -0.0233241554396, -0.0127914848925, -0.00515836827508, -0.0209864779144, -0.0475368628257, -0.0782299905969, -0.0909360283527, -0.0983527406681, -0.0938199589393, -0.0898861541372, -0.0810532718198, -0.0815503996776, -0.0796236089322, -0.0749972587191, -0.0599151413182},
{0.0, 0.534078389658, 0.294930996327, 0.0991032789063, -0.0272295349878, -0.0770236019127, -0.0705005212384, -0.0257307535864, 0.0146092582685, 0.0379865150974, 0.0359872042115, 0.0230902724993, -0.000725294921074, -0.0174606095957, -0.0236682462783, -0.00669570447944, 0.021362472642, 0.0512331674592, 0.0584309807453, 0.044407663655, 0.00900197651083, -0.0217065441135, -0.037007819832, -0.0257650758221, -0.00590170187955, 0.0194528583388, 0.0320489572473, 0.030386820278, 0.00581345215134, -0.02347217653, -0.0472400829369, -0.0430772235107},
0.2500
},
{
32, /* pro_jr */
{0.767221028694, 0.44461349725, 0.324943328323, 0.187583401624, 0.0585707039826, -0.036895934974, -0.0893306767021, -0.0935390488322, -0.0758668196298, -0.0408583447944, -0.0309741335777, -0.0417509830741, -0.0550681782871, -0.0417048932451, -0.0295194484972, -0.0177059350466, -0.0189517349534, -0.0180093708399, -0.0336546809637, -0.0511188019942, -0.0648908298083, -0.0619349722444, -0.058573104994, -0.0470621645996, -0.0597723312001, -0.0855574645731, -0.109611426273, -0.113648127167, -0.112000636381, -0.0909222239483, -0.0727424833493, -0.0362189061166},
{0.0, 0.62970643913, 0.293410547935, 0.0578415012725, -0.0672697168081, -0.094466063484, -0.0596589574295, 0.000648321788151, 0.0432524054305, 0.0596811813862, 0.0366985835211, 0.0035102339371, -0.0156226319473, -0.00793362884142, 0.00010974226424, 0.00946937446156, 0.012432810986, 0.0166353982569, 0.00885441580212, 0.00176491241101, -0.00465797946213, -0.00706780418692, -0.0169292031721, -0.0217285491491, -0.0298358684941, -0.0214136482558, 0.00603804801211, 0.0432427970473, 0.0585065590431, 0.0472784452715, -0.00622503195681, -0.0778508047032},
0.2500
},
};
Model32
CabinetII::models88200 [] = {
{
1, /* identity */
{1},
{0},
0.2500
},
{
32, /* matchless_off */
{0.581241782856, 0.351127495653, 0.293447498861, 0.203644809011, 0.122868019886, 0.0798136296596, 0.0627344813847, 0.0582949053671, 0.0495483041303, 0.0432630437098, 0.040778980166, 0.0502959955879, 0.06102889964, 0.0717641907075, 0.0748815766238, 0.0800912666293, 0.0851573668252, 0.0920643205477, 0.0863975575062, 0.0672325976358, 0.0316956083124, -0.00434276880274, -0.0353531287352, -0.0497775530225, -0.0549729489782, -0.0523697171754, -0.0527017492312, -0.0496529350336, -0.0432932459975, -0.02532771672, -0.00704167237723, 0.0048594343372},
{0.0, 0.527351085198, 0.306902134941, 0.121666659808, -0.000869395618695, -0.0590060725486, -0.0800321294614, -0.0812161497866, -0.0756902882293, -0.0586480829698, -0.035146960319, -0.00693669578546, 0.0137459416267, 0.0260709477944, 0.027142282904, 0.0255107789641, 0.0184912445046, 0.00787311464383, -0.012080806249, -0.0326630439808, -0.0479929895538, -0.0449384101923, -0.0257392906728, 0.00611234738181, 0.0331632616028, 0.0482685642726, 0.0424761053987, 0.0227984734932, -0.00689764194843, -0.0338856201523, -0.0506606159856, -0.036511204766},
0.0505
},
{
32, /* matchless_on */
{0.754229081317, 0.28992460445, 0.25506113675, 0.205825249671, 0.149736304722, 0.0965453802519, 0.043937905171, 0.000434005345846, -0.0380077969732, -0.0648440473691, -0.0820185458195, -0.0804769096126, -0.067903181347, -0.0457545818062, -0.024506391213, 0.000276698483466, 0.0227732012504, 0.0462273060245, 0.0612627993168, 0.0704402843546, 0.0668070265589, 0.0560526861609, 0.0360109823522, 0.0171596012087, -0.00254977646019, -0.0169995310247, -0.0326802854744, -0.0431803638863, -0.0527933574758, -0.0537365642607, -0.0484658084207, -0.0264410770067},
{0.0, 0.473323278291, 0.304447812378, 0.156758267418, 0.0407417557578, -0.0375541115795, -0.084174169143, -0.0995647273704, -0.0953485009639, -0.0745284443813, -0.047611748504, -0.0160860230858, 0.00812108121932, 0.022520582633, 0.0218183858734, 0.0139436350809, -0.000684654971314, -0.0149097687302, -0.0306604197839, -0.0406022715147, -0.0468128179642, -0.0432623683601, -0.0334406989274, -0.0143890226248, 0.00558711901527, 0.0240338814196, 0.0313772357088, 0.0288551286613, 0.0119537593072, -0.0137695197232, -0.0487934292115, -0.0830489298052},
0.0977
},
{
32, /* superchamp */
{0.564442248694, 0.417375378841, 0.328112930961, 0.184087902524, 0.0606877879374, -0.000888211396509, -0.0240417949471, -0.0386494407725, -0.0623924578122, -0.077697122496, -0.0771755477917, -0.0625887925073, -0.0567602691995, -0.0606788148043, -0.0653972735044, -0.0482177616776, -0.0134706312764, 0.0255178539984, 0.0454519306153, 0.0488386058553, 0.040370247527, 0.0309888915369, 0.0118591255626, -0.0166655766779, -0.0524886753799, -0.0735764853592, -0.0712539506456, -0.0409275497673, 2.56200623182e-05, 0.0407426819337, 0.0605288133085, 0.045387360314},
{0.0, 0.58401876341, 0.301866612426, 0.0749234792796, -0.0512250579262, -0.0884380547633, -0.0859341509964, -0.0715039798048, -0.0533786975427, -0.0228940420774, 0.00895370138835, 0.029234699873, 0.0249642271923, 0.00815472752255, -0.00852123193886, -0.013874697166, -0.0177655062032, -0.0244009337209, -0.0348366549323, -0.0341041586519, -0.0199202543104, 0.0049040564053, 0.0228786117828, 0.0307412470467, 0.0270357914745, 0.0202102683028, 0.00600213627849, -0.0132416519396, -0.035306113292, -0.0400865958576, -0.0122081055945, 0.061547236583},
0.0940
},
{
32, /* fender_68 */
{0.636561915308, 0.539655264394, 0.306940867236, -0.00798666364572, -0.2010016693, -0.196429989976, -0.0871390659769, 0.0058320328314, 0.0205797064122, -0.0137249327682, -0.0511542488948, -0.0559233911299, -0.0365269209833, -0.00933607201861, 0.00259528983031, 0.000820508580746, -0.014655020786, -0.0298656923381, -0.0363760565053, -0.0164103072662, 0.0180980103038, 0.0427307277871, 0.0289045601723, -0.00365819240342, -0.0221033761769, -0.00613002097262, 0.0137359639094, 0.0126374765575, -0.0109039821774, -0.022164851067, -0.00644276895272, 0.0406179316828},
{0.0, 0.660098640191, 0.135995579965, -0.18643602906, -0.21362553946, -0.0765656086032, 0.0304158765248, 0.0317377501315, -0.0306547118834, -0.0742288333657, -0.0716640946732, -0.0400798411024, -0.016998894935, -0.0119106601837, -0.0209707717803, -0.0285105267284, -0.033043355156, -0.03016836978, -0.0235112391223, -0.0121431367424, -0.0083970652792, -0.0121227777881, -0.0179437299751, -0.0103936611338, 0.000810102779011, -0.000708784846704, -0.0234941025684, -0.0371015701952, -0.0180247391719, 0.0220761118738, 0.0243771530897, -0.0421517051891},
0.2672
},
{
32, /* marshall */
{0.487781223472, 0.313482625917, 0.286350463893, 0.224656866466, 0.146402144474, 0.0763559834141, 0.0205530599391, -0.0145817074955, -0.0366424375139, -0.0445785898927, -0.0458160857045, -0.037336889106, -0.0251791786851, -0.00575399966305, 0.0148942739024, 0.0391750282507, 0.0589388969198, 0.0752812491289, 0.0811254049966, 0.0801917939348, 0.0685526132261, 0.0533549680139, 0.0342811257343, 0.0215399491933, 0.0147049499229, 0.0198301270011, 0.030982725155, 0.0503002628087, 0.0693130546132, 0.0867905978395, 0.0881891739024, 0.0641468491285},
{0.0, 0.51943795847, 0.326729354403, 0.149229797985, 0.0109767104297, -0.0732768493116, -0.111088974509, -0.110789539541, -0.0898934114003, -0.0564578047856, -0.0231004313356, 0.0069673688738, 0.0254240918116, 0.0337510299271, 0.0282514883492, 0.0147089116391, -0.00647743824673, -0.0269028751263, -0.0446349197143, -0.0510564142456, -0.0478791675617, -0.0324832722564, -0.0121393859238, 0.0104982033447, 0.0241412382054, 0.026038551433, 0.00994372364326, -0.0158545375622, -0.0436236854665, -0.0537883431888, -0.032574614307, 0.0387767610956},
0.0666
},
{
32, /* mesa */
{0.800744790951, 0.174351736281, 0.162047205857, 0.138794638546, 0.105850412073, 0.0723656260485, 0.0365740092219, 0.00431957094182, -0.0274260159266, -0.052813754463, -0.0747002681782, -0.0875980404567, -0.0959206315285, -0.0964326627017, -0.0951211930444, -0.0890916136798, -0.0839336477818, -0.0759587618493, -0.0699911420952, -0.0618103150195, -0.0562569921852, -0.0497246788081, -0.0474425366429, -0.045118852226, -0.0466084665482, -0.0466183009403, -0.048518031551, -0.0465434705359, -0.0436608634155, -0.0338725172886, -0.0202008830722, 0.0028064387038},
{0.0, 0.348283848037, 0.278266186344, 0.208315992656, 0.142238507639, 0.0877418515218, 0.0426721771028, 0.0101211230173, -0.0142620082039, -0.0279206093939, -0.0353541175487, -0.0344380562925, -0.030780249931, -0.0230674032021, -0.016830370528, -0.00961683532214, -0.00530786189015, -0.000225073976316, 0.00276994164541, 0.00775476255757, 0.0114020240885, 0.0168731013913, 0.0203623771917, 0.0249313385656, 0.0267810408235, 0.0287888955038, 0.026286558388, 0.0210452426156, 0.00782753651909, -0.0119982689793, -0.0444362907999, -0.0880582917654},
0.2500
},
{
32, /* pro_jr */
{0.909835294941, 0.237116967883, 0.220493213781, 0.188296175986, 0.145804855731, 0.1035213914, 0.0593519550104, 0.018535542445, -0.0211984146917, -0.0519769598497, -0.0754321013216, -0.0869433378891, -0.0920887014799, -0.087865027182, -0.0792907797887, -0.0637463287733, -0.0482713945314, -0.0325419993651, -0.0236382913314, -0.0187673840883, -0.0211003792763, -0.0242608362201, -0.0299312006336, -0.032972160693, -0.038384284548, -0.0433373155719, -0.0522438990617, -0.0598215574684, -0.0680342990137, -0.0706075753658, -0.0696540637908, -0.0592163425206},
{0.0, 0.437589362895, 0.316542061632, 0.204591087565, 0.109610647885, 0.0395709089276, -0.0101168883103, -0.0392507441308, -0.0536854447628, -0.0525994961208, -0.0429181531663, -0.0263178649123, -0.0108114446263, 0.00361113168474, 0.0124368978113, 0.0183164472387, 0.0173632458965, 0.0133080282174, 0.00451776800884, -0.0027292732446, -0.0084152300302, -0.00649797485834, 0.000343904158769, 0.0135853277076, 0.0260015642816, 0.0368350330248, 0.0397116093504, 0.035854103932, 0.0201173890247, -0.00598389310382, -0.0479894271495, -0.10412912505},
0.2500
},
};
Model32
CabinetII::models96000 [] = {
{
1, /* identity */
{1},
{0},
0.2500
},
{
32, /* matchless_off */
{0.576442585125, 0.306361432085, 0.256653772545, 0.177316081395, 0.0989986029973, 0.0501811139283, 0.0263950297254, 0.0211066378234, 0.0164669956215, 0.0145939695187, 0.0131915301884, 0.0224302343035, 0.0359395096833, 0.0545608587271, 0.0665798797365, 0.0764638706987, 0.0822020713116, 0.0937583388117, 0.104705110174, 0.114731370205, 0.110211793209, 0.0934363192015, 0.0618719406527, 0.0288732783262, -0.00358352145982, -0.0259155247732, -0.0441021836484, -0.0555310772717, -0.0670712283056, -0.0677576234809, -0.0520883036287, -0.00170675690763},
{0.0, 0.508669145362, 0.322479739387, 0.156892646838, 0.035020841423, -0.0347440829067, -0.0701806136041, -0.0825008530376, -0.0845801358275, -0.0733688333095, -0.0530323023958, -0.0234536559399, 0.00360324038712, 0.0244342060698, 0.0314240125922, 0.0309189326677, 0.0230090442585, 0.0145937492998, 0.00103582471628, -0.0152025378362, -0.0363464186106, -0.0511215757185, -0.0557855361088, -0.0408742372067, -0.0130066718085, 0.023938151261, 0.0530188010691, 0.0671220652663, 0.0540529792683, 0.0164681954699, -0.0469180610472, -0.125461903689},
0.0494
},
{
32, /* matchless_on */
{0.941618036526, 0.239866043796, 0.218739659026, 0.1855624175, 0.143119285622, 0.098285134927, 0.049823345838, 0.00491072985196, -0.0372254815054, -0.0706502777402, -0.0969381684649, -0.109632530255, -0.11098328422, -0.0987132407502, -0.080379871629, -0.0559715185867, -0.0315381582245, -0.00496109501612, 0.0171690268626, 0.0357166271116, 0.0446464487104, 0.0469813895302, 0.0390401137056, 0.0262125456401, 0.00716840214371, -0.0108017316884, -0.028794724005, -0.0413800680682, -0.0514241182155, -0.0535943709963, -0.0501288491812, -0.0356807796594},
{0.0, 0.436259688495, 0.303343311728, 0.180401443304, 0.0755768977826, -0.00298141418929, -0.057401860201, -0.0849229021135, -0.0921029201246, -0.0808710238831, -0.0601563297725, -0.031853887802, -0.00462566662184, 0.0189321520075, 0.0307568282097, 0.0327103981415, 0.0234112049746, 0.00941773902223, -0.00880302596543, -0.024628145492, -0.0377321989901, -0.0420727660837, -0.0390726798885, -0.0257996845469, -0.00726121684029, 0.0160981879938, 0.0352540092546, 0.0464046029779, 0.0397882340227, 0.0142875216435, -0.0354360590043, -0.105598440048},
0.0651
},
{
32, /* superchamp */
{0.598483659576, 0.392119614921, 0.324128816827, 0.207253363884, 0.0944270102152, 0.0269672658419, -0.00338080597527, -0.0154386943775, -0.0337854523788, -0.0528375781544, -0.0668037964282, -0.0644824272507, -0.0581122839458, -0.053620685433, -0.05941095465, -0.0599621297144, -0.0476930600655, -0.0143047492128, 0.0237480623761, 0.0559779339589, 0.0689887990541, 0.0713731354487, 0.0647181254244, 0.0560812069922, 0.0357682629483, 0.00706245212017, -0.0286615791021, -0.052644819201, -0.0590210498647, -0.041747604451, -0.0168191702228, 0.00465326173164},
{0.0, 0.555306548005, 0.304384349565, 0.0955876011242, -0.032790937176, -0.0803585769889, -0.0834509085546, -0.069736664388, -0.0558345347225, -0.0359974911327, -0.012046069248, 0.0128302949846, 0.0234882264814, 0.0187166685281, 0.00137741738003, -0.0128825824672, -0.0213054248788, -0.0224842024484, -0.0275110748888, -0.0335451251688, -0.0384673765875, -0.0306779974376, -0.0133415017904, 0.00900611383706, 0.0222626768741, 0.0253127037082, 0.0166771042914, 0.00577574428968, -0.00773567900765, -0.0175688399329, -0.021338142267, -0.00199213694503},
0.0903
},
{
32, /* fender_68 */
{0.61042243584, 0.519167522444, 0.338455322638, 0.0598120788559, -0.155008295955, -0.209996385603, -0.142681005045, -0.0444570079209, 0.00529240289956, 7.6909429568e-05, -0.03354602127, -0.0535687744411, -0.049519403779, -0.0238846177015, -0.000920606029846, 0.010434338433, 0.002228774067, -0.0140907683377, -0.035872619499, -0.0481212880009, -0.0457673542097, -0.0191076772359, 0.0133771714316, 0.0322386165839, 0.0178556687427, -0.0095701287891, -0.0237842865664, -0.00484822472333, 0.0227424513401, 0.0316309750773, 0.00586735474858, -0.0235815475244},
{0.0, 0.658865798836, 0.184872779539, -0.137135473027, -0.210003150161, -0.108221769211, 0.0110221508749, 0.0526622948184, 0.0145697157163, -0.0398151102571, -0.0659793146563, -0.0534131399862, -0.0284152189248, -0.0091286709165, -0.00636536651116, -0.00986403078301, -0.0155052371976, -0.0165973690878, -0.018011376701, -0.0154898840826, -0.0109104131649, -0.00195757926804, 0.00204116721073, 0.00157889259535, -0.00328499795407, -0.00244356007769, -0.00227606009504, -0.00603112779608, -0.0173439459505, -0.0176299504059, -0.00210293292278, 0.00864548631897},
0.2359
},
{
32, /* marshall */
{0.519039714244, 0.28397066065, 0.263752501402, 0.216763224092, 0.153202060875, 0.093498887372, 0.0418092813984, 0.00649080672527, -0.0187685887985, -0.0313725205166, -0.0386046242494, -0.0368255569808, -0.0319398932261, -0.0190867195417, -0.00429792893597, 0.0171895051396, 0.038509421914, 0.0625518463058, 0.0812007261611, 0.0971807963934, 0.10334087256, 0.104223746243, 0.0947810421376, 0.0820175331544, 0.0638964900287, 0.049888405421, 0.0379912291508, 0.0353343445924, 0.036271348736, 0.0455456726476, 0.0571810093304, 0.0753869660867},
{0.0, 0.495552580909, 0.326384725738, 0.167649233033, 0.0373913746446, -0.049802990613, -0.0971841927371, -0.108389008817, -0.0971430950292, -0.0702481334571, -0.0402478232447, -0.00995144228925, 0.0120995492915, 0.0263638891687, 0.0284733804008, 0.0226883648689, 0.00733748451124, -0.0103570201666, -0.0304762029918, -0.0447374261541, -0.0534169560389, -0.0509721172171, -0.0411315716449, -0.0225994268992, -0.00306083571223, 0.0162408742504, 0.0258967736277, 0.0250750690024, 0.00862389563333, -0.0164131934788, -0.0455336929843, -0.0629205840552},
0.0663
},
{
32, /* mesa */
{0.816556812848, 0.150293117891, 0.141795487793, 0.124839522537, 0.099196755702, 0.0723627786971, 0.0428519140057, 0.0161310830227, -0.0110923701697, -0.034007587359, -0.0554568663798, -0.070468417213, -0.0823376819593, -0.0871844200317, -0.0898440433576, -0.0874713568897, -0.0851052646307, -0.0795934648554, -0.075427699709, -0.0687589148326, -0.0639930873009, -0.0572185996408, -0.0529180674694, -0.0475806410473, -0.0456514163291, -0.0429752541034, -0.0432519558816, -0.0416267292155, -0.0408422393067, -0.0352236824279, -0.0269876908459, -0.0103181623672},
{0.0, 0.324884890574, 0.268628249933, 0.210962607294, 0.15379956812, 0.104238396719, 0.0608732030517, 0.0272239220101, -0.000475953058616, -0.019347999351, -0.0332735025431, -0.039418716712, -0.0418327023679, -0.0385575041649, -0.0342524288567, -0.0267537524134, -0.0202714386094, -0.0115967221036, -0.00421419915726, 0.0052675530631, 0.0133773574491, 0.0230906449798, 0.0303465968119, 0.0374618799867, 0.0402738707573, 0.0409671902591, 0.0352080031286, 0.0253938319794, 0.0070273986645, -0.0175302180141, -0.0528396401673, -0.0967034944448},
0.2500
},
{
32, /* pro_jr */
{0.925365759135, 0.205121186223, 0.193282167511, 0.170964672217, 0.139026712618, 0.10615970874, 0.0712170483592, 0.0387785989803, 0.00565122246474, -0.022529589284, -0.0474765753603, -0.0638195347354, -0.0750789855745, -0.0781013105481, -0.0774023301337, -0.0698851353181, -0.0601759362765, -0.0460666153988, -0.033417255906, -0.0207620686126, -0.0136627961657, -0.008873845834, -0.00924040700271, -0.00959897299234, -0.0122760927684, -0.0134131792383, -0.0169861222268, -0.0197237904964, -0.0247929394164, -0.0274056341581, -0.0290372582108, -0.0230858292646},
{0.0, 0.384047660947, 0.296120799911, 0.212432468703, 0.136561707438, 0.0758675413252, 0.0277844248084, -0.00596868040294, -0.0302987351926, -0.0432929213814, -0.0490562834943, -0.0462652470517, -0.0403753213236, -0.030690892845, -0.022025662253, -0.0121577019191, -0.00457323646437, 0.00324370289428, 0.00777535572758, 0.0119010287281, 0.0130997070818, 0.0155933691883, 0.0177344547654, 0.023362610354, 0.028693981754, 0.0347549373256, 0.0352150834795, 0.0303066390313, 0.0145055938776, -0.0110543984149, -0.0508137742561, -0.102480420772},
0.2500
},
};

View File

@@ -0,0 +1,304 @@
/*
Cabinet.cc
Copyright 2002-7 Tim Goetze <tim@quitte.de>
http://quitte.de/dsp/
CabinetI - 16th order IIR filters modeled after various impulse responses
from Steve Harris' 'imp' plugin. Limited to 44.1 kHz sample rate.
CabinetII - 32nd order IIR filters modeled after the same impulse responses
using a different algorithm. Versions for 44.1 / 48 / 88.2 / 96 kHz sample
rates, switched at runtime.
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
#include "basics.h"
#include "Cabinet.h"
#include "Descriptor.h"
Model16
CabinetI::models [] =
{
{
1, /* identity */
{1},
{0},
1,
}, {
16, /* unmatched, off-axis */
{0.44334744339382504, 0.49764265352620912, 0.19936863766114088, 0.0038388115609433826, -0.072080744430548876, -0.092589757815768933, -0.023760971045285254, 0.058929802988203807, 0.11073296313735595, 0.14389046518738619, 0.052981055774577353, -0.11817321919764193, -0.22957467728465422, -0.26301284181138151, -0.25586853638823448, -0.10768194462289554},
{0.0, 0.71138736215182674, 0.19571088506350195, -0.086897678126924227, -0.18344238266155902, -0.13338660100611671, -0.017424587504494098, 0.062014975470330351, 0.077979014877680469, 0.039134631327482176, -0.046646061538403727, -0.040653480351542266, 0.068462230153713749, 0.14313162261479676, 0.058844200671679531, -0.17878548463526983},
1 / 4.4,
}, {
16, /* unmatched */
{0.58952373363852417, 0.43625942509584309, 0.235412203403113, 0.048252951521505258, -0.1002076253350956, -0.14467958356216026, -0.074304873528329402, 0.060945557247412331, 0.15959845551788171, 0.20970924189636411, 0.15361368007229703, 0.025339061869183478, -0.11824836967489599, -0.19384403813690479, -0.19295873693806628, -0.058904754435169508},
{0.0, 0.6596380198994729, 0.20825670531468143, -0.10508784276271754, -0.24698905246649294, -0.21519418569376192, -0.10057489587096909, -0.006175810942624007, -0.0019249936836196989, -0.045827268144865707, -0.10184708094147235, -0.09177820187618313, -0.017740067693127175, 0.058828573780348989, 0.010559807576350673, -0.19412688465216324},
1 / 5.7,
}, {
16, /* superchamp */
{0.5335550129666069, 0.61768496153556829, 0.2080126894040209, -0.0067283013134491337, -0.10433152421155355, -0.16159936513841233, -0.10593018443866807, -0.091854930675998661, 0.0023550324496513643, 0.14327516190088724, 0.14688292534697539, 0.089539872443625601, -0.053854769352683005, -0.15693904535289377, -0.083502230074838577, 0.0090113128614708465},
{0.0, 0.67745858591653696, 0.044802106922746734, -0.1932642251200547, -0.18922360327572652, -0.052980570047914469, 0.020446992577988904, -0.028951451474818618, -0.085212072485126716, -0.12241212382510525, -0.089259371449674357, 0.010469056928395087, 0.049019357277555603, 0.037212453438250505, -0.046374612934843573, -0.045133341919937828},
1 / 4.0,
}, {
16, /* fender-vibrolux-68 */
{0.68447985102464837, 0.61538710771230021, -0.28804707230137599, -0.23656583372846893, 0.083100874242250655, -0.027792816938813913, 0.055558334440593965, 0.044458161718059774, -0.25467542393376252, -0.33660492613760157, -0.024663941486403884, 0.1172751972420942, -0.021832135450802759, 0.21440383631745469, 0.20828506390107443, -0.10289957687018361},
{0.0, 0.50968169360260451, -0.48159141882733714, -0.10856607456906017, -0.026802006374108955, -0.24967309940249552, -0.21422424792787859, 0.019271890369619571, 0.08065394481240884, -0.061665636719946543, 0.031613782215493547, 0.069574436103950893, -0.07180222507768147, -0.14447996563879059, 0.012044815820150268, -0.0073237976200291044},
1 / 2.0,
}, {
16, /* marshall */
{0.38455665328113242, 0.50304089890302783, 0.33653970418909934, 0.085604896315814846, -0.070239383452479598, -0.10479060878654689, -0.060150883776583335, 0.030121882977878822, 0.12441775056532201, 0.18287316824579156, 0.17035705141865287, 0.10315414401519916, 0.036357097566567576, 0.024474446155666255, 0.042359967009557103, 0.059946316626725109},
{0.0, 0.68167571829534335, 0.16877527811114035, -0.17427551663276897, -0.25780056810728452, -0.16065744581310681, -0.032007062964857856, 0.033882840656718101, -0.0038880045892747792, -0.084876415098991506, -0.13865107122780057, -0.10073571899064113, -0.013199668806255366, 0.038170305284592504, -0.026492576852036546, -0.12667775510054707},
1 / 4.2,
}
};
/* //////////////////////////////////////////////////////////////////////// */
void
CabinetI::init()
{
h = 0;
model = 0;
}
void
CabinetI::switch_model (int m)
{
if (m < 0) m = 0;
else if (m > 5) m = 5;
model = m;
n = models[m].n;
a = models[m].a;
b = models[m].b;
gain = models[m].gain * DSP::db2lin (getport(2));
memset (x, 0, sizeof (x));
memset (y, 0, sizeof (y));
}
void
CabinetI::activate()
{
switch_model ((int) getport(1));
gain = models[model].gain * DSP::db2lin (getport(2));
}
template <sample_func_t F>
void
CabinetI::one_cycle (int frames)
{
sample_t * s = ports[0];
int m = (int) getport (1);
if (m != model) switch_model (m);
sample_t g = models[model].gain * DSP::db2lin (getport(2));
double gf = pow (g / gain, 1 / (double) frames);
sample_t * d = ports[3];
for (int i = 0; i < frames; ++i)
{
register cabinet_float out = s[i] + normal;
x[h] = out;
out *= a[0];
for (int j = 1, z = h - 1; j < n; --z, ++j)
{
z &= 15;
out += a[j] * x[z];
out += b[j] * y[z];
}
y[h] = out;
h = (h + 1) & 15;
F (d, i, gain * out, adding_gain);
gain *= gf;
}
}
/* //////////////////////////////////////////////////////////////////////// */
PortInfo
CabinetI::port_info [] =
{
{
"in",
INPUT | AUDIO,
{BOUNDED, -1, 1}
}, {
"model",
INPUT | CONTROL,
{BOUNDED | INTEGER | DEFAULT_1, 0, 5}
}, {
"gain (dB)",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -24, 24}
}, {
"out",
OUTPUT | AUDIO,
{0}
}
};
/* //////////////////////////////////////////////////////////////////////// */
template <> void
Descriptor<CabinetI>::setup()
{
UniqueID = 1766;
Label = "CabinetI";
Properties = HARD_RT;
Name = CAPS "CabinetI - Loudspeaker cabinet emulation";
Maker = "Tim Goetze <tim@quitte.de>";
Copyright = "GPL, 2002-7";
/* fill port info and vtable */
autogen();
}
/* CabinetII ////////////////////////////////////////////////////////////// */
#include "Cabinet-Models32.h"
void
CabinetII::init()
{
if (fs < 46000)
models = models44100;
else if (fs < 72000)
models = models48000;
else if (fs < 92000)
models = models88200;
else
models = models96000;
h = 0;
model = 0;
}
void
CabinetII::switch_model (int m)
{
model = m;
n = models[m].n;
a = models[m].a;
b = models[m].b;
gain = models[m].gain * DSP::db2lin (getport(2));
memset (x, 0, sizeof (x));
memset (y, 0, sizeof (y));
}
void
CabinetII::activate()
{
switch_model ((int) getport(1));
}
template <sample_func_t F>
void
CabinetII::one_cycle (int frames)
{
sample_t * s = ports[0];
int m = (int) getport (1);
if (m != model) switch_model (m);
sample_t g = models[model].gain * DSP::db2lin (getport(2));
double gf = pow (g / gain, 1 / (double) frames);
sample_t * d = ports[3];
for (int i = 0; i < frames; ++i)
{
register cabinet_float out = s[i] + normal;
x[h] = out;
out *= a[0];
for (int j = 1, z = h - 1; j < n; --z, ++j)
{
z &= 31;
out += a[j] * x[z];
out += b[j] * y[z];
}
y[h] = out;
h = (h + 1) & 31;
F (d, i, gain * out, adding_gain);
gain *= gf;
}
}
/* //////////////////////////////////////////////////////////////////////// */
PortInfo
CabinetII::port_info [] =
{
{
"in",
INPUT | AUDIO,
{BOUNDED, -1, 1}
}, {
"model",
INPUT | CONTROL,
{BOUNDED | INTEGER | DEFAULT_1, 0, 7}
}, {
"gain (dB)",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -24, 24}
}, {
"out",
OUTPUT | AUDIO,
{0}
}
};
/* //////////////////////////////////////////////////////////////////////// */
template <> void
Descriptor<CabinetII>::setup()
{
UniqueID = 2581;
Label = "CabinetII";
Properties = HARD_RT;
Name = CAPS "CabinetII - Refined loudspeaker cabinet emulation";
Maker = "Tim Goetze <tim@quitte.de>";
Copyright = "GPL, 2002-7";
/* fill port info and vtable */
autogen();
}

View File

@@ -0,0 +1,135 @@
/*
Cabinet.h
Copyright 2002-5 Tim Goetze <tim@quitte.de>
http://quitte.de/dsp/
CabinetI - 16th order IIR filters modeled after various impulse responses
from Steve Harris' 'imp' plugin. Limited to 44.1 kHz sample rate.
CabinetII - 32nd order IIR filters modeled after the same impulse responses
using a different algorithm. Versions for 44.1 / 48 / 88.2 / 96 kHz sample
rates, switched at runtime.
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
#ifndef _CABINET_H_
#define _CABINET_H_
#include "dsp/util.h"
/* cabinet_float sets the data type used for the IIR history and thus the
* computing precision. doubles tend to make the sound more vivid and lively.
* You can squeeze out a few extra cycles by making this 'float' if needed.
* Be warned though that CabinetII has not been tested with 32-bit floats and
* might become unstable due to the lower computing precision. */
typedef double cabinet_float;
typedef struct {
int n;
cabinet_float a[16], b[16];
float gain;
} Model16;
typedef struct {
int n;
cabinet_float a[32], b[32];
float gain;
} Model32;
class CabinetI
: public Plugin
{
public:
sample_t gain;
static Model16 models [];
int model;
void switch_model (int m);
int n, h;
cabinet_float * a, * b;
cabinet_float x[16], y[16];
template <sample_func_t F>
void one_cycle (int frames);
public:
static PortInfo port_info [];
void init();
void activate();
void run (int n)
{
one_cycle<store_func> (n);
}
void run_adding (int n)
{
one_cycle<adding_func> (n);
}
};
/* Second version with 32nd order filters precalculated for
* 44.1 / 48 / 88.2 / 96 kHz sample rates */
class CabinetII
: public Plugin
{
public:
sample_t gain;
static Model32 models44100 [];
static Model32 models48000 [];
static Model32 models88200 [];
static Model32 models96000 [];
Model32 * models;
int model;
void switch_model (int m);
int n, h;
cabinet_float * a, * b;
cabinet_float x[32], y[32];
template <sample_func_t F>
void one_cycle (int frames);
public:
static PortInfo port_info [];
sample_t adding_gain;
void init();
void activate();
void run (int n)
{
one_cycle<store_func> (n);
}
void run_adding (int n)
{
one_cycle<adding_func> (n);
}
};
#endif /* _CABINET_H_ */

View File

@@ -0,0 +1,506 @@
/*
Chorus.cc
Copyright 2004-7 Tim Goetze <tim@quitte.de>
http://quitte.de/dsp/
mono and mono-to-stereo chorus units.
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
#include "basics.h"
#include "Chorus.h"
#include "Descriptor.h"
template <sample_func_t F>
void
ChorusI::one_cycle (int frames)
{
sample_t * s = ports[0];
double one_over_n = 1 / (double) frames;
double ms = .001 * fs;
double t = time;
time = getport(1) * ms;
double dt = (time - t) * one_over_n;
double w = width;
width = getport(2) * ms;
/* clamp, or we need future samples from the delay line */
if (width >= t - 3) width = t - 3;
double dw = (width - w) * one_over_n;
if (rate != *ports[3])
lfo.set_f (max (rate = getport(3), .000001), fs, lfo.get_phase());
double blend = getport(4);
double ff = getport(5);
double fb = getport(6);
sample_t * d = ports[7];
DSP::FPTruncateMode truncate;
for (int i = 0; i < frames; ++i)
{
sample_t x = s[i];
/* truncate the feedback tap to integer, better quality for less
* cycles (just a bit of zipper when changing 't', but it does sound
* interesting) */
int ti;
fistp (t, ti);
x -= fb * delay[ti];
delay.put (x + normal);
# if 0
/* allpass delay sounds a little cleaner for a chorus
* but sucks big time when flanging. */
x = blend * x + ff * tap.get (delay, t + w * lfo.get());
# elif 0
/* linear interpolation */
x = blend * x + ff * delay.get_at (t + w * lfo.get());
# else
/* cubic interpolation */
x = blend * x + ff * delay.get_cubic (t + w * lfo.get());
# endif
F (d, i, x, adding_gain);
t += dt;
w += dw;
}
}
/* //////////////////////////////////////////////////////////////////////// */
PortInfo
ChorusI::port_info [] =
{
{
"in",
INPUT | AUDIO,
{BOUNDED, -1, 1}
}, {
"t (ms)",
INPUT | CONTROL,
{BOUNDED | LOG | DEFAULT_LOW, 2.5, 40}
}, {
"width (ms)",
INPUT | CONTROL,
{BOUNDED | DEFAULT_1, .5, 10}
}, {
"rate (Hz)",
INPUT | CONTROL,
{BOUNDED | DEFAULT_LOW, 0, 5}
}, {
"blend",
INPUT | CONTROL,
{BOUNDED | DEFAULT_1, 0, 1}
}, {
"feedforward",
INPUT | CONTROL,
{BOUNDED | DEFAULT_LOW, 0, 1}
}, {
"feedback",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, 0, 1}
}, {
"out",
OUTPUT | AUDIO,
{0}
}
};
template <> void
Descriptor<ChorusI>::setup()
{
UniqueID = 1767;
Label = "ChorusI";
Properties = HARD_RT;
Name = CAPS "ChorusI - Mono chorus/flanger";
Maker = "Tim Goetze <tim@quitte.de>";
Copyright = "GPL, 2004-7";
/* fill port info and vtable */
autogen();
}
/* //////////////////////////////////////////////////////////////////////// */
template <sample_func_t F>
void
StereoChorusI::one_cycle (int frames)
{
sample_t * s = ports[0];
double one_over_n = 1 / (double) frames;
double ms = .001 * fs;
double t = time;
time = getport(1) * ms;
double dt = (time - t) * one_over_n;
double w = width;
width = getport(2) * ms;
/* clamp, or we need future samples from the delay line */
if (width >= t - 1) width = t - 1;
double dw = (width - w) * one_over_n;
if (rate != *ports[3] && phase != *ports[4])
{
rate = getport(3);
phase = getport(4);
double phi = left.lfo.get_phase();
left.lfo.set_f (max (rate, .000001), fs, phi);
right.lfo.set_f (max (rate, .000001), fs, phi + phase * M_PI);
}
double blend = getport(5);
double ff = getport(6);
double fb = getport(7);
sample_t * dl = ports[8];
sample_t * dr = ports[9];
/* to go sure (on i386) that the fistp instruction does the right thing
* when looking up fractional sample indices */
DSP::FPTruncateMode truncate;
for (int i = 0; i < frames; ++i)
{
sample_t x = s[i];
/* truncate the feedback tap to integer, better quality for less
* cycles (just a bit of zipper when changing 't', but it does sound
* interesting) */
int ti;
fistp (t, ti);
x -= fb * delay[ti];
delay.put (x + normal);
sample_t l = blend * x + ff * delay.get_cubic (t + w * left.lfo.get());
sample_t r = blend * x + ff * delay.get_cubic (t + w * right.lfo.get());
F (dl, i, l, adding_gain);
F (dr, i, r, adding_gain);
t += dt;
w += dw;
}
}
/* //////////////////////////////////////////////////////////////////////// */
PortInfo
StereoChorusI::port_info [] =
{
{
"in",
INPUT | AUDIO,
{BOUNDED, -1, 1}
}, {
"t (ms)",
INPUT | CONTROL,
{BOUNDED | DEFAULT_MIN, 2.5, 40}
}, {
"width (ms)",
INPUT | CONTROL,
{BOUNDED | DEFAULT_1, .5, 10}
}, {
"rate (Hz)",
INPUT | CONTROL,
{BOUNDED | DEFAULT_LOW, 0, 5}
}, {
"phase",
INPUT | CONTROL,
{BOUNDED | DEFAULT_MAX, 0, 1}
}, {
"blend",
INPUT | CONTROL,
{BOUNDED | DEFAULT_1, 0, 1}
}, {
"feedforward",
INPUT | CONTROL,
{BOUNDED | DEFAULT_LOW, 0, 1}
}, {
"feedback",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, 0, 1}
}, {
"out:l",
OUTPUT | AUDIO,
{0}
}, {
"out:r",
OUTPUT | AUDIO,
{0}
}
};
template <> void
Descriptor<StereoChorusI>::setup()
{
UniqueID = 1768;
Label = "StereoChorusI";
Properties = HARD_RT;
Name = CAPS "StereoChorusI - Stereo chorus/flanger";
Maker = "Tim Goetze <tim@quitte.de>";
Copyright = "GPL, 2004-7";
/* fill port info and vtable */
autogen();
}
/* //////////////////////////////////////////////////////////////////////// */
template <sample_func_t F>
void
ChorusII::one_cycle (int frames)
{
sample_t * s = ports[0];
double one_over_n = 1 / (double) frames;
double ms = .001 * fs;
double t = time;
time = getport(1) * ms;
double dt = (time - t) * one_over_n;
double w = width;
width = getport(2) * ms;
/* clamp, or we need future samples from the delay line */
if (width >= t - 3) width = t - 3;
double dw = (width - w) * one_over_n;
if (rate != *ports[3])
set_rate (*ports[3]);
double blend = getport(4);
double ff = getport(5);
double fb = getport(6);
sample_t * d = ports[7];
DSP::FPTruncateMode truncate;
for (int i = 0; i < frames; ++i)
{
sample_t x = s[i];
x -= fb * delay.get_cubic (t);
delay.put (filter.process (x + normal));
double a = 0;
for (int j = 0; j < Taps; ++j)
a += taps[j].get (delay, t, w);
x = blend * x + ff * a;
F (d, i, x, adding_gain);
t += dt;
w += dw;
}
}
/* //////////////////////////////////////////////////////////////////////// */
PortInfo
ChorusII::port_info [] =
{
{
"in",
INPUT | AUDIO,
{BOUNDED, -1, 1}
}, {
"t (ms)",
INPUT | CONTROL,
{BOUNDED | LOG | DEFAULT_LOW, 2.5, 40}
}, {
"width (ms)",
INPUT | CONTROL,
{BOUNDED | DEFAULT_1, .5, 10}
}, {
"rate",
INPUT | CONTROL,
{BOUNDED | DEFAULT_LOW, 0, 1}
}, {
"blend",
INPUT | CONTROL,
{BOUNDED | DEFAULT_1, 0, 1}
}, {
"feedforward",
INPUT | CONTROL,
{BOUNDED | DEFAULT_LOW, 0, 1}
}, {
"feedback",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, 0, 1}
}, {
"out",
OUTPUT | AUDIO,
{0}
}
};
template <> void
Descriptor<ChorusII>::setup()
{
UniqueID = 2583;
Label = "ChorusII";
Properties = HARD_RT;
Name = CAPS "ChorusII - Mono chorus/flanger modulated by a fractal";
Maker = "Tim Goetze <tim@quitte.de>";
Copyright = "GPL, 2004-7";
/* fill port info and vtable */
autogen();
}
/* //////////////////////////////////////////////////////////////////////// */
template <sample_func_t F>
void
StereoChorusII::one_cycle (int frames)
{
sample_t * s = ports[0];
double one_over_n = 1 / (double) frames;
double ms = .001 * fs;
double t = time;
time = getport(1) * ms;
double dt = (time - t) * one_over_n;
double w = width;
width = getport(2) * ms;
/* clamp, or we need future samples from the delay line */
if (width >= t - 1) width = t - 1;
double dw = (width - w) * one_over_n;
set_rate (*ports[3]);
double blend = getport(4);
double ff = getport(5);
double fb = getport(6);
sample_t * dl = ports[7];
sample_t * dr = ports[8];
/* to go sure (on i386) that the fistp instruction does the right thing
* when looking up fractional sample indices */
DSP::FPTruncateMode truncate;
for (int i = 0; i < frames; ++i)
{
sample_t x = s[i];
/* truncate the feedback tap to integer, better quality for less
* cycles (just a bit of zipper when changing 't', but it does sound
* interesting) */
int ti;
fistp (t, ti);
x -= fb * delay[ti];
delay.put (x + normal);
double m;
m = left.lfo_lp.process (left.fractal.get());
sample_t l = blend * x + ff * delay.get_cubic (t + w * m);
m = right.lfo_lp.process (right.fractal.get());
sample_t r = blend * x + ff * delay.get_cubic (t + w * m);
F (dl, i, l, adding_gain);
F (dr, i, r, adding_gain);
t += dt;
w += dw;
}
}
/* //////////////////////////////////////////////////////////////////////// */
PortInfo
StereoChorusII::port_info [] =
{
{
"in",
INPUT | AUDIO,
{BOUNDED, -1, 1}
}, {
"t (ms)",
INPUT | CONTROL,
{BOUNDED | DEFAULT_LOW, 2.5, 40}
}, {
"width (ms)",
INPUT | CONTROL,
{BOUNDED | DEFAULT_LOW, .5, 10}
}, {
"rate",
INPUT | CONTROL,
{BOUNDED | DEFAULT_LOW, 0, 1}
}, {
"blend",
INPUT | CONTROL,
{BOUNDED | DEFAULT_1, 0, 1}
}, {
"feedforward",
INPUT | CONTROL,
{BOUNDED | DEFAULT_MID, 0, 1}
}, {
"feedback",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, 0, 1}
}, {
"out:l",
OUTPUT | AUDIO,
{0}
}, {
"out:r",
OUTPUT | AUDIO,
{0}
}
};
template <> void
Descriptor<StereoChorusII>::setup()
{
UniqueID = 2584;
Label = "StereoChorusII";
Properties = HARD_RT;
Name = CAPS "StereoChorusII - Stereo chorus/flanger modulated by a fractal";
Maker = "Tim Goetze <tim@quitte.de>";
Copyright = "GPL, 2004-7";
/* fill port info and vtable */
autogen();
}

View File

@@ -0,0 +1,301 @@
/*
Chorus.h
Copyright 2004-5 Tim Goetze <tim@quitte.de>
http://quitte.de/dsp/
mono and stereo chorus/flanger units, traditional designs and some
differentiated a bit further.
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
#ifndef _CHORUS_H_
#define _CHORUS_H_
#include "dsp/Sine.h"
#include "dsp/Roessler.h"
#include "dsp/Lorenz.h"
#include "dsp/Delay.h"
#include "dsp/OnePole.h"
#include "dsp/BiQuad.h"
#include "dsp/RBJ.h"
class ChorusStub
: public Plugin
{
public:
sample_t time, width, rate;
};
class ChorusI
: public ChorusStub
{
public:
DSP::Sine lfo;
DSP::Delay delay;
DSP::DelayTapA tap;
template <sample_func_t>
void one_cycle (int frames);
public:
static PortInfo port_info [];
void init()
{
rate = .15;
delay.init ((int) (.040 * fs));
}
void activate()
{
time = 0;
width = 0;
rate = *ports[3];
delay.reset();
tap.reset();
lfo.set_f (rate, fs, 0);
}
void run (int n)
{
one_cycle<store_func> (n);
}
void run_adding (int n)
{
one_cycle<adding_func> (n);
}
};
class StereoChorusI
: public ChorusStub
{
public:
sample_t rate;
sample_t phase;
DSP::Delay delay;
struct {
DSP::Sine lfo;
DSP::DelayTapA tap;
} left, right;
template <sample_func_t>
void one_cycle (int frames);
public:
static PortInfo port_info [];
void init()
{
rate = .15;
phase = .5; /* pi */
delay.init ((int) (.040 * fs));
}
void activate()
{
time = 0;
width = 0;
delay.reset();
left.tap.reset();
right.tap.reset();
left.lfo.set_f (rate, fs, 0);
right.lfo.set_f (rate, fs, phase * M_PI);
}
void run (int n)
{
one_cycle<store_func> (n);
}
void run_adding (int n)
{
one_cycle<adding_func> (n);
}
};
/* //////////////////////////////////////////////////////////////////////// */
#define FRACTAL_RATE 0.02
/* fractally modulated Chorus units */
class FracTap
{
public:
DSP::Lorenz f1;
DSP::Roessler f2;
DSP::OnePoleLP lp;
void init (double fs)
{
lp.set_f (30. / fs);
f1.init (.001, frandom());
f2.init (.001, frandom());
}
void set_rate (sample_t r)
{
f1.set_rate (r * FRACTAL_RATE);
f2.set_rate (3.3 * r * FRACTAL_RATE);
}
/* t = time, w = width, should inline nicely */
sample_t get (DSP::Delay & d, double t, double w)
{
double m = lp.process (f1.get() + .3 * f2.get());
return d.get_cubic (t + w * m);
}
};
class ChorusII
: public ChorusStub
{
public:
enum {
Taps = 1
};
FracTap taps[Taps];
DSP::BiQuad filter;
DSP::Delay delay;
template <sample_func_t>
void one_cycle (int frames);
void set_rate (sample_t r)
{
rate = r;
for (int i = 0; i < Taps; ++i)
{
taps[i].set_rate (rate * (i * FRACTAL_RATE) / Taps);
// fprintf (stderr, "[%d] %.3f\n", i, (rate * (i * FRACTAL_RATE) / Taps));
}
}
public:
static PortInfo port_info [];
void init()
{
delay.init ((int) (.040 * fs));
for (int i = 0; i < Taps; ++i)
taps[i].init (fs);
DSP::RBJ::HiShelve (1000. / fs, 1., 6, filter.a, filter.b);
}
void activate()
{
time = 0;
width = 0;
set_rate (*ports[3]);
delay.reset();
filter.reset();
}
void run (int n)
{
one_cycle<store_func> (n);
}
void run_adding (int n)
{
one_cycle<adding_func> (n);
}
};
class StereoChorusII
: public ChorusStub
{
public:
sample_t rate;
sample_t phase;
DSP::Delay delay;
struct {
DSP::Roessler fractal;
DSP::OnePoleLP lfo_lp;
DSP::DelayTapA tap;
} left, right;
template <sample_func_t>
void one_cycle (int frames);
void set_rate (sample_t r)
{
rate = r;
left.fractal.set_rate (rate * FRACTAL_RATE);
right.fractal.set_rate (rate * FRACTAL_RATE);
left.lfo_lp.set_f (3. / fs);
right.lfo_lp.set_f (3. / fs);
}
public:
static PortInfo port_info [];
sample_t adding_gain;
void init()
{
phase = .5; /* pi */
delay.init ((int) (.040 * fs));
left.fractal.init (.001, frandom());
right.fractal.init (.001, frandom());
}
void activate()
{
time = 0;
width = 0;
delay.reset();
left.tap.reset();
right.tap.reset();
set_rate (*ports[3]);
}
void run (int n)
{
one_cycle<store_func> (n);
}
void run_adding (int n)
{
one_cycle<adding_func> (n);
}
};
#endif /* _CHORUS_H_ */

View File

@@ -0,0 +1,233 @@
/*
Click.cc
Copyright 2002-7 Tim Goetze <tim@quitte.de>
http://quitte.de/dsp/
Plugins playing a sound snippet in regular intervals.
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
#include "basics.h"
#include "waves/click.h"
#include "waves/money.h"
#include "Click.h"
#include "Descriptor.h"
void
ClickStub::init (float * _wave, int _N)
{
wave = _wave;
N = _N;
bpm = -1;
}
template <sample_func_t F>
void
ClickStub::one_cycle (int frames)
{
bpm = getport(0);
sample_t gain = getport(1) * *ports[1];
lp.set (1 - *ports[2]);
sample_t * d = ports[3];
while (frames)
{
if (period == 0)
{
period = (int) (fs * 60 / bpm);
played = 0;
}
int n = min (frames, period);
if (played < N)
{
n = min (n, N - played);
for (int i = 0; i < n; ++i)
{
sample_t x = gain * wave [played + i] + normal;
F (d, i, lp.process (x), adding_gain);
normal = -normal;
}
played += n;
}
else
{
for (int i = 0; i < n; ++i)
{
F (d, i, lp.process (normal), adding_gain);
normal = -normal;
}
}
period -= n;
frames -= n;
d += n;
}
}
/* //////////////////////////////////////////////////////////////////////// */
PortInfo
ClickStub::port_info [] =
{
{
"bpm",
INPUT | CONTROL,
{BOUNDED | DEFAULT_LOW, 4, 244}
}, {
"volume",
INPUT | CONTROL,
{BOUNDED | DEFAULT_MID, 0, 1}
}, {
"damping",
INPUT | CONTROL,
{BOUNDED | DEFAULT_HIGH, 0, 1}
}, {
"out",
OUTPUT | AUDIO,
{0}
}
};
/* //////////////////////////////////////////////////////////////////////// */
#define LENGTH(W) ((int) (sizeof (W) / sizeof (float)))
void
Click::init()
{
this->ClickStub::init (click, LENGTH (click));
}
template <> void
Descriptor<Click>::setup()
{
UniqueID = 1769;
Label = "Click";
Properties = HARD_RT;
Name = CAPS "Click - Metronome";
Maker = "Tim Goetze <tim@quitte.de>";
Copyright = "GPL, 2004-7";
/* fill port info and vtable */
autogen();
}
/* //////////////////////////////////////////////////////////////////////// */
PortInfo
CEO::port_info [] =
{
{
"mpm",
INPUT | CONTROL,
{BOUNDED | DEFAULT_HIGH, 4, 244}
}, {
"volume",
INPUT | CONTROL,
{BOUNDED | DEFAULT_MID, 0, 1}
}, {
"damping",
INPUT | CONTROL,
{BOUNDED | DEFAULT_MIN, 0, 1}
}, {
"out",
OUTPUT | AUDIO,
{0}
}
};
void
CEO::init()
{
this->ClickStub::init (money, LENGTH (money));
}
template <> void
Descriptor<CEO>::setup()
{
UniqueID = 1770;
Label = "CEO";
Properties = HARD_RT;
Name = CAPS "CEO - Chief Executive Oscillator";
Maker = "Tim Goetze <tim@quitte.de>";
Copyright = "GPL, 2004-7";
/* fill port info and vtable */
autogen();
}
/* //////////////////////////////////////////////////////////////////////// */
float dirac [] = { 1, };
PortInfo
Dirac::port_info [] =
{
{
"ppm",
INPUT | CONTROL,
{BOUNDED | DEFAULT_MIN, 30, 60}
}, {
"volume",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, 0, 1}
}, {
"damping",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, 0, 1}
}, {
"out",
OUTPUT | AUDIO,
{0}
}
};
void
Dirac::init()
{
this->ClickStub::init (dirac, LENGTH (dirac));
}
template <> void
Descriptor<Dirac>::setup()
{
UniqueID = 2585;
Label = "Dirac";
Properties = HARD_RT;
Name = CAPS "Dirac - One-sample impulse generator";
Maker = "Tim Goetze <tim@quitte.de>";
Copyright = "GPL, 2004-7";
/* fill port info and vtable */
autogen();
}

View File

@@ -0,0 +1,98 @@
/*
Click.h
Copyright 2004-5 Tim Goetze <tim@quitte.de>
http://quitte.de/dsp/
units perpetually repeating a recorded sample.
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
#ifndef _CLICK_H_
#define _CLICK_H_
#include "dsp/OnePole.h"
#include "dsp/util.h"
class ClickStub
: public Plugin
{
public:
sample_t bpm;
float * wave;
int N; /* number of samples in wave */
DSP::OnePoleLP lp;
int period; /* frames remaining in period */
int played; /* frames played from sample */
template <sample_func_t F>
void one_cycle (int frames);
public:
static PortInfo port_info [];
void init (float * _wave, int _N);
void activate()
{
played = 0;
period = 0;
}
void run (int n)
{
one_cycle<store_func> (n);
}
void run_adding (int n)
{
one_cycle<adding_func> (n);
}
};
class Click
: public ClickStub
{
public:
void init();
};
class CEO
: public ClickStub
{
public:
void init();
static PortInfo port_info [];
};
class Dirac
: public ClickStub
{
public:
void init();
static PortInfo port_info [];
};
#endif /* _CLICK_H_ */

View File

@@ -0,0 +1,148 @@
/*
Clip.cc
Copyright 2003-7 Tim Goetze <tim@quitte.de>
http://quitte.de/dsp/
simple oversampled hard clipper
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
#include "basics.h"
#include "Clip.h"
#include "Descriptor.h"
void
Clip::init()
{
gain = 1;
threshold[0] = -.9;
threshold[1] = +.9;
/* going a bit lower than nominal with fc */
double f = .5 * M_PI / OVERSAMPLE;
/* construct the upsampler filter kernel */
DSP::sinc (f, up.c, FIR_SIZE);
DSP::kaiser<DSP::apply_window> (up.c, FIR_SIZE, 6.4);
/* copy upsampler filter kernel for downsampler, make sum */
double s = 0;
for (int i = 0; i < up.n; ++i)
down.c[i] = up.c[i],
s += up.c[i];
/* scale downsampler kernel for unity gain */
s = 1 / s;
for (int i = 0; i < down.n; ++i)
down.c[i] *= s;
/* scale upsampler kernel for unity gain */
s *= OVERSAMPLE;
for (int i = 0; i < up.n; ++i)
up.c[i] *= s;
}
inline sample_t
Clip::clip (sample_t a)
{
if (a < threshold[0])
return threshold[0];
if (a > threshold[1])
return threshold[1];
return a;
}
template <sample_func_t F>
void
Clip::one_cycle (int frames)
{
sample_t * s = ports[0];
double g = getport (1);
double gf;
if (g == gain_db)
gf = 1;
else
{
gain_db = g;
sample_t g = DSP::db2lin (gain_db);
gf = pow (g / gain, 1 / (double) frames);
}
sample_t * d = ports[2];
*ports[3] = OVERSAMPLE;
for (int i = 0; i < frames; ++i)
{
register sample_t a = gain * s[i];
a = down.process (clip (up.upsample (a)));
for (int o = 1; o < OVERSAMPLE; ++o)
down.store (clip (up.pad (o)));
F (d, i, a, adding_gain);
gain *= gf;
}
}
/* //////////////////////////////////////////////////////////////////////// */
PortInfo
Clip::port_info [] =
{
{
"in",
INPUT | AUDIO,
{BOUNDED, -1, 1}
}, {
"gain (dB)",
INPUT | CONTROL,
{BOUNDED | DEFAULT_1, -72, 72}
}, {
"out",
OUTPUT | AUDIO,
{0}
}, {
"latency",
OUTPUT | CONTROL,
{0}
}
};
template <> void
Descriptor<Clip>::setup()
{
UniqueID = 1771;
Label = "Clip";
Properties = HARD_RT;
Name = CAPS "Clip - Hard clipper, 8x oversampled";
Maker = "Tim Goetze <tim@quitte.de>";
Copyright = "GPL, 2003-7";
/* fill port info and vtable */
autogen();
}

View File

@@ -0,0 +1,87 @@
/*
Clip.h
Copyright 2004-5 Tim Goetze <tim@quitte.de>
http://quitte.de/dsp/
oversampled hard ('diode', 'transistor', sometimes 'op-amp') clipper.
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
#ifndef _CLIP_H_
#define _CLIP_H_
#include "dsp/util.h"
#include "dsp/FIR.h"
#include "dsp/sinc.h"
#include "dsp/windows.h"
class Clip
: public Plugin
{
public:
sample_t gain, gain_db;
sample_t threshold[2];
enum {
OVERSAMPLE = 8,
FIR_SIZE = 64,
};
/* antialias filters */
DSP::FIRUpsampler up;
DSP::FIR down;
template <sample_func_t F>
void one_cycle (int frames);
inline sample_t clip (sample_t x);
public:
static PortInfo port_info[];
Clip()
: up (FIR_SIZE, OVERSAMPLE),
down (FIR_SIZE)
{ }
void init();
void activate()
{
up.reset();
down.reset();
gain_db = *ports[1];
gain = DSP::db2lin (gain_db);
}
void run (int n)
{
one_cycle<store_func> (n);
}
void run_adding (int n)
{
one_cycle<adding_func> (n);
}
};
#endif /* _CLIP_H_ */

View File

@@ -0,0 +1,153 @@
/*
Compress.cc
Copyright 2004-7 Tim Goetze <tim@quitte.de>
http://quitte.de/dsp/
mono compressor suitable for solo instruments. adaptation of Steve
Harris' 'sc1' unit, with minor tweaks: table lookup for attack and
release time to frames mapping replaced with exp. port order changed.
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
#include "basics.h"
#include "Compress.h"
#include "Descriptor.h"
template <sample_func_t F>
void
Compress::one_cycle (int frames)
{
sample_t * s = ports[0];
sample_t range = DSP::db2lin (getport(1));
sample_t ratio = (*ports[2] - 1) / getport(2);
/* sc1 has lookup tables here, and they're only 40 % used (400 ms/1 s).
* thus, sc1's attack/release controls are a bit coarse due to truncation
* error. calling exp() once per cycle doesn't seem too expensive.
*
* besides, i have a suspicion that the attack and release parameters
* don't work like they should. if they, as the code suggests, control
* an exponential envelope fade, pow (.5, n_frames) or something like
* that seems more appropriate. so ...
*
* TODO: check whether these parameters work like they should, try pow()
*/
double ga = exp (-1 / (fs * getport(3)));
double gr = exp (-1 / (fs * getport(4)));
sample_t threshold = getport(5);
sample_t knee = getport(6);
sample_t * d = ports[7];
sample_t knee0 = DSP::db2lin (threshold - knee);
sample_t knee1 = DSP::db2lin (threshold + knee);
sample_t ef_a = ga * .25;
sample_t ef_ai = 1 - ef_a;
for (int i = 0; i < frames; ++i)
{
sum += s[i] * s[i];
if (amp > env)
env = env * ga + amp * (1 - ga);
else
env = env * gr + amp * (1 - gr);
if ((count++ & 3) == 3)
{
amp = rms.process (sum * .25);
sum = 0;
if (env < knee0)
gain_t = 1;
else if (env < knee1)
{
float x = -(threshold - knee - DSP::lin2db (env)) / knee;
gain_t = DSP::db2lin (-knee * ratio * x * x * 0.25f);
}
else
gain_t = DSP::db2lin ((threshold - DSP::lin2db (env)) * ratio);
}
gain = gain * ef_a + gain_t * ef_ai;
F (d, i, s[i] * gain * range, adding_gain);
}
}
/* //////////////////////////////////////////////////////////////////////// */
PortInfo
Compress::port_info [] =
{
{
"in",
INPUT | AUDIO,
{BOUNDED, -1, 1}
}, {
"gain (dB)",
INPUT | CONTROL,
{BOUNDED | DEFAULT_LOW, 0, 24}
}, {
"ratio (1:n)",
INPUT | CONTROL,
{BOUNDED | DEFAULT_MIN, 1, 10}
}, {
"attack (s)",
INPUT | CONTROL,
{BOUNDED | DEFAULT_MIN, .001, 1}
}, {
"release (s)",
INPUT | CONTROL,
{BOUNDED | DEFAULT_MID, .001, 1}
}, {
"threshold (dB)",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -30, 400}
}, {
"knee radius (dB)",
INPUT | CONTROL,
{BOUNDED | DEFAULT_LOW, 1, 10}
}, {
"out",
OUTPUT | AUDIO,
{0}
}
};
template <> void
Descriptor<Compress>::setup()
{
UniqueID = 1772;
Label = "Compress";
Properties = HARD_RT;
Name = CAPS "Compress - Mono compressor";
Maker = "Tim Goetze <tim@quitte.de>, Steve Harris <steve@plugin.org.uk>";
Copyright = "GPL, 2004-7";
/* fill port info and vtable */
autogen();
}

View File

@@ -0,0 +1,78 @@
/*
Compress.h
Copyright 2004-5 Tim Goetze <tim@quitte.de>, Steve Harris
http://quitte.de/dsp/
mono compressor.
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
#ifndef _COMPRESS_H_
#define _COMPRESS_H_
#include "dsp/RMS.h"
#include "dsp/util.h"
class Compress
: public Plugin
{
public:
double fs;
sample_t f;
DSP::RMS rms;
sample_t sum, amp, env, gain, gain_t;
int count;
template <sample_func_t F>
void one_cycle (int frames);
public:
static PortInfo port_info [];
void init() {}
void activate()
{
rms.reset();
sum = 0;
count = 0;
amp = 0;
env = 0;
gain = 0;
gain_t = 0;
}
void run (int n)
{
one_cycle<store_func> (n);
}
void run_adding (int n)
{
one_cycle<adding_func> (n);
}
};
#endif /* _COMPRESS_H_ */

View File

@@ -0,0 +1,222 @@
/*
Descriptor.h
Copyright 2004-10 Tim Goetze <tim@quitte.de>
http://quitte.de/dsp/
Creating a LADSPA_Descriptor for a CAPS plugin via a C++ template,
saving a virtual function call compared to the usual method used
for C++ plugins in a C context.
Descriptor<P> expects P to declare some common methods, like init(),
activate() etc, plus a static port_info[] and LADSPA_Data * ports[]
and adding_gain. (P should derive from Plugin, too.)
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
#ifndef _DESCRIPTOR_H_
#define _DESCRIPTOR_H_
#ifdef __SSE__
#include <xmmintrin.h>
#endif
#ifdef __SSE3__
#include <pmmintrin.h>
#endif
/* common stub for Descriptor makes it possible to delete() without special-
* casing for every plugin class.
*/
class DescriptorStub
: public LADSPA_Descriptor
{
public:
DescriptorStub()
{
PortCount = 0;
}
~DescriptorStub()
{
if (PortCount)
{
delete [] PortNames;
delete [] PortDescriptors;
delete [] PortRangeHints;
}
}
};
inline void
processor_specific_denormal_measures()
{
#ifdef __SSE3__
/* this one works reliably on a 6600 Core2 */
_MM_SET_DENORMALS_ZERO_MODE (_MM_DENORMALS_ZERO_ON);
#endif
#ifdef __SSE__
/* this one doesn't ... */
_MM_SET_FLUSH_ZERO_MODE (_MM_FLUSH_ZERO_ON);
#endif
}
template <class T>
class Descriptor
: public DescriptorStub
{
public:
LADSPA_PortRangeHint * ranges;
public:
Descriptor() { setup(); }
void setup();
void autogen()
{
PortCount = (sizeof (T::port_info) / sizeof (PortInfo));
/* unroll PortInfo members */
const char ** names = new const char * [PortCount];
LADSPA_PortDescriptor * desc = new LADSPA_PortDescriptor [PortCount];
ranges = new LADSPA_PortRangeHint [PortCount];
/* could also assign directly but const_cast is ugly. */
for (int i = 0; i < (int) PortCount; ++i)
{
names[i] = T::port_info[i].name;
desc[i] = T::port_info[i].descriptor;
ranges[i] = T::port_info[i].range;
}
PortNames = names;
PortDescriptors = desc;
PortRangeHints = ranges;
/* LADSPA_Descriptor vtable entries */
instantiate = _instantiate;
connect_port = _connect_port;
activate = _activate;
run = _run;
run_adding = _run_adding;
set_run_adding_gain = _set_run_adding_gain;
deactivate = 0;
cleanup = _cleanup;
}
static LADSPA_Handle _instantiate (
const struct _LADSPA_Descriptor * d, ulong fs)
{
T * plugin = new T();
int n = (int) d->PortCount;
LADSPA_PortRangeHint * ranges = ((Descriptor *) d)->ranges;
plugin->ranges = ranges;
plugin->ports = new sample_t * [n];
/* connect to lower bound as a safety measure */
for (int i = 0; i < n; ++i)
plugin->ports[i] = &(ranges[i].LowerBound);
plugin->fs = fs;
plugin->normal = NOISE_FLOOR;
plugin->init();
return plugin;
}
static void _connect_port (LADSPA_Handle h, ulong i, LADSPA_Data * p)
{
((T *) h)->ports[i] = p;
}
static void _activate (LADSPA_Handle h)
{
T * plugin = (T *) h;
plugin->first_run = 1;
/* since none of the plugins do any RT-critical work in
* activate(), it's safe to defer the actual call to the
* plugin's activate() method for the first run() after
* the host called in here.
*
* It's the simplest way to prevent a parameter smoothing sweep
* in the first audio block after activation.
plugin->activate();
*/
}
static void _run (LADSPA_Handle h, ulong n)
{
T * plugin = (T *) h;
/* We don't reset the processor flags later, it's true. */
processor_specific_denormal_measures();
/* If this is the first audio block after activation,
* initialize the plugin from the current set of parameters. */
if (plugin->first_run)
{
plugin->activate();
plugin->first_run = 0;
}
plugin->run (n);
plugin->normal = -plugin->normal;
}
static void _run_adding (LADSPA_Handle h, ulong n)
{
T * plugin = (T *) h;
/* We don't reset the processor flags later, it's true. */
processor_specific_denormal_measures();
/* If this is the first audio block after activation,
* initialize the plugin from the current set of parameters. */
if (plugin->first_run)
{
plugin->activate();
plugin->first_run = 0;
}
plugin->run_adding (n);
plugin->normal = -plugin->normal;
}
static void _set_run_adding_gain (LADSPA_Handle h, LADSPA_Data g)
{
T * plugin = (T *) h;
plugin->adding_gain = g;
}
static void _cleanup (LADSPA_Handle h)
{
T * plugin = (T *) h;
delete [] plugin->ports;
delete plugin;
}
};
#endif /* _DESCRIPTOR_H_ */

View File

@@ -0,0 +1,335 @@
/*
Eq.cc
Copyright 2002-7 Tim Goetze <tim@quitte.de>
http://quitte.de/dsp/
10-band octave-spread equalizer.
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
#include "basics.h"
#include <stdio.h>
#include "Eq.h"
#include "Descriptor.h"
/* slight adjustments to gain to keep response optimally flat at
* 0 dB gain in all bands */
inline static double
adjust_gain (int i, double g)
{
static float adjust[] = {
0.69238604707174034, 0.67282771124180096,
0.67215187672467813, 0.65768648447259315,
0.65988083755898952, 0.66359580101701909,
0.66485139160960427, 0.65890297086039662,
0.6493229390740376, 0.82305724539749325
};
return g * adjust[i];
}
#define Q 1.414
void
Eq::init()
{
eq.init (fs, Q);
}
void
Eq::activate()
{
for (int i = 0; i < 10; ++i)
{
gain[i] = getport (1 + i);
eq.gain[i] = adjust_gain (i, DSP::db2lin (gain[i]));
eq.gf[i] = 1;
}
}
template <sample_func_t F>
void
Eq::one_cycle (int frames)
{
sample_t * s = ports[0];
/* evaluate band gain changes and compute recursion factor to prevent
* zipper noise */
double one_over_n = frames > 0 ? 1. / frames : 1;
for (int i = 0; i < 10; ++i)
{
sample_t g = getport (1 + i);
if (g == gain[i])
{
/* no gain factoring */
eq.gf[i] = 1;
continue;
}
gain[i] = g;
double want = adjust_gain (i, DSP::db2lin (g));
eq.gf[i] = pow (want / eq.gain[i], one_over_n);
}
sample_t * d = ports[11];
for (int i = 0; i < frames; ++i)
{
sample_t x = s[i];
x = eq.process (x);
F (d, i, x, adding_gain);
}
eq.normal = -normal;
eq.flush_0();
}
/* //////////////////////////////////////////////////////////////////////// */
PortInfo
Eq::port_info [] =
{
{
"in",
INPUT | AUDIO,
{0, -1, 1}
}, {
"31 Hz",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -48, 24}
}, {
"63 Hz",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -48, 24}
}, {
"125 Hz",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -48, 24}
}, {
"250 Hz",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -48, 24}
}, {
"500 Hz",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -48, 24}
}, {
"1 kHz",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -48, 24}
}, {
"2 kHz",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -48, 24}
}, {
"4 kHz",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -48, 24}
}, {
"8 kHz",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -48, 24}
}, {
"16 kHz",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -48, 24}
}, {
"out",
OUTPUT | AUDIO,
{0}
}
};
template <> void
Descriptor<Eq>::setup()
{
UniqueID = 1773;
Label = "Eq";
Properties = HARD_RT;
Name = CAPS "Eq - 10-band equalizer";
Maker = "Tim Goetze <tim@quitte.de>";
Copyright = "GPL, 2004-7";
/* fill port info and vtable */
autogen();
}
/* //////////////////////////////////////////////////////////////////////// */
void
Eq2x2::init()
{
for (int c = 0; c < 2; ++c)
eq[c].init (fs, Q);
}
void
Eq2x2::activate()
{
/* Fetch current parameter settings so we won't sweep band gains in the
* first block to process.
*/
for (int i = 0; i < 10; ++i)
{
gain[i] = getport (2 + i);
double a = adjust_gain (i, DSP::db2lin (gain[i]));
for (int c = 0; c < 2; ++c)
eq[c].gf[i] = 1,
eq[c].gain[i] = a;
}
}
template <sample_func_t F>
void
Eq2x2::one_cycle (int frames)
{
/* evaluate band gain changes and compute recursion factor to prevent
* zipper noise */
double one_over_n = frames > 0 ? 1. / frames : 1;
for (int i = 0; i < 10; ++i)
{
double a;
if (*ports [2 + i] == gain[i])
/* still same value, no gain fade */
a = 1;
else
{
gain[i] = getport (2 + i);
/* prepare factor for logarithmic gain fade */
a = adjust_gain (i, DSP::db2lin (gain[i]));
a = pow (a / eq[0].gain[i], one_over_n);
}
for (int c = 0; c < 2; ++c)
eq[c].gf[i] = a;
}
for (int c = 0; c < 2; ++c)
{
sample_t
* s = ports[c],
* d = ports[12 + c];
for (int i = 0; i < frames; ++i)
{
sample_t x = s[i];
x = eq[c].process (x);
F (d, i, x, adding_gain);
}
}
/* flip 'renormal' values */
for (int c = 0; c < 2; ++c)
{
eq[c].normal = normal;
eq[c].flush_0();
}
}
PortInfo
Eq2x2::port_info [] =
{
{
"in:l",
INPUT | AUDIO,
{0, -1, 1}
}, {
"in:r",
INPUT | AUDIO,
{0, -1, 1}
}, {
"31 Hz",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -48, 24}
}, {
"63 Hz",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -48, 24}
}, {
"125 Hz",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -48, 24}
}, {
"250 Hz",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -48, 24}
}, {
"500 Hz",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -48, 24}
}, {
"1 kHz",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -48, 24}
}, {
"2 kHz",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -48, 24}
}, {
"4 kHz",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -48, 24}
}, {
"8 kHz",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -48, 24}
}, {
"16 kHz",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -48, 24}
}, {
"out:l",
OUTPUT | AUDIO,
{0}
}, {
"out:r",
OUTPUT | AUDIO,
{0}
}
};
template <> void
Descriptor<Eq2x2>::setup()
{
UniqueID = 2594;
Label = "Eq2x2";
Properties = HARD_RT;
Name = CAPS "Eq2x2 - stereo 10-band equalizer";
Maker = "Tim Goetze <tim@quitte.de>";
Copyright = "GPL, 2004-7";
/* fill port info and vtable */
autogen();
}
/* //////////////////////////////////////////////////////////////////////// */
/*
todo: parametric -- 20-400, 60-1k, 150-2.5k, 500-8k, 1k-20k
bandwidth 0-2 octaves
*/

View File

@@ -0,0 +1,93 @@
/*
Eq.h
Copyright 2004-5 Tim Goetze <tim@quitte.de>
http://quitte.de/dsp/
equalizer circuit using recursive filtering.
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
#ifndef _EQ_H_
#define _EQ_H_
#include "dsp/util.h"
#include "dsp/Eq.h"
#include "dsp/BiQuad.h"
#include "dsp/RBJ.h"
class Eq
: public Plugin
{
public:
sample_t gain[10];
DSP::Eq<10> eq;
int block;
enum { BlockSize = 64 };
template <sample_func_t F>
void one_cycle (int frames);
public:
static PortInfo port_info [];
void init();
void activate();
void run (int n)
{
one_cycle<store_func> (n);
}
void run_adding (int n)
{
one_cycle<adding_func> (n);
}
};
class Eq2x2
: public Plugin
{
public:
sample_t gain[10];
DSP::Eq<10> eq[2];
template <sample_func_t F>
void one_cycle (int frames);
public:
static PortInfo port_info [];
void init();
void activate();
void run (int n)
{
one_cycle<store_func> (n);
}
void run_adding (int n)
{
one_cycle<adding_func> (n);
}
};
#endif /* _EQ_H_ */

View File

@@ -0,0 +1,148 @@
/*
HRTF.cc
Copyright 2002-7 Tim Goetze <tim@quitte.de>
http://quitte.de/dsp/
high-order IIR filtering modeled after HRTF impulse responses
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
#include "basics.h"
#include "HRTF.h"
#include "Descriptor.h"
#include "elev0.h"
/* //////////////////////////////////////////////////////////////////////// */
void
HRTF::init()
{
h = 0;
}
void
HRTF::set_pan (int p)
{
n = 31;
pan = p;
if (p >= 0)
{
left.a = elev0[p].left.a;
left.b = elev0[p].left.b;
right.a = elev0[p].right.a;
right.b = elev0[p].right.b;
}
else
{
p = -p;
left.a = elev0[p].right.a;
left.b = elev0[p].right.b;
right.a = elev0[p].left.a;
right.b = elev0[p].left.b;
}
memset (left.y, 0, sizeof (left.y));
memset (right.y, 0, sizeof (right.y));
}
template <sample_func_t F>
void
HRTF::one_cycle (int frames)
{
sample_t * s = ports[0];
int p = (int) getport(1);
if (p != pan) set_pan (p);
sample_t * dl = ports[2];
sample_t * dr = ports[3];
double l, r;
for (int i = 0; i < frames; ++i)
{
x[h] = l = r = s[i] + normal;
l *= left.a[0];
r *= right.a[0];
for (int j = 1, z = h - 1; j < n; --z, ++j)
{
z &= 31;
l += left.a[j] * x[z];
l += left.b[j] * left.y[z];
r += right.a[j] * x[z];
r += right.b[j] * right.y[z];
}
left.y[h] = l;
right.y[h] = r;
h = (h + 1) & 31;
F (dl, i, l, adding_gain);
F (dr, i, r, adding_gain);
}
}
/* //////////////////////////////////////////////////////////////////////// */
PortInfo
HRTF::port_info [] =
{
{
"in",
INPUT | AUDIO,
{BOUNDED, -1, 1}
}, {
"pan",
INPUT | CONTROL,
{BOUNDED | INTEGER | DEFAULT_0, -36, 36}
}, {
"out:l",
OUTPUT | AUDIO,
{0}
}, {
"out:r",
OUTPUT | AUDIO,
{0}
}
};
template <> void
Descriptor<HRTF>::setup()
{
UniqueID = 1787;
Label = "HRTF";
Properties = HARD_RT;
Name = CAPS "HRTF - Head-related transfer function at elevation 0";
Maker = "Tim Goetze <tim@quitte.de>";
Copyright = "GPL, 2004-7";
/* fill port info and vtable */
autogen();
}

View File

@@ -0,0 +1,72 @@
/*
HRTF.h
Copyright 2002-5 Tim Goetze <tim@quitte.de>
http://quitte.de/dsp/
IIR filtering based on HRTF data sets
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
#ifndef _HRTF_H_
#define _HRTF_H_
#include "dsp/util.h"
class HRTF
: public Plugin
{
public:
int pan;
int n, h;
double x[32];
struct {
double * a, * b;
double y[32];
} left, right;
void set_pan (int p);
template <sample_func_t F>
void one_cycle (int frames);
public:
static PortInfo port_info [];
void init();
void activate()
{
set_pan ((int) *ports[1]);
}
void run (int n)
{
one_cycle<store_func> (n);
}
void run_adding (int n)
{
one_cycle<adding_func> (n);
}
};
#endif /* _HRTF_H_ */

View File

@@ -0,0 +1,114 @@
/*
Lorenz.cc
Copyright 2002-11 Tim Goetze <tim@quitte.de>
http://quitte.de/dsp/
the sound of a Lorenz attractor.
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
#include <stdlib.h>
#include "basics.h"
#include "Lorenz.h"
#include "Descriptor.h"
void
Lorenz::init()
{
lorenz.init (h = .001, 0.1 * frandom());
gain = 0;
}
template <sample_func_t F>
void
Lorenz::one_cycle (int frames)
{
lorenz.set_rate (*ports[0]);
double g = (gain == *ports[4]) ?
1 : pow (getport(4) / gain, 1. / (double) frames);
sample_t * d = ports[5];
sample_t x, sx = getport(1), sy = getport(2), sz = getport(3);
for (int i = 0; i < frames; ++i)
{
lorenz.step();
x = sx * lorenz.get_x() + sy * lorenz.get_y() + sz * lorenz.get_z();
F (d, i, gain * x, adding_gain);
gain *= g;
}
gain = getport(4);
}
/* //////////////////////////////////////////////////////////////////////// */
PortInfo
Lorenz::port_info [] =
{
{
"h",
INPUT | CONTROL,
{BOUNDED | DEFAULT_LOW, 0, 1}
}, {
"x",
INPUT | CONTROL,
{BOUNDED | DEFAULT_1, 0, 1}
}, {
"y",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, 0, 1}
}, {
"z",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, 0, 1}
}, {
"volume",
INPUT | CONTROL,
{BOUNDED | DEFAULT_MID, MIN_GAIN, 1}
}, {
"out",
OUTPUT | AUDIO,
{0}
}
};
template <> void
Descriptor<Lorenz>::setup()
{
UniqueID = 1774;
Label = "Lorenz";
Properties = HARD_RT;
Name = CAPS "Lorenz - The sound of a Lorenz attractor";
Maker = "Tim Goetze <tim@quitte.de>";
Copyright = "GPL, 2004-7";
/* fill port info and vtable */
autogen();
}

View File

@@ -0,0 +1,62 @@
/*
Lorenz.h
Copyright 2004-11 Tim Goetze <tim@quitte.de>
http://quitte.de/dsp/
turns the state of a Lorenz fractal into sound.
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
#ifndef _LORENZ_H_
#define _LORENZ_H_
#include "dsp/Lorenz.h"
class Lorenz
: public Plugin
{
public:
sample_t h, gain;
DSP::Lorenz lorenz;
template <sample_func_t F>
void one_cycle (int frames);
public:
static PortInfo port_info [];
void init();
void activate()
{ gain = getport(4); }
void run (int n)
{
one_cycle<store_func> (n);
}
void run_adding (int n)
{
one_cycle<adding_func> (n);
}
};
#endif /* _LORENZ_H_ */

View File

@@ -0,0 +1,251 @@
/*
Pan.cc
Copyright 2002-11 Tim Goetze <tim@quitte.de>
http://quitte.de/dsp/
panorama with width control,
stereo image width reduction
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
#include "basics.h"
#include "Pan.h"
#include "Descriptor.h"
void
Pan::init()
{
delay.init ((int) (.040 * fs));
}
void
Pan::activate()
{
delay.reset();
tap.reset (400 / fs);
set_pan (getport (1));
}
inline void
Pan::set_pan (sample_t p)
{
pan = p;
double phi = (pan + 1) * M_PI * .25;
gain_l = cos (phi);
gain_r = sin (phi);
}
template <sample_func_t F>
void
Pan::one_cycle (int frames)
{
sample_t * s = ports[0];
if (pan != *ports[1])
set_pan (getport(1));
sample_t g = getport(2);
sample_t
width_l = g * gain_r,
width_r = g * gain_l;
tap.t = (int) (getport(3) * fs * .001);
bool mono = getport(4);
sample_t * dl = ports[5];
sample_t * dr = ports[6];
sample_t x, xt;
if (mono) for (int i = 0; i < frames; ++i)
{
x = s[i];
xt = tap.get (delay);
delay.put (x + normal);
x = (gain_l * x + gain_r * x + width_l * xt + width_r * xt) * .5;
F (dl, i, x, adding_gain);
F (dr, i, x, adding_gain);
normal = -normal;
}
else /* stereo */ for (int i = 0; i < frames; ++i)
{
x = s[i];
xt = tap.get (delay);
delay.put (x + normal);
F (dl, i, gain_l * x + width_l * xt, adding_gain);
F (dr, i, gain_r * x + width_r * xt, adding_gain);
normal = -normal;
}
}
/* //////////////////////////////////////////////////////////////////////// */
PortInfo
Pan::port_info [] =
{
{
"in",
INPUT | AUDIO,
{0}
}, {
"pan",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, -1, 1}
}, {
"width",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0, 0, 1}
}, {
"t",
INPUT | CONTROL,
{BOUNDED | DEFAULT_LOW, 0.1, 40}
}, {
"mono",
INPUT | CONTROL,
{BOUNDED | DEFAULT_0 | INTEGER | TOGGLE, 0, 1}
}, {
"out:l",
OUTPUT | AUDIO,
{0}
}, {
"out:r",
OUTPUT | AUDIO,
{0}
}
};
template <> void
Descriptor<Pan>::setup()
{
UniqueID = 1788;
Label = "Pan";
Properties = HARD_RT;
Name = CAPS "Pan - Pan and width";
Maker = "Tim Goetze <tim@quitte.de>";
Copyright = "GPL, 2004-7";
/* fill port info and vtable */
autogen();
}
/* //////////////////////////////////////////////////////////////////////// */
void
Narrower::init()
{
}
void
Narrower::activate()
{
}
template <sample_func_t F>
void
Narrower::one_cycle (int frames)
{
sample_t * sl = ports[0];
sample_t * sr = ports[1];
if (strength != *ports[2])
strength = *ports[2];
sample_t * dl = ports[3];
sample_t * dr = ports[4];
double xl, xr, m;
double dry = 1 - strength, wet = strength;
for (int i = 0; i < frames; ++i)
{
xl = sl[i];
xr = sr[i];
m = wet * (xl + xr) * .5;
xl *= dry;
xr *= dry;
xl += m;
xr += m;
F (dl, i, xl, adding_gain);
F (dr, i, xr, adding_gain);
}
}
/* //////////////////////////////////////////////////////////////////////// */
PortInfo
Narrower::port_info [] =
{
{
"in:l",
INPUT | AUDIO,
{0}
}, {
"in:r",
INPUT | AUDIO,
{0}
}, {
"strength",
INPUT | CONTROL,
{BOUNDED | DEFAULT_LOW, 0, 1}
}, {
"out:l",
OUTPUT | AUDIO,
{0}
}, {
"out:r",
OUTPUT | AUDIO,
{0}
}
};
template <> void
Descriptor<Narrower>::setup()
{
UniqueID = 2595;
Label = "Narrower";
Properties = HARD_RT;
Name = CAPS "Narrower - Stereo image width reduction";
Maker = "Tim Goetze <tim@quitte.de>";
Copyright = "GPL, 2011";
/* fill port info and vtable */
autogen();
}

View File

@@ -0,0 +1,114 @@
/*
Pan.h
Copyright 2004-11 Tim Goetze <tim@quitte.de>
http://quitte.de/dsp/
panorama with width control,
stereo image width reduction
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
#ifndef _PAN_H_
#define _PAN_H_
#include "dsp/Delay.h"
#include "dsp/OnePole.h"
class PanTap
{
public:
int t;
DSP::OnePoleLP damper;
sample_t get (DSP::Delay & delay)
{
return damper.process (delay[t]);
}
void reset (double c)
{
damper.set_f (c);
damper.reset();
}
};
class Pan
: public Plugin
{
public:
sample_t pan;
sample_t gain_l, gain_r;
DSP::Delay delay;
PanTap tap;
template <sample_func_t F>
void one_cycle (int frames);
inline void set_pan (sample_t);
public:
static PortInfo port_info [];
void init();
void activate();
void run (int n)
{
one_cycle<store_func> (n);
}
void run_adding (int n)
{
one_cycle<adding_func> (n);
}
};
/* stereo width reduction */
class Narrower
: public Plugin
{
public:
sample_t strength;
template <sample_func_t F>
void one_cycle (int frames);
public:
static PortInfo port_info [];
void init();
void activate();
void run (int n)
{
one_cycle<store_func> (n);
}
void run_adding (int n)
{
one_cycle<adding_func> (n);
}
};
#endif /* _PAN_H_ */

View File

@@ -0,0 +1,231 @@
/*
Phaser.cc
Copyright 2002-7 Tim Goetze <tim@quitte.de>
http://quitte.de/dsp/
One simple mono phaser, 6 all-pass lines, the usual controls.
Another unit in the same vein with the filter modulation controlled by
a Lorenz fractal.
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
#include "basics.h"
#include "Phaser.h"
#include "Descriptor.h"
template <sample_func_t F>
void
PhaserI::one_cycle (int frames)
{
sample_t * s = ports[0];
if (rate != *ports[1])
{
rate = getport(1);
lfo.set_f (max (.001, rate * (double) blocksize), fs, lfo.get_phase());
}
double depth = getport(2);
double spread = 1 + getport(3);
double fb = getport(4);
sample_t * dst = ports[5];
while (frames)
{
if (remain == 0) remain = 32;
int n = min (remain, frames);
double d = delay.bottom + delay.range * (1. - fabs (lfo.get()));
for (int j = 5; j >= 0; --j)
{
ap[j].set (d);
d *= spread;
}
for (int i = 0; i < n; ++i)
{
sample_t x = s[i];
sample_t y = x + y0 * fb + normal;
for (int j = 5; j >= 0; --j)
y = ap[j].process (y);
y0 = y;
F (dst, i, x + y * depth, adding_gain);
}
s += n;
dst += n;
frames -= n;
remain -= n;
}
}
/* //////////////////////////////////////////////////////////////////////// */
PortInfo
PhaserI::port_info [] =
{
{
"in",
INPUT | AUDIO,
{BOUNDED, -1, 1}
}, {
"rate (Hz)",
INPUT | CONTROL,
{BOUNDED | DEFAULT_1, 0, 10}
}, {
"depth",
INPUT | CONTROL,
{BOUNDED | DEFAULT_HIGH, 0, 1}
}, {
"spread",
INPUT | CONTROL,
{BOUNDED | DEFAULT_LOW, 0, M_PI}
}, {
"feedback",
INPUT | CONTROL,
{BOUNDED | DEFAULT_HIGH, 0, .999}
}, {
"out",
OUTPUT | AUDIO,
{0}
}
};
template <> void
Descriptor<PhaserI>::setup()
{
UniqueID = 1775;
Label = "PhaserI";
Properties = HARD_RT;
Name = CAPS "PhaserI - Mono phaser";
Maker = "Tim Goetze <tim@quitte.de>";
Copyright = "GPL, 2002-7";
/* fill port info and vtable */
autogen();
}
/* //////////////////////////////////////////////////////////////////////// */
template <sample_func_t F>
void
PhaserII::one_cycle (int frames)
{
sample_t * s = ports[0];
lorenz.set_rate (getport(1) * .08);
double depth = getport(2);
double spread = 1 + getport(3);
double fb = getport(4);
sample_t * dst = ports[5];
while (frames)
{
if (remain == 0) remain = 32;
int n = min (remain, frames);
double d = delay.bottom + delay.range * (.3 * lorenz.get());
for (int j = 5; j >= 0; --j)
{
ap[j].set (d);
d *= spread;
}
for (int i = 0; i < n; ++i)
{
sample_t x = s[i];
sample_t y = x + y0 * fb + normal;
for (int j = 5; j >= 0; --j)
y = ap[j].process (y);
y0 = y;
F (dst, i, x + y * depth, adding_gain);
}
s += n;
dst += n;
frames -= n;
remain -= n;
}
}
/* //////////////////////////////////////////////////////////////////////// */
PortInfo
PhaserII::port_info [] =
{
{
"in",
INPUT | AUDIO,
{BOUNDED, -1, 1}
}, {
"rate",
INPUT | CONTROL,
{BOUNDED | DEFAULT_LOW, 0, 1}
}, {
"depth",
INPUT | CONTROL,
{BOUNDED | DEFAULT_HIGH, 0, 1}
}, {
"spread",
INPUT | CONTROL,
{BOUNDED | DEFAULT_LOW, 0, M_PI * .5}
}, {
"feedback",
INPUT | CONTROL,
{BOUNDED | DEFAULT_HIGH, 0, .999}
}, {
"out",
OUTPUT | AUDIO,
{0}
}
};
template <> void
Descriptor<PhaserII>::setup()
{
UniqueID = 2586;
Label = "PhaserII";
Properties = HARD_RT;
Name = CAPS "PhaserII - Mono phaser modulated by a Lorenz fractal";
Maker = "Tim Goetze <tim@quitte.de>";
Copyright = "GPL, 2002-7";
/* fill port info and vtable */
autogen();
}

View File

@@ -0,0 +1,164 @@
/*
Phaser.h
Copyright 2002-5 Tim Goetze <tim@quitte.de>
http://quitte.de/dsp/
Standard and fractal-modulated phaser units.
*/
/*
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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA or point your web browser to http://www.gnu.org.
*/
#ifndef _PHASER_H_
#define _PHASER_H_
#include "dsp/Sine.h"
#include "dsp/Lorenz.h"
#include "dsp/Delay.h"
/* all-pass as used by the phaser. */
class PhaserAP
{
public:
sample_t a, m;
PhaserAP()
{
a = m = 0.;
}
void set (double delay)
{
a = (1 - delay) / (1 + delay);
}
sample_t process (sample_t x)
{
register sample_t y = -a * x + m;
m = a * y + x;
return y;
}
};
class PhaserI
: public Plugin
{
public:
PhaserAP ap[6];
DSP::Sine lfo;
sample_t rate;
sample_t y0;
struct {
double bottom, range;
} delay;
template <sample_func_t>
void one_cycle (int frames);
int blocksize, remain;
public:
static PortInfo port_info [];
void init()
{
blocksize = 32;
}
void activate()
{
y0 = 0.;
remain = 0;
delay.bottom = 400. / fs;
delay.range = 2200. / fs;
rate = -1; /* force lfo reset in one_cycle() */
}
void run (int n)
{
one_cycle<store_func> (n);
}
void run_adding (int n)
{
one_cycle<adding_func> (n);
}
};
/* same as above, but filter sweep is controlled by a Lorenz fractal */
class PhaserII
: public Plugin
{
public:
double fs;
PhaserAP ap[6];
DSP::Lorenz lorenz;
sample_t rate;
sample_t y0;
struct {
double bottom, range;
} delay;
template <sample_func_t>
void one_cycle (int frames);
int blocksize, remain;
public:
static PortInfo port_info [];
void init()
{
blocksize = 32;
lorenz.init();
}
void activate()
{
y0 = 0.;
remain = 0;
delay.bottom = 400. / fs;
delay.range = 2200. / fs;
rate = -1; /* force lfo reset in one_cycle() */
}
void run (int n)
{
one_cycle<store_func> (n);
}
void run_adding (int n)
{
one_cycle<adding_func> (n);
}
};
#endif /* _PHASER_H_ */

Some files were not shown because too many files have changed in this diff Show More