fundamental changes in plugin-architecture, added plugin-browser, bug-fixes - see ChangeLog for further details

git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@18 0778d3d1-df1d-0410-868b-ea421aaaa00d
This commit is contained in:
Tobias Doerffel
2005-10-20 13:02:26 +00:00
parent 58fe360793
commit f55c124be6
96 changed files with 5262 additions and 234 deletions

2
plugins/Makefile.am Normal file
View File

@@ -0,0 +1,2 @@
SUBDIRS = audio_file_processor ladspa_sine_1063 midi_out plucked_string_synth triple_oscillator vestige

View File

@@ -0,0 +1,34 @@
AUTOMAKE_OPTIONS = foreign 1.4
INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/src/lib -I.
AM_CXXFLAGS := $(AM_CXXFLAGS) $(QT_CXXFLAGS) -DPLUGIN_NAME="audiofileprocessor"
%.moc: ./%.h
$(MOC) -o $@ $<
MOC_FILES = ./audio_file_processor.moc
BUILT_SOURCES = $(MOC_FILES) ./embedded_resources.h
EMBEDDED_RESOURCES = $(wildcard *png)
./embedded_resources.h: $(EMBEDDED_RESOURCES)
$(top_builddir)/buildtools/bin2res $(EMBEDDED_RESOURCES) > $@
EXTRA_DIST = $(EMBEDDED_RESOURCES)
CLEANFILES = $(MOC_FILES) ./embedded_resources.h
pkglib_LTLIBRARIES= libaudiofileprocessor.la
libaudiofileprocessor_la_SOURCES = audio_file_processor.cpp audio_file_processor.h
$(libaudiofileprocessor_la_SOURCES): ./embedded_resources.h

Binary file not shown.

View File

@@ -0,0 +1,628 @@
/*
* audio_file_processor.cpp - instrument for using audio-files
*
* Linux MultiMedia Studio
* Copyright (c) 2004-2005 Tobias Doerffel <tobydox@users.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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
*/
#include "qt3support.h"
#ifdef QT4
#include <QPainter>
#include <QButtonGroup>
#include <QBitmap>
#include <Qt/QtXml>
#include <QFileInfo>
#else
#include <qpainter.h>
#include <qbuttongroup.h>
#include <qbitmap.h>
#include <qdom.h>
#include <qfileinfo.h>
#define isChecked isOn
#define setChecked setOn
#endif
#include "audio_file_processor.h"
#include "song_editor.h"
#include "channel_track.h"
#include "note_play_handle.h"
#include "paths.h"
#include "interpolation.h"
#include "buffer_allocator.h"
#include "pixmap_button.h"
#include "knob.h"
#include "tooltip.h"
#include "embed.cpp"
extern "C"
{
plugin::descriptor audiofileprocessor_plugin_descriptor =
{
STRINGIFY_PLUGIN_NAME( PLUGIN_NAME ),
"AudioFileProcessor",
QT_TRANSLATE_NOOP( "plugin",
"simple sampler with various settings for "
"using samples (e.g. drums) in a channel" ),
"Tobias Doerffel <tobydox@users.sf.net>",
0x0100,
plugin::INSTRUMENT,
PLUGIN_NAME::findEmbeddedData( "logo.png" )
} ;
}
QPixmap * audioFileProcessor::s_artwork = NULL;
audioFileProcessor::audioFileProcessor( channelTrack * _channel_track ) :
instrument( _channel_track,
audiofileprocessor_plugin_descriptor.public_name ),
specialBgHandlingWidget( PLUGIN_NAME::getIconPixmap( "artwork" ) ),
m_sampleBuffer( "" ),
m_drawMethod( sampleBuffer::LINE_CONNECT )
{
connect( &m_sampleBuffer, SIGNAL( sampleUpdated() ), this,
SLOT( sampleUpdated() ) );
if( s_artwork == NULL )
{
s_artwork = new QPixmap( PLUGIN_NAME::getIconPixmap(
"artwork" ) );
}
m_openAudioFileButton = new pixmapButton( this );
m_openAudioFileButton->setCheckable( FALSE );
m_openAudioFileButton->setCursor( QCursor( Qt::PointingHandCursor ) );
m_openAudioFileButton->move( 200, 90 );
m_openAudioFileButton->setActiveGraphic( embed::getIconPixmap(
"project_open_down" ) );
m_openAudioFileButton->setInactiveGraphic( embed::getIconPixmap(
"project_open" ) );
m_openAudioFileButton->setBgGraphic( getBackground(
m_openAudioFileButton ) );
connect( m_openAudioFileButton, SIGNAL( clicked() ), this,
SLOT( openAudioFile() ) );
toolTip::add( m_openAudioFileButton, tr( "Open other sample" ) );
#ifdef QT4
m_openAudioFileButton->setWhatsThis(
#else
QWhatsThis::add( m_openAudioFileButton,
#endif
tr( "Click here, if you want to open another audio-file. After "
"clicking on this button, a file-open-dialog appears "
"and you can select your file. Settings like Looping-"
"Mode, start- and end-point, amplify-value and so on "
"are not reset, so please don't wonder if your sample "
"doesn't sound like the original one..." ) );
m_reverseButton = new pixmapButton( this );
m_reverseButton->move( 160, 124 );
m_reverseButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
"reverse_on" ) );
m_reverseButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
"reverse_off" ) );
m_reverseButton->setBgGraphic( getBackground( m_reverseButton ) );
connect( m_reverseButton, SIGNAL( toggled( bool ) ), this,
SLOT( reverseBtnToggled( bool ) ) );
toolTip::add( m_reverseButton, tr( "Reverse sample" ) );
#ifdef QT4
m_reverseButton->setWhatsThis(
#else
QWhatsThis::add( m_reverseButton,
#endif
tr( "If you enable this button, the whole sample is reversed. "
"This is useful for cool effects, e.g. a reversed "
"crash." ) );
m_loopButton = new pixmapButton( this );
m_loopButton->move( 180, 124 );
m_loopButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
"loop_on" ) );
m_loopButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
"loop_off" ) );
m_loopButton->setBgGraphic( getBackground( m_loopButton ) );
toolTip::add( m_loopButton,
tr( "Loop sample at start- and end-point" ) );
#ifdef QT4
m_loopButton->setWhatsThis(
#else
QWhatsThis::add( m_loopButton,
#endif
tr( "Here you can set, whether Looping-Mode is enabled. If "
"enabled, AudioFileProcessor loops between start- and "
"end-point of a sample until the whole note is played. "
"This is useful for things like string- and choir-"
"samples." ) );
m_ampKnob = new knob( knobDark_28, this, tr( "Amplify" ) );
m_ampKnob->setRange( 0, 500, 1.0f );
m_ampKnob->move( 6, 114 );
m_ampKnob->setValue( 100.0f, TRUE );
m_ampKnob->setHintText( tr( "Amplify:" )+" ", "%" );
m_ampKnob->setLabel( tr( "AMP" ) );
connect( m_ampKnob, SIGNAL( valueChanged( float ) ), this,
SLOT( ampKnobChanged( float ) ) );
#ifdef QT4
m_ampKnob->setWhatsThis(
#else
QWhatsThis::add( m_ampKnob,
#endif
tr( "With this knob you can set the amplify-ratio. When you "
"set a value of 100% your sample isn't changed. "
"Otherwise it will be amplified up or down (your "
"actual sample-file isn't touched!)" ) );
m_startKnob = new knob( knobDark_28, this, tr( "Start of sample" ) );
m_startKnob->setRange( 0, 1.0, 0.00001 );
m_startKnob->move( 46, 114 );
m_startKnob->setValue( 0.0, TRUE );
m_startKnob->setHintText( tr( "Startpoint:" )+" ", "" );
m_startKnob->setLabel( tr( "START" ) );
connect( m_startKnob, SIGNAL( valueChanged( float ) ), this,
SLOT( startKnobChanged( float ) ) );
#ifdef QT4
m_startKnob->setWhatsThis(
#else
QWhatsThis::add( m_startKnob,
#endif
tr( "With this knob you can set the point where "
"AudioFileProcessor should begin playing your sample. "
"If you enable Looping-Mode, this is the point to "
"which AudioFileProcessor returns if a note is longer "
"than the sample between start- and end-point." ) );
m_endKnob = new knob( knobDark_28, this, tr( "End of sample" ) );
m_endKnob->setRange( 0, 1.0, 0.00001 );
m_endKnob->move( 84, 114 );
m_endKnob->setValue( 1.0, TRUE );
m_endKnob->setHintText( tr( "Endpoint:" )+" ", "" );
m_endKnob->setLabel( tr( "END" ) );
connect( m_endKnob, SIGNAL( valueChanged( float ) ), this,
SLOT( endKnobChanged( float ) ) );
#ifdef QT4
m_endKnob->setWhatsThis(
#else
QWhatsThis::add( m_endKnob,
#endif
tr( "With this knob you can set the point where "
"AudioFileProcessor should stop playing your sample. "
"If you enable Looping-Mode, this is the point where "
"AudioFileProcessor returns if a note is longer than "
"the sample between start- and end-point." ) );
m_viewLinesPB = new pixmapButton( this );
m_viewLinesPB->move( 154, 158 );
m_viewLinesPB->setBgGraphic( getBackground( m_viewLinesPB ) );
if( m_drawMethod == sampleBuffer::LINE_CONNECT )
{
#ifdef QT4
m_viewLinesPB->setChecked( TRUE );
#else
m_viewLinesPB->setOn( TRUE );
#endif
}
connect( m_viewLinesPB, SIGNAL( toggled( bool ) ), this,
SLOT( lineDrawBtnToggled( bool ) ) );
#ifdef QT4
m_viewLinesPB->setWhatsThis(
#else
QWhatsThis::add( m_viewLinesPB,
#endif
tr( "Activate this button, if your sample should be drawn "
"with connected lines. This doesn't change the "
"sound itself. It just gives you another view to your "
"sample." ) );
m_viewDotsPB = new pixmapButton( this );
m_viewDotsPB->move( 204, 158 );
m_viewDotsPB->setBgGraphic( getBackground( m_viewDotsPB ) );
if( m_drawMethod == sampleBuffer::DOTS )
{
#ifdef QT4
m_viewDotsPB->setChecked( TRUE );
#else
m_viewDotsPB->setOn( TRUE );
#endif
}
connect( m_viewDotsPB, SIGNAL( toggled( bool ) ), this,
SLOT( dotDrawBtnToggled( bool ) ) );
#ifdef QT4
m_viewDotsPB->setWhatsThis(
#else
QWhatsThis::add( m_viewDotsPB,
#endif
tr( "Activate this button, if your sample should be drawn "
"with dots. This doesn't change the sound itself. "
"It just gives you another view to your sample." ) );
QButtonGroup * view_group = new QButtonGroup( this );
view_group->addButton( m_viewLinesPB );
view_group->addButton( m_viewDotsPB );
view_group->setExclusive( TRUE );
#ifndef QT4
view_group->hide();
setBackgroundMode( Qt::NoBackground );
#endif
}
audioFileProcessor::~audioFileProcessor()
{
}
void audioFileProcessor::saveSettings( QDomDocument & _doc,
QDomElement & _parent )
{
QDomElement afp_de = _doc.createElement( nodeName() );
afp_de.setAttribute( "src", m_sampleBuffer.audioFile() );
afp_de.setAttribute( "sframe", QString::number(
m_sampleBuffer.startFrame() /
(float)m_sampleBuffer.frames() ) );
afp_de.setAttribute( "eframe", QString::number(
m_sampleBuffer.endFrame() /
(float)m_sampleBuffer.frames() ) );
afp_de.setAttribute( "reversed", QString::number(
m_reverseButton->isChecked() ) );
afp_de.setAttribute( "looped", QString::number(
m_loopButton->isChecked() ) );
afp_de.setAttribute( "amp", QString::number( m_ampKnob->value() ) );
_parent.appendChild( afp_de );
}
void audioFileProcessor::loadSettings( const QDomElement & _this )
{
setAudioFile( _this.attribute( "src" ) );
setStartAndEndKnob( _this.attribute( "sframe" ).toFloat(),
_this.attribute( "eframe" ).toFloat() );
m_reverseButton->setChecked( _this.attribute( "reversed" ).toInt() );
m_loopButton->setChecked( _this.attribute( "looped" ).toInt() );
m_ampKnob->setValue( _this.attribute( "amp" ).toFloat() );
}
QString audioFileProcessor::nodeName( void ) const
{
return( audiofileprocessor_plugin_descriptor.name );
}
void audioFileProcessor::setParameter( const QString & _param,
const QString & _value )
{
if( _param == "audiofile" )
{
setAudioFile( _value );
}
}
Uint32 audioFileProcessor::getBeatLen( notePlayHandle * _n ) const
{
const float freq_factor = BASE_FREQ /
( getChannelTrack()->frequency( _n ) *
DEFAULT_SAMPLE_RATE /
mixer::inst()->sampleRate() );
return( static_cast<Uint32>( floorf( ( m_sampleBuffer.endFrame() -
m_sampleBuffer.startFrame() ) *
freq_factor ) ) );
}
void audioFileProcessor::setAudioFile( const QString & _audio_file )
{
// is current channel-name equal to previous-filename??
if( getChannelTrack()->name() ==
QFileInfo( m_sampleBuffer.audioFile() ).fileName() ||
m_sampleBuffer.audioFile() == "" )
{
// then set it to new one
getChannelTrack()->setName( QFileInfo( _audio_file
).fileName() );
}
// else we don't touch the channel-name, because the user named it self
m_sampleBuffer.setAudioFile( _audio_file );
setStartAndEndKnob( 0.0f, 1.0f );
}
void audioFileProcessor::playNote( notePlayHandle * _n )
{
const Uint32 frames = mixer::inst()->framesPerAudioBuffer();
sampleFrame * buf = bufferAllocator::alloc<sampleFrame>( frames );
// calculate frequency of note
const float note_freq = getChannelTrack()->frequency( _n ) /
( mixer::inst()->sampleRate() /
DEFAULT_SAMPLE_RATE );
if( m_sampleBuffer.play( buf, _n->totalFramesPlayed(),
frames, note_freq,
m_loopButton->isChecked(),
&_n->m_pluginData ) == TRUE )
{
getChannelTrack()->processAudioBuffer( buf, frames, _n );
}
bufferAllocator::free( buf );
}
void audioFileProcessor::deleteNotePluginData( notePlayHandle * _n )
{
if( _n->m_pluginData != NULL )
{
m_sampleBuffer.deleteResamplingData( &_n->m_pluginData );
}
}
void audioFileProcessor::paintEvent( QPaintEvent * )
{
#ifdef QT4
QPainter p( this );
#else
QPixmap pm( rect().size() );
pm.fill( this, rect().topLeft() );
QPainter p( &pm, this );
#endif
p.drawPixmap( 0, 0, *s_artwork );
QString file_name = "";
Uint16 idx = m_sampleBuffer.audioFile().length();
p.setFont( pointSize<8>( p.font() ) );
QFontMetrics fm( font() );
// simple algorithm for creating a text from the filename that
// matches in the white rectangle
#ifdef QT4
while( idx > 0 &&
fm.size( Qt::TextSingleLine, file_name + "..." ).width() < 225 )
#else
while( idx > 0 &&
fm.size( Qt::SingleLine, file_name + "..." ).width() < 225 )
#endif
{
file_name = m_sampleBuffer.audioFile()[--idx] + file_name;
}
if( idx > 0 )
{
file_name = "..." + file_name;
}
p.setPen( QColor( 255, 255, 255 ) );
p.drawText( 8, 84, file_name );
p.drawPixmap( 2, 172, m_graph );
p.setPen( QColor( 0xFF, 0xAA, 0x00 ) );
const QRect graph_rect( 4, 174, 241, 70 );
const Uint32 frames = tMax( m_sampleBuffer.frames(),
static_cast<Uint32>( 1 ) );
const Uint16 start_frame_x = m_sampleBuffer.startFrame() *
graph_rect.width() / frames;
const Uint16 end_frame_x = m_sampleBuffer.endFrame() *
( graph_rect.width() - 1 ) / frames;
p.drawLine( start_frame_x + graph_rect.x(), graph_rect.y(),
start_frame_x + graph_rect.x(),
graph_rect.height() + graph_rect.y() );
p.drawLine( end_frame_x + graph_rect.x(), graph_rect.y(),
end_frame_x + graph_rect.x(),
graph_rect.height() + graph_rect.y() );
#ifndef QT4
bitBlt( this, rect().topLeft(), &pm );
#endif
}
void audioFileProcessor::sampleUpdated( void )
{
m_graph = QPixmap( 245, 75 );
copyBlt( &m_graph, 0, 0, s_artwork, 2, 172, m_graph.width(),
m_graph.height() );
QPainter p( &m_graph );
m_sampleBuffer.drawWaves( p, QRect( 2, 2, m_graph.width() - 4,
m_graph.height() - 4 ),
m_drawMethod );
update();
}
void audioFileProcessor::reverseBtnToggled( bool _on )
{
m_sampleBuffer.setReversed( _on );
songEditor::inst()->setModified();
}
void audioFileProcessor::lineDrawBtnToggled( bool _on )
{
if( _on )
{
m_drawMethod = sampleBuffer::LINE_CONNECT;
sampleUpdated();
}
}
void audioFileProcessor::dotDrawBtnToggled( bool _on )
{
if( _on )
{
m_drawMethod = sampleBuffer::DOTS;
sampleUpdated();
}
}
void audioFileProcessor::ampKnobChanged( float _val )
{
m_sampleBuffer.setAmplification( _val/100.0f );
}
void audioFileProcessor::setStartAndEndKnob( float _s, float _e )
{
// because the signal-handlers of valuechanges of start- and end-knob
// do range checking, depending on value of the other knob, we have to
// disconnect the signal-handlers, set then the values, connect again
// and then let the changes take effect...
m_startKnob->disconnect();
m_endKnob->disconnect();
m_startKnob->setValue( _s );
m_endKnob->setValue( _e );
connect( m_startKnob, SIGNAL( valueChanged( float ) ), this,
SLOT( startKnobChanged( float ) ) );
connect( m_endKnob, SIGNAL( valueChanged( float ) ), this,
SLOT( endKnobChanged( float ) ) );
m_startKnob->setValue( _s );
m_endKnob->setValue( _e );
}
void audioFileProcessor::startKnobChanged( float _new_value )
{
if( _new_value < m_endKnob->value() )
{
m_sampleBuffer.setStartFrame( static_cast<Uint32>( _new_value *
m_sampleBuffer.frames() ) );
}
else
{
m_startKnob->setValue( m_endKnob->value() - 0.01 );
}
update();
}
void audioFileProcessor::endKnobChanged( float _new_value )
{
if( _new_value > m_startKnob->value() )
{
m_sampleBuffer.setEndFrame( static_cast<Uint32>( _new_value *
m_sampleBuffer.frames() ) - 1 );
}
else
{
m_endKnob->setValue( m_startKnob->value() + 0.01 );
}
update();
}
void audioFileProcessor::openAudioFile( void )
{
QString af = m_sampleBuffer.openAudioFile();
if( af != "" )
{
setAudioFile( af );
songEditor::inst()->setModified();
}
}
extern "C"
{
// neccessary for getting instance out of shared lib
plugin * lmms_plugin_main( void * _data )
{
return( new audioFileProcessor(
static_cast<channelTrack *>( _data ) ) );
}
}
#undef isChecked
#undef isOn
#include "audio_file_processor.moc"

View File

@@ -0,0 +1,116 @@
/*
* audio_file_processor.h - declaration of class audioFileProcessor
* (instrument-plugin for using audio-files)
*
* Linux MultiMedia Studio
* Copyright (c) 2004-2005 Tobias Doerffel <tobydox@users.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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
*/
#ifndef _AUDIO_FILE_PROCESSOR_H
#define _AUDIO_FILE_PROCESSOR_H
#include "qt3support.h"
#ifdef QT4
#include <QPixmap>
#else
#include <qpixmap.h>
#endif
#include "instrument.h"
#include "sample_buffer.h"
#include "spc_bg_hndl_widget.h"
class knob;
class pixmapButton;
class audioFileProcessor : public instrument, public specialBgHandlingWidget
{
Q_OBJECT
public:
audioFileProcessor( channelTrack * _channel_track );
virtual ~audioFileProcessor();
virtual void FASTCALL playNote( notePlayHandle * _n );
virtual void FASTCALL deleteNotePluginData( notePlayHandle * _n );
virtual void FASTCALL saveSettings( QDomDocument & _doc,
QDomElement & _parent );
virtual void FASTCALL loadSettings( const QDomElement & _this );
virtual void FASTCALL setParameter( const QString & _param,
const QString & _value );
virtual QString nodeName( void ) const;
virtual Uint32 FASTCALL getBeatLen( notePlayHandle * _n ) const;
public slots:
void setAudioFile( const QString & _audio_file );
protected slots:
void openAudioFile( void );
void reverseBtnToggled( bool _on );
void ampKnobChanged( float _new_value );
void startKnobChanged( float _new_value );
void endKnobChanged( float _new_value );
void lineDrawBtnToggled( bool _on );
void dotDrawBtnToggled( bool _on );
void sampleUpdated( void );
protected:
void paintEvent( QPaintEvent * );
private:
static QPixmap * s_artwork;
sampleBuffer m_sampleBuffer;
sampleBuffer::drawMethods m_drawMethod;
QPixmap m_graph;
knob * m_ampKnob;
knob * m_startKnob;
knob * m_endKnob;
pixmapButton * m_openAudioFileButton;
pixmapButton * m_viewLinesPB;
pixmapButton * m_viewDotsPB;
pixmapButton * m_reverseButton;
pixmapButton * m_loopButton;
void updateSample( void );
void FASTCALL setStartAndEndKnob( float _s, float _e );
} ;
#endif

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,33 @@
AUTOMAKE_OPTIONS = foreign 1.4
INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/src/lib -I.
AM_CXXFLAGS := $(AM_CXXFLAGS) $(QT_CXXFLAGS) -DPLUGIN_NAME="pluckedstringsynth"
%.moc: ./%.h
$(MOC) -o $@ $<
MOC_FILES = ./plucked_string_synth.moc
BUILT_SOURCES = $(MOC_FILES) ./embedded_resources.h
EMBEDDED_RESOURCES = $(wildcard *png)
./embedded_resources.h: $(EMBEDDED_RESOURCES)
$(top_builddir)/buildtools/bin2res $(EMBEDDED_RESOURCES) > $@
EXTRA_DIST = $(EMBEDDED_RESOURCES)
CLEANFILES = $(MOC_FILES) ./embedded_resources.h
pkglib_LTLIBRARIES= libpluckedstringsynth.la
libpluckedstringsynth_la_SOURCES = plucked_string_synth.cpp plucked_string_synth.h
$(libpluckedstringsynth_la_SOURCES): ./embedded_resources.h

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,274 @@
/*
* plucked_string_synth.cpp - instrument which uses the Karplus-Strong-algorithm
*
* Linux MultiMedia Studio
* Copyright (c) 2004-2005 Tobias Doerffel <tobydox@users.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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
*/
#include "qt3support.h"
#ifdef QT4
#include <Qt/QtXml>
#include <QMap>
#else
#include <qdom.h>
#include <qmap.h>
#endif
#include "plucked_string_synth.h"
#include "channel_track.h"
#include "note_play_handle.h"
#include "templates.h"
#include "buffer_allocator.h"
#include "knob.h"
#include "embed.cpp"
extern "C"
{
plugin::descriptor pluckedstringsynth_plugin_descriptor =
{
STRINGIFY_PLUGIN_NAME( PLUGIN_NAME ),
"PluckedStringSynth",
QT_TRANSLATE_NOOP( "plugin",
"cheap synthesis of guitar/harp-like sounds" ),
"Tobias Doerffel <tobydox@users.sf.net>",
0x0100,
plugin::INSTRUMENT,
PLUGIN_NAME::findEmbeddedData( "logo.png" )
} ;
}
// TODO: make this synth stereo for better better spacial (room) feeling and
// add distortion
pluckedStringSynth::pluckedStringSynth( channelTrack * _channel_track ) :
instrument( _channel_track,
pluckedstringsynth_plugin_descriptor.public_name )
{
m_pickKnob = new knob( knobDark_28, this, tr( "Pick position" ) );
m_pickKnob->setRange( 0.0f, 0.5f, 0.005f );
m_pickKnob->setValue( 0.0f, TRUE );
m_pickKnob->move( 86, 134 );
m_pickKnob->setHintText( tr( "Pick position:" ) + " ", "" );
m_pickupKnob = new knob( knobDark_28, this, tr( "Pickup position" ) );
m_pickupKnob->setRange( 0.0f, 0.5f, 0.005f );
m_pickupKnob->setValue( 0.05f, TRUE );
m_pickupKnob->move( 138, 134 );
m_pickupKnob->setHintText( tr( "Pickup position:" ) + " ", "" );
#ifdef QT4
QPalette pal;
pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap(
"artwork" ) );
setPalette( pal );
#else
setErasePixmap( PLUGIN_NAME::getIconPixmap( "artwork" ) );
#endif
}
pluckedStringSynth::~pluckedStringSynth()
{
}
void pluckedStringSynth::saveSettings( QDomDocument & _doc,
QDomElement & _parent )
{
QDomElement pss_de = _doc.createElement( nodeName() );
pss_de.setAttribute( "pick", QString::number( m_pickKnob->value() ) );
pss_de.setAttribute( "pickup", QString::number(
m_pickupKnob->value() ) );
_parent.appendChild( pss_de );
}
void pluckedStringSynth::loadSettings( const QDomElement & _this )
{
m_pickKnob->setValue( _this.attribute( "pick" ).toFloat() );
m_pickupKnob->setValue( _this.attribute( "pickup" ).toFloat() );
}
QString pluckedStringSynth::nodeName( void ) const
{
return( pluckedstringsynth_plugin_descriptor.name );
}
void pluckedStringSynth::playNote( notePlayHandle * _n )
{
if ( _n->totalFramesPlayed() == 0 )
{
float freq = getChannelTrack()->frequency( _n );
_n->m_pluginData = new pluckSynth( freq, m_pickKnob->value(),
m_pickupKnob->value() );
}
const Uint32 frames = mixer::inst()->framesPerAudioBuffer();
sampleFrame * buf = bufferAllocator::alloc<sampleFrame>( frames );
pluckSynth * ps = static_cast<pluckSynth *>( _n->m_pluginData );
for( Uint32 frame = 0; frame < frames; ++frame )
{
const sampleType cur = ps->nextStringSample();
for( Uint8 chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl )
{
buf[frame][chnl] = cur;
}
}
getChannelTrack()->processAudioBuffer( buf, frames, _n );
bufferAllocator::free( buf );
}
void pluckedStringSynth::deleteNotePluginData( notePlayHandle * _n )
{
delete static_cast<pluckSynth *>( _n->m_pluginData );
}
pluckSynth::delayLine * FASTCALL pluckSynth::initDelayLine( int _len )
{
delayLine * dl = new pluckSynth::delayLine[_len];
dl->length = _len;
if( _len > 0 )
{
dl->data = new sampleType[_len];
}
else
{
dl->data = NULL;
}
dl->pointer = dl->data;
dl->end = dl->data + _len - 1;
return( dl );
}
void FASTCALL pluckSynth::freeDelayLine( delayLine * _dl )
{
if( _dl && _dl->data )
{
delete[] _dl->data;
}
_dl->data = NULL;
delete[] _dl;
}
pluckSynth::pluckSynth( float _pitch, float _pick, float _pickup )
{
const float AMP = 1.5f;
int rail_length = static_cast<int>( mixer::inst()->sampleRate() / 2 /
_pitch ) + 1;
// Round pick position to nearest spatial sample.
// A pick position at x = 0 is not allowed.
int pick_sample = static_cast<int>( tMax<float>( rail_length * _pick,
1.0f ) );
float initial_shape[rail_length];
m_upperRail = pluckSynth::initDelayLine( rail_length );
m_lowerRail = pluckSynth::initDelayLine( rail_length );
//#define METALLIC_PLUCK
#ifdef METALLIC_PLUCK
for ( int i = 0; i < rail_length; i++ )
{
initial_shape[i] = rand() * AMP / RAND_MAX;
}
initial_shape[pick_sample] = 0.5;
initial_shape[pick_sample+1] = 0.5;
#else
float upslope = AMP / pick_sample;
const float downslope = AMP / ( rail_length - pick_sample - 1 );
for( int i = 0; i < pick_sample; i++ )
{
initial_shape[i] = upslope * i;
}
for( int i = pick_sample; i < rail_length; i++ )
{
initial_shape[i] = downslope * ( rail_length - 1 - i );
}
#endif
// Initial conditions for the ideal plucked string.
// "Past history" is measured backward from the end of the array.
pluckSynth::setDelayLine( m_lowerRail, initial_shape, 0.5f );
pluckSynth::setDelayLine( m_upperRail, initial_shape, 0.5f );
m_pickupLoc = static_cast<int>( _pickup * rail_length );
}
extern "C"
{
// neccessary for getting instance out of shared lib
plugin * lmms_plugin_main( void * _data )
{
return( new pluckedStringSynth(
static_cast<channelTrack *>( _data ) ) );
}
}

View File

@@ -0,0 +1,216 @@
/*
* plucked_string_sytn.h - declaration of class pluckedStringSynth which
* is a synth for plucked string-sounds
*
* Linux MultiMedia Studio
* Copyright (c) 2004-2005 Tobias Doerffel <tobydox@users.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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
*/
#ifndef _PLUCKED_STRING_SYNTH_H
#define _PLUCKED_STRING_SYNTH_H
#include "instrument.h"
class knob;
class notePlayHandle;
// the actual synth
class pluckSynth
{
public:
pluckSynth( float _pitch, float _pick, float _pickup );
inline ~pluckSynth( void )
{
pluckSynth::freeDelayLine( m_upperRail );
pluckSynth::freeDelayLine( m_lowerRail );
}
inline sampleType nextStringSample( void )
{
// Output at pickup position
sampleType outsamp = rgDlAccess( m_upperRail, m_pickupLoc );
outsamp += lgDlAccess( m_lowerRail, m_pickupLoc );
// Sample traveling into "bridge"
sampleType ym0 = lgDlAccess( m_lowerRail, 1 );
// Sample to "nut"
sampleType ypM = rgDlAccess( m_upperRail,
m_upperRail->length - 2 );
// String state update
// Decrement pointer and then update
rgDlUpdate( m_upperRail, -bridgeReflection( ym0 ) );
// Update and then increment pointer
lgDlUpdate( m_lowerRail, -ypM );
return( outsamp );
}
private:
struct delayLine
{
sampleType * data;
int length;
sampleType * pointer;
sampleType * end;
} ;
delayLine * m_upperRail;
delayLine * m_lowerRail;
int m_pickupLoc;
static delayLine * FASTCALL initDelayLine( int _len );
static void FASTCALL freeDelayLine( delayLine * _dl );
static inline void setDelayLine( delayLine * _dl, float * _values,
float _scale )
{
for( int i = 0; i < _dl->length; ++i )
{
_dl->data[i] = _scale * _values[i];
}
}
/* lgDlUpdate(dl, insamp);
* Places "nut-reflected" sample from upper delay-line into
* current lower delay-line pointer position (which represents
* x = 0 position). The pointer is then incremented (i.e. the
* wave travels one sample to the left), turning the previous
* position into an "effective" x = L position for the next
* iteration. */
static inline void lgDlUpdate( delayLine * _dl, sampleType _insamp )
{
register sampleType * ptr = _dl->pointer;
*ptr = _insamp;
++ptr;
if( ptr > _dl->end )
{
ptr = _dl->data;
}
_dl->pointer = ptr;
}
/* rgDlUpdate(dl, insamp);
* Decrements current upper delay-line pointer position (i.e.
* the wave travels one sample to the right), moving it to the
* "effective" x = 0 position for the next iteration. The
* "bridge-reflected" sample from lower delay-line is then placed
* into this position. */
static inline void rgDlUpdate( delayLine * _dl, sampleType _insamp )
{
register sampleType * ptr = _dl->pointer;
--ptr;
if( ptr < _dl->data )
{
ptr = _dl->end;
}
*ptr = _insamp;
_dl->pointer = ptr;
}
/* dlAccess(dl, position);
* Returns sample "position" samples into delay-line's past.
* Position "0" points to the most recently inserted sample. */
static inline sampleType dlAccess( delayLine * _dl, int _position )
{
sampleType * outpos = _dl->pointer + _position;
while( outpos < _dl->data )
{
outpos += _dl->length;
}
while( outpos > _dl->end )
{
outpos -= _dl->length;
}
return( *outpos );
}
/*
* Right-going delay line:
* -->---->---->---
* x=0
* (pointer)
* Left-going delay line:
* --<----<----<---
* x=0
* (pointer)
*/
/* rgDlAccess(dl, position);
* Returns spatial sample at position "position", where position zero
* is equal to the current upper delay-line pointer position (x = 0).
* In a right-going delay-line, position increases to the right, and
* delay increases to the right => left = past and right = future. */
static inline sampleType rgDlAccess( delayLine * _dl, int _position )
{
return( dlAccess( _dl, _position ) );
}
/* lgDlAccess(dl, position);
* Returns spatial sample at position "position", where position zero
* is equal to the current lower delay-line pointer position (x = 0).
* In a left-going delay-line, position increases to the right, and
* delay DEcreases to the right => left = future and right = past. */
static inline sampleType lgDlAccess( delayLine * _dl, int _position )
{
return( dlAccess( _dl, _position ) );
}
static inline sampleType bridgeReflection( sampleType _insamp )
{
static sampleType state = 0.0f; // filter memory
// Implement a one-pole lowpass with feedback coefficient = 0.5
return( state = state*0.5f + _insamp*0.5f );
}
} ;
class pluckedStringSynth : public instrument
{
public:
pluckedStringSynth(channelTrack * _channel_track );
virtual ~pluckedStringSynth();
virtual void FASTCALL playNote( notePlayHandle * _n );
virtual void FASTCALL deleteNotePluginData( notePlayHandle * _n );
virtual void FASTCALL saveSettings( QDomDocument & _doc,
QDomElement & _parent );
virtual void FASTCALL loadSettings( const QDomElement & _this );
virtual QString nodeName( void ) const;
private:
knob * m_pickKnob;
knob * m_pickupKnob;
} ;
#endif

View File

@@ -0,0 +1,33 @@
AUTOMAKE_OPTIONS = foreign 1.4
INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/src/lib -I.
AM_CXXFLAGS := $(AM_CXXFLAGS) $(QT_CXXFLAGS) -DPLUGIN_NAME="tripleoscillator"
%.moc: ./%.h
$(MOC) -o $@ $<
MOC_FILES = ./triple_oscillator.moc
BUILT_SOURCES = $(MOC_FILES) ./embedded_resources.h
EMBEDDED_RESOURCES = $(wildcard *png)
./embedded_resources.h: $(EMBEDDED_RESOURCES)
$(top_builddir)/buildtools/bin2res $(EMBEDDED_RESOURCES) > $@
EXTRA_DIST = $(EMBEDDED_RESOURCES)
CLEANFILES = $(MOC_FILES) ./embedded_resources.h
pkglib_LTLIBRARIES= libtripleoscillator.la
libtripleoscillator_la_SOURCES = triple_oscillator.cpp triple_oscillator.h
$(libtripleoscillator_la_SOURCES): ./embedded_resources.h

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,167 @@
/*
* triple_oscillator.h - declaration of class tripleOscillator a powerful
* instrument-plugin with 3 oscillators
*
* Linux MultiMedia Studio
* Copyright (c) 2004-2005 Tobias Doerffel <tobydox@users.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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
*/
#ifndef _TRIPLE_OSCILLATOR_H
#define _TRIPLE_OSCILLATOR_H
#include "instrument.h"
#include "oscillator.h"
#include "sample_buffer.h"
class knob;
class pixmapButton;
class notePlayHandle;
const int NUM_OF_OSCILLATORS = 3;
class tripleOscillator : public instrument
{
Q_OBJECT
public:
tripleOscillator( channelTrack * _channel ) FASTCALL;
virtual ~tripleOscillator();
virtual void FASTCALL playNote( notePlayHandle * _n );
virtual void FASTCALL deleteNotePluginData( notePlayHandle * _n );
virtual void FASTCALL saveSettings( QDomDocument & _doc,
QDomElement & _parent );
virtual void FASTCALL loadSettings( const QDomElement & _this );
virtual QString nodeName( void ) const;
protected slots:
// Slots for Osc 1
void osc01SinWaveCh( bool _on );
void osc01TriangleWaveCh( bool _on );
void osc01SawWaveCh( bool _on );
void osc01SquareWaveCh( bool _on );
void osc01MoogSawWaveCh( bool _on );
void osc01ExpWaveCh( bool _on );
void osc01WhiteNoiseCh( bool _on );
void osc01UserDefWaveCh( bool _on );
void osc01UserDefWaveDblClick( void );
// Slots for Osc 2
void osc02SinWaveCh( bool _on );
void osc02TriangleWaveCh( bool _on );
void osc02SawWaveCh( bool _on );
void osc02SquareWaveCh( bool _on );
void osc02MoogSawWaveCh( bool _on );
void osc02ExpWaveCh( bool _on );
void osc02WhiteNoiseCh( bool _on );
void osc02UserDefWaveCh( bool _on );
void osc02UserDefWaveDblClick( void );
// Slots for Osc 3
void osc03SinWaveCh( bool _on );
void osc03TriangleWaveCh( bool _on );
void osc03SawWaveCh( bool _on );
void osc03SquareWaveCh( bool _on );
void osc03MoogSawWaveCh( bool _on );
void osc03ExpWaveCh( bool _on );
void osc03WhiteNoiseCh( bool _on );
void osc03UserDefWaveCh( bool _on );
void osc03UserDefWaveDblClick( void );
// modulation-type-button slots
void fm1BtnToggled( bool _on );
void am1BtnToggled( bool _on );
void mix1BtnToggled( bool _on );
void sync1BtnToggled( bool _on );
void fm2BtnToggled( bool _on );
void am2BtnToggled( bool _on );
void mix2BtnToggled( bool _on );
void sync2BtnToggled( bool _on );
private:
channelTrack * m_channelTrack;
struct oscillatorData
{
oscillator::waveShapes waveShape;
knob * volKnob;
knob * panKnob;
knob * coarseKnob;
knob * fineLKnob;
knob * fineRKnob;
knob * phaseOffsetKnob;
knob * stereoPhaseDetuningKnob;
pixmapButton * sinWaveBtn;
pixmapButton * triangleWaveBtn;
pixmapButton * sawWaveBtn;
pixmapButton * sqrWaveBtn;
pixmapButton * moogSawWaveBtn;
pixmapButton * expWaveBtn;
pixmapButton * whiteNoiseWaveBtn;
pixmapButton * usrWaveBtn;
sampleBuffer m_sampleBuffer;
} m_osc[NUM_OF_OSCILLATORS];
struct oscPtr
{
oscillator * oscLeft;
oscillator * oscRight;
} ;
void FASTCALL doSinWaveBtn( oscillatorData * _osc );
void FASTCALL doTriangleWaveBtn( oscillatorData * _osc );
void FASTCALL doSawWaveBtn( oscillatorData * _osc );
void FASTCALL doSqrWaveBtn( oscillatorData * _osc );
void FASTCALL doMoogSawWaveBtn( oscillatorData * _osc );
void FASTCALL doExpWaveBtn( oscillatorData * _osc );
void FASTCALL doWhiteNoiseWaveBtn( oscillatorData * _osc );
void FASTCALL doUsrWaveBtn( oscillatorData * _osc );
pixmapButton * FASTCALL getModulationButton(
oscillator::modulationAlgos _modulation_algo, int _n );
void FASTCALL setModulationAlgo(
oscillator::modulationAlgos _new_modulation_algo, int _n );
oscillator::modulationAlgos FASTCALL getModulationAlgo( int _n );
pixmapButton * m_fm1OscBtn;
pixmapButton * m_am1OscBtn;
pixmapButton * m_mix1OscBtn;
pixmapButton * m_sync1OscBtn;
pixmapButton * m_fm2OscBtn;
pixmapButton * m_am2OscBtn;
pixmapButton * m_mix2OscBtn;
pixmapButton * m_sync2OscBtn;
oscillator::modulationAlgos m_modulationAlgo1;
oscillator::modulationAlgos m_modulationAlgo2;
} ;
#endif

View File

@@ -0,0 +1,35 @@
AUTOMAKE_OPTIONS = foreign 1.4
INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/src/lib -I.
AM_CXXFLAGS := $(AM_CXXFLAGS) $(QT_CXXFLAGS) -DPLUGIN_NAME="vestige"
%.moc: ./%.h
$(MOC) -o $@ $<
MOC_FILES = ./vestige.moc
BUILT_SOURCES = $(MOC_FILES) ./embedded_resources.h
EMBEDDED_RESOURCES = $(wildcard *png)
./embedded_resources.h: $(EMBEDDED_RESOURCES)
$(top_builddir)/buildtools/bin2res $(EMBEDDED_RESOURCES) > $@
EXTRA_DIST = $(EMBEDDED_RESOURCES)
CLEANFILES = $(MOC_FILES) ./embedded_resources.h
pkglib_LTLIBRARIES= libvestige.la
libvestige_la_SOURCES = vestige.cpp vestige.h
# libvestige_la_LIBADD = -lfst
$(libvestige_la_SOURCES): ./embedded_resources.h

BIN
plugins/vestige/artwork.png Normal file

Binary file not shown.

BIN
plugins/vestige/logo.png Normal file

Binary file not shown.

860
plugins/vestige/vestige.cpp Normal file
View File

@@ -0,0 +1,860 @@
/*
* vestige.cpp - instrument-plugin for hosting VST-plugins
*
* Linux MultiMedia Studio
* Copyright (c) 2004-2005 Tobias Doerffel <tobydox@users.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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
*/
#include "qt3support.h"
#ifdef QT4
#include <Qt/QtXml>
#include <QMessageBox>
#include <QFileDialog>
#include <QFileInfo>
#include <QDir>
#include <QCursor>
#else
#include <qdom.h>
#include <qmessagebox.h>
#include <qfiledialog.h>
#include <qfileinfo.h>
#include <qdir.h>
#include <qcursor.h>
#endif
#include "vestige.h"
#include "channel_track.h"
#include "note_play_handle.h"
#include "buffer_allocator.h"
#include "mixer.h"
#include "song_editor.h"
#include "instrument_play_handle.h"
#include "pixmap_button.h"
#include "tooltip.h"
#include "spc_bg_hndl_widget.h"
#include "embed.cpp"
extern "C"
{
plugin::descriptor vestige_plugin_descriptor =
{
STRINGIFY_PLUGIN_NAME( PLUGIN_NAME ),
"VeSTige",
QT_TRANSLATE_NOOP( "plugin",
"experimental VST-hoster for using VST-plugins "
"within LMMS" ),
"Tobias Doerffel <tobydox@users.sf.net>",
0x0100,
plugin::INSTRUMENT,
PLUGIN_NAME::findEmbeddedData( "logo.png" )
} ;
}
bool vestigeInstrument::s_initialized = FALSE;
bool vestigeInstrument::s_threadAdopted = FALSE;
QPixmap * vestigeInstrument::s_artwork = NULL;
vestigeInstrument::vestigeInstrument( channelTrack * _channel_track ) :
instrument( _channel_track, vestige_plugin_descriptor.public_name ),
specialBgHandlingWidget( PLUGIN_NAME::getIconPixmap( "artwork" ) ),
m_handle( NULL ),
m_fst( NULL )
{
if( s_artwork == NULL )
{
s_artwork = new QPixmap( PLUGIN_NAME::getIconPixmap(
"artwork" ) );
}
#ifdef QT4
QPalette pal;
pal.setBrush( backgroundRole(), *s_artwork);
setPalette( pal );
#else
setErasePixmap( *s_artwork );
#endif
m_openPluginButton = new pixmapButton( this );
m_openPluginButton->setCheckable( FALSE );
m_openPluginButton->setCursor( PointingHandCursor );
m_openPluginButton->move( 200, 70 );
m_openPluginButton->setActiveGraphic( embed::getIconPixmap(
"project_open_down" ) );
m_openPluginButton->setInactiveGraphic( embed::getIconPixmap(
"project_open" ) );
m_openPluginButton->setBgGraphic( getBackground(
m_openPluginButton ) );
connect( m_openPluginButton, SIGNAL( clicked() ), this,
SLOT( openPlugin() ) );
toolTip::add( m_openPluginButton, tr( "Open other VST-plugin" ) );
#ifdef QT4
m_openPluginButton->setWhatsThis(
#else
QWhatsThis::add( m_openPluginButton,
#endif
tr( "Click here, if you want to open another VST-plugin. After "
"clicking on this button, a file-open-dialog appears "
"and you can select your file." ) );
if( s_initialized == FALSE )
{
if( fst_init( fst_signal_handler ) )
{
// TODO: message-box
return;
}
s_initialized = TRUE;
}
// now we need a play-handle which cares for calling play()
instrumentPlayHandle * iph = new instrumentPlayHandle( this );
mixer::inst()->addPlayHandle( iph );
}
vestigeInstrument::~vestigeInstrument()
{
// this single call automates the rest of cleanup like trashing our
// play-handle and so on
invalidate();
closePlugin();
}
void vestigeInstrument::loadSettings( const QDomElement & _this )
{
}
void vestigeInstrument::saveSettings( QDomDocument & _doc,
QDomElement & _parent )
{
QDomElement vst_de = _doc.createElement( nodeName() );
_parent.appendChild( vst_de );
}
QString vestigeInstrument::nodeName( void ) const
{
return( vestige_plugin_descriptor.name );
}
void vestigeInstrument::setParameter( const QString & _param,
const QString & _value )
{
if( _param == "plugin" )
{
closePlugin();
m_plugin = _value;
if( ( m_handle = fst_load(
#ifdef QT4
m_plugin.constData().toAscii()
#else
m_plugin.ascii()
#endif
) ) == NULL )
{
QMessageBox::information( this,
tr( "Failed loading plugin" ),
tr( "The VST-plugin %1 couldn't be "
"loaded for some reason." ).arg(
m_plugin ),
QMessageBox::Ok );
return;
}
if( ( m_fst = fst_instantiate( m_handle, hostCallback,
this ) ) == NULL )
{
QMessageBox::information( this,
tr( "Failed instantiating plugin" ),
tr( "Couldn't create an instance of "
"VST-plugin %1 for some "
"reason." ).arg( m_plugin ),
QMessageBox::Ok );
fst_unload( m_handle );
m_handle = NULL;
return;
}
// set sample-rate and blocksize
m_fst->plugin->dispatcher( m_fst->plugin, effSetSampleRate, 0,
0, NULL, mixer::inst()->sampleRate() );
m_fst->plugin->dispatcher( m_fst->plugin, effSetBlockSize, 0,
mixer::inst()->framesPerAudioBuffer(),
NULL, 0.0f );
// set program to zero
m_fst->plugin->dispatcher( m_fst->plugin, effSetProgram, 0, 0,
NULL, 0.0f );
// resume
m_fst->plugin->dispatcher( m_fst->plugin, effMainsChanged, 0,
1, NULL, 0.0f );
/* if( fst_run_editor( m_fst ) )
{
printf( "VeSTige: cannot create editor\n" );
}*/
int vst_version = m_fst->plugin->dispatcher( m_fst->plugin,
effGetVstVersion, 0, 0,
NULL, 0.0f );
if( vst_version < 2 )
{
QMessageBox::information( this,
tr( "VST-plugin too old" ),
tr( "The version of VST-plugin %1 "
"is smaller than 2, which "
"isn't supported." ).arg(
m_plugin ),
QMessageBox::Ok );
return;
}
/* printf("WID:%d %d\n", (int)fst_get_XID( m_fst ),
(int)QWidget::find( fst_get_XID( m_fst ) ) );*/
//printf("%d\n", m_fst->plugin->numParams);
}
}
void vestigeInstrument::play( void )
{
// the very first time, we have to adopt fst-thread
if( !s_threadAdopted )
{
fst_adopt_thread();
s_threadAdopted = TRUE;
}
if( m_handle == NULL || m_fst == NULL )
{
return;
}
// first we gonna post all MIDI-events we enqueued so far
if( m_midiEvents.size() )
{
// since MIDI-events are not received immediately, we have
// to have them stored somewhere even after dispatcher-call,
// so we create static copies of the data and post them
static VstEvents events;
static vvector<VstMidiEvent> cur_events;
cur_events = m_midiEvents;
m_midiEvents.clear();
for( csize i = 0; i < cur_events.size(); ++i )
{
events.events[i] = (VstEvent *) &cur_events[i];
}
events.numEvents = cur_events.size();
events.reserved = 0;
m_fst->plugin->dispatcher( m_fst->plugin, effProcessEvents,
0, 0, &events, 0.0f );
}
// now we're ready to fetch sound from VST-plugin
const Uint32 frames = mixer::inst()->framesPerAudioBuffer();
int ch_in = m_fst->plugin->numInputs;
int ch_out = m_fst->plugin->numOutputs;
float * ins[ch_in];
float * outs[ch_out];
for( int i = 0; i < ch_in; ++i )
{
ins[i] = bufferAllocator::alloc<float>( frames );
}
for( int i = 0; i < ch_out; ++i )
{
outs[i] = bufferAllocator::alloc<float>( frames );
}
if( m_fst->plugin->flags & effFlagsCanReplacing )
{
m_fst->plugin->processReplacing( m_fst->plugin, ins, outs,
frames );
}
else
{
printf("normal process\n");
//mixer::inst()->clearAudioBuffer( buf, frames );
m_fst->plugin->process( m_fst->plugin, ins, outs, frames );
printf("normal process done\n");
}
for( int i = 0; i < ch_in; ++i )
{
bufferAllocator::free( ins[i] );
}
// got our data, now we just have to merge the 1/2 out-buffers
sampleFrame * buf = bufferAllocator::alloc<sampleFrame>( frames );
Uint8 chnls = tMax<Uint8>( ch_out, DEFAULT_CHANNELS );
if( chnls != DEFAULT_CHANNELS )
{
mixer::inst()->clearAudioBuffer( buf, frames );
}
for( Uint32 f = 0; f < frames; ++f )
{
for( Uint8 chnl = 0; chnl < chnls; ++chnl )
{
buf[f][chnl] = outs[chnl][f];
}
}
for( int i = 0; i < ch_out; ++i )
{
bufferAllocator::free( outs[i] );
}
getChannelTrack()->processAudioBuffer( buf, frames, NULL );
bufferAllocator::free( buf );
}
void vestigeInstrument::playNote( notePlayHandle * _n )
{
if( _n->totalFramesPlayed() == 0 )
{
enqueueEvent( midiEvent( NOTE_ON, 0, _n->key(),
_n->getVolume() ), _n->framesAhead() );
}
}
void vestigeInstrument::deleteNotePluginData( notePlayHandle * _n )
{
enqueueEvent( midiEvent( NOTE_OFF, 0, _n->key(), 0 ) );
}
void vestigeInstrument::openPlugin( void )
{
#ifdef QT4
QFileDialog ofd( NULL, tr( "Open VST-plugin" ) );
#else
QFileDialog ofd( QString::null, QString::null, NULL, "", TRUE );
ofd.setWindowTitle( tr( "Open VST-plugin" ) );
#endif
QString dir;
if( m_plugin != "" )
{
#ifdef QT4
dir = QFileInfo( m_plugin ).absolutePath();
#else
dir = QFileInfo( m_plugin ).dirPath( TRUE );
#endif
}
else
{
dir = QDir::home().path();
}
// change dir to position of previously opened file
ofd.setDirectory( dir );
ofd.setFileMode( QFileDialog::ExistingFiles );
// set filters
#ifdef QT4
QStringList types;
types << tr( "DLL-files (*.dll)" )
<< tr( "EXE-files (*.exe)" )
;
ofd.setFilters( types );
#else
ofd.addFilter( tr( "DLL-files (*.dll)" ) );
ofd.addFilter( tr( "EXE-files (*.exe)" ) );
ofd.setSelectedFilter( tr( "DLL-files (*.dll)" ) );
#endif
if( m_plugin != "" )
{
// select previously opened file
ofd.selectFile( QFileInfo( m_plugin ).fileName() );
}
if ( ofd.exec () == QDialog::Accepted )
{
if( ofd.selectedFiles().isEmpty() )
{
return;
}
setParameter( "plugin", ofd.selectedFiles()[0] );
}
}
void vestigeInstrument::paintEvent( QPaintEvent * )
{
#ifdef QT4
QPainter p( this );
#else
QPixmap pm( rect().size() );
pm.fill( this, rect().topLeft() );
QPainter p( &pm, this );
#endif
p.drawPixmap( 0, 0, *s_artwork );
QString plugin_name = ( m_handle != NULL && m_fst != NULL ) ?
QString( m_handle->name ) + " " +
QString::number( m_fst->plugin->dispatcher(
m_fst->plugin,
effGetVendorVersion,
0, 0, NULL, 0.0f ) ):
tr( "No VST-plugin loaded" );
QFont f = p.font();
f.setBold( TRUE );
p.setFont( pointSize<10>( f ) );
p.setPen( QColor( 0, 0, 0 ) );
p.drawText( 20, 80, plugin_name );
if( m_handle != NULL && m_fst != NULL )
{
p.setPen( QColor( 64, 128, 64 ) );
f.setBold( FALSE );
p.setFont( pointSize<8>( f ) );
char buf[1024];
m_fst->plugin->dispatcher( m_fst->plugin, effGetVendorString,
0, 0, buf, 0 );
p.drawText( 20, 94, tr( "by" ) + " " + QString( buf ) );
}
#ifndef QT4
bitBlt( this, rect().topLeft(), &pm );
#endif
}
void vestigeInstrument::closePlugin( void )
{
if( m_fst != NULL && m_handle != NULL )
{
//fst_destroy_editor( m_fst );
printf( "closing VST-plugin" );
fst_close( m_fst );
printf( "unloading VST-plugin" );
fst_unload( m_handle );
m_fst = NULL;
m_handle = NULL;
}
}
void vestigeInstrument::enqueueEvent( const midiEvent & _e,
Uint32 _frames_ahead )
{
if( m_handle == NULL || m_fst == NULL )
{
return;
}
VstMidiEvent event;
event.type = kVstMidiType;
event.byteSize = 24;
event.deltaFrames = _frames_ahead;
event.flags = 0;
event.detune = 0;
event.noteLength = 0;
event.noteOffset = 0;
event.noteOffVelocity = 0;
event.reserved1 = 0;
event.reserved2 = 0;
event.midiData[0] = _e.m_type + _e.m_channel;
event.midiData[1] = _e.key();
event.midiData[2] = _e.velocity();
event.midiData[3] = 0;
m_midiEvents.push_back( event );
}
#define DEBUG_CALLBACKS
#ifdef DEBUG_CALLBACKS
#define SHOW_CALLBACK printf
#else
#define SHOW_CALLBACK(...)
#endif
long vestigeInstrument::hostCallback( AEffect * _effect, long _opcode,
long _index, long _value, void * _ptr,
float _opt )
{
static VstTimeInfo _timeInfo;
SHOW_CALLBACK( "host-callback, opcode = %d", (int) _opcode );
switch( _opcode )
{
case audioMasterAutomate:
SHOW_CALLBACK( "amc: audioMasterAutomate\n" );
// index, value, returns 0
_effect->setParameter( _effect, _index, _opt );
return( 0 );
case audioMasterVersion:
SHOW_CALLBACK( "amc: audioMasterVersion\n" );
// vst version, currently 2 (0 for older)
return( 2 );
case audioMasterCurrentId:
SHOW_CALLBACK( "amc: audioMasterCurrentId\n" );
// returns the unique id of a plug that's currently
// loading
return( 0 );
case audioMasterIdle:
SHOW_CALLBACK ("amc: audioMasterIdle\n");
// call application idle routine (this will
// call effEditIdle for all open editors too)
_effect->dispatcher( _effect, effEditIdle, 0, 0, NULL,
0.0f );
return( 0 );
case audioMasterPinConnected:
SHOW_CALLBACK( "amc: audioMasterPinConnected\n" );
// inquire if an input or output is beeing connected;
// index enumerates input or output counting from zero:
// value is 0 for input and != 0 otherwise. note: the
// return value is 0 for <true> such that older versions
// will always return true.
return( 1 );
case audioMasterWantMidi:
SHOW_CALLBACK( "amc: audioMasterWantMidi\n" );
// <value> is a filter which is currently ignored
return( 0 );
case audioMasterGetTime:
SHOW_CALLBACK( "amc: audioMasterGetTime\n" );
// returns const VstTimeInfo* (or 0 if not supported)
// <value> should contain a mask indicating which
// fields are required (see valid masks above), as some
// items may require extensive conversions
memset( &_timeInfo, 0, sizeof( _timeInfo ) );
//tstate = jack_transport_query (jackvst->client, &jack_pos);
_timeInfo.samplePos = 0;
_timeInfo.sampleRate = mixer::inst()->sampleRate();
_timeInfo.flags = 0;
_timeInfo.tempo = songEditor::inst()->getBPM();
_timeInfo.timeSigNumerator = 4;//(long) floor (jack_pos.beats_per_bar);
_timeInfo.timeSigDenominator = 4;//(long) floor (jack_pos.beat_type);
_timeInfo.flags |= (kVstBarsValid|kVstTempoValid);
// if (tstate == JackTransportRolling) {
_timeInfo.flags |= kVstTransportPlaying;
// }
return( (long)&_timeInfo );
case audioMasterProcessEvents:
SHOW_CALLBACK( "amc: audioMasterProcessEvents\n" );
// VstEvents* in <ptr>
return( 0 );
case audioMasterSetTime:
SHOW_CALLBACK( "amc: audioMasterSetTime\n" );
// VstTimenfo* in <ptr>, filter in <value>, not
// supported
case audioMasterTempoAt:
SHOW_CALLBACK( "amc: audioMasterTempoAt\n" );
// returns tempo (in bpm * 10000) at sample frame
// location passed in <value>
return( 0 );
case audioMasterGetNumAutomatableParameters:
SHOW_CALLBACK( "amc: audioMasterGetNumAutomatable"
"Parameters\n" );
return( 0 );
case audioMasterGetParameterQuantization:
SHOW_CALLBACK( "amc: audioMasterGetParameter\n"
"Quantization\n" );
// returns the integer value for +1.0 representation,
// or 1 if full single float precision is maintained
// in automation. parameter index in <value> (-1: all,
// any)
return( 0 );
case audioMasterIOChanged:
SHOW_CALLBACK( "amc: audioMasterIOChanged\n" );
// numInputs and/or numOutputs has changed
return( 0 );
case audioMasterNeedIdle:
SHOW_CALLBACK( "amc: audioMasterNeedIdle\n" );
// plug needs idle calls (outside its editor window)
return( 0 );
case audioMasterSizeWindow:
// TODO using lmms-main-window-size
SHOW_CALLBACK( "amc: audioMasterSizeWindow\n" );
// index: width, value: height
return( 0 );
case audioMasterGetSampleRate:
// TODO using mixer-call
SHOW_CALLBACK( "amc: audioMasterGetSampleRate\n" );
return( 0 );
case audioMasterGetBlockSize:
// TODO using mixer-call
SHOW_CALLBACK( "amc: audioMasterGetBlockSize\n" );
return( 0 );
case audioMasterGetInputLatency:
// TODO using mixer-call
SHOW_CALLBACK( "amc: audioMasterGetInputLatency\n" );
return( 0 );
case audioMasterGetOutputLatency:
// TODO using mixer-call
SHOW_CALLBACK( "amc: audioMasterGetOutputLatency\n" );
return( 0 );
case audioMasterGetPreviousPlug:
SHOW_CALLBACK( "amc: audioMasterGetPreviousPlug\n" );
// input pin in <value> (-1: first to come), returns
// cEffect*
return( 0 );
case audioMasterGetNextPlug:
SHOW_CALLBACK( "amc: audioMasterGetNextPlug\n" );
// output pin in <value> (-1: first to come), returns
// cEffect*
return( 0 );
case audioMasterWillReplaceOrAccumulate:
SHOW_CALLBACK( "amc: audioMasterWillReplaceOr"
"Accumulate\n" );
// returns: 0: not supported, 1: replace, 2: accumulate
return( 0 );
case audioMasterGetCurrentProcessLevel:
SHOW_CALLBACK( "amc: audioMasterGetCurrentProcess"
"Level\n" );
// returns: 0: not supported,
// 1: currently in user thread (gui)
// 2: currently in audio thread (where process is
// called)
// 3: currently in 'sequencer' thread (midi, timer etc)
// 4: currently offline processing and thus in user
// thread
// other: not defined, but probably pre-empting user
// thread.
return( 0 );
case audioMasterGetAutomationState:
SHOW_CALLBACK( "amc: audioMasterGetAutomationState\n" );
// returns 0: not supported, 1: off, 2:read, 3:write,
// 4:read/write offline
return( 0 );
case audioMasterOfflineStart:
SHOW_CALLBACK( "amc: audioMasterOfflineStart\n" );
return( 0 );
case audioMasterOfflineRead:
SHOW_CALLBACK( "amc: audioMasterOfflineRead\n" );
// ptr points to offline structure, see below.
// return 0: error, 1 ok
return( 0 );
case audioMasterOfflineWrite:
SHOW_CALLBACK( "amc: audioMasterOfflineWrite\n" );
// same as read
return( 0 );
case audioMasterOfflineGetCurrentPass:
SHOW_CALLBACK( "amc: audioMasterOfflineGetCurrent"
"Pass\n" );
return( 0 );
case audioMasterOfflineGetCurrentMetaPass:
SHOW_CALLBACK( "amc: audioMasterOfflineGetCurrentMeta"
"Pass\n");
return( 0 );
case audioMasterSetOutputSampleRate:
SHOW_CALLBACK( "amc: audioMasterSetOutputSample"
"Rate\n" );
// for variable i/o, sample rate in <opt>
return( 0 );
case audioMasterGetSpeakerArrangement:
SHOW_CALLBACK( "amc: audioMasterGetSpeaker"
"Arrangement\n" );
// (long)input in <value>, output in <ptr>
return( 0 );
case audioMasterGetVendorString:
SHOW_CALLBACK( "amc: audioMasterGetVendorString\n" );
// fills <ptr> with a string identifying the vendor
// (max 64 char)
strcpy( (char *) _ptr, "LAD");
return( 0 );
case audioMasterGetProductString:
SHOW_CALLBACK( "amc: audioMasterGetProductString\n" );
// fills <ptr> with a string with product name
// (max 64 char)
strcpy( (char *) _ptr, "VeSTige" );
return( 0 );
case audioMasterGetVendorVersion:
SHOW_CALLBACK( "amc: audioMasterGetVendorVersion\n" );
// TODO
// returns vendor-specific version
return( 1000 );
case audioMasterVendorSpecific:
SHOW_CALLBACK( "amc: audioMasterVendorSpecific\n" );
// no definition, vendor specific handling
return( 0 );
case audioMasterSetIcon:
SHOW_CALLBACK( "amc: audioMasterSetIcon\n" );
// TODO
// void* in <ptr>, format not defined yet
return( 0 );
case audioMasterCanDo:
SHOW_CALLBACK( "amc: audioMasterCanDo\n" );
// string in ptr, see below
return( 0 );
case audioMasterGetLanguage:
SHOW_CALLBACK( "amc: audioMasterGetLanguage\n" );
// TODO
// see enum
return( 0 );
case audioMasterOpenWindow:
SHOW_CALLBACK( "amc: audioMasterOpenWindow\n" );
// TODO
// returns platform specific ptr
return( 0 );
case audioMasterCloseWindow:
SHOW_CALLBACK( "amc: audioMasterCloseWindow\n" );
// TODO
// close window, platform specific handle in <ptr>
return( 0 );
case audioMasterGetDirectory:
SHOW_CALLBACK( "amc: audioMasterGetDirectory\n" );
// TODO
// get plug directory, FSSpec on MAC, else char*
return( 0 );
case audioMasterUpdateDisplay:
SHOW_CALLBACK( "amc: audioMasterUpdateDisplay\n" );
// something has changed, update 'multi-fx' display
_effect->dispatcher( _effect, effEditIdle, 0, 0, NULL,
0.0f );
return( 0 );
case audioMasterBeginEdit:
SHOW_CALLBACK( "amc: audioMasterBeginEdit\n" );
// begin of automation session (when mouse down),
// parameter index in <index>
return( 0 );
case audioMasterEndEdit:
SHOW_CALLBACK( "amc: audioMasterEndEdit\n" );
// end of automation session (when mouse up),
// parameter index in <index>
return( 0 );
case audioMasterOpenFileSelector:
SHOW_CALLBACK( "amc: audioMasterOpenFileSelector\n" );
// open a fileselector window with VstFileSelect*
// in <ptr>
return( 0 );
default:
SHOW_CALLBACK( "VST master dispatcher: undefed: "
"%d, %d\n", (int) _opcode,
effKeysRequired );
break;
}
return( 0 );
}
extern "C"
{
// neccessary for getting instance out of shared lib
plugin * lmms_plugin_main( void * _data )
{
return( new vestigeInstrument( static_cast<channelTrack *>( _data ) ) );
}
}
#include "vestige.moc"

110
plugins/vestige/vestige.h Normal file
View File

@@ -0,0 +1,110 @@
/*
* vestige.h - instrument VeSTige for hosting VST-plugins
*
* Linux MultiMedia Studio
* Copyright (c) 2004-2005 Tobias Doerffel <tobydox@users.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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
*/
#ifndef _VESTIGE_H
#define _VESTIGE_H
#include "instrument.h"
#include "midi.h"
#ifdef QT4
#include <QVector>
#else
#include <qvector.h>
#endif
#include <fst.h>
#include <vst/aeffectx.h>
#include "spc_bg_hndl_widget.h"
class pixmapButton;
class QPixmap;
class vestigeInstrument : public instrument, public specialBgHandlingWidget
{
Q_OBJECT
public:
vestigeInstrument( channelTrack * _channel_track );
virtual ~vestigeInstrument();
virtual void play( void );
virtual void FASTCALL playNote( notePlayHandle * _n );
virtual void FASTCALL deleteNotePluginData( notePlayHandle * _n );
virtual void FASTCALL saveSettings( QDomDocument & _doc,
QDomElement & _parent );
virtual void FASTCALL loadSettings( const QDomElement & _this );
virtual QString nodeName( void ) const;
virtual void FASTCALL setParameter( const QString & _param,
const QString & _value );
protected slots:
void openPlugin( void );
protected:
virtual void paintEvent( QPaintEvent * _pe );
private:
void closePlugin( void );
void enqueueEvent( const midiEvent & _e, Uint32 _frames_ahead = 0 );
static long hostCallback( AEffect *, long, long, long, void *, float );
static bool s_initialized;
static bool s_threadAdopted;
static QPixmap * s_artwork;
FSTHandle * m_handle;
FST * m_fst;
vvector<VstMidiEvent> m_midiEvents;
pixmapButton * m_openPluginButton;
QString m_plugin;
} ;
#endif