soundfont 2 beta

git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@732 0778d3d1-df1d-0410-868b-ea421aaaa00d
This commit is contained in:
Paul Giblock
2008-02-28 21:57:52 +00:00
parent fc1f97a99c
commit 704527de7d
11 changed files with 1293 additions and 0 deletions

View File

@@ -23,6 +23,7 @@ SUBDIRS = \
midi_import \
organic \
patman \
sf2_player \
$(SINGERBOT_DIR) \
$(STK_DIR) \
triple_oscillator \

View File

@@ -0,0 +1,39 @@
AUTOMAKE_OPTIONS = foreign 1.4
INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/src/lib -I.
AM_CXXFLAGS := $(AM_CXXFLAGS) $(QT_CXXFLAGS) -DPLUGIN_NAME="sf2player"
%.moc: ./%.h
$(MOC) -o $@ $<
%.ui: ./%.ui
$(UIC) -o ui_$@ $<
MOC_FILES = ./sf2_player.moc ./patches_dialog.moc
UIC_FILES = ./ui_patches_dialog.h
BUILT_SOURCES = $(MOC_FILES) ${UIC_FILES} ./embedded_resources.h
EMBEDDED_RESOURCES = $(wildcard *png)
./embedded_resources.h: $(EMBEDDED_RESOURCES)
$(BIN2RES) $(EMBEDDED_RESOURCES) > $@
EXTRA_DIST = $(EMBEDDED_RESOURCES)
CLEANFILES = $(MOC_FILES) ./embedded_resources.h
pkglib_LTLIBRARIES = libsf2player.la
libsf2player_la_SOURCES = sf2_player.cpp sf2_player.h patches_dialog.cpp patches_dialog.h ui_patches_dialog.h
libsf2player_la_LDFLAGS = -L /usr/lib -lfluidsynth
$(libsf2player_la_SOURCES): ./embedded_resources.h

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
plugins/sf2_player/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -0,0 +1,364 @@
/*
* patches_dialog.cpp - display sf2 patches
*
* Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail/dot/com>
*
* 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 "patches_dialog.h"
#include <QHeaderView>
//#include <QFileInfo>
// Custom list-view item (as for numerical sort purposes...)
class patchItem : public QTreeWidgetItem
{
public:
// Constructor.
patchItem( QTreeWidget *pListView,
QTreeWidgetItem *pItemAfter )
: QTreeWidgetItem( pListView, pItemAfter ) {}
// Sort/compare overriden method.
bool operator< ( const QTreeWidgetItem& other ) const
{
int iColumn = QTreeWidgetItem::treeWidget()->sortColumn();
const QString& s1 = text( iColumn );
const QString& s2 = other.text( iColumn );
if( iColumn == 0 || iColumn == 2 )
{
return( s1.toInt() < s2.toInt() );
}
else
{
return( s1 < s2 );
}
}
};
// Constructor.
patchesDialog::patchesDialog( QWidget *pParent, Qt::WindowFlags wflags )
: QDialog( pParent, wflags )
{
// Setup UI struct...
setupUi( this );
m_pSynth = NULL;
m_iChan = 0;
m_iBank = 0;
m_iProg = 0;
// Soundfonts list view...
QHeaderView *pHeader = m_progListView->header();
// pHeader->setResizeMode(QHeaderView::Custom);
pHeader->setDefaultAlignment(Qt::AlignLeft);
// pHeader->setDefaultSectionSize(200);
pHeader->setMovable(false);
pHeader->setStretchLastSection(true);
m_progListView->resizeColumnToContents(0); // Prog.
//pHeader->resizeSection(1, 200); // Name.
// Initial sort order...
m_bankListView->sortItems(0, Qt::AscendingOrder);
m_progListView->sortItems(0, Qt::AscendingOrder);
// UI connections...
QObject::connect(m_bankListView,
SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
SLOT(bankChanged()));
QObject::connect(m_progListView,
SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
SLOT(progChanged(QTreeWidgetItem*,QTreeWidgetItem*)));
QObject::connect(m_progListView,
SIGNAL(itemActivated(QTreeWidgetItem*,int)),
SLOT(accept()));
QObject::connect(m_okButton,
SIGNAL(clicked()),
SLOT(accept()));
QObject::connect(m_cancelButton,
SIGNAL(clicked()),
SLOT(reject()));
}
// Destructor.
patchesDialog::~patchesDialog()
{
}
// Dialog setup loader.
void patchesDialog::setup ( fluid_synth_t *pSynth, int iChan, const QString & _chanName,
lcdSpinBoxModel * _bankModel, lcdSpinBoxModel * _progModel )
{
// We'll going to changes the whole thing...
m_dirty = 0;
m_bankModel = _bankModel;
m_progModel = _progModel;
// Set the proper caption...
setWindowTitle( _chanName + " - Soundfont patches");
// set m_pSynth to NULL so we don't trigger any progChanged events
m_pSynth = NULL;
// Load bank list from actual synth stack...
m_bankListView->setSortingEnabled(false);
m_bankListView->clear();
// now it should be safe to set internal stuff
m_pSynth = pSynth;
m_iChan = iChan;
fluid_preset_t preset;
QTreeWidgetItem *pBankItem = NULL;
// For all soundfonts (in reversed stack order) fill the available banks...
int cSoundFonts = ::fluid_synth_sfcount(m_pSynth);
for (int i = 0; i < cSoundFonts; i++) {
fluid_sfont_t *pSoundFont = ::fluid_synth_get_sfont(m_pSynth, i);
if (pSoundFont) {
#ifdef CONFIG_FLUID_BANK_OFFSET
int iBankOffset = ::fluid_synth_get_bank_offset(m_pSynth, pSoundFont->id);
#endif
pSoundFont->iteration_start(pSoundFont);
while (pSoundFont->iteration_next(pSoundFont, &preset)) {
int iBank = preset.get_banknum(&preset);
#ifdef CONFIG_FLUID_BANK_OFFSET
iBank += iBankOffset;
#endif
if (!findBankItem(iBank)) {
pBankItem = new patchItem(m_bankListView, pBankItem);
if (pBankItem)
pBankItem->setText(0, QString::number(iBank));
}
}
}
}
m_bankListView->setSortingEnabled(true);
// Set the selected bank.
m_iBank = 0;
fluid_preset_t *pPreset = ::fluid_synth_get_channel_preset(m_pSynth, m_iChan);
if (pPreset) {
m_iBank = pPreset->get_banknum(pPreset);
#ifdef CONFIG_FLUID_BANK_OFFSET
m_iBank += ::fluid_synth_get_bank_offset(m_pSynth, (pPreset->sfont)->id);
#endif
}
pBankItem = findBankItem(m_iBank);
m_bankListView->setCurrentItem(pBankItem);
m_bankListView->scrollToItem(pBankItem);
bankChanged();
// Set the selected program.
if (pPreset)
m_iProg = pPreset->get_num(pPreset);
QTreeWidgetItem *pProgItem = findProgItem(m_iProg);
m_progListView->setCurrentItem(pProgItem);
m_progListView->scrollToItem(pProgItem);
// Done with setup...
//m_iDirtySetup--;
}
// Stabilize current state form.
void patchesDialog::stabilizeForm()
{
m_okButton->setEnabled(validateForm());
}
// Validate form fields.
bool patchesDialog::validateForm()
{
bool bValid = true;
bValid = bValid && (m_bankListView->currentItem() != NULL);
bValid = bValid && (m_progListView->currentItem() != NULL);
return bValid;
}
// Realize a bank-program selection preset.
void patchesDialog::setBankProg ( int iBank, int iProg )
{
if (m_pSynth == NULL)
return;
// just select the synth's program preset...
::fluid_synth_bank_select(m_pSynth, m_iChan, iBank);
::fluid_synth_program_change(m_pSynth, m_iChan, iProg);
// Maybe this is needed to stabilize things around.
::fluid_synth_program_reset(m_pSynth);
}
// Validate form fields and accept it valid.
void patchesDialog::accept()
{
if (validateForm()) {
// Unload from current selected dialog items.
int iBank = (m_bankListView->currentItem())->text(0).toInt();
int iProg = (m_progListView->currentItem())->text(0).toInt();
// And set it right away...
setBankProg(iBank, iProg);
if (m_dirty > 0) {
m_bankModel->setValue(iBank);
m_progModel->setValue(iProg);
}
// Do remember preview state...
// if (m_pOptions)
// m_pOptions->bPresetPreview = m_ui.PreviewCheckBox->isChecked();
// We got it.
QDialog::accept();
}
}
// Reject settings (Cancel button slot).
void patchesDialog::reject (void)
{
// Reset selection to initial selection, if applicable...
if (m_dirty > 0)
setBankProg(m_bankModel->value(), m_progModel->value());
// Done (hopefully nothing).
QDialog::reject();
}
// Find the bank item of given bank number id.
QTreeWidgetItem *patchesDialog::findBankItem ( int iBank )
{
QList<QTreeWidgetItem *> banks
= m_bankListView->findItems(
QString::number(iBank), Qt::MatchExactly, 0);
QListIterator<QTreeWidgetItem *> iter(banks);
if (iter.hasNext())
return iter.next();
else
return NULL;
}
// Find the program item of given program number id.
QTreeWidgetItem *patchesDialog::findProgItem ( int iProg )
{
QList<QTreeWidgetItem *> progs
= m_progListView->findItems(
QString::number(iProg), Qt::MatchExactly, 0);
QListIterator<QTreeWidgetItem *> iter(progs);
if (iter.hasNext())
return iter.next();
else
return NULL;
}
// Bank change slot.
void patchesDialog::bankChanged (void)
{
if (m_pSynth == NULL)
return;
QTreeWidgetItem *pBankItem = m_bankListView->currentItem();
if (pBankItem == NULL)
return;
int iBankSelected = pBankItem->text(0).toInt();
// Clear up the program listview.
m_progListView->setSortingEnabled(false);
m_progListView->clear();
fluid_preset_t preset;
QTreeWidgetItem *pProgItem = NULL;
// For all soundfonts (in reversed stack order) fill the available programs...
int cSoundFonts = ::fluid_synth_sfcount(m_pSynth);
for (int i = 0; i < cSoundFonts && !pProgItem; i++) {
fluid_sfont_t *pSoundFont = ::fluid_synth_get_sfont(m_pSynth, i);
if (pSoundFont) {
#ifdef CONFIG_FLUID_BANK_OFFSET
int iBankOffset = ::fluid_synth_get_bank_offset(m_pSynth, pSoundFont->id);
#endif
pSoundFont->iteration_start(pSoundFont);
while (pSoundFont->iteration_next(pSoundFont, &preset)) {
int iBank = preset.get_banknum(&preset);
#ifdef CONFIG_FLUID_BANK_OFFSET
iBank += iBankOffset;
#endif
int iProg = preset.get_num(&preset);
if (iBank == iBankSelected && !findProgItem(iProg)) {
pProgItem = new patchItem(m_progListView, pProgItem);
if (pProgItem) {
pProgItem->setText(0, QString::number(iProg));
pProgItem->setText(1, preset.get_name(&preset));
//pProgItem->setText(2, QString::number(pSoundFont->id));
//pProgItem->setText(3, QFileInfo(
// pSoundFont->get_name(pSoundFont)).baseName());
}
}
}
}
}
m_progListView->setSortingEnabled(true);
// Stabilize the form.
stabilizeForm();
}
// Program change slot.
void patchesDialog::progChanged (QTreeWidgetItem * _curr, QTreeWidgetItem * _prev)
{
if (m_pSynth == NULL || _curr == NULL)
return;
// Which preview state...
if( validateForm() ) {
// Set current selection.
int iBank = (m_bankListView->currentItem())->text(0).toInt();
int iProg = _curr->text(0).toInt();
// And set it right away...
setBankProg(iBank, iProg);
// Now we're dirty nuff.
m_dirty++;
}
// Stabilize the form.
stabilizeForm();
}
#include "patches_dialog.moc"

View File

@@ -0,0 +1,93 @@
/*
* patches_dialog.h - display sf2 patches
*
* Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail/dot/com>
*
* 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 _PATCHES_DIALOG_H
#define _PATCHES_DIALOG_H
#include "ui_patches_dialog.h"
#include "lcd_spinbox.h"
#include <fluidsynth.h>
#include <QWidget>
//----------------------------------------------------------------------------
// qsynthPresetForm -- UI wrapper form.
class patchesDialog : public QDialog, private Ui::patchesDialog
{
Q_OBJECT
public:
// Constructor.
patchesDialog(QWidget *pParent = 0, Qt::WindowFlags wflags = 0);
// Destructor.
virtual ~patchesDialog();
void setup(fluid_synth_t *pSynth, int iChan, const QString & _chanName,
lcdSpinBoxModel * _bankModel, lcdSpinBoxModel * _progModel );
public slots:
void stabilizeForm();
void bankChanged();
void progChanged( QTreeWidgetItem * _curr, QTreeWidgetItem * _prev );
protected slots:
void accept();
void reject();
protected:
void setBankProg(int iBank, int iProg);
QTreeWidgetItem *findBankItem(int iBank);
QTreeWidgetItem *findProgItem(int iProg);
bool validateForm();
private:
// Instance variables.
fluid_synth_t *m_pSynth;
int m_iChan;
int m_iBank;
int m_iProg;
//int m_iDirtySetup;
//int m_iDirtyCount;
int m_dirty;
lcdSpinBoxModel * m_bankModel;
lcdSpinBoxModel * m_progModel;
};
#endif

View File

@@ -0,0 +1,216 @@
<ui version="4.0" >
<author>rncbc aka Rui Nuno Capela</author>
<comment>qsynth - A fluidsynth Qt GUI Interface.
Copyright (C) 2003-2007, rncbc aka Rui Nuno Capela. All rights reserved.
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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
</comment>
<class>patchesDialog</class>
<widget class="QDialog" name="patchesDialog" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>480</width>
<height>350</height>
</rect>
</property>
<property name="minimumSize" >
<size>
<width>300</width>
<height>150</height>
</size>
</property>
<property name="windowTitle" >
<string>Qsynth: Channel Preset</string>
</property>
<property name="windowIcon" >
<iconset/>
</property>
<layout class="QVBoxLayout" >
<item>
<widget class="QSplitter" name="m_splitter" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<widget class="QTreeWidget" name="m_bankListView" >
<property name="minimumSize" >
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="maximumSize" >
<size>
<width>80</width>
<height>32767</height>
</size>
</property>
<property name="toolTip" >
<string>Bank selector</string>
</property>
<property name="alternatingRowColors" >
<bool>true</bool>
</property>
<property name="indentation" >
<number>4</number>
</property>
<property name="rootIsDecorated" >
<bool>false</bool>
</property>
<property name="uniformRowHeights" >
<bool>true</bool>
</property>
<property name="itemsExpandable" >
<bool>false</bool>
</property>
<property name="sortingEnabled" >
<bool>true</bool>
</property>
<property name="allColumnsShowFocus" >
<bool>true</bool>
</property>
<column>
<property name="text" >
<string>Bank</string>
</property>
</column>
</widget>
<widget class="QTreeWidget" name="m_progListView" >
<property name="toolTip" >
<string>Program selector</string>
</property>
<property name="alternatingRowColors" >
<bool>true</bool>
</property>
<property name="indentation" >
<number>4</number>
</property>
<property name="rootIsDecorated" >
<bool>false</bool>
</property>
<property name="uniformRowHeights" >
<bool>true</bool>
</property>
<property name="itemsExpandable" >
<bool>false</bool>
</property>
<property name="sortingEnabled" >
<bool>true</bool>
</property>
<property name="allColumnsShowFocus" >
<bool>true</bool>
</property>
<column>
<property name="text" >
<string>Patch</string>
</property>
</column>
<column>
<property name="text" >
<string>Name</string>
</property>
</column>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="spacing" >
<number>4</number>
</property>
<property name="leftMargin" >
<number>0</number>
</property>
<property name="topMargin" >
<number>0</number>
</property>
<property name="rightMargin" >
<number>0</number>
</property>
<property name="bottomMargin" >
<number>0</number>
</property>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType" >
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" >
<size>
<width>120</width>
<height>8</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="m_okButton" >
<property name="toolTip" >
<string/>
</property>
<property name="text" >
<string>OK</string>
</property>
<property name="iconSize" >
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="default" >
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_cancelButton" >
<property name="toolTip" >
<string/>
</property>
<property name="text" >
<string>Cancel</string>
</property>
<property name="iconSize" >
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<layoutdefault spacing="4" margin="4" />
<tabstops>
<tabstop>m_okButton</tabstop>
<tabstop>m_cancelButton</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,421 @@
/*
* sf2_player.cpp - a soundfont2 player using fluidSynth
*
* Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail/dot/com>
*
* 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/QLayout>
#include <QtGui/QLabel>
#include <QtGui/QFileDialog>
#include <Qt/QtXml>
#include "sf2_player.h"
#include "engine.h"
#include "instrument_track.h"
#include "instrument_play_handle.h"
#include "note_play_handle.h"
#include "knob.h"
#include "song.h"
#include "automatable_model_templates.h"
#include "main_window.h"
#include "patches_dialog.h"
#include "tooltip.h"
#include "lcd_spinbox.h"
#undef SINGLE_SOURCE_COMPILE
#include "embed.cpp"
extern "C"
{
plugin::descriptor sf2_plugin_descriptor =
{
STRINGIFY_PLUGIN_NAME( PLUGIN_NAME ),
"Sf2Player",
QT_TRANSLATE_NOOP( "pluginBrowser",
"SoundFont synthesizer" ),
"Paul Giblock <drfaygo/at/gmail/dot/com>",
0x0100,
plugin::Instrument,
new QPixmap( PLUGIN_NAME::getIconPixmap( "logo" ) ),
NULL
} ;
}
sf2Instrument::sf2Instrument( instrumentTrack * _instrument_track ) :
instrument( _instrument_track, &sf2_plugin_descriptor ),
m_bankNum( -1, -1, 999, 1, this ),
m_patchNum( -1, -1, 127, 1, this ),
m_filename( "" ),
m_fontId( 0 )
{
m_settings = new_fluid_settings();
/* Set the synthesizer settings, if necessary */
m_synth = new_fluid_synth( m_settings );
//fluid_settings_setstr(settings, "audio.driver", "jack");
//adriver = new_fluid_audio_driver(settings, synth);
instrumentPlayHandle * iph = new instrumentPlayHandle( this );
engine::getMixer()->addPlayHandle( iph );
connect( &m_bankNum, SIGNAL( dataChanged() ),
this, SLOT( updatePatch() ) );
connect( &m_patchNum, SIGNAL( dataChanged() ),
this, SLOT( updatePatch() ) );
}
sf2Instrument::~sf2Instrument()
{
engine::getMixer()->removePlayHandles( getInstrumentTrack() );
delete_fluid_synth( m_synth );
delete_fluid_settings( m_settings );
}
void sf2Instrument::saveSettings( QDomDocument & _doc,
QDomElement & _this )
{
_this.setAttribute( "src", m_filename );
m_patchNum.saveSettings( _doc, _this, "patch" );
m_bankNum.saveSettings( _doc, _this, "bank" );
}
void sf2Instrument::loadSettings( const QDomElement & _this )
{
openFile( _this.attribute( "src" ) );
m_patchNum.loadSettings( _this, "patch" );
m_bankNum.loadSettings( _this, "bank" );
}
QString sf2Instrument::nodeName( void ) const
{
return( sf2_plugin_descriptor.name );
}
void sf2Instrument::openFile( const QString & _sf2File )
{
if( m_filename != "")
{
fluid_synth_sfunload( m_synth, m_fontId, TRUE);
}
m_fontId = fluid_synth_sfload( m_synth, _sf2File.toLocal8Bit(), TRUE );
if( m_fontId >= 0)
{
m_patchNum.setValue(0);
m_bankNum.setValue(0);
m_filename = _sf2File;
emit fileChanged();
}
}
void sf2Instrument::updatePatch( void )
{
printf("update patch\n");
if( m_bankNum.value() >= 0 && m_patchNum.value() >= 0 ) {
fluid_synth_program_select( m_synth, 1, m_fontId,
m_bankNum.value(), m_patchNum.value() );
}
}
void sf2Instrument::playNote( notePlayHandle * _n, bool )
{
const float LOG440 = 2.64345267649f;
const int defaultVelocity = 80;
const f_cnt_t tfp = _n->totalFramesPlayed();
int midiNote = (int)floor( ( log2( _n->frequency() ) - LOG440 ) * 12 +69-58)+0.5;
if ( tfp == 0 )
{
_n->m_pluginData = new int( midiNote );
fluid_synth_noteon( m_synth, 1, midiNote, defaultVelocity );
}
else if( _n->released() )
{
// Doesn't happen with release frames = 0
}
}
void sf2Instrument::waitForWorkerThread( void )
{
// No waiting required, at least not that I know of
return;
}
void sf2Instrument::play( bool _try_parallelizing )
{
const fpp_t frames = engine::getMixer()->framesPerPeriod();
sampleFrame * buf = new sampleFrame[frames];
// Assumes stereo and float sample_t
fluid_synth_write_float( m_synth, frames, buf, 0, 2, buf, 1, 2 );
getInstrumentTrack()->processAudioBuffer( buf, frames, NULL );
delete[] buf;
if( !_try_parallelizing )
{
waitForWorkerThread();
}
}
void sf2Instrument::deleteNotePluginData( notePlayHandle * _n )
{
int * midiNote = static_cast<int *>( _n->m_pluginData );
fluid_synth_noteoff( m_synth, 1, *midiNote );
delete midiNote;
_n->noteOff();
}
pluginView * sf2Instrument::instantiateView( QWidget * _parent )
{
return( new sf2InstrumentView( this, _parent ) );
}
sf2InstrumentView::sf2InstrumentView( instrument * _instrument,
QWidget * _parent ) :
instrumentView( _instrument, _parent )
{
QVBoxLayout * vl = new QVBoxLayout( this );
QHBoxLayout * hl = new QHBoxLayout();
// File Button
m_fileDialogButton = new pixmapButton( this, NULL );
m_fileDialogButton->setCursor( QCursor( Qt::PointingHandCursor ) );
m_fileDialogButton->setActiveGraphic( embed::getIconPixmap(
"project_open_down" ) );
m_fileDialogButton->setInactiveGraphic( embed::getIconPixmap(
"project_open" ) );
connect( m_fileDialogButton, SIGNAL( clicked() ),
this, SLOT( showFileDialog() ) );
toolTip::add( m_fileDialogButton, tr( "Open other SoundFont file" ) );
m_fileDialogButton->setWhatsThis(
tr( "Click here to open another SF2 file" ) );
// Patch Button
m_patchDialogButton = new pixmapButton( this, NULL );
m_patchDialogButton->setCursor( QCursor( Qt::PointingHandCursor ) );
m_patchDialogButton->setActiveGraphic( embed::getIconPixmap(
"track_op_menu" ) );
m_patchDialogButton->setInactiveGraphic( embed::getIconPixmap(
"track_op_menu" ) );
connect( m_patchDialogButton, SIGNAL( clicked() ),
this, SLOT( showPatchDialog() ) );
toolTip::add( m_patchDialogButton, tr( "Choose the patch" ) );
// LCDs
m_bankNumLcd = new lcdSpinBox( 3, this, "Bank" );
m_bankNumLcd->setLabel( "Bank:" );
m_bankNumLcd->addTextForValue( -1, "---" );
m_bankNumLcd->setEnabled( FALSE );
m_patchNumLcd = new lcdSpinBox( 3, this, "Patch" );
m_patchNumLcd->setLabel( "Patch:" );
m_patchNumLcd->addTextForValue( -1, "---" );
m_patchNumLcd->setEnabled( FALSE );
hl->addWidget( m_fileDialogButton );
hl->addWidget( m_bankNumLcd );
hl->addWidget( m_patchNumLcd );
hl->addWidget( m_patchDialogButton );
vl->addLayout( hl );
// Next row
hl = new QHBoxLayout();
m_filenameLabel = new QLabel( this );
hl->addWidget( m_filenameLabel );
vl->addLayout( hl );
setAutoFillBackground( TRUE );
QPalette pal;
pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap(
"artwork" ) );
setPalette( pal );
updateFilename();
}
sf2InstrumentView::~sf2InstrumentView()
{
}
void sf2InstrumentView::modelChanged( void )
{
sf2Instrument * k = castModel<sf2Instrument>();
m_bankNumLcd->setModel( &k->m_bankNum );
m_patchNumLcd->setModel( &k->m_patchNum );
connect(k, SIGNAL( fileChanged( void ) ),
this, SLOT( updateFilename( void ) ) );
updateFilename();
}
void sf2InstrumentView::updateFilename( void )
{
m_filenameLabel->setText("File: " +
castModel<sf2Instrument>()->m_filename +
"\nPatch: TODO");
update();
}
void sf2InstrumentView::showFileDialog( void )
{
sf2Instrument * k = castModel<sf2Instrument>();
QFileDialog ofd( NULL, tr( "Open SoundFont file" ) );
ofd.setFileMode( QFileDialog::ExistingFiles );
QStringList types;
types << tr( "SoundFont2-Files (*.sf2)" );
ofd.setFilters( types );
if( k->m_filename == "" )
{
ofd.setDirectory(
configManager::inst()->userSamplesDir() );
}
else if( QFileInfo( k->m_filename ).isRelative() )
{
QString f = configManager::inst()->userSamplesDir()
+ k->m_filename;
if( QFileInfo( f ).exists() == FALSE )
{
f = configManager::inst()->factorySamplesDir()
+ k->m_filename;
}
ofd.selectFile( f );
}
else
{
ofd.selectFile( k->m_filename );
}
// HACK
ofd.setDirectory("/home/llama");
if( ofd.exec() == QDialog::Accepted && !ofd.selectedFiles().isEmpty() )
{
QString f = ofd.selectedFiles()[0];
if( f != "" )
{
k->openFile( f );
engine::getSong()->setModified();
}
}
}
patchesDialog * sf2InstrumentView::s_patchDialog = NULL;
void sf2InstrumentView::showPatchDialog( void ) {
sf2Instrument * k = castModel<sf2Instrument>();
if( s_patchDialog == NULL ) {
printf("Creating patchDialog\n");
s_patchDialog = new patchesDialog(this);
}
s_patchDialog->setup( k->m_synth, 1, k->getInstrumentTrack()->name(),
&k->m_bankNum, &k->m_patchNum );
s_patchDialog->exec();
}
extern "C"
{
// neccessary for getting instance out of shared lib
plugin * lmms_plugin_main( model *, void * _data )
{
return( new sf2Instrument(
static_cast<instrumentTrack *>( _data ) ) );
}
}
#include "sf2_player.moc"

View File

@@ -0,0 +1,143 @@
/*
* sf2_player.h - a soundfont2 player using fluidSynth
*
* Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail/dot/com>
*
* 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 _SF2_PLAYER_H
#define _SF2_PLAYER_H
#include "instrument.h"
#include "pixmap_button.h"
#include "instrument_view.h"
#include "knob.h"
#include "lcd_spinbox.h"
#include "fluidsynth.h"
class sf2InstrumentView;
class notePlayHandle;
class patchesDialog;
class QLabel;
class sf2Instrument : public instrument
{
Q_OBJECT
public:
sf2Instrument( instrumentTrack * _instrument_track );
virtual ~sf2Instrument();
virtual void play( bool _try_parallelizing );
virtual void FASTCALL playNote( notePlayHandle * _n,
bool _try_parallelizing );
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 f_cnt_t desiredReleaseFrames( void ) const
{
return( 0 );
}
virtual bool notePlayHandleBased( void ) const
{
return( FALSE );
}
virtual bool supportsParallelizing( void ) const
{
return( FALSE );
}
virtual void waitForWorkerThread( void );
virtual pluginView * instantiateView( QWidget * _parent );
public slots:
void openFile( const QString & _sf2File );
void updatePatch( void );
private:
fluid_settings_t* m_settings;
fluid_synth_t* m_synth;
fluid_audio_driver_t* m_adriver;
int m_fontId;
QString m_filename;
lcdSpinBoxModel m_bankNum;
lcdSpinBoxModel m_patchNum;
friend class sf2InstrumentView;
signals:
void fileChanged( void );
void patchChanged( void );
} ;
class sf2InstrumentView : public instrumentView
{
Q_OBJECT
public:
sf2InstrumentView( instrument * _instrument,
QWidget * _parent );
virtual ~sf2InstrumentView();
private:
virtual void modelChanged( void );
pixmapButton * m_fileDialogButton;
pixmapButton * m_patchDialogButton;
lcdSpinBox * m_bankNumLcd;
lcdSpinBox * m_patchNumLcd;
QLabel * m_filenameLabel;
static patchesDialog * s_patchDialog;
protected slots:
void showFileDialog( void );
void showPatchDialog( void );
void updateFilename( void );
} ;
#endif