diff options
Diffstat (limited to 'attic/muse/synti/fluidsynth')
-rw-r--r-- | attic/muse/synti/fluidsynth/Makefile.am | 19 | ||||
-rw-r--r-- | attic/muse/synti/fluidsynth/README.txt | 45 | ||||
-rw-r--r-- | attic/muse/synti/fluidsynth/TODO | 13 | ||||
-rw-r--r-- | attic/muse/synti/fluidsynth/fluidsynthgui.cpp | 819 | ||||
-rw-r--r-- | attic/muse/synti/fluidsynth/fluidsynthgui.h | 225 | ||||
-rw-r--r-- | attic/muse/synti/fluidsynth/fluidsynthguibase.ui | 632 | ||||
-rw-r--r-- | attic/muse/synti/fluidsynth/fluidsynti.cpp | 1314 | ||||
-rw-r--r-- | attic/muse/synti/fluidsynth/fluidsynti.h | 150 |
8 files changed, 3217 insertions, 0 deletions
diff --git a/attic/muse/synti/fluidsynth/Makefile.am b/attic/muse/synti/fluidsynth/Makefile.am new file mode 100644 index 00000000..817c3cb5 --- /dev/null +++ b/attic/muse/synti/fluidsynth/Makefile.am @@ -0,0 +1,19 @@ +include $(top_srcdir)/common.am +include $(top_srcdir)/synti/synti-install.am + +AM_CXXFLAGS += -fPIC -O3 -ffast-math -fno-exceptions + +synthi_LTLIBRARIES = fluidsynth.la + +fluidsynth_la_SOURCES = fluidsynti.h fluidsynti.cpp \ + fluidsynthgui.cpp fluidsynthgui.h \ + fluidsynthguibase.ui + +nodist_fluidsynth_la_SOURCES = moc_fluidsynthgui.cpp + +fluidsynth_la_LIBADD = ../libsynti/libsynti.la -lfluidsynth +fluidsynth_la_LDFLAGS = -module -avoid-version + +CLEANFILES = $(wildcard *.ui~) +EXTRA_DIST = TODO README.txt + diff --git a/attic/muse/synti/fluidsynth/README.txt b/attic/muse/synti/fluidsynth/README.txt new file mode 100644 index 00000000..7764edb6 --- /dev/null +++ b/attic/muse/synti/fluidsynth/README.txt @@ -0,0 +1,45 @@ +README.txt +---------- + +Graphical frontend and built-in softsynth (MusE Experimental Soft Synth) for MusE, based on Fluidsynth +(http://www.fluidsynth.org). + +Features: +--------- +- Loading/unloading of soundfonts +- Easy control of fluidsynth's send effects and their parameters +- Mapping of soundfonts to fluidsynth channels +- Stores all settings in the current project file and automatically loads all effect parameters, + soundfonts, channel settings and presets when re-opening the project. +- Makes it possible to use several soundfonts in one single fluidsynth instance (thereby reducing CPU usage since they share + the same send effects) + + +Changelog/History +----------------- +040524 +- Err... Fount out that this changelog is neglected. See ../../Changelog.txt instead. +031019 +- Bugfixes and changes in storing/retrieving init parameters (Mathias Lundgren) +031009 +- Unloading of soundfonts works (Mathias Lundgren) +- Last dir stored in project-file (Mathias Lundgren) +- Ordinary controller-events enabled (Mathias Lundgren) +031008 +- Mapping of soundfonts to fluidchannels and selection of patches implemented. (Mathias Lundgren) +- Permanent storage of channels & patches. Extended GUI. (Mathias Lundgren) +031002 +- Various communication problems fixed between GUI and client (Mathias Lundgren) +- Storage of synth parameters and soundfonts enabled (Mathias Lundgren/Robert Jonsson) + +0309xx +- Problem with loading of soundfonts resulting in Jack timeout fixed by moving loading of soundfonts to separate thread. (Robert Jonsson) + +Original code written by Robert Ham (no information about the history of his work) + + +Known problems/TODO: +-------------------------------------------------------------- +* Turning on the chorus and/or modifying chorus parameters locks the client. +* Illegal chorus parameters can be sent to fluidsynth. +* Drum patches (lbank=128) not implemented yet diff --git a/attic/muse/synti/fluidsynth/TODO b/attic/muse/synti/fluidsynth/TODO new file mode 100644 index 00000000..e941e1e9 --- /dev/null +++ b/attic/muse/synti/fluidsynth/TODO @@ -0,0 +1,13 @@ + TODO + +o preset loading/saving +o configuration loading/saving +o soundfont information display +o remembering the last directory that was dealt with +o change gui<->synth communication to nrpns + + DONE + +o get all controllers working +o soundfont stack operations +o patch name retrieval diff --git a/attic/muse/synti/fluidsynth/fluidsynthgui.cpp b/attic/muse/synti/fluidsynth/fluidsynthgui.cpp new file mode 100644 index 00000000..c8fe72b4 --- /dev/null +++ b/attic/muse/synti/fluidsynth/fluidsynthgui.cpp @@ -0,0 +1,819 @@ +/* + * MusE FLUID Synth softsynth plugin + * + * Copyright (C) 2004 Mathias Lundgren (lunar_shuttle@users.sourcforge.net) + * + * $Id: fluidsynthgui.cpp,v 1.13.2.2 2009/08/12 20:47:01 spamatica Exp $ + * + */ + +#include "fluidsynthgui.h" +#include "fluidsynti.h" +#include <qpushbutton.h> + +#include <iostream> +#include <qlineedit.h> +#include <qfiledialog.h> +#include <qsocketnotifier.h> +#include <qpixmap.h> +#include <qlistview.h> +#include <qslider.h> +#include <qmessagebox.h> +#include <qpopupmenu.h> +#include <qheader.h> +#include <qcheckbox.h> +#include <qspinbox.h> +#include <qcombobox.h> + +#include "muse/midi.h" +#include "xpm/buttondown.xpm" + + + /* +#include "muse/debug.h" +#include <iomanip> +#include <qtooltip.h> +#include <qapplication.h> +#include <qlistbox.h> +#define MUSE_FLUID_DEBUG false +*/ + +FluidSynthGui::FluidSynthGui() + : MessGui() + { + //Connect socketnotifier to fifo + QSocketNotifier* s = new QSocketNotifier(readFd, QSocketNotifier::Read); + connect(s, SIGNAL(activated(int)), SLOT(readMessage(int))); + connect (Push, SIGNAL (clicked()), SLOT(loadClicked())); + + lastdir = ""; + + channelListView->setColumnWidthMode(FS_CHANNEL_COL,QListView::Maximum); + channelListView->setColumnWidthMode(FS_SF_ID_COL,QListView::Maximum); + ReverbFrame->setEnabled(true); + ChorusFrame->setEnabled(true); + + if (!FS_DEBUG) + dumpInfoButton->hide(); + + //Init reverb sliders: + /*ReverbRoomSize->setValue((int)(16383*FS_PREDEF_REVERB_ROOMSIZE)); + ReverbDamping->setValue((int)(16383*FS_PREDEF_REVERB_DAMPING)); + ReverbWidth->setValue((int)(16383*FS_PREDEF_REVERB_WIDTH));*/ + + connect(Gain, SIGNAL(valueChanged(int)), SLOT(changeGain(int))); + connect(dumpInfoButton , SIGNAL(clicked()), SLOT(dumpInfo())); + connect(channelListView, SIGNAL(pressed(QListViewItem*,const QPoint&,int)), + this, SLOT(channelItemClicked(QListViewItem*,const QPoint&,int))); + + connect(Reverb, SIGNAL (toggled(bool)), SLOT(toggleReverb(bool))); + connect(ReverbLevel, SIGNAL (valueChanged (int)), SLOT(changeReverbLevel(int))); + connect(ReverbRoomSize, SIGNAL (valueChanged (int)), SLOT(changeReverbRoomSize(int))); + connect(ReverbDamping, SIGNAL (valueChanged (int)), SLOT(changeReverbDamping(int))); + connect(ReverbWidth, SIGNAL (valueChanged (int)), SLOT(changeReverbWidth(int))); + + connect (Pop, SIGNAL (clicked()), SLOT(popClicked())); + connect(sfListView, SIGNAL(pressed(QListViewItem*,const QPoint&,int)), + this, SLOT(sfItemClicked(QListViewItem*,const QPoint&,int))); + connect(Chorus, SIGNAL (toggled (bool)), SLOT(toggleChorus (bool))); + connect(ChorusNumber, SIGNAL (valueChanged (int)), SLOT(changeChorusNumber (int))); + connect(ChorusType, SIGNAL (activated (int)), SLOT(changeChorusType (int))); + connect(ChorusSpeed, SIGNAL (valueChanged (int)), SLOT(changeChorusSpeed (int))); + connect(ChorusDepth, SIGNAL (valueChanged (int)), SLOT(changeChorusDepth (int))); + connect(ChorusLevel, SIGNAL (valueChanged (int)), SLOT(changeChorusLevel (int))); +/* + _notifier = new QSocketNotifier(0, QSocketNotifier::Read); + connect(_notifier, SIGNAL(activated(int)), SLOT(readData(int))); + + //Setup the ListView + sfListView->setColumnWidthMode(MUSE_FLUID_ID_COL,QListView::Maximum); + sfListView->setColumnWidthMode(MUSE_FLUID_SFNAME_COL,QListView::Maximum); + + sfListView->setColumnAlignment(MUSE_FLUID_ID_COL,AlignHCenter); + sfListView->setSorting(MUSE_FLUID_ID_COL,true); + channelListView->setColumnAlignment(MUSE_FLUID_CHANNEL_COL,AlignHCenter); + + _currentlySelectedFont = -1; //No selected font to start with + // The GUI-process is killed every time the window is shut, + // need to get all parameters from the synth + + requestAllParameters(); + + */ + + //Clear channels + for (int i=0; i<FS_MAX_NR_OF_CHANNELS; i++) + channels[i] = FS_UNSPECIFIED_ID; + + // work around for probable QT/WM interaction bug. + // for certain window managers, e.g xfce, this window is + // is displayed although not specifically set to show(); + // bug: 2811156 Softsynth GUI unclosable with XFCE4 (and a few others) + show(); + hide(); + } + +FluidSynthGui::~FluidSynthGui() + { + /* + delete _notifier; + */ + } + +void FluidSynthGui::toggleReverb(bool on) { sendController(0, FS_REVERB_ON, on); } +void FluidSynthGui::changeReverbLevel(int val) { sendController(0, FS_REVERB_LEVEL, val); } +void FluidSynthGui::changeReverbRoomSize(int val) { sendController(0, FS_REVERB_ROOMSIZE, val); } +void FluidSynthGui::changeReverbWidth(int val) { sendController(0, FS_REVERB_WIDTH, val); } +void FluidSynthGui::changeReverbDamping(int val) { sendController(0, FS_REVERB_DAMPING, val); } + +void FluidSynthGui::toggleChorus(bool val) { sendController(0, FS_CHORUS_ON, val); } +void FluidSynthGui::changeChorusNumber(int val) { sendController(0, FS_CHORUS_NUM, val); } +void FluidSynthGui::changeChorusType(int val) { sendController(0, FS_CHORUS_TYPE, val); } +void FluidSynthGui::changeChorusSpeed(int val) { sendController(0, FS_CHORUS_SPEED, val); } +void FluidSynthGui::changeChorusDepth(int val) { sendController(0, FS_CHORUS_DEPTH, val); } +void FluidSynthGui::changeChorusLevel(int val) { sendController(0, FS_CHORUS_LEVEL, val); } + + /* + +void FluidSynthGui::pushClicked() + { + const QString& fns = Filename->text(); + if (fns.isEmpty()) + return; + const char * fn = fns.latin1(); + + int datalen = strlen(fn) + 3; + unsigned char data [datalen]; + data[0] = MUSE_FLUID_SOUNDFONT_PUSH; + data[1] = MUSE_FLUID_UNSPECIFIED_ID; //This makes the client choose next available external id + memcpy(data + 2, fn, strlen(fn) + 1 ); //Store filename + sendSysex(data, datalen); + data[0] = MUSE_FLUID_GUI_REQ_SOUNDFONTS; //For simplicity's sake, just get all the soundfont data again. + sendSysex(data, 1); + printf("Gui sent Sysex.\n"); + + return; + } + */ + +void FluidSynthGui::loadClicked() + { + QString filename = QFileDialog::getOpenFileName(lastdir, QString("*.[Ss][Ff]2"), + this, + "Load Soundfont dialog", + "Choose soundfont"); + if (filename != QString::null) { + int lastslash = filename.findRev('/'); + lastdir = filename.left(lastslash); + + sendLastdir(lastdir); + sendLoadFont(filename); + } + } + +//--------------------------------------------------------- +// sendLastdir +// Send the last dir-value to the client +//--------------------------------------------------------- + +void FluidSynthGui::sendLastdir(QString dir) + { + int l = strlen(dir)+2; + byte data[l]; + data[0] = FS_LASTDIR_CHANGE; + memcpy(data+1, dir.latin1(), strlen(dir)+1); + sendSysex(data,l); + } + +//--------------------------------------------------------- +// sendLoadFont +// Tell the client to load a font with first available id +//--------------------------------------------------------- + +void FluidSynthGui::sendLoadFont(QString filename) + { + int l = filename.length()+3; + byte data[l]; + data[0] = FS_PUSH_FONT; + data[1] = FS_UNSPECIFIED_ID; + memcpy(data+2, filename.latin1(), filename.length()+1); + sendSysex(data,l); + } + +//--------------------------------------------------------- +// processEvent +//--------------------------------------------------------- + +void FluidSynthGui::processEvent(const MidiPlayEvent& ev) + { + //Sysexes sent from the client + if (ev.type() == ME_SYSEX) { + byte* data = ev.data(); + switch (*data) { + case FS_LASTDIR_CHANGE: + lastdir = QString((const char*)data+1); + break; + case FS_ERROR: { + char* msg = (char*) (data+1); + + printf("Muse: fluidsynth error: %s\n", msg); + + break; + } + case FS_SEND_SOUNDFONTDATA: { + int chunk_len; + int filename_len; + + int count = (int)*(data+1); //Number of elements + byte* cp = data+2; //Point to beginning of first chunk + sfListView->clear(); //Clear the listview + stack.clear(); //Clear the stack since we're starting over again + + while (count) { + FluidGuiSoundFont font; + filename_len = strlen((const char*)cp) + 1; + font.name = (const char*)cp; + font.id = *(cp + filename_len); + chunk_len = filename_len + FS_SFDATALEN; + stack.push_front(font); + cp += chunk_len; //Move to next chunk + count--; + } + updateSoundfontListView(); + updateChannelListView(); + break; + } + case FS_SEND_CHANNELINFO: { + byte* chptr = (data+1); + for (int i=0; i< FS_MAX_NR_OF_CHANNELS; i++) { + byte id = *chptr; + byte channel = *(chptr+1); + channels[channel] = id; + chptr+=2; + } + updateChannelListView(); + + break; + } + case FS_SEND_DRUMCHANNELINFO: { + byte* drumchptr = (data+1); + for (int i=0; i<FS_MAX_NR_OF_CHANNELS; i++) { + drumchannels[i] = *drumchptr; + drumchptr++; + } + updateChannelListView(); + break; + } + default: + if (FS_DEBUG) + printf("FluidSynthGui::processEvent() : Unknown Sysex received: %d\n", ev.type()); + break; + } + } + //Controllers sent from the client: + else + if(ev.type() == ME_CONTROLLER) { + int id = ev.dataA(); + int val = ev.dataB(); + switch (id) { + case FS_GAIN: { + bool sb = Gain->signalsBlocked(); + Gain->blockSignals(true); + // Update Gain-slider without causing it to respond to it's own signal (and send another msg to the synth) + Gain->setValue(val); + Gain->blockSignals(sb); + break; + } + case FS_REVERB_ON: { + bool sb = Reverb->signalsBlocked(); + Reverb->blockSignals(true); + Reverb->setChecked(val); + Reverb->blockSignals(sb); + break; + } + case FS_REVERB_LEVEL: { + bool sb = ReverbLevel->signalsBlocked(); + ReverbLevel->blockSignals(true); + ReverbLevel->setValue(val); + ReverbLevel->blockSignals(sb); + break; + } + case FS_REVERB_DAMPING: { + bool sb = ReverbDamping->signalsBlocked(); + ReverbDamping->blockSignals(true); + ReverbDamping->setValue(val); + ReverbDamping->blockSignals(sb); + break; + } + case FS_REVERB_ROOMSIZE: { + bool sb = ReverbRoomSize->signalsBlocked(); + ReverbRoomSize->blockSignals(true); + ReverbRoomSize->setValue(val); + ReverbRoomSize->blockSignals(sb); + break; + } + case FS_REVERB_WIDTH: { + bool sb = ReverbWidth->signalsBlocked(); + ReverbWidth->blockSignals(true); + ReverbWidth->setValue(val); + ReverbWidth->blockSignals(sb); + break; + } + case FS_CHORUS_ON: { + Chorus->blockSignals(true); + Chorus->setChecked(val); + Chorus->blockSignals(false); + break; + } + case FS_CHORUS_SPEED: { + ChorusSpeed->blockSignals(true); + ChorusSpeed->setValue(val); + ChorusSpeed->blockSignals(false); + break; + } + case FS_CHORUS_NUM: { + ChorusNumber->blockSignals(true); + ChorusNumber->setValue(val); + ChorusNumber->blockSignals(false); + break; + } + case FS_CHORUS_TYPE: { + ChorusType->blockSignals(true); + ChorusType->setCurrentItem(val); + ChorusType->blockSignals(false); + break; + } + case FS_CHORUS_DEPTH: { + ChorusDepth->blockSignals(true); + ChorusDepth->setValue(val); + ChorusDepth->blockSignals(false); + break; + } + case FS_CHORUS_LEVEL: { + ChorusLevel->blockSignals(true); + ChorusLevel->setValue(val); + ChorusLevel->blockSignals(false); + break; + } + default: + if (FS_DEBUG) + printf("FluidSynthGui::processEvent() : Unknown controller sent to gui: %x\n",id); + break; + } + } + else + if (FS_DEBUG) + printf("FluidSynthGui::processEvent - unknown event of type %dreceived from synth.\n", ev.type()); + } + +//--------------------------------------------------------- +// readMessage +//--------------------------------------------------------- +void FluidSynthGui::readMessage(int) + { + MessGui::readMessage(); + } + +//--------------------------------------------------------- +// updateChannels +//--------------------------------------------------------- +void FluidSynthGui::updateChannelListView() + { + if (FS_DEBUG) + printf("FluidSynthGui::updateChannelListView\n"); + channelListView->clear(); + for (int i=0; i<FS_MAX_NR_OF_CHANNELS; i++) { + QString chanstr, sfidstr, drumchanstr; + + //Soundfont id string: + if (channels[i] == FS_UNSPECIFIED_ID) + sfidstr = "unspecified"; + else + sfidstr = getSoundFontName(channels[i]); + //Channel string: + chanstr = QString::number(i+1); + if (chanstr.length()==1) + chanstr = "0" + chanstr; + + //Drumchan string: + if (drumchannels[i]) + drumchanstr = "Yes"; + else + drumchanstr = "No"; + QListViewItem* qlvNewItem = new QListViewItem(channelListView); + qlvNewItem->setText(FS_CHANNEL_COL, chanstr); + qlvNewItem->setPixmap(FS_SF_ID_COL, buttondown_xpm); + qlvNewItem->setText(FS_SF_ID_COL, sfidstr); + qlvNewItem->setPixmap(FS_DRUM_CHANNEL_COL, buttondown_xpm); + qlvNewItem->setText(FS_DRUM_CHANNEL_COL, drumchanstr); + channelListView->insertItem(qlvNewItem); + } + } + +//--------------------------------------------------------- +// updateSoundfontListView +//--------------------------------------------------------- +void FluidSynthGui::updateSoundfontListView() + { + sfListView->clear(); //Clear the listview + for (std::list<FluidGuiSoundFont>::iterator it = stack.begin(); it != stack.end(); it++) { + QListViewItem* qlvNewItem = new QListViewItem(sfListView); + QString qsid = QString("%1").arg(it->id); + qlvNewItem->setText(FS_ID_COL, qsid); + qlvNewItem->setText(FS_SFNAME_COL, QString(it->name)); + sfListView->insertItem(qlvNewItem); + } + sfListView->sort(); + } + +//--------------------------------------------------------- +// changeGain +//--------------------------------------------------------- +void FluidSynthGui::changeGain(int value) + { + sendController(0, FS_GAIN, value); + } + + +//--------------------------------------------------------- +// dumpInfoButton +//--------------------------------------------------------- +void FluidSynthGui::dumpInfo() + { + byte data[1]; + data[0] = FS_DUMP_INFO; + sendSysex(data, 1); + } + +//--------------------------------------------------------- +// getSoundFontName +//--------------------------------------------------------- + +QString FluidSynthGui::getSoundFontName(int id) + { + QString name = NULL; + for (std::list<FluidGuiSoundFont>::iterator it = stack.begin(); it != stack.end(); it++) { + if (id == it->id) { + name = it->name; + continue; + } + } + return name; + } + +//--------------------------------------------------------- +// channelItemClicked +// change channel parameters like soundfont / drumchannel on/off +//--------------------------------------------------------- + +void FluidSynthGui::channelItemClicked(QListViewItem* item, const QPoint&, int col) + { + if (col == FS_SF_ID_COL) { + QPopupMenu* popup = new QPopupMenu(this); + QPoint ppt = channelListView->itemRect(item).bottomLeft(); + QListView* listView = item->listView(); + ppt += QPoint(listView->header()->sectionPos(col), listView->header()->height()); + ppt = listView->mapToGlobal(ppt); + + int i = 0; + for (std::list<FluidGuiSoundFont>::reverse_iterator it = stack.rbegin(); it != stack.rend(); it++) { + i++; + /*byte* d = (byte*) it->name.latin1(); + for (int i=0; i<96; i++) { + if (i%16 == 0) + printf("%x:",(i+d)); + + printf("%x ",*(d-48+i)); + + if (i%16 == 15) + printf("\n"); + } + for (int i=0; i<96; i++) { + if (i%16 == 0) + printf("%x:",(i+d-48)); + + printf("%c ",*(d-48+i)); + + if (i%16 == 15) + printf("\n"); + } + printf("\n\n");*/ + popup->insertItem(it->name,i); + } + int lastindex = i+1; + popup->insertItem("unspecified",lastindex); + int index = popup->exec(ppt, 0); + if (index !=-1) { + byte sfid; + QString fontname; + if (index == lastindex) { + sfid = FS_UNSPECIFIED_ID; + fontname = "unspecified"; //Actually, it's not possible to reset fluid-channels as for now, + } //so this is just a dummy that makes the synth block any events for the channel + else { + sfid = getSoundFontId(popup->text(index)); + fontname = getSoundFontName(sfid); + } + byte channel = atoi(item->text(FS_CHANNEL_COL).latin1()) - 1; + sendChannelChange(sfid, channel); + item->setText(FS_SF_ID_COL, fontname); + } + delete popup; + } + // Drumchannel column: + else if (col == FS_DRUM_CHANNEL_COL) { + QPopupMenu* popup = new QPopupMenu(this); + QPoint ppt = channelListView->itemRect(item).bottomLeft(); + QListView* listView = item->listView(); + ppt += QPoint(listView->header()->sectionPos(col), listView->header()->height()); + ppt = listView->mapToGlobal(ppt); + popup->insertItem("Yes", 1); + popup->insertItem("No", 0); + byte channel = atoi(item->text(FS_CHANNEL_COL).latin1()) - 1; + + int index = popup->exec(ppt, 0); + if (index != drumchannels[channel] && index !=-1) { + sendDrumChannelChange(index, channel); + drumchannels[channel] = index; + item->setText(FS_DRUM_CHANNEL_COL, index == 0 ? "No" : "Yes" ); + } + } + } + +//--------------------------------------------------------- +// getSoundFontId +//--------------------------------------------------------- + +int FluidSynthGui::getSoundFontId(QString q) + { + int id = -1; + for (std::list<FluidGuiSoundFont>::iterator it = stack.begin(); it != stack.end(); it++) { + if (q == it->name) + id = it->id; + } + return id; + } + +//--------------------------------------------------------- +// sendChannelChange +// Tell the client to set a soundfont to a specific fluid channel +//--------------------------------------------------------- + +void FluidSynthGui::sendChannelChange(byte font_id, byte channel) + { + byte data[3]; + data[0] = FS_SOUNDFONT_CHANNEL_SET; + data[1] = font_id; + data[2] = channel; + sendSysex(data, 3); + } + +//--------------------------------------------------------- +// sendDrumChannelChange +// Tell the client to set a specific channel to drum channel (equiv to midichan 10) +//--------------------------------------------------------- +void FluidSynthGui::sendDrumChannelChange(byte onoff, byte channel) + { + byte data[3]; + data[0] = FS_DRUMCHANNEL_SET; + data[1] = onoff; + data[2] = channel; + sendSysex(data, 3); + if (FS_DEBUG) + printf("Sent FS_DRUMCHANNEL_SET for channel %d, status: %d\n", channel, onoff); + } + +void FluidSynthGui::popClicked() + { + byte data[2]; + data[0] = FS_SOUNDFONT_POP; + data[1] = currentlySelectedFont; + sendSysex(data,2); + } + +void FluidSynthGui::sfItemClicked(QListViewItem* item, const QPoint&, int /*col*/) + { + if (item != 0) { + currentlySelectedFont = atoi(item->text(FS_ID_COL)); + Pop->setEnabled(true); + } + else { + currentlySelectedFont = -1; + Pop->setEnabled(false); + } + } + +#if 0 + + + +void FluidSynthGui::readData (int fd) + { + unsigned char buffer[512]; + int n = ::read(fd, buffer, 512); +// dataInput(buffer, n); + } + + + +void FluidSynthGui::changeReverbRoomSize (int value) { + sendParameterChange(MUSE_FLUID_PARAMETER_REVERB, + "roomsize", value); +} + +void FluidSynthGui::changeReverbDamping (int value) { + sendParameterChange(MUSE_FLUID_PARAMETER_REVERB, + "damping", value); +} + +void FluidSynthGui::changeReverbWidth (int value) { + sendParameterChange(MUSE_FLUID_PARAMETER_REVERB, + "width", value); +} + + +void FluidSynthGui::changeChorusNumber (int value) { + sendParameterChange(MUSE_FLUID_PARAMETER_CHORUS, + "number", value); +} + +void FluidSynthGui::changeChorusType (int value) { + sendParameterChange(MUSE_FLUID_PARAMETER_CHORUS, + "type", value); +} + +void FluidSynthGui::changeChorusSpeed (int value) { + sendParameterChange(MUSE_FLUID_PARAMETER_CHORUS, + "speed", value); //TODO: Right now illegal values may be sent. + //Make sure they stay within fluidsynths legal boundaries (0.29-5Hz) dunno what that is in doubles + //This might be the case for the other chorus parameters as well +} + +void FluidSynthGui::changeChorusDepth (int value) { + sendParameterChange(MUSE_FLUID_PARAMETER_CHORUS, + "depth", value); +} + +void FluidSynthGui::changeChorusLevel (int value) { + sendParameterChange(MUSE_FLUID_PARAMETER_CHORUS, + "level", value); +} + + +void FluidSynthGui::sysexReceived(unsigned char const * data, int len) + { + char * cp; + double * dp; + //std::cerr << "FluidSynthGui, sysexReceived: " << (int) *data << std::endl; + switch (*data) { + case MUSE_FLUID_CLIENT_SEND_PARAMETER: + cp = (char *) (data + 2); + dp = (double *) (data + strlen (cp) + 3); + setParameter ((int) *(data+1), cp, *dp); + break; + + case MUSE_FLUID_GAIN_GET: + dp = (double *) (data + 1); + Gain->setValue ((int) (*dp * 12.8)); + break; + + case MUSE_FLUID_CLIENT_LASTDIR_CHANGE: { + if (*(char*)(data+1) != MUSE_FLUID_UNSPECIFIED_LASTDIR) + _lastDir = QString((char*)(data+1)); + else + _lastDir=""; + } + + default: + break; + } + } + + + + + + + +void FluidSynthGui::requestAllParameters () { + unsigned char data[1]; + + //data[0] = MUSE_FLUID_ADVGUI_GET; + //sendSysex (data, 1); + dbgMsg("Requesting all parameters!\n"); + sendParameterRequest (MUSE_FLUID_PARAMETER_REVERB, "on"); + sendParameterRequest (MUSE_FLUID_PARAMETER_REVERB, "roomsize"); + sendParameterRequest (MUSE_FLUID_PARAMETER_REVERB, "damping"); + sendParameterRequest (MUSE_FLUID_PARAMETER_REVERB, "width"); + sendParameterRequest (MUSE_FLUID_PARAMETER_REVERB, "level"); + sendParameterRequest (MUSE_FLUID_PARAMETER_CHORUS, "on"); + sendParameterRequest (MUSE_FLUID_PARAMETER_CHORUS, "number"); + sendParameterRequest (MUSE_FLUID_PARAMETER_CHORUS, "type"); + sendParameterRequest (MUSE_FLUID_PARAMETER_CHORUS, "speed"); + sendParameterRequest (MUSE_FLUID_PARAMETER_CHORUS, "depth"); + sendParameterRequest (MUSE_FLUID_PARAMETER_CHORUS, "level"); + data[0] = MUSE_FLUID_GAIN_GET; + sendSysex (data, 1); + data[0] = MUSE_FLUID_GUI_REQ_SOUNDFONTS; + sendSysex (data, 1); +} + +bool FluidSynthGui::sendParameterRequest (int parameterSet, const char * parameter) { + size_t parameterMem = strlen (parameter) + 1; + int datalen = 2 + parameterMem; + unsigned char * data = new unsigned char [datalen]; + *data = MUSE_FLUID_GUI_REQ_FXPARAMETER_GET; + *(data + 1) = (char) parameterSet; + memcpy (data + 2, parameter, parameterMem); + sendSysex (data, datalen); + delete data; + return true; +} + +void FluidSynthGui::setParameter (int parameterSet, const char * parameter, double value) { + int ival = (int) (value * 128); + std::string ps (parameter); + if (parameterSet == MUSE_FLUID_PARAMETER_REVERB) { + if (ps == "roomsize") { + ReverbRoomSize->setValue (ival); + } else if (ps == "damping") { + ReverbDamping->setValue (ival); + } else if (ps == "width") { + ReverbWidth->setValue (ival); + } else if (ps == "level") { + ReverbLevel->setValue (ival); + } else if (ps == "on") { + Reverb->setChecked (ival); + } + } else { + if (ps == "number") { + ChorusNumber->setValue (ival); + } else if (ps == "type") { + ChorusType->setCurrentItem (ival); + } else if (ps == "speed") { + ChorusSpeed->setValue (ival); + } else if (ps == "depth") { + ChorusDepth->setValue (ival); + } else if (ps == "level") { + ChorusLevel->setValue (ival); + } else if (ps == "on") { + Chorus->setChecked (ival); + } + } +} + +//Sends parameter to reverb or chorus +bool FluidSynthGui::sendParameterChange (int parameterSet, const char * parameter, int value) { + size_t parameterMem = strlen (parameter) + 1; + int datalen = 2 + parameterMem + sizeof (double); + unsigned char * data = new unsigned char [datalen]; + *data = (unsigned char) MUSE_FLUID_GUI_REQ_FXPARAMETER_SET; + *(data + 1) = (unsigned char) parameterSet; + memcpy (data + 2, parameter, parameterMem); + double * dp = (double *) (data + 2 + parameterMem); + *dp = ((double) value) / ((double) 128.0); + sendSysex (data, datalen); + delete data; + return true; +} + +void FluidSynthGui::dbgMsg(const char* msg) + { + if (MUSE_FLUID_DEBUG) + std::cerr << msg << std::endl; + } +//--------------------------------------------------------- +// main +//--------------------------------------------------------- + +/*QString museProject; +QString museGlobalShare; +QString museUser;*/ + + +int main(int argc, char* argv[]) +{ +/* + museUser = getenv("MUSEHOME"); + if (museUser == 0) + museUser = getenv("HOME"); + museGlobalShare = getenv("MUSE"); + if (museGlobalShare == 0) { + museGlobalShare = "/usr/muse"; + if (access(museGlobalShare.latin1(), R_OK) != 0) { + museGlobalShare = "/usr/local/muse"; + if (access(museGlobalShare.latin1(), R_OK) != 0) + museGlobalShare = museUser; + } + }*/ + char * instanceName = argv[1]; + QApplication app (argc, argv, true); + QWidget* w = new FluidSynthGui (); + if (argc > 1) + w->setCaption(QString(instanceName)); + w->show(); + app.connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit())); + qApp->exec(); +} + +#endif diff --git a/attic/muse/synti/fluidsynth/fluidsynthgui.h b/attic/muse/synti/fluidsynth/fluidsynthgui.h new file mode 100644 index 00000000..bb94421b --- /dev/null +++ b/attic/muse/synti/fluidsynth/fluidsynthgui.h @@ -0,0 +1,225 @@ +/* + * MusE FLUID Synth softsynth plugin + * + * Copyright (C) 2004 Mathias Lundgren (lunar_shuttle@users.sourcforge.net) + * + * $Id: fluidsynthgui.h,v 1.10.2.3 2009/02/02 21:38:02 terminator356 Exp $ + * + */ + +#ifndef __MUSE_FLUIDSYNTHGUI_H__ +#define __MUSE_FLUIDSYNTHGUI_H__ + +#include "fluidsynthguibase.h" +#include "libsynti/gui.h" +#include <list> + +struct FluidChannel; +#define FS_DEBUG 0 //Turn on/off debug +/* +#include <list> +#include <string> +#include <qscrollview.h> + +#include <qevent.h> +#include <qmenubar.h> +#include <qsocketnotifier.h> +#include <alsa/asoundlib.h> +#include <qlistview.h> +#include <qheader.h> +#include "muse/debug.h" +*/ + +#define FS_MAX_NR_OF_CHANNELS 16 +#define FS_UNSPECIFIED_FONT 126 +#define FS_UNSPECIFIED_ID 127 +#define FS_UNSPECIFIED_PRESET 129 +#define FS_CHANNEL_COL 0 +#define FS_ID_COL 0 +#define FS_SFNAME_COL 1 +#define FS_SF_ID_COL 1 +#define FS_DRUM_CHANNEL_COL 2 + +#define FS_SFDATALEN 1 +#define FS_VERSION_MAJOR 0 +#define FS_VERSION_MINOR 4 +#define FS_INIT_DATA_HEADER_SIZE 4 +#define FS_INIT_CHANNEL_SECTION 255 + +// Predefined init-values for fluidsynth +#define FS_PREDEF_VOLUME 0.063 +#define FS_PREDEF_REVERB_LEVEL 0.125 +#define FS_PREDEF_REVERB_ROOMSIZE 0.125 +#define FS_PREDEF_REVERB_DAMPING 0.3 +#define FS_PREDEF_REVERB_WIDTH 0.125 +#define FS_PREDEF_CHORUS_NUM 3 +#define FS_PREDEF_CHORUS_TYPE 1 +#define FS_PREDEF_CHORUS_SPEED 0.5 +#define FS_PREDEF_CHORUS_DEPTH 0.3 +#define FS_PREDEF_CHORUS_LEVEL 0.5 +typedef unsigned char byte; + + +/* + + +#define MUSE_FLUID_UNSPECIFIED_CHANNEL 127 + + +#define MUSE_FLUID_UNSPECIFIED_LASTDIR 127 +*/ + +//Various messages the gui and the client uses to communicate +enum { + FS_LASTDIR_CHANGE = 1, + FS_PUSH_FONT + }; + +enum { + //FS_GAIN_SET, + FS_SEND_SOUNDFONTDATA = 4, + FS_SEND_CHANNELINFO, //Used by synth to send info about all channels, on init + FS_SOUNDFONT_CHANNEL_SET, + FS_SOUNDFONT_POP, + FS_SEND_DRUMCHANNELINFO, //Used by synth to send drumchannel status about all channels, on init + FS_DRUMCHANNEL_SET //Used by gui to set drumchannel status for specific channel + }; + +enum + { + FS_DUMP_INFO = 240, + FS_ERROR, + FS_INIT_DATA + }; +/* +enum { + MUSE_FLUID_REVERB = 100, + MUSE_FLUID_REVERB_ROOMSIZE, + MUSE_FLUID_REVERB_DAMPING, + MUSE_FLUID_REVERB_WIDTH, + MUSE_FLUID_REVERB_LEVEL, + MUSE_FLUID_CHORUS, + MUSE_FLUID_CHORUS_NUMBER, + MUSE_FLUID_CHORUS_TYPE, + MUSE_FLUID_CHORUS_SPEED, + MUSE_FLUID_CHORUS_DEPTH, + MUSE_FLUID_CHORUS_LEVEL, + MUSE_FLUID_GAIN, + MUSE_FLUID_SOUNDFONT, + MUSE_FLUID_STRING, + MUSE_FLUID_STRING_END + }; + +enum { + MUSE_FLUID_CLIENT_SEND_PARAMETER = 33, + MUSE_FLUID_CLIENT_SEND_SOUNDFONTS, + MUSE_FLUID_PARAMETER_GET, + MUSE_FLUID_PARAMETER_REVERB, + MUSE_FLUID_PARAMETER_CHORUS, + + MUSE_FLUID_GAIN_GET, + MUSE_FLUID_SOUNDFONT_PUSH, + MUSE_FLUID_SOUNDFONT_POP, + + MUSE_FLUID_CLIENT_SEND_ERROR = 44, + MUSE_FLUID_SOUNDFONT_LOAD, + , + MUSE_FLUID_CLIENT_RESTORE_CHANNELDATA, + MUSE_FLUID_CLIENT_INIT_PARAMS, + MUSE_FLUID_CLIENT_LASTDIR_CHANGE, + + MUSE_FLUID_GUI_REQ_SOUNDFONTS = 60, + MUSE_FLUID_GUI_REQ_FXPARAMETER_SET, + MUSE_FLUID_GUI_REQ_FXPARAMETER_GET, + MUSE_FLUID_GUI_SEND_ERROR, + MUSE_FLUID_GUI_LASTDIR_CHANGE + }; +*/ + +struct FluidGuiSoundFont + { + QString filename; + QString name; + byte id; + }; + +//--------------------------------------------------------- +// FluidSynthGui +//--------------------------------------------------------- + +class FluidSynthGui : public FLUIDSynthGuiBase, public MessGui + { + Q_OBJECT + private: + virtual void processEvent(const MidiPlayEvent& ev); + void sendLastdir(QString); + void sendLoadFont(QString); + void sendChannelChange(byte font_id, byte channel); + void sendDrumChannelChange(byte onoff, byte channel); + void updateSoundfontListView(); + void updateChannelListView(); + + QString getSoundFontName(int id); + int getSoundFontId(QString q); + QString lastdir; + std::list<FluidGuiSoundFont> stack; + byte channels[FS_MAX_NR_OF_CHANNELS]; //Array of bytes, for mapping soundfonts to individual channels + byte drumchannels[FS_MAX_NR_OF_CHANNELS]; // Array of bytes for setting channels to drumchannels or not (equiv to midichan 10) + + int currentlySelectedFont; //Font currently selected in sfListView. -1 if none selected + +/* + unsigned _smallH; + unsigned _bigH; + QSocketNotifier * _notifier; + bool sendParameterChange (int, const char *, int); + void setParameter (int, const char *, double); + void requestAllParameters (); + void dbgMsg(const char*); + bool sendParameterRequest(int, const char *); + //void dealWithSysex (unsigned char const * data, int datalen); + + + + + + +*/ + private slots: + void loadClicked(); + void readMessage(int); + void changeGain(int); + void dumpInfo(); + void channelItemClicked(QListViewItem* item, const QPoint&, int col); + void toggleReverb(bool); + void changeReverbLevel (int); + void changeReverbRoomSize(int val); + void changeReverbWidth(int val); + void changeReverbDamping(int val); + void toggleChorus(bool); + void changeChorusNumber(int); + void changeChorusType(int); + void changeChorusSpeed(int); + void changeChorusDepth(int); + void changeChorusLevel(int); + + void popClicked(); + void sfItemClicked(QListViewItem* item, const QPoint&, int col); + /* + void readData(int); + + + + + */ + + public: +// virtual void sysexReceived (const unsigned char *, int); +// virtual void controllerReceived(int, int, int); + + FluidSynthGui(); + ~FluidSynthGui(); +}; + + +#endif /* __MUSE_FLUIDSYNTHGUI_H__ */ diff --git a/attic/muse/synti/fluidsynth/fluidsynthguibase.ui b/attic/muse/synti/fluidsynth/fluidsynthguibase.ui new file mode 100644 index 00000000..336ee9fa --- /dev/null +++ b/attic/muse/synti/fluidsynth/fluidsynthguibase.ui @@ -0,0 +1,632 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>FLUIDSynthGuiBase</class> +<widget class="QDialog"> + <property name="name"> + <cstring>FLUIDSynthGuiBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>617</width> + <height>514</height> + </rect> + </property> + <property name="caption"> + <string>FLUID Synth</string> + </property> + <property name="icon"> + <pixmap>image0</pixmap> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="spacing"> + <number>1</number> + </property> + <property name="resizeMode"> + <enum>Minimum</enum> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QFrame" row="2" column="0"> + <property name="name"> + <cstring>DiskButtons</cstring> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QPushButton"> + <property name="name"> + <cstring>Push</cstring> + </property> + <property name="text"> + <string>Load</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>Pop</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Delete</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>dumpInfoButton</cstring> + </property> + <property name="text"> + <string>Dump Info</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QListView" row="1" column="0"> + <column> + <property name="text"> + <string>ID</string> + </property> + <property name="clickable"> + <bool>false</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <column> + <property name="text"> + <string>Fontname</string> + </property> + <property name="clickable"> + <bool>false</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <property name="name"> + <cstring>sfListView</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QListView" row="1" column="1"> + <column> + <property name="text"> + <string>Chnl</string> + </property> + <property name="clickable"> + <bool>false</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <column> + <property name="text"> + <string>Soundfont</string> + </property> + <property name="clickable"> + <bool>false</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <column> + <property name="text"> + <string>Drum Chnl</string> + </property> + <property name="clickable"> + <bool>false</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <property name="name"> + <cstring>channelListView</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QFrame" row="3" column="0"> + <property name="name"> + <cstring>ReverbFrame</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QSlider" row="4" column="1"> + <property name="name"> + <cstring>ReverbLevel</cstring> + </property> + <property name="maxValue"> + <number>16383</number> + </property> + <property name="lineStep"> + <number>16</number> + </property> + <property name="pageStep"> + <number>1638</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="tickmarks"> + <enum>Both</enum> + </property> + <property name="tickInterval"> + <number>1638</number> + </property> + </widget> + <widget class="QSlider" row="3" column="1"> + <property name="name"> + <cstring>ReverbWidth</cstring> + </property> + <property name="maxValue"> + <number>16383</number> + </property> + <property name="lineStep"> + <number>16</number> + </property> + <property name="pageStep"> + <number>1638</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="tickmarks"> + <enum>Both</enum> + </property> + <property name="tickInterval"> + <number>1638</number> + </property> + </widget> + <widget class="QSlider" row="2" column="1"> + <property name="name"> + <cstring>ReverbDamping</cstring> + </property> + <property name="maxValue"> + <number>16383</number> + </property> + <property name="lineStep"> + <number>16</number> + </property> + <property name="pageStep"> + <number>1638</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="tickmarks"> + <enum>Both</enum> + </property> + <property name="tickInterval"> + <number>1638</number> + </property> + </widget> + <widget class="QSlider" row="1" column="1"> + <property name="name"> + <cstring>ReverbRoomSize</cstring> + </property> + <property name="maxValue"> + <number>16383</number> + </property> + <property name="lineStep"> + <number>16</number> + </property> + <property name="pageStep"> + <number>1638</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="tickmarks"> + <enum>Both</enum> + </property> + <property name="tickInterval"> + <number>1638</number> + </property> + </widget> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>ReverbLevelLabel</cstring> + </property> + <property name="text"> + <string>Level</string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>ReverbWidthLabel</cstring> + </property> + <property name="text"> + <string>Width</string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>ReverbDampingLabel</cstring> + </property> + <property name="text"> + <string>Damping</string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>ReverbRoomSizeLabel</cstring> + </property> + <property name="text"> + <string>Room Size</string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>Reverb</cstring> + </property> + <property name="text"> + <string>Reverb</string> + </property> + </widget> + </grid> + </widget> + <widget class="QLabel" row="0" column="1"> + <property name="name"> + <cstring>fontSetupLabel</cstring> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>CHANNEL SETUP</string> + </property> + <property name="scaledContents"> + <bool>false</bool> + </property> + <property name="alignment"> + <set>WordBreak|AlignCenter</set> + </property> + </widget> + <widget class="QFrame" row="3" column="1"> + <property name="name"> + <cstring>ChorusFrame</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QComboBox" row="1" column="4"> + <item> + <property name="text"> + <string>Sine</string> + </property> + <property name="pixmap"> + <pixmap>image1</pixmap> + </property> + </item> + <item> + <property name="text"> + <string>Triangle</string> + </property> + <property name="pixmap"> + <pixmap>image2</pixmap> + </property> + </item> + <property name="name"> + <cstring>ChorusType</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="3"> + <property name="name"> + <cstring>ChorusTypeLabel</cstring> + </property> + <property name="text"> + <string>Type</string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + <widget class="QSpinBox" row="1" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>ChorusNumber</cstring> + </property> + <property name="maxValue"> + <number>127</number> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>ChorusNumberLabel</cstring> + </property> + <property name="text"> + <string>Number</string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + <widget class="QSlider" row="2" column="2" rowspan="1" colspan="3"> + <property name="name"> + <cstring>ChorusSpeed</cstring> + </property> + <property name="maxValue"> + <number>16383</number> + </property> + <property name="lineStep"> + <number>16</number> + </property> + <property name="pageStep"> + <number>1638</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="tickmarks"> + <enum>Both</enum> + </property> + <property name="tickInterval"> + <number>1638</number> + </property> + </widget> + <widget class="QLabel" row="2" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>ChorusSpeedLabel</cstring> + </property> + <property name="text"> + <string>Speed</string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + <widget class="QSlider" row="3" column="2" rowspan="1" colspan="3"> + <property name="name"> + <cstring>ChorusDepth</cstring> + </property> + <property name="maxValue"> + <number>16383</number> + </property> + <property name="lineStep"> + <number>16</number> + </property> + <property name="pageStep"> + <number>1638</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="tickmarks"> + <enum>Both</enum> + </property> + <property name="tickInterval"> + <number>1638</number> + </property> + </widget> + <widget class="QLabel" row="3" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>ChorusDepthLabel</cstring> + </property> + <property name="text"> + <string>Depth</string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + <widget class="QSlider" row="4" column="2" rowspan="1" colspan="3"> + <property name="name"> + <cstring>ChorusLevel</cstring> + </property> + <property name="maxValue"> + <number>16383</number> + </property> + <property name="lineStep"> + <number>16</number> + </property> + <property name="pageStep"> + <number>1638</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="tickmarks"> + <enum>Both</enum> + </property> + <property name="tickInterval"> + <number>1638</number> + </property> + </widget> + <widget class="QLabel" row="4" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>ChorusLevelLabel</cstring> + </property> + <property name="text"> + <string>Level</string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="5"> + <property name="name"> + <cstring>Chorus</cstring> + </property> + <property name="text"> + <string>Chorus</string> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox" row="4" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>GainBox</cstring> + </property> + <property name="title"> + <string></string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>GainLabel</cstring> + </property> + <property name="text"> + <string>Gain</string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + <widget class="QSlider"> + <property name="name"> + <cstring>Gain</cstring> + </property> + <property name="minValue"> + <number>0</number> + </property> + <property name="maxValue"> + <number>127</number> + </property> + <property name="pageStep"> + <number>5</number> + </property> + <property name="value"> + <number>13</number> + </property> + <property name="tracking"> + <bool>true</bool> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="tickmarks"> + <enum>Both</enum> + </property> + <property name="tickInterval"> + <number>7</number> + </property> + </widget> + </hbox> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>loadedFontsLabel</cstring> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>LOADED SOUNDFONTS</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignCenter</set> + </property> + </widget> + <widget class="QLabel" row="2" column="1"> + <property name="name"> + <cstring>pixmapLabel</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>4</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="pixmap"> + <pixmap>image3</pixmap> + </property> + <property name="scaledContents"> + <bool>true</bool> + </property> + </widget> + </grid> + </widget> + </hbox> +</widget> +<images> + <image name="image0"> + <data format="PNG" length="1195">89504e470d0a1a0a0000000d4948445200000020000000200806000000737a7af400000472494441545885c557cb6e1c45143db7a77b7a5ec699b1c71e3fb0239010f80ff807d6d9b065c12f20250a2862c10690901c93c40111136c1ca448880dec67c18e05444158b63103f22376e2b167babaaafb66d1efd780c2c225cda3abcebdf7dc5375abaaa9dbede2229bf67f8c7f3416f8a7f2225f0881efed0e379a33a8373bf8c1997b61127afce1eb3d83c7da1d58fd535c693fa35186f5e60c94f4f8d7c63bc0596f64a08dfd0657c79be81fede3ed793bf49d5060627109f5d61c5af3afe1dba37661561b87930c3621052005c05cc683934e217e7dbfc5ad975f47bd3587c9c5a5c4584860e3609635ad062508aea3a331b150984d656c0aca262801289f84596f17e2c7da97c1ae01250844556c1e2f8464430266bd0d290065fb59b9657cf3cf426e56a55223c42adb23415a2d37f8fdde5ca856e0bb5c9dc82a0054c28c824fb996cd6a6def323bb294c13ab686fbbd573384cdfa5406cbae992470e7b769766c2d03845bc6178fe7134ecbd5c90c2ef81895163eeb9642fceaa319663673c812ee3ef62a470700add480140c04a60410111880d9e8e0f6af1a2b5ba0d2b884ea4b065c051001cc9e01b38707e9189f59c2e7bf3c612a95506f4e43d914f9856f0780b43a4202400d52a4c563dfc040a5311bf64a2b1a4be38908546aa076c973ce0e41298053780201a84604d82d7b9287aed807e505f3dc913f42218672b1488c47fdae6b44041ca5c3f165050249bddf22074414e2029b64e0081f27cbe1782946c0a64cd024816c0bc7f3120fc213122403169e9dd7496fbdb7c56393af8099235fcce122cccf8863dfc162a418249a42cec107937cf66407baeb185e6dc663b8482a3eaa152850d81f4b8a5d03ba23c9dfcf933629d5121aa495cf1da7c8679e1f6640490dbab4140c334636cd209f7c3edb603898f73cd631901c4a50b7dbc5079b6f7242af3438a70a463db3bf8646e19919ef5ff999740050820b252e4a20dd97988ea0a246f8092a4c07006939007975995f7ee9d0c5cf9efdbfe3999d88803db4a1e95580a333205a95f1dd4c8bf5e568c3c1024c2d8e9c8a701d152760c128a7cff3a862953880eb9cc3a8cc82b46a6c9b8ee302c702caee41d3aad0cd6950c1b55379874a406000a0950fb44fb072759b00e09d6b43361b6f80fce94a3466b82c21fa8fb0fae19000e0dd1b25d6cda95cbf5e4cff3e70ebfa1e498b33e7b6b41cac5cfd3d4cf7ce8d3ed9e7cff2ef0336c11e9c86c10160e5da16c9a1cafa15c0adeb7f52a8000048cb0669d14d05001cf934c37cd8efc1ac37915713f6e03083178363948ca40acc32fc1f4e903d3849317560f577330ed73e3923391c66b31a0adcfbf86966fbfaf2a32d525652057b1025161258fb749ba425fd4ba60b717e88f56591bb1f8ac1912f7b74291583e33ca88f3f8012ae7f295510677b59020020ce77600f4f21067f637d79bb7033deb8d92329546c4e1d6cdcdc2dc4af2fef9238ffcbf37db68bcddb51628937a3efee1e1170549849bcd98343687a0700c1758ab30fda83d51e01d9b7a7177e377c786f87e4f014d23ac5c3affef8af8777a6d145bf9e3f076c35aaedab709f8e0000000049454e44ae426082</data> + </image> + <image name="image1"> + <data format="XPM.GZ" length="269">789cd3d7528808f055d0d2e72a2e492cc94c5648ce482c52d04a29cdcdad8c8eb5ade652323452002220a1a4c3a5a4a790aca06c000620ae32886b649096666801964502e4f1959595c13c6565301fc481626c7c985a65b2ed43e2d75a73010022253132</data> + </image> + <image name="image2"> + <data format="XPM.GZ" length="269">789cd3d7528808f055d0d2e72a2e492cc94c5648ce482c52d04a29cdcdad8c8eb5ade652323452002220a1a4c3a5a4a790aca06c000620ae32886b649096666801964502e4f29551f9ca500165085f590f86c17ca81c88063b07aa57991cfb6badb90035703153</data> + </image> + <image name="image3"> + <data format="PNG" length="7252">89504e470d0a1a0a0000000d494844520000007c0000002d08060000006ccc0fec00001c1b49444154789ced9c779ca55779dfbfe7bcedf6693bb333b35d5af506261059103b9490c40ec514f1010ca60a442008448bb1892146b11cba648a4237b215d1314522d8a0c4a08201f5bada5d6d9d3e73fb7dcb394ffe786f9d99d5162448849fcfe7eedeb9e7bee7fc9ee777cef33ca75df5939ffc847f96df1c717fdd007e9572cd4c227e2e87761c4061ad256935b9705ca95f37b65f95fc4610fe95e5408a1bc6993ab584e3fb28a54001229824e1fbb5aad4171728cfcdf08a9dc5c734f98f69c2af3ea864646a33635b87f1820cda7152b2fb443942decd11e446c80e4f70edec21b9703c7ccc927ec2847ff6be86f8d9028ee7910e17b0d660a290b05ee3b567977ead46bb76be201bb66e22532ca21d1750580322abbfa950ca41bb59b2251fc7cbf1b5f919795e69f1d74efa1776c5e20401da75514a03828913a2469d579d9639217cea7893b62fedf3253fba81205fc4f503b476daee11440463624c1811b51ab4aa2bd4571679cd192706ee44e5cb8ba3529a98c6cf76e2f57a440f4a67e08bb5c451486d719685bdbb78edd9f95f19f6ab6eaf497e740399e21041ae90dad7f5d05aa50005ac589230246ad668ac2cf392e9fa71e13b66c23f738f95e1e9ad644b235df7c87ab94edbb2622d2689899a0d1a2b8b94670f70d1398f3ef1d72e8c4b697c0a2f936d8f8a75c8eea090bef77d4522421285d496e759dcf720179d1d3caab83ffeb3b20c4f6da130328e972be07a3e5aebd4bedd9e2803efc55a9238a4592d539d3bc4cb7744c784f19808fffcae408627b7121487701cb71b07bb76ecbce96bb2f3d68ac5c43161bd4265fe307fb4e3f87ae4b1ca95b754647cdba914366cc40bb2eb635c4d74ffff7da0bba4c7318d950596f63fc845e7ba8f0aeecfddef4b69629a4c6918d70b505a0f405a83b10db0f3913586a859a33a7788976e59392ac6a312feb9fb72323cbd153f57eabac71311b196a8d5a0b134c7e2c10779d3bf2c3c6206bce2e69a8c6d3985c2d8465c3fb326313b5111041b2734ca8b2c1fdccdebce7b64a76f5fdc332cc5f169fc6c01e568d46a7773ac38c512b71a54e60ef1b26d0f9f7b3c2ce157dde1cbc8f44904853eb28f120b8fdc523bc6c711cdf222cb0776f386273abfb401afb8a925a39b77921f19c7f18394ec13c5b85afaa66eadca12cb87f7f2fac7d95f1ef38d0d19d97432f99109dc2083d269e83921dcdddc43885b75ca33fb78e5a9e523623c22e11fbb31920d5bcf203b34f6cb93dd074e009bc484d515ca33fb78fd6f1d5bec594f3efe532d4353dbc81447713cef9125bb236d743649086b652a73fbb9e8bce60963bef2662bc35327911d1ac5f1fc47067307a3b544f50acb071fe4f58f5fdfae4724fc33f74e4871c3e674d4c0609c5b2f06f657bf5e92b42a4e4a3bf6d4976628cf3ec4a54f3fbe058f4fdd5692e2d8347eae8876ddf5db5dafedf56279bfaca74b3fe6b0417d7186f2ec5e2e7ddaf185a54fdd5a94c28669825c09e5ba6b1df8c3e1399acddb1f4912d3589967e1a17b78eb53736b9a5897f02b6f0e64786a277eb688d27dcf1ccda0ab9a17e8a6c8695c550320452c268a08ebcb345616682ccff1b667ae05d92f57dc14487e6423d9c2084e90e966e25d7c5d3c82f4a5e7ddf6d7e05e950dada75fbf5622d838a455afd05899a3be7488b73ff3e13beb1537fa921f9920531cc5f5b33d17be2eee556df7656f47d5a78d2f099b54e6f7f1fac72d1f1be19fbe7b9be48626504ebb178a60ad41ac49696ccf10faa73b2a45d155c6c42149d4c49a04a5348e17e006391cd7efe9d8ae43acc1c41149ab4ed8ac10376b189320d62236c1442d945678d922417e984c6138ed8c8ed7af6b57ac8949c20649d442c4a2b5831be4dac67652e3598b3531d618446c5a8fd668eda0b48352ce1a627a8e42b056b07144dcaa12d62b44ad1ad624088242a19db4cd203744ba4015a0da6bf8eb6146a46b5b543fa1824d12e2a88169eba3b48337a0cf2a8cd6d2aa2db3b8ff6edef6f4c1d9c59a95b68ffe9fbc0c4d0e61add3f6112979adda12617591240e01dbabbd5b9dc2717dfcdc30ae9fa1b13243757e2f51b38ae3fae447a7199a3c954c712c5dace907a81c9493c5cb06b8c110b6942e89890851b342656e0fb5857d98782f6e90a5b0611b43933bc9e486413b03c6b3d610d6565899d9457de900260ef132054ae33b284e6cc70b72247148585f26ac2d11b76a98380495e277fd2c5eb644901fc6cf96d0ae3f10013abaa2143819bcac8f130c93ed5bc64bcda2504ea7f368ac0071f7e9dea016c1241149d448712068c7c70b72385e808810d65628cfeca2b6b81f9384784181d2c693294decc0cb14d64e41d168374fa6b011581ce0770de15e760c510126692713626955ab540fdfc3c9a30fe0fb4d10db06dc171c95423901771ddc82ce4d636a7b79fb85b36c9b8638862bafad31b73286eb0fa15d67c0883dcfa5db2f0f940086248c715afbb9eca2794687157152e77d5735689637e0baa5d4a87df84d92d058596254ddcfe56fa9e33ac262b9c27b3f1be3664688fd88dae27eea73f772c69645769e6a2966856628946b707016f63d54a09a3b8591e933090a831d74d0b0fd98dd81793c805845db791c317d30494ca33c4b65e6011a9539b096a030cad0e429e44736815234569628997bf8c0db5b08c2c25299f77d41087213289debe2ebe25220d6c70b4678ffdfed97773fab17260708bffc3a4f86274b8871b036adc15a216a3491e60caf7eae61f3547e751fe94a1c0baf7bef616a951c342b0c178489319f30b2903488a216712b41bbba0b4f298d529de54f9bbad7f6aa523abf8c499a7546861413631e512424610dd38a4822413b923e6753d76ce290b05e27676a8c8fbab8ae423044f50aad4a99249ea510dfca656f8dd93419acab47a365f8932bf7b2bc3285760a28edb6d30f95bad04ede20360d3b6d8f97cea3536a45a413e77a1ddaf64247c715c7ad16d5b9835cb0f37e5ef4ef52977ae3ad2b7ce9872e8e5bc2f102c27a8320aa313e9601c0241151bd42d868e078611753baccddc1a0d04e9e4c6e2350edea3640b8178c82ca6012d5cdccad85244e932b39da823420cac31a8518191809d6589230a25959c6442d4c12a2b4c6cb14f1b3c328a5895a65e266056b13b4e3e30679e230a6d110bef5bd59724184e053a94e901dd298d812870de26699b0b182895b884d685666c967ebc050bb238189135ab50a61793f7f7649c4a6c934fe4791e5a1032d40b171dca358704120095bb44c0591c35893eaee781982dc305ea684528a246c10369649a27a5aee0638ae8f3131364e47a3d62e4a3b589360e21628851714f073c3388e4fb3b640757e3fcef60a1363230064bd2a95d9dd68278f171468d596c8a910c8b4f5115ab532d5f903b4aa65001c3f4b901bc1cb0ee1b8417bc004b8c1c8fa84ff976b9a32baa984b50e12d3f53962041b0bd6d0f51922f0b1abf6f0bd1b62fcec50374eb941119bd986972d92583de0fbacb1848d3a716b17a3fe1eb4ada21d97e595cdc44367a1b44bb8722fc3de5eb021a2f32c2f6d4779251a0dcbf77fb880c465b49fa7ca387e6c69d56a34cb87682cdecfcea979a6a7131696626eb96799c6581da144c7899ac410d66b8ce69638657b4af6e252c4abdeb28ba5e6047e7608c4303ddee2b71fef530b4789a23a3a3cc0903f8b3531b114595a3a8da18da7a11d8fdae24338cd3b29788b80d04c4acc568b0c656b943265c41a2a7587b945d8b609ce3d196a0dcb7d0fe528eb1d640a638495036cf077335288bab62a64123666f71044153005660e4169acd9edc0885099db83af9b9c775e1e470bbb0ffacc1dde4c69e3196487a671bc0ca051bac0fbbe6ae53dcf4fa75b5dc2dd600454169bf422a202ac01938035423f83f586b052cb104811ed7828ede0eb118aa531d019c40e66b8d60871ab894a56b8f4ad754e3b39edadeffed001ee5b9844bb3e63de7e2ebf542815331c9c0979e787f6530bb752ca593ef6eeb3d838ee1146c26bdf1dd1acd7891a0ddce66dbcff0d4dce3ad5031c20e0e06197abafadf5b99d540fe2844ccef4ab4d2b04ad3378c1286e90672571b9fee71e7e7604ed0839672f1f7807e4b201cbe516af78d75dd4bd619c20c7cae1fb79f7abe679f2bf4843c3e59f9ce7bb372cf1dc671b5ef3e22200d7fffd1cd5bae5f9cf9aecee7d349a09977dfc0e6eb967845266850ffcd721c6467a79c2054f1ae18227a5a37d662ee1b9af3e4852ead95ec4f27bbfa3b8e4e22172b9f4396384af5cb787bffd8105952153dc988622021c6f04483d415773c71902f131f1e08441ba84f74d4b143ced29c36c9a8ed04e0ba52294d21c5e34fce3ddc33899098ce91bded271ab16a2b03b0d0230514812c568a3304e88d0898f4212862436c1edc4f54ee789635ae10a617d8ecbdedce0ac53d3a95e9208fb0eb6182eb9bced4da70faca98b2820c3c1b90c6164097cc5d8a8cf373e773a37fdbcce5d0f1ce4f6fb14bb0f16f00b9bf07301daf1393c5fe4c73f2df36f7e27cb5051f3dbe794f9c77bf6e1664ae4d421ce3ddd6b1313f3d3db144a0588edb9d067fceb71b4562cafc4948a694e91cb6afee3cb3c6ebe643f91d5345b2e71acf03cdd252f8a2c82a2150ae02136eed639b931cb1f5f7a06b57a42a361c8e51c1c47f1dc67f8fcf4f643ec59de82e38de2780e625d1ca7b09670a57318a3519226c89d2cd2da1ee1fd2efafc278e72fe1307fa06b7dc1a72c3cf2b881a41561d36100b36018c0c92d7f95cc076e6a1eda6ac4d43899581a6d378dc5862e7f42ce79d9103d284f15def9fe5a6dbb264fd0a7ffe8e3ce73fa1d07bc83a386e8928dacc67aeb99f37bc3c0b4036ab79ea938b3cf5c9e9d7169612aefefa2ebe7f4b037fe854943bce37fed7019efe14d01a9efd0c8feffe68176e50e499e737c9e75237fb939fc5ac3426707d0768749b8d13e1f22b66f9ce0f2ddba742fee693db715dc5d484c7f438ec9919e105172df2a2df8f78cbeb370370fd3f2cf29e0f54c90d6fc1cb0c931b2ea2ddfddd3a1d47f1f5ef2ef3c1ab1a783ae2caffb691b34ecb11048ad3b7b7b8f7276592a118c820a2416579e7a7eb72f96bf269baf98eff118948808d15261292184cdfcb26d0372801a8d513169622169762169662169712aaf5b472b14eda41fa446cda69ecaa7a8c114c92be06ca24cd1f4c2288194c166d62089b55ce3cc9e279e928bef3de2677ee1e6778fa7c54fe5cbe7e5d3cf88c4de359a674325fbf610797fc59931b6e6cd06c0d02da30eaf29f5e95e339bf3b4b636506ed16d97d688c5fdcd102e0f49d19b66d384879e66e9ef6db1aad53cf72dd8f345e760ae5e491be89e203bb237e70f328c3534f64b632cdfe4361b76c722243909f26377c32aed7eb9cdacd50183b9dd12dff8a914d17901d3a09ed64076cf6e9ff09d9d1276083d3b8e39e9eae13638a386c9144a6cd9d0209503a1d182ea42326890d6956dfb7d9aa52a24ccc008122f0914f1ce49b3fc8121426d318ae5cbccc30d9e16da01cac1ddc14b0168c016554dbbd760a246ddbc4e026dd291ab43b4802abd2018c154c1451c8f51a285784448a04fe184e028b2b1e564077366c8c601270fd8020bf95bb0fe4b8f5ca4554b2c4f6e9324f382be605cf2a32b9314843d6050edff8fb25c46ec1ea4d7ce3fa799e705e3aca9ff36f7d0eeddfcbb9676e07e0b6bb43f6cf8de196c649a218faf4db7fc8e0f81bf1b35324cd455aad956e99a315a8f66e99ea9f3069b453c4f136e0b839945a01e919617129a1190f932d4e610db4c2a5de935a30b12189413b2957496c3071027829e11f7c434efde9d54d711cbb7639b1edd2578f70c72f51d87026850d67b4416994e3a1b547d45a4456ef225ab046a3ad3330923d27a25599076078a24536d3ebc95daf30982f22a210f1a8f69da528e61524355ad525e2d632439331dd6d00019b58c27a99b07a98a8b988e0e37aa3e08fb17ba6cc9df73ec8fe0387b8ec4fb7e3388a5c06f281a1253e5e768a9befd8cb43fbeb6cdbe2f37bcf9c60e6708520486df5fd1b0cb19ac2774a889d1b0865710ce023e221d6c15819d0c31a8d883340a8584bd8acd2aa2de2fa3171d8c2eade737122201e223e222ea6df9bdade4031719ae045cd26fffd75a92becb612356a246142120dbaf38e5b905523532917ed0ee3fa13b8c1465c7f02ed8e02396ca2d3b9f8c00857202ec6665858ea153cfe4ca13a771ba67a3b17fe07bfeba23bcf58a3d6761e71d04e91bbefd7a9f2c0d96766d83935c3d2fe9b88566ee759cf1c5c44b489216a9429b90ff2f9bf98e3594fb99f02bfa0327b27f5e5bd28b3cc19a76670da5bf48da6a2dac800591c778c84cd7ce37ba9ebcc655d2e7eed6928958eb61fffac80174c2264db78fbb182350a93a4bab02aaf11a311f1a8d47a846f99d6d07c8085bd3f66e9c03fd15839848907439458854d54db65f725a7a403d4c469684e2243d4ece5145dab84f565dccc349e17f4ce4eb52b3609a8554617ab11a3b18933e08645da40cc2008442312801ee5f6bb67f9dd0bd2b2173d6f034f3ebf45a9e876173d7adb91e94953cb809d403c1c6f84db1f88b9ebde151e77b64fe06b3e7ad924f73ed060c368894dd39941c2adc2c60613b7d8b625e0d28b87b8f462a8d50cd59a6164781b994ccfe8fffb264b2b1ec757795019dccc26befbc30779d58b0dc5a2d3ed183ffc7144b5b999627114315e5bef7edd535bd884b5798d805817ed061c9c0968b5844c4671f619397ef4cd2d542a09dff9c1437cec330e66381c78b6d789069363042401132b944e77f6a27a2f8c7435fcc8254ac5cd1a496407477892ba6218ec084a7988789844adf508c601fc6ef50a052a40a902ae3fcdd7ae1fe3b6bb7a3d76f3748638163ef2577b58a9743e4f9f11eba35406d5838ad2591c6f0341fe34defbc12c0fec4e00c864348f3ba74026e3f0d18fef218e6db72ea5b22855a4158d72d7bd71d7488582c3d4a4df253b4e84af7e3be26fff6e123fbb0d911c625cb433422d9ce05bd7d50708fbd6f50ac79b060a58e322e2035e0f2b0e48801817a532a46b059dc2005416c71de7ce5dd37ce5db09619402f35cc5d8a8473e9b761445ff3270ca8758172400e5f595b9083e2681244a8f9585b5e55e93fddba3977c785832c51d2827689394c680245c26aadec5d6f107f0741d94cbecf2040d1e4f90df82d67eff8e01495426acdcc3960df7e3bb55048f03f39b31de39385e91a87188b072374f3a77816d9b2d8bcbf0e35b842882534e02d711129b63dfdc0ec49b42e2c36cdfb81b57b740f9ec3eb40d277736dacd10d60e20e1fd3cf19c45b64c19e6162d37fed412468ad34fd1a42be905761f3a0937b71513576955f790f36639fde490cd5342b1a0301666e714b7df9763b9be996c69277e6e12edf898a441d498a5be7c079b477ec157bfb815805bef68f1a6f78c110c3f85203f8dd88456ed214ade1d8c0fcd212294eba3ccd7cec4cb4e10d5f7b175fc5e02b78a522e07e6a7899d73703363c4ad459ae55d649c036c1c6b90cf5a509a956a89430b1b08bc063bb72e203622b17976cf9c4a503889245c6638b88bb1a10510a1dc1865be7636d9e2c928c725acede3c36f5e50eb127ef19fc7921d3a0bd71f193858606d8889ca24d10ad64628348e97c70dc670dc1c7d8ea2cd794c129749c265ac0d51cac1f14a78c1084afb581b92842b9868196b429476d14e065088c488085afb38fe108e93c5240d4c5cc6da18a55c5cbf9462d41ed684986825c5669aa09cb42ea5c126edf56c1fd71fc6f18a88586c52c7c4354c52c79a30f5814aa39d00c72be27843385e01ad5c4c52a7597d90e6cadde4bc3dbcf1d59a0b9fb701803f797f837ff8a7df223f7a2eae57020413d789a3656c5c0304ede670fd51b41360927a1b67ab7d46a0d82ecb209260e31a495cc59a162249ea1f9c0cdacd2136c62475444c5b9f111caf80b51149b48c8d1bedf6b2699d5e1e9bd46994efe5137f2ceb130ef0c6cb27c4cf6d46eba02f14497b17ab2f9a7677b9d63becd1f7fdceaed1c0f73be59dc3072a75d98a813604d5bd71d10b542add0357fa08753150d6794629dddbe5eac327fd3aa1fb0e3e681043d49a87d6cd7cfcb2839c795a2f11bcfdae888bde368c5fba003fbf09adbc74d74c2c22d29bd976f46eeffeb1ba3ddd674369efbc49ffb424c59e6e5cd94ea5e91982ce3e7867e5aa631fe5802444cdc35cf1f6430f7f0022accfa174e750a0ee5582c340fc795839daf757970b562c6223ac6922128124a9224aa170514e80d6d934dcd80ea1ebb775f43d3dc56aaf34f0ace9fd91ae0ea6c9519208e58ae1c65b123ef4c93c78a70379e26635f54c366ad7e0b637943cb49345e9a0ddd9d7b1c94022d7d95b3f5ef106fe4a0f55b408ebf36bbeb9ee11a78bde3b2e7e760b4afb9ce859e9a349efa896c1da26362993448b24e1029ffb8bec9a465ff1aeaab8de28ae3f81e38fa075b6bb58f1e8204c519aa44a58df4358db8549aa28ede378237899711c278fb54d4cbcb23ee677d6c40dc670bd311c6f18ede441ad7378f111450c6223e2d6213ef59ec3c776a6ede56f0f25533813c71b4b5d1cb41338569dcf59759243f515f59f77eb3bffd67b2f88c4987899383ccce72e3bf6a3bfaff8cf8e78c1261c6f14add3d8df7f45e0486dd387b3fbbd3e053a7b085ddd14883558dbc026f534ae8a416c8b245ee2f3971f3b77af7ca71637b309d71f43e980347cb471b07edbeb613c9acd05838d57086bf7f1f9bf5c7b71e288c794ffe81d39f13327a1dd7c1a7fe98563691ba7237dd1a3d731fa1be9fed347b66d9284b384adbd5cfde1e3bf73f69237af48903b19cf9f423939c01934525fdbab5d7c3f4eb5aa40d61854ba8614d3c4c4f3448d3d7ce9230f7fbaf648f2ca778d8aeb4fa576edc3dcc13dc06d3fc147b379bb026b9bc4cd87f8fce5eb5f3b7ad89b272f7fdbb878c1344a67dbb174b0a17e43f6ad62ae6da44f2130585327090ff2c50f1efd2ed4d1e4656fcd8a176c45bb25c01d1835ab71f6635c356806bebba64c6c8a393ac8173fb0f6e8eff163ce89176c413925941ac4bc1eae8e1cc9e690c66d9198249ae10b7f79f088188f7ab7ec656f9914c79b6a271e6beb7938d207ff9674ea91ac904407f89b8fb51eb150f69237f9e206db71dca18118b99ee1068cc49189efe2168bb53592e800577fe4c857788e575efc46c40bb6e3b8a3a0fcee805a0ff7ea46d7e06c936d9305a2c6fd5cf38923df763d2ae12f78cd8af899d3d0de647baeacd718add3f86a0376cbc52212629345a2700f5ffed4237f13f3c28b1df1fc1d6877241d35abf0f5635cfd7eb5f446b6c19a1a49bc9f6bfeaafa88637ee16b1be26576e0781328956d4f1bd5bad83bb8faa53b906c844de6895bbbb9f62aef61711ef3fdf00b5f372cae3785d29d4cb3afde553da003046c3a5d31554c32c7b59faa3c9a092a2f788d1237d8817646dba4abf5738a6eccebbdef026be3173188ad9244fbf8f2558fce15e78ebcf0a28c38de245a0f7513ba7ee28f6cdf348134c91c49b89baf7ef6e83f5e705cbf00f1bc57228e3b85764648d7c6dd145cb74bb6171e302011d6d611b38c4966f9fa17d64e5b1e0df983578ab8de0eb43386521eeb05f5fe9c42b587b3f47d98925d2689f7f1b5cf3e72a1e768f2fc57e745eb11945340a9205d8757edb979ef272a10312021d656b0c90c5ffd6c7ccc188ffb273f009ef38789286718ad5360698fb48844883411db426c9d6f7ee997bf0e7c22f2ec9786e2fa3bd0ce787bc342b32640ae7e0f6db223ac59c224fbf8e65ffff257834f449ef387a1285d48377c74b6cfc6a436b635ac2df3cdbf3efefbea2744f8ff2ff2ec978e887626513a9f7a23516b032190269426edac661e93ece1dbd79cd8b4ebff75794c130ef0fb2f44943385d223e9366b270c011da2a185d832d6ccf19d6b1fbb3fd905bf018477e4dfff8111a537802af4ae3661db216889ebbe661ed34477e43786f07f9654fe2fc2ee9042ad017b950000000049454e44ae426082</data> + </image> +</images> +<layoutdefaults spacing="3" margin="8"/> +</UI> diff --git a/attic/muse/synti/fluidsynth/fluidsynti.cpp b/attic/muse/synti/fluidsynth/fluidsynti.cpp new file mode 100644 index 00000000..e9165b6b --- /dev/null +++ b/attic/muse/synti/fluidsynth/fluidsynti.cpp @@ -0,0 +1,1314 @@ +/* + * MusE FLUID Synth softsynth plugin + * + * Copyright (C) 2004 Mathias Lundgren (lunar_shuttle@users.sourcforge.net) + * + * $Id: fluidsynti.cpp,v 1.19.2.18 2009/12/06 10:05:00 terminator356 Exp $ + * + */ + +#include "fluidsynti.h" +#include "muse/midi.h" + +#include <list> +#include <iostream> +#include <qfileinfo.h> + + +FluidCtrl FluidSynth::fluidCtrl[] = { + //{ "Expression", CTRL_EXPRESSION, 0, 127 }, + //{ "Sustain", CTRL_SUSTAIN, 0, 127 }, + //{ "Portamento", CTRL_PORTAMENTO, 0, 127 }, + //{ "Soft Pedal", CTRL_SOFT_PEDAL, 0, 127 }, + //{ "Variation", CTRL_VARIATION_SEND, 0, 127 }, + //{ "Channel reverb send", CTRL_REVERB_SEND, 0, 127 }, + //{ "Channel chorus send", CTRL_CHORUS_SEND, 0, 127 }, + //{ "Pitch", CTRL_PITCH, -8192, 8191 } + + // These controllers' initial values are set by the FS_PREDEF_ values, so just set them to zero here. + { "Gain", FS_GAIN ,0, 127, 0}, + { "Master reverb on/off", FS_REVERB_ON , 0, 1, 0}, + { "Master reverb level", FS_REVERB_LEVEL, 0, 16384, 0}, + { "Master reverb size", FS_REVERB_ROOMSIZE, 0, 16384, 0}, // Interval: [0,1] + { "Master reverb damping", FS_REVERB_DAMPING, 0, 16384, 0}, // Interval: [0,1] + { "Master reverb width", FS_REVERB_WIDTH, 0, 16384, 0}, // Interval: [0,100] + { "Master chorus on/off", FS_CHORUS_ON, 0, 1, 0}, + { "Master chorus num delay lines", FS_CHORUS_NUM, 0, 10, 0}, //Default: 3 + { "Master chorus type", FS_CHORUS_TYPE, 0, 1, 0}, + { "Master chorus speed", FS_CHORUS_SPEED, 0, 16384, 0}, // (0.291,5) Hz + { "Master chorus depth", FS_CHORUS_DEPTH, 0, 16384, 0}, // [0,40] + { "Master chorus level", FS_CHORUS_LEVEL, 0, 16384, 0}, // [0,1] + + { "Program", CTRL_PROGRAM, 0, 0xffffff, 0}, + { "Modulation", CTRL_MODULATION, 0, 127, 0}, + { "Portamento time", CTRL_PORTAMENTO_TIME, 0, 127, 0}, + { "Volume", CTRL_VOLUME, 0, 127, 100}, + { "Pan", CTRL_PANPOT, -64, 63, 0}, + { "Expression", CTRL_EXPRESSION, 0, 127, 127}, + { "Sustain", CTRL_SUSTAIN, 0, 127, 0}, + { "Portamento", CTRL_PORTAMENTO, 0, 127, 0}, + { "Soft Pedal", CTRL_SOFT_PEDAL, 0, 127, 0}, + { "Variation", CTRL_VARIATION_SEND, 0, 127, 0}, + { "Channel reverb send", CTRL_REVERB_SEND, 0, 127, 40}, + { "Channel chorus send", CTRL_CHORUS_SEND, 0, 127, 0}, + { "Pitch", CTRL_PITCH, -8192, 8191, 0}, + // Added by T356 + { "Pitch bend sensitivity", FS_PITCHWHEELSENS, 0, 24, 2} + }; + +static int NUM_CONTROLLER = sizeof(FluidSynth::fluidCtrl)/sizeof(*(FluidSynth::fluidCtrl)); + +QString *projPathPtr; +// +// Fluidsynth +// +FluidSynth::FluidSynth(int sr, pthread_mutex_t *_Globalsfloader_mutex) : Mess(2) + { + setSampleRate(sr); + fluid_settings_t* s = new_fluid_settings(); + fluid_settings_setnum(s, (char*) "synth.sample-rate", float(sampleRate())); + fluidsynth = new_fluid_synth(s); + if (!fluidsynth) { + printf("Error while creating fluidsynth!\n"); + return; + } + + //Set up channels: + for (int i=0; i<FS_MAX_NR_OF_CHANNELS; i++) { + //channels[i].font = 0; + channels[i].font_extid = FS_UNSPECIFIED_ID; + channels[i].font_intid = FS_UNSPECIFIED_ID; + channels[i].preset = FS_UNSPECIFIED_PRESET; + channels[i].drumchannel= false; + } + //pthread_mutex_init(&_sfloader_mutex,NULL); + _sfloader_mutex = _Globalsfloader_mutex; + +/* + buffer = 0; + bufferlen = 0; + */ + } + +FluidSynth::~FluidSynth() + { + int err = delete_fluid_synth (fluidsynth); + delete gui; + +/* if (buffer) + delete [] buffer;*/ + if (err == -1) { + std::cerr << DEBUG_ARGS << "error while destroying synth: " << fluid_synth_error(fluidsynth) << std::endl; + return; + } + //Destroy the mutex +/* if (pthread_mutex_destroy(&_sfloader_mutex) != 0) + std::cerr << DEBUG_ARGS << "Strange, mutex busy! Should not be!" << std::endl;*/ + + } + +bool FluidSynth::init(const char* name) + { + debug("FluidSynth::init\n"); + + gui = new FluidSynthGui(); + gui->show(); + gui->setCaption(name); + + lastdir= ""; + currentlyLoadedFonts = 0; + nrOfSoundfonts = 0; + sendChannelData(); + cho_on = false; + cho_num = FS_PREDEF_CHORUS_NUM; + cho_type = FS_PREDEF_CHORUS_TYPE; + cho_level = FS_PREDEF_CHORUS_LEVEL; + cho_speed = FS_PREDEF_CHORUS_SPEED; + cho_depth = FS_PREDEF_CHORUS_DEPTH; + setController(0, FS_GAIN, (int)(fluidCtrl[0].max*FS_PREDEF_VOLUME)); + setController(0, FS_REVERB_ON, 0); + setController(0, FS_REVERB_LEVEL, (int)(fluidCtrl[2].max*FS_PREDEF_REVERB_LEVEL)); + setController(0, FS_REVERB_ROOMSIZE, (int)(fluidCtrl[3].max*FS_PREDEF_REVERB_ROOMSIZE)); + setController(0, FS_REVERB_DAMPING, (int)(fluidCtrl[4].max*FS_PREDEF_REVERB_DAMPING)); + setController(0, FS_REVERB_WIDTH, (int)(fluidCtrl[5].max*FS_PREDEF_REVERB_WIDTH)); + setController(0, FS_CHORUS_ON, 0); + setController(0, FS_CHORUS_NUM, FS_PREDEF_CHORUS_NUM); + //setController(0, FS_CHORUS_TYPE, FS_PREDEF_CHORUS_TYPE); //? + setController(0, FS_CHORUS_SPEED, (int)(fluidCtrl[9].max*FS_PREDEF_CHORUS_SPEED)); + setController(0, FS_CHORUS_DEPTH, (int)(fluidCtrl[10].max*FS_PREDEF_CHORUS_DEPTH)); + setController(0, FS_CHORUS_LEVEL, (int)(fluidCtrl[11].max*FS_PREDEF_CHORUS_LEVEL)); + return false; + } + +//--------------------------------------------------------- +// processMessages +// Called from host always, even if output path is unconnected. +//--------------------------------------------------------- + +void FluidSynth::processMessages() +{ + //Process messages from the gui + while (gui->fifoSize()) + { + MidiPlayEvent ev = gui->readEvent(); + if (ev.type() == ME_SYSEX) + { + sysex(ev.len(), ev.data()); + sendEvent(ev); + } + else if (ev.type() == ME_CONTROLLER) + { + setController(ev.channel(), ev.dataA(), ev.dataB(), true); + sendEvent(ev); + } + else + { + if (FS_DEBUG) + printf("FluidSynth::process(): unknown event, type: %d\n", ev.type()); + } + } + +} + +//--------------------------------------------------------- +// process +// Called from host, ONLY if output path is connected. +//--------------------------------------------------------- + +void FluidSynth::process(float** ports, int offset, int len) + { + /* + //Process messages from the gui + while (gui->fifoSize()) { + MidiPlayEvent ev = gui->readEvent(); + if (ev.type() == ME_SYSEX) { + sysex(ev.len(), ev.data()); + sendEvent(ev); + } + else if (ev.type() == ME_CONTROLLER) { + setController(ev.channel(), ev.dataA(), ev.dataB(), true); + sendEvent(ev); + } + else { + if (FS_DEBUG) + printf("FluidSynth::process(): unknown event, type: %d\n", ev.type()); + } + } + */ + + if (fluid_synth_write_float(fluidsynth, len, ports[0], offset, 1, ports[1], offset, 1)) { + M_ERROR("Error writing from synth!"); + return; + } + } + +//--------------------------------------------------------- +// getInitData +// Prepare data that will restore the synth's state on load +//--------------------------------------------------------- +void FluidSynth::getInitData(int* n, const unsigned char** data) const + { + + //printf("projPathPtr "); + //std::cout << *projPathPtr << std::endl; + + // Data setup: + // FS_INIT_DATA (1 byte) + // FluidSynth version (2 bytes, x.y) + // n = Number of soundfonts (1 byte) + // Lastdir (variable size) + // + // FS_FONTS_BEGIN + // n blocks with font path (variable size) + // n bytes with font external id + // + // for all channels (16), 1 byte each for external id + 1 byte for preset + 1 byte for bankno + // which is mapped to internal id after all fonts are loaded. + // + // reverb + chorus on/off (2 bytes) + if (FS_DEBUG) + printf("FluidSynth::getInitData()\n"); + + //Calculate length: + int len = FS_INIT_DATA_HEADER_SIZE + strlen(lastdir.c_str()) + 1; //header size + for (std::list<FluidSoundFont>::const_iterator it = stack.begin(); it!=stack.end(); it++) { + + // if the soundfont is located under the projectPath we extract this from the filename + int fileLen = strlen(it->filename.c_str()); + if (QString(it->filename).startsWith(*projPathPtr)) { + printf("project path found in filename, len %d shortened with %d\n",fileLen, projPathPtr->length()+1); + fileLen = fileLen - projPathPtr->length()-1; + } + len+=fileLen + 2; + } + //Add length for lastdir and channels: + len+=strlen(lastdir.c_str())+1; + len+=(FS_MAX_NR_OF_CHANNELS*4); // 4 bytes: ext+int id + bankno + drumchannel status + // + reverb + len+=2; + + if (FS_DEBUG) + printf("Total length of init sysex: %d\n", len); + byte* d = new byte[len]; + + // Header: + d[0] = FS_INIT_DATA; + d[1] = FS_VERSION_MAJOR; + d[2] = FS_VERSION_MINOR; + d[3] = stack.size(); + + //Lastdir: + byte* chptr = d + FS_INIT_DATA_HEADER_SIZE; + memcpy(chptr, lastdir.c_str(), strlen(lastdir.c_str())+1); + + //For each font... + chptr+=strlen(lastdir.c_str())+1; + for (std::list<FluidSoundFont>::const_iterator it =stack.begin(); it!=stack.end(); it++) { + + // if the soundfont is located under the projectPath we extract this from the filename + int offset=0; + if (QString(it->filename).startsWith(*projPathPtr)) { + offset= projPathPtr->length()+1; + } + + memcpy(chptr, it->filename.c_str()+offset, strlen(it->filename.c_str())-offset+1); + //printf("path name stored=%s\n", it->filename.c_str()+offset); + chptr = chptr + 1 + strlen(it->filename.c_str())-offset; + } + + //For each font again... + *chptr = FS_INIT_CHANNEL_SECTION; + chptr++; + for (std::list<FluidSoundFont>::const_iterator it =stack.begin(); it!=stack.end(); it++) { + *chptr = it->extid; + chptr++; + } + + //External id:s & preset for all channels: + for(int i=0; i<FS_MAX_NR_OF_CHANNELS; i++) { + *chptr = channels[i].font_extid; chptr++; + *chptr = channels[i].preset; chptr++; + *chptr = channels[i].banknum; chptr++; + *chptr = channels[i].drumchannel; chptr++; + } + + //Reverb: + *chptr = rev_on; chptr++; + *chptr = cho_on; chptr++; + if (FS_DEBUG) { + for (int i=0; i<len; i++) + printf("%c ", d[i]); + printf("\n"); + for (int i=0; i<len; i++) + printf("%x ", d[i]); + printf("\n"); + } + // Give values to host: + *data = d; + *n = len; + } + +//----------------------------------- +// parseInitData +//----------------------------------- +void FluidSynth::parseInitData(int n, const byte* d) + { + printf("projPathPtr "); + std::cout << *projPathPtr << std::endl; + + bool load_drumchannels = true; // Introduced in initdata ver 0.3 + bool handle_bankvalue = true; // Introduced in initdata ver 0.4 + + if (FS_DEBUG) { + printf("--- PARSING INIT DATA ---\n"); + for (int i=0; i<n; i++) + printf("%c ", d[i]); + printf("\n"); + } + + byte version_major, version_minor; + version_major = d[1]; version_minor = d[2]; + + // Check which version of the initdata we're using and if it's OK + if (!(version_major == FS_VERSION_MAJOR && version_minor == FS_VERSION_MINOR)) { + if (FS_DEBUG) { + printf("Project saved with other version of fluidsynth format. Ver: %d.%d\n", version_major, version_minor); + } + + if (version_major == 0 && version_minor == 1) { + sendError("Initialization data created with different version of FluidSynth Mess, will be ignored."); + return; + } + + if (version_major == 0 && version_minor <= 2) { + load_drumchannels = false; + } + + if (version_major == 0 && version_minor <= 3) { + handle_bankvalue = false; + } + } + + byte nr_of_fonts = d[3]; + nrOfSoundfonts = nr_of_fonts; //"Global" counter + const byte* chptr = (d + 4); + + //Get lastdir: + lastdir = std::string((char*)chptr); + sendLastdir(lastdir.c_str()); + + chptr+=strlen(lastdir.c_str())+1; + + FluidSoundFont fonts[nrOfSoundfonts]; //Just a temp one + //Fonts: + for (int i=0; i<nr_of_fonts; i++) { + fonts[i].filename = (char*)(chptr); + chptr+=(strlen(fonts[i].filename.c_str())+1); + + if (QFileInfo(fonts[i].filename).isRelative()) { + printf("path is relative, we append full path!\n"); + fonts[i].filename = projPathPtr->ascii() + std::string("/")+ fonts[i].filename; + } + std::cout << "SOUNDFONT FILENAME + PATH " << fonts[i].filename << std::endl; + } + + if (*chptr != FS_INIT_CHANNEL_SECTION) { + sendError("Init-data corrupt... Projectfile error. Initdata ignored.\n"); + return; + } + + chptr++; + for (int i=0; i<nr_of_fonts; i++) { + fonts[i].extid = *chptr; + chptr++; + //printf("Extid, %d: %d\n",i,fonts[i].extid); + } + + // All channels external id + preset + for (int i=0; i<FS_MAX_NR_OF_CHANNELS; i++) { + channels[i].font_extid = *chptr; chptr++; + channels[i].preset = *chptr; chptr++; + if (handle_bankvalue) { // Ver 0.4 and later + channels[i].banknum = *chptr; chptr++; + } + else { + channels[i].banknum = 0; + } + + if (load_drumchannels) { // Ver 0.3 and later + channels[i].drumchannel = *chptr; + chptr++; + } + } + + //Reverb: + setController(0, FS_REVERB_ON, *chptr); chptr++; + setController(0, FS_CHORUS_ON, *chptr); chptr++; + + if (FS_DEBUG) + printf("--- END PARSE INIT DATA ---\n"); + //Load the shit: + for (int i=0; i<nrOfSoundfonts; i++) { + pushSoundfont(fonts[i].filename.c_str(), fonts[i].extid); + } + } + + +//--------------------------------------------------------- +// processEvent +// All events from the sequencer goes here +//--------------------------------------------------------- + +bool FluidSynth::processEvent(const MidiPlayEvent& ev) + { + switch(ev.type()) { + case ME_CONTROLLER: + if (FS_DEBUG_DATA) { + printf("*** FluidSynth::process - Controller. Chan: %x dataA: %x dataB: %x\n", ev.channel(), ev.dataA(), ev.dataB()); + for (int i=0; i< ev.len(); i++) + printf("%x ", ev.data()[i]); + } + setController(ev.channel(), ev.dataA(), ev.dataB(), false); + return true; + case ME_NOTEON: + return playNote(ev.channel(), ev.dataA(), ev.dataB()); + case ME_NOTEOFF: + return playNote(ev.channel(), ev.dataA(), 0); + case ME_SYSEX: + //Debug print + if (FS_DEBUG_DATA) { + printf("*** FluidSynth::process - Sysex received\n"); + for (int i=0; i< ev.len(); i++) + printf("%x ", ev.data()[i]); + printf("\n"); + } + return sysex(ev.len(), ev.data()); + case ME_PITCHBEND: + setController(ev.channel(), CTRL_PITCH, ev.dataA(), false); + break; + + case ME_PROGRAM: + setController(ev.channel(), CTRL_PROGRAM, ev.dataA(), false); + break; + default: + break; + } + return false; + } + +//--------------------------------------------------------- +// sysex +//--------------------------------------------------------- + +bool FluidSynth::sysex(int n, const unsigned char* d) + { + switch(*d) { + case FS_LASTDIR_CHANGE: { + lastdir = std::string((char*)(d+1)); + sendLastdir(lastdir.c_str()); + break; + } + case FS_PUSH_FONT: { + int extid = d[1]; + + if (FS_DEBUG) + printf("Client: Got push font %s, id: %d\n",(d+1), extid); + + const char* filename = (const char*)(d+2); + if (!pushSoundfont(filename, extid)) + sendError("Could not load soundfont "); + break; + } + case FS_DUMP_INFO: { + dumpInfo(); + break; + } + case FS_SOUNDFONT_CHANNEL_SET: { + sfChannelChange(*(d+1), *(d+2)); + break; + } + case FS_INIT_DATA: { + parseInitData(n,d); + break; + } + case FS_SOUNDFONT_POP: + popSoundfont(*(d+1)); + break; + case FS_DRUMCHANNEL_SET: { + byte onoff = (*(d+1)); + byte channel = (*(d+2)); + channels[channel].drumchannel = onoff; + if (FS_DEBUG) + printf("Client: Set drumchannel on chan %d to %d\n",channel, onoff); + break; + } + default: + if (FS_DEBUG) + printf("FluidSynth::sysex() : unknown sysex received: %d\n",*d); + break; + } + return false; + } + +//--------------------------------------------------------- +// sendSysex +//--------------------------------------------------------- +void FluidSynth::sendSysex(int l, const unsigned char* d) + { + MidiPlayEvent ev(0, 0, ME_SYSEX, d, l); + gui->writeEvent(ev); + } + +//----------------------------------- +// pushSoundfont - load a soundfont +//----------------------------------- +bool FluidSynth::pushSoundfont (const char* filename, int extid) + { + pthread_attr_t* attributes = (pthread_attr_t*) malloc(sizeof(pthread_attr_t)); + pthread_attr_init(attributes); + pthread_attr_setdetachstate(attributes, PTHREAD_CREATE_DETACHED); + + FS_Helper* helper = new FS_Helper; + helper->fptr = this; + helper->filename = filename; + helper->id = extid; + + if (pthread_create(&fontThread, attributes, ::fontLoadThread, (void*) helper)) + perror("creating thread failed:"); + + pthread_attr_destroy(attributes); + return true; + } + +//--------------------------------------------------------- +// fontLoadThread +// helper thread to load soundfont in the +// background +//--------------------------------------------------------- + +static void* fontLoadThread(void* t) + { + //Init vars + FS_Helper* h = (FS_Helper*) t; + FluidSynth* fptr = h->fptr; + const char* filename = h->filename.c_str(); + pthread_mutex_t* sfloader_mutex = (fptr->_sfloader_mutex); + + //Let only one loadThread have access to the fluidsynth-object at the time + pthread_mutex_lock(sfloader_mutex); + int rv = fluid_synth_sfload(fptr->fluidsynth, filename, 1); + + if (rv ==-1) { + fptr->sendError(fluid_synth_error(fptr->fluidsynth)); + if (FS_DEBUG) + std::cerr << DEBUG_ARGS << "error loading soundfont: " << fluid_synth_error(fptr->fluidsynth) << std::endl; + + //Unlock the mutex, or else we might be stuck here forever... + pthread_mutex_unlock(sfloader_mutex); + delete h; + pthread_exit(0); + } + + //Deal with internal and external id etc. + if (FS_DEBUG) + printf("Soundfont %s loaded, index %d\n", filename, rv); + + FluidSoundFont font; + font.filename = h->filename;//strdup(filename); + + font.intid = rv; + if (h->id == FS_UNSPECIFIED_ID) { + font.extid = fptr->getNextAvailableExternalId(); + if (FS_DEBUG) + printf("Font got extid %d\n",font.extid); + } + else + font.extid = h->id; + if (FS_DEBUG) + printf("Font has external id: %d int id:%d\n", font.extid, font.intid); + + //Strip off the filename + QString temp = QString(filename); + QString name = temp.right(temp.length() - temp.findRev('/',-1) - 1); + name = name.left(name.length()-4); //Strip off ".sf2" + font.name = name.latin1(); + fptr->stack.push_front(font); + fptr->currentlyLoadedFonts++; + + //Cleanup & unlock: + pthread_mutex_unlock(sfloader_mutex); + delete h; + + if (FS_DEBUG) + printf("Currently loaded fonts: %d Nr of soundfonts: %d\n",fptr->currentlyLoadedFonts, fptr->nrOfSoundfonts); + //Check whether this was the last font or not. If so, run initSynth(); + if (fptr->nrOfSoundfonts <= fptr->currentlyLoadedFonts) { + if (FS_DEBUG) + printf("This was the last font, rewriting channel settings...\n"); + fptr->rewriteChannelSettings(); + //Update data in GUI-window. + fptr->sendSoundFontData();; + fptr->sendChannelData(); + } + + pthread_exit(0); + } + +//--------------------------------------------------------- +// playNote +// called from host +//--------------------------------------------------------- + +bool FluidSynth::playNote(int channel, int pitch, int velo) + { + if (channels[channel].font_intid == FS_UNSPECIFIED_FONT || + channels[channel].font_intid == FS_UNSPECIFIED_ID) + return false; + + if (velo) { + if (fluid_synth_noteon(fluidsynth, channel, pitch, velo)) { + if (FS_DEBUG) + std::cerr << DEBUG_ARGS << "error processing noteon event: " << fluid_synth_error(fluidsynth); + } + } + else { + if (fluid_synth_noteoff(fluidsynth, channel, pitch)) + if (FS_DEBUG) + std::cerr << DEBUG_ARGS << "error processing noteoff event: " << fluid_synth_error(fluidsynth) << std::endl; + } + return false; + } +//--------------------------------------------------------- +// sendSoundFontData +//--------------------------------------------------------- +void FluidSynth::sendSoundFontData() + { + int ndatalen = 2; //2 bytes for command and length + + //Calculate length in chars of all strings in the soundfontstack in one string + for (std::list<FluidSoundFont>::iterator it = stack.begin(); it != stack.end(); it++) { + ndatalen += 1 + strlen(it->name.c_str()); + ndatalen += FS_SFDATALEN; //unsigned char for ID + } + byte ndata[ndatalen]; + *ndata = FS_SEND_SOUNDFONTDATA; //The command + *(ndata + 1) = (unsigned char)stack.size (); //Nr of Soundfonts + + // Copy the stuff to ndatalen: + char* chunk_start = (char*)(ndata + 2); + int chunk_len, name_len; + for (std::list<FluidSoundFont>::iterator it = stack.begin(); it != stack.end(); ++it) { + name_len = strlen(it->name.c_str()) + 1; + chunk_len = name_len + FS_SFDATALEN; + memcpy(chunk_start, it->name.c_str(), name_len); //First, store the fontname + *(chunk_start + name_len) = it->extid; //The GUI only needs to know about the external id, store that here + chunk_start += chunk_len; + } + sendSysex(ndatalen, ndata); + } + +//--------------------------------------------------------- +// sendChannelData +//--------------------------------------------------------- +void FluidSynth::sendChannelData() + { + int chunk_size = 2; + int chdata_length = (chunk_size * FS_MAX_NR_OF_CHANNELS) +1 ; //Command and the 2 channels * 16 + byte chdata[chdata_length]; + byte* chdptr; + chdata[0] = FS_SEND_CHANNELINFO; + chdptr = (chdata + 1); + for (int i=0; i<FS_MAX_NR_OF_CHANNELS; i++) { + *(chdptr) = channels[i].font_extid; //Font external id + *(chdptr+1) = i; //Channel nr + chdptr += chunk_size; + } + sendSysex(chdata_length, chdata); + // Send drum channel info afterwards (later addition, not very neat, but works...) + + int drumchdata_length = FS_MAX_NR_OF_CHANNELS + 1; //1 byte for the command, one byte for each channel + byte drumchdata[drumchdata_length ]; + byte* drumchdataptr = drumchdata; + *drumchdata = FS_SEND_DRUMCHANNELINFO; + + for (int i=0; i<FS_MAX_NR_OF_CHANNELS; i++) { + drumchdataptr++; + *drumchdataptr = channels[i].drumchannel; + } + sendSysex(drumchdata_length, drumchdata); + } + +//--------------------------------------------------------- +// dumpInfo +//--------------------------------------------------------- + +void FluidSynth::dumpInfo() + { + printf("-----------------------------------------------------\n"); + printf("Dumping info...\n"); + printf("Last dir: %s\n", lastdir.c_str()); + for (int i=0; i<FS_MAX_NR_OF_CHANNELS; i++) + printf("Chan %d\tFont extid:%d\tintid:%d\tdrumchan:%d\tpreset: %d\n", i, channels[i].font_extid, channels[i].font_intid, channels[i].drumchannel, channels[i].preset); + + printf("\n"); + for (std::list<FluidSoundFont>::iterator it = stack.begin(); it != stack.end(); it++) + printf("Font: %s\tintid: %d\textid %d\tfilename:%s\n", it->name.c_str(), it->intid, it->extid, it->filename.c_str()); + printf("Reverb on: %d, width: %f, size: %f level: %f damp: %f\n",rev_on, rev_width, rev_size, rev_level, rev_damping); + printf("-----------------------------------------------------\n"); + } + +//--------------------------------------------------------- +// guiVisible +//--------------------------------------------------------- + +bool FluidSynth::guiVisible() const + { + return gui->isVisible(); + } + + +//--------------------------------------------------------- +// showGui +//--------------------------------------------------------- + +void FluidSynth::showGui(bool val) + { + gui->setShown(val); + } + +//--------------------------------------------------------- +// setController +//--------------------------------------------------------- + +bool FluidSynth::setController(int channel, int id, int val) + { + setController(channel, id, val, false); + return false; + } + +//--------------------------------------------------------- +// setController +//--------------------------------------------------------- + +void FluidSynth::setController(int channel, int id, int val, bool fromGui) + { + // + // Channelless controllers + // + int err = 0; + switch (id) { + case FS_GAIN: { + fluid_synth_set_gain(fluidsynth, (float) val/25); //gives val an interval of approximately[0,5] + //Forward to gui if not from Gui + if (!fromGui) { + MidiPlayEvent ev(0, 0, 0, ME_CONTROLLER, FS_GAIN, val); + gui->writeEvent(ev); + } + break; + } + case FS_REVERB_ON: { + rev_on = val; + fluid_synth_set_reverb_on(fluidsynth, val); // 0 or 1 + //if (rev_on) + // fluid_synth_set_reverb(fluidsynth, rev_size, rev_damping, rev_width, rev_level); + if (!fromGui) { + MidiPlayEvent ev(0, 0, 0, ME_CONTROLLER, FS_REVERB_ON, val); + gui->writeEvent(ev); + } + break; + } + case FS_REVERB_LEVEL: + //Interval: 0-2 + rev_level = (double)2*val/16384; //[0,2] + //if (rev_on) + fluid_synth_set_reverb(fluidsynth, rev_size, rev_damping, rev_width, rev_level); + if (!fromGui) { + MidiPlayEvent ev(0, 0, 0, ME_CONTROLLER, FS_REVERB_LEVEL, val); + gui->writeEvent(ev); + } + break; + case FS_REVERB_WIDTH: // + rev_width = (double)val/164; //[0,100] + //if (rev_on) + fluid_synth_set_reverb(fluidsynth, rev_size, rev_damping, rev_width, rev_level); + if (!fromGui) { + MidiPlayEvent ev(0, 0, 0, ME_CONTROLLER, FS_REVERB_WIDTH, val); + gui->writeEvent(ev); + } + break; + case FS_REVERB_DAMPING: //[0,1] + rev_damping = (double)val/16384; + //if (rev_on) + fluid_synth_set_reverb(fluidsynth, rev_size, rev_damping, rev_width, rev_level); + if (!fromGui) { + MidiPlayEvent ev(0, 0, 0, ME_CONTROLLER, FS_REVERB_DAMPING, val); + gui->writeEvent(ev); + } + break; + case FS_REVERB_ROOMSIZE: //[0,1] + rev_size = (double)val/16384; + //if (rev_on) + fluid_synth_set_reverb(fluidsynth, rev_size, rev_damping, rev_width, rev_level); + if (!fromGui) { + MidiPlayEvent ev(0, 0, 0, ME_CONTROLLER, FS_REVERB_ROOMSIZE, val); + gui->writeEvent(ev); + } + break; + case FS_CHORUS_ON: {// 0 or 1 + cho_on = val; + fluid_synth_set_chorus_on(fluidsynth, val); + if (!fromGui) { + MidiPlayEvent ev(0, 0, 0, ME_CONTROLLER, FS_CHORUS_ON, val); + gui->writeEvent(ev); + } + break; + } + case FS_CHORUS_NUM: {//Number of delay lines + cho_num = val; + fluid_synth_set_chorus(fluidsynth, cho_num, cho_level, cho_speed, cho_depth, cho_type); + if (!fromGui) { + MidiPlayEvent ev(0, 0, 0, ME_CONTROLLER, FS_CHORUS_NUM, val); + gui->writeEvent(ev); + } + break; + } + case FS_CHORUS_TYPE: {//? + cho_type = val; + fluid_synth_set_chorus(fluidsynth, cho_num, cho_level, cho_speed, cho_depth, cho_type); + if (!fromGui) { + MidiPlayEvent ev(0, 0, 0, ME_CONTROLLER, FS_CHORUS_TYPE, val); + gui->writeEvent(ev); + } + break; + } + case FS_CHORUS_SPEED: {//(0.291,5) Hz + cho_speed = (double)(0.291 + (double)val/3479); + fluid_synth_set_chorus(fluidsynth, cho_num, cho_level, cho_speed, cho_depth, cho_type); + if (!fromGui) { + MidiPlayEvent ev(0, 0, 0, ME_CONTROLLER, FS_CHORUS_SPEED, val); + gui->writeEvent(ev); + } + break; + } + case FS_CHORUS_DEPTH: { //[0,40] + cho_depth = (double) val*40/16383; + fluid_synth_set_chorus(fluidsynth, cho_num, cho_level, cho_speed, cho_depth, cho_type); + if (!fromGui) { + MidiPlayEvent ev(0, 0, 0, ME_CONTROLLER, FS_CHORUS_DEPTH, val); + gui->writeEvent(ev); + } + break; + } + case FS_CHORUS_LEVEL: { //[0,1] + cho_level = (double) val/16383; + fluid_synth_set_chorus(fluidsynth, cho_num, cho_level, cho_speed, cho_depth, cho_type); + if (!fromGui) { + MidiPlayEvent ev(0, 0, 0, ME_CONTROLLER, FS_CHORUS_LEVEL, val); + gui->writeEvent(ev); + } + break; + } + // + // Controllers that depend on channels + // + case CTRL_PITCH: + // MusE's range is from -8192 to +8191, fluidsynth seems to be [0, 16384] + val +=8192; + err = fluid_synth_pitch_bend (fluidsynth, channel, val); + break; + + // Added by T356 + case FS_PITCHWHEELSENS: + err = fluid_synth_pitch_wheel_sens(fluidsynth, channel, val); + break; + + case CTRL_PROGRAM: { + //Check if MusE is trying to set a preset on an unspecified font. If so, ignore. + if (FS_DEBUG) + printf("Program select : channel %d val %d\n",channel, val); + byte font_intid = channels[channel].font_intid; + + if (font_intid == FS_UNSPECIFIED_ID || font_intid == FS_UNSPECIFIED_FONT) + return; + + byte banknum = ((val >> 16) & 0xff); + byte patch = (val & 0xff); + //printf("val: %d banknum: %x patch: %d\n", val, banknum, patch); + + err = fluid_synth_program_select(fluidsynth, channel, font_intid , banknum, patch); + if (err) + printf("FluidSynth::setController() - Error changing program on soundfont %s, channel: %d\n", fluid_synth_error(fluidsynth), channel); + else { + channels[channel].preset = val;//setChannelPreset(val, channel); + channels[channel].banknum = banknum; + } + break; + } + default: + if (FS_DEBUG) + printf("Setting controller on channel: %d with id: 0x%x to val: %d\n",channel, id, val); + err = fluid_synth_cc(fluidsynth, channel, id, val); + break; + } + + if (err) + printf ("FluidSynth::setController() - error processing controller event: %s\n", fluid_synth_error(fluidsynth)); + } + +//--------------------------------------------------------- +// getControllerInfo +//--------------------------------------------------------- +int FluidSynth::getControllerInfo(int id, const char** name, int* controller, int* min, int* max, int* initval) const + { + if (id >= NUM_CONTROLLER) + return 0; + *controller = fluidCtrl[id].num; + *name = fluidCtrl[id].name; + *min = fluidCtrl[id].min; + *max = fluidCtrl[id].max; + switch(id) + { + case 0: + *initval = (int)(fluidCtrl[0].max*FS_PREDEF_VOLUME); + break; + case 1: + *initval = 0; + break; + case 2: + *initval = (int)(fluidCtrl[2].max*FS_PREDEF_REVERB_LEVEL); + break; + case 3: + *initval = (int)(fluidCtrl[3].max*FS_PREDEF_REVERB_ROOMSIZE); + break; + case 4: + *initval = (int)(fluidCtrl[4].max*FS_PREDEF_REVERB_DAMPING); + break; + case 5: + *initval = (int)(fluidCtrl[5].max*FS_PREDEF_REVERB_WIDTH); + break; + case 6: + *initval = 0; + break; + case 7: + *initval = (int)(fluidCtrl[7].max*FS_PREDEF_CHORUS_NUM); + break; + case 8: + *initval = (int)(fluidCtrl[8].max*FS_PREDEF_CHORUS_TYPE); + break; + case 9: + *initval = (int)(fluidCtrl[9].max*FS_PREDEF_CHORUS_SPEED); + break; + case 10: + *initval = (int)(fluidCtrl[10].max*FS_PREDEF_CHORUS_DEPTH); + break; + case 11: + *initval = (int)(fluidCtrl[11].max*FS_PREDEF_CHORUS_LEVEL); + break; + default: + *initval = fluidCtrl[id].initval; + break; + } + + if (FS_DEBUG) + //printf("FluidSynth::getControllerInfo() id: %d name: %s controller: %d min: %d max: %d\n",id,*name,*controller,*min,*max); + printf("FluidSynth::getControllerInfo() id: %d name: %s controller: %d min: %d max: %d initval: %d\n",id,*name,*controller,*min,*max,*initval); + return ++id; + } + +//--------------------------------------------------------- +// sendError +//--------------------------------------------------------- +void FluidSynth::sendError(const char *errorMessage) + { + int len = 2 + strlen(errorMessage); + unsigned char data[len]; + *data = FS_ERROR; + memcpy(data + 1, errorMessage, len - 1); + sendSysex(len, data); + } + +//--------------------------------------------------------- +// getNextAvailableExternalId +//--------------------------------------------------------- + +int FluidSynth::getNextAvailableExternalId() + { + unsigned char place[FS_MAX_NR_OF_CHANNELS]; + for(int i=0; i<FS_MAX_NR_OF_CHANNELS; i++) + place[i] = 0; + for (std::list<FluidSoundFont>::iterator it = stack.begin(); it != stack.end(); it++) + place[it->extid] = 1; + + int i=0; + while (i < FS_MAX_NR_OF_CHANNELS && place[i] == 1) + i++; + + return i; + } + +//--------------------------------------------------------- +// sfChannelChange +//--------------------------------------------------------- + +void FluidSynth::sfChannelChange(byte extid, byte channel) + { + if (FS_DEBUG) + printf("FluidSynth::sfChannelChange()-Setting channel %d to font with extid %d intid %d\n",channel, extid, getFontInternalIdByExtId(extid)); + channels[channel].font_extid = extid; + channels[channel].font_intid = getFontInternalIdByExtId(extid); + } + +//--------------------------------------------------------- +// getFontInternalIdByExtId +//--------------------------------------------------------- +byte FluidSynth::getFontInternalIdByExtId(byte ext_id) + { + for (std::list<FluidSoundFont>::iterator it = stack.begin(); it !=stack.end(); it++) { + if (it->extid == ext_id) + return it->intid; + } + return FS_UNSPECIFIED_FONT; + } + +//--------------------------------------------------------- +// sendLastDir +//--------------------------------------------------------- +void FluidSynth::sendLastdir(const char* lastdir) + { + int n = strlen(lastdir) + 2; + byte d[n]; + d[0] = FS_LASTDIR_CHANGE; + memcpy(d+1,lastdir, strlen(lastdir)+1); + + MidiPlayEvent ev(0,0, ME_SYSEX, d, n); + gui->writeEvent(ev); + } + + +//--------------------------------------------------------- +// rewriteChannelSettings +//--------------------------------------------------------- +void FluidSynth::rewriteChannelSettings() + { + //Walk through the channels, remap internal ID:s to external ID:s (something that actually only needs to be done at + //startup, since the fonts aren't loaded yet at that time and it isn't possible to give them a correct internal id + //since they don't have any at that time, this can probably be fixed in a smarter way (but it works..)) + for (int i=0; i<FS_MAX_NR_OF_CHANNELS; i++) { + int ext_id = channels[i].font_extid;//getFontExternalIdByChannel(i); + if (ext_id != FS_UNSPECIFIED_ID) //Check if ext_id is set to any sane font + { + channels[i].font_intid = getFontInternalIdByExtId(ext_id);//(getFontInternalIdByExtId(ext_id));//if so, get value from the stack + } + else + channels[i].font_intid = FS_UNSPECIFIED_FONT; //if not, set it to unspecified + } + + //Assign correct presets to all channels + for (int i=0; i<FS_MAX_NR_OF_CHANNELS; i++) { + int preset = channels[i].preset; + int int_id = channels[i].font_intid; + byte banknum = channels[i].banknum; + + if (channels[i].drumchannel) + banknum = 128; + + //printf("Channel %d, font int-id %d ext_id %d, preset %d\n",i, int_id, getFontExternalIdByChannel(i), preset); + if (!(preset == FS_UNSPECIFIED_PRESET + || int_id == FS_UNSPECIFIED_FONT + || int_id == FS_UNSPECIFIED_ID)) { + int rv = fluid_synth_program_select(fluidsynth, i, int_id, banknum, preset); + if (rv) + std::cerr << DEBUG_ARGS << "Error changing preset! " << fluid_synth_error(fluidsynth) << std::endl; + } + } + } +//--------------------------------------------------------- +// getPatchName +//--------------------------------------------------------- +const char* FluidSynth::getPatchName(int i, int, int, bool /*drum*/) const + { + if (channels[i].font_intid == FS_UNSPECIFIED_FONT || + channels[i].font_intid == FS_UNSPECIFIED_ID) + //return "no preset"; + return "<unknown>"; + else if (channels[i].preset == FS_UNSPECIFIED_PRESET) + //return "no preset"; + return "<unknown>"; + else { + fluid_preset_t *preset = fluid_synth_get_channel_preset(fluidsynth, i); + //if (!preset) return "no preset"; + if (!preset) return "<unknown>"; + return preset->get_name(preset); + } + } +//--------------------------------------------------------- +// getPatchInfo +//--------------------------------------------------------- +const MidiPatch* FluidSynth::getPatchInfo(int i, const MidiPatch* patch) const + { + //if (channels[i].font_intid == FS_UNSPECIFIED_FONT) + if (channels[i].font_intid == FS_UNSPECIFIED_FONT || + channels[i].font_intid == FS_UNSPECIFIED_ID) + return 0; + //else if (channels[i].preset == FS_UNSPECIFIED_PRESET) + // return 0; + else { + //printf("Getpatchname, channel: %d\n",channel); + if (!patch) + //Deliver first patch + return getFirstPatch(i); + else + //Deliver next patch + return getNextPatch(i, patch); + } + } + +//--------------------------------------------------------- +// getFirstPatch +//--------------------------------------------------------- +const MidiPatch* FluidSynth::getFirstPatch (int channel) const + { + static MidiPatch midiPatch; + + midiPatch.typ = 0; + midiPatch.lbank = 0; + + fluid_preset_t* preset; + int font_id = channels[channel].font_intid; + //if (font_id == FS_UNSPECIFIED_FONT) + if (font_id == FS_UNSPECIFIED_FONT || font_id == FS_UNSPECIFIED_ID) + return 0; + + fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(fluidsynth, font_id); + + if (!channels[channel].drumchannel) { + for (unsigned bank = 0; bank < 128; ++bank) { + for (unsigned patch = 0; patch < 128; ++patch) { + preset = sfont->get_preset (sfont, bank, patch); + if (preset) { + midiPatch.hbank = bank; + midiPatch.prog = patch; + midiPatch.name = preset->get_name (preset); + return &midiPatch; + } + } + } + return 0; + } + else { //This is a drumchannel + int bank = 128; + for (unsigned patch = 0; patch < 128; ++patch) { + preset = sfont->get_preset (sfont, bank, patch); + if (preset) { + midiPatch.hbank = bank; + midiPatch.prog = patch; + midiPatch.name = preset->get_name(preset); + return &midiPatch; + } + } + } + return 0; + } + +//--------------------------------------------------------- +// getNextPatch +//--------------------------------------------------------- +const MidiPatch* FluidSynth::getNextPatch (int channel, const MidiPatch* patch) const + { + static MidiPatch midiPatch; + //First check if there actually is any soundfont associated to the channel. If not, don't bother + int font_id = channels[channel].font_intid; + if (font_id == FS_UNSPECIFIED_FONT || font_id == FS_UNSPECIFIED_ID) + return 0; + if (patch == 0) + return getFirstPatch(channel); + midiPatch.typ = 0; + midiPatch.lbank = 0; + + //printf("Font has internal id: %d\n",font_id); + fluid_preset_t* preset; + fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(fluidsynth, font_id); + + if (!channels[channel].drumchannel) { + unsigned prog = patch->prog + 1; + + for (unsigned bank = patch->hbank; bank < 128; ++bank) { + for ( ; prog < 128; ++prog) { + preset = sfont->get_preset (sfont, bank, prog); + if (preset) { + //printf("Preset info: bank: %d prog: %d name: %s\n", bank, prog, preset->get_name(preset)); + midiPatch.hbank = bank; + midiPatch.prog = prog; + midiPatch.name = preset->get_name (preset); + return &midiPatch; + } + } + prog = 0; // Reset if we "come around" + } + } + else { //This is a drum channel + unsigned bank = 128; + unsigned prog = patch->prog; + for (prog = patch->prog + 1; prog < 128; ++prog) { + preset = sfont->get_preset (sfont, bank, prog); + if (preset) { + //printf("Preset info: bank: %d prog: %d name: %s\n",bank, prog, preset->get_name(preset)); + midiPatch.hbank = bank; + midiPatch.prog = prog; + midiPatch.name = preset->get_name (preset); + return &midiPatch; + } + } + } + return 0; + } + +//--------------------------------------------------------- +// popSoundfont +//--------------------------------------------------------- + +bool FluidSynth::popSoundfont (int ext_id) + { + bool success = false; + int int_id = getFontInternalIdByExtId(ext_id); + + //if (int_id == FS_UNSPECIFIED_FONT) { + if (int_id == FS_UNSPECIFIED_FONT || int_id == FS_UNSPECIFIED_ID) { + std::cerr << DEBUG_ARGS << "Internal error! Request for deletion of Soundfont that is not registered!" << std::endl; + } + else + { + //Try to unload soundfont + int err = fluid_synth_sfunload(fluidsynth, int_id, 0); + if (err != -1) {//Success + //Check all channels that the font is used in + for (int i=0; i<FS_MAX_NR_OF_CHANNELS; i++) { + //Set them to unspecified and reset preset settings + if (channels[i].font_intid == int_id) { + channels[i].font_intid = FS_UNSPECIFIED_ID; + channels[i].font_extid = FS_UNSPECIFIED_ID; + channels[i].preset = FS_UNSPECIFIED_PRESET; + } + } + //Remove it from soundfont stack + for (std::list<FluidSoundFont>::iterator it =stack.begin(); it !=stack.end(); it++) { + if (it->intid == int_id) { + stack.erase(it); + break; + } + } + //Resend fontdata & re-initialize + sendSoundFontData(); + sendChannelData(); + rewriteChannelSettings(); + success = true; + currentlyLoadedFonts--; + } + else //OK, there was trouble + std::cerr << DEBUG_ARGS << "Error unloading soundfont!" << fluid_synth_error(fluidsynth) << std::endl; + } + if (FS_DEBUG) + printf("Removed soundfont with ext it: %d\n",ext_id); + return success; + } + +//--------------------------------------------------------- +// instantiate +// construct a new synthesizer instance +//--------------------------------------------------------- + +class QWidget; +static pthread_mutex_t globalMutex; +static bool mutexEnabled = false; + + +static Mess* instantiate(int sr, QWidget*, QString* projectPathPtr, const char* name) + { +printf("fluidsynth sampleRate %d\n", sr); + projPathPtr=projectPathPtr; + + if (!mutexEnabled) { + pthread_mutex_init(&globalMutex,NULL); + mutexEnabled = true; + } + + FluidSynth* synth = new FluidSynth(sr, &globalMutex); + if (synth->init(name)) { + delete synth; + synth = 0; + } + return synth; + } + +extern "C" + { + static MESS descriptor = { + "FluidSynth", + "FluidSynth soundfont loader by Mathias Lundgren", //Mathias Lundgren (lunar_shuttle@users.sf.net) + "0.1", //Version string + MESS_MAJOR_VERSION, MESS_MINOR_VERSION, + instantiate, + }; + const MESS* mess_descriptor() { return &descriptor; } + } + diff --git a/attic/muse/synti/fluidsynth/fluidsynti.h b/attic/muse/synti/fluidsynth/fluidsynti.h new file mode 100644 index 00000000..10b96b9d --- /dev/null +++ b/attic/muse/synti/fluidsynth/fluidsynti.h @@ -0,0 +1,150 @@ +/* + * MusE FLUID Synth softsynth plugin + * + * Copyright (C) 2004 Mathias Lundgren (lunar_shuttle@users.sourcforge.net) + * + * $Id: fluidsynti.h,v 1.15.2.5 2009/11/19 04:20:33 terminator356 Exp $ + * + */ + +#ifndef __MUSE_FLUIDSYNTI_H__ +#define __MUSE_FLUIDSYNTI_H__ + +#include <fluidsynth.h> +#include <qstring.h> +#include <pthread.h> +#include <string> +#include "fluidsynthgui.h" +#include "libsynti/mess.h" +#include "muse/debug.h" +#include "libsynti/mpevent.h" +#include "muse/midictrl.h" + +#define FS_DEBUG_DATA 0 //Turn on/off debug print of midi data sent to fluidsynth + +typedef unsigned char byte; + +struct FluidSoundFont + { + std::string filename; + std::string name; + byte extid, intid; + }; + +struct FluidCtrl { + const char* name; + int num; + int min, max; + //int val; + int initval; + }; + +// NRPN-controllers: +static const int FS_GAIN = 0 + CTRL_NRPN14_OFFSET; +static const int FS_REVERB_ON = 1 + CTRL_NRPN14_OFFSET; +static const int FS_REVERB_LEVEL = 2 + CTRL_NRPN14_OFFSET; +static const int FS_REVERB_ROOMSIZE = 3 + CTRL_NRPN14_OFFSET; +static const int FS_REVERB_DAMPING = 4 + CTRL_NRPN14_OFFSET; +static const int FS_REVERB_WIDTH = 5 + CTRL_NRPN14_OFFSET; +static const int FS_CHORUS_ON = 6 + CTRL_NRPN14_OFFSET; +static const int FS_CHORUS_NUM = 7 + CTRL_NRPN14_OFFSET; +static const int FS_CHORUS_TYPE = 8 + CTRL_NRPN14_OFFSET; +static const int FS_CHORUS_SPEED = 9 + CTRL_NRPN14_OFFSET; +static const int FS_CHORUS_DEPTH = 10 + CTRL_NRPN14_OFFSET; +static const int FS_CHORUS_LEVEL = 11 + CTRL_NRPN14_OFFSET; +// Added by T356 +static const int FS_PITCHWHEELSENS = 0 + CTRL_RPN_OFFSET; + +// FluidChannel is used to map different soundfonts to different fluid-channels +// This is to be able to select different presets from specific soundfonts, since +// Fluidsynth has a quite strange way of dealing with fontloading and channels +// We also need this since getFirstPatch and getNextPatch only tells us which channel is +// used, so this works as a connection between soundfonts and fluid-channels (one channel +// can only have one soundfont, but one soundfont can have many channels) + +struct FluidChannel + { + byte font_extid, font_intid, preset, drumchannel; + byte banknum; // hbank + //FluidSoundFont* font; + }; + +/*#include <string> +#include <list> +#include <map> +*/ + +class FluidSynth : public Mess { + private: + bool pushSoundfont (const char*, int); + void sendSysex(int l, const unsigned char* d); + void sendLastdir(const char*); + void sfChannelChange(unsigned char font_id, unsigned char channel); + void parseInitData(int n, const byte* d); + + byte getFontInternalIdByExtId (byte channel); + + void debug(const char* msg) { if (FS_DEBUG) printf("Debug: %s\n",msg); } + void dumpInfo(); //Prints out debug info + + FluidChannel channels[FS_MAX_NR_OF_CHANNELS]; + std::string lastdir; + pthread_t fontThread; + const MidiPatch * getFirstPatch (int channel) const; + const MidiPatch* getNextPatch (int, const MidiPatch *) const; + + //For reverb and chorus: + double rev_size, rev_damping, rev_width, rev_level, cho_level, cho_speed, cho_depth; + bool rev_on, cho_on; + int cho_num, cho_type; + +public: + FluidSynth(int sr, pthread_mutex_t *_Globalsfloader_mutex); + ~FluidSynth(); + bool init(const char*); + virtual void processMessages(); + virtual void process(float**, int, int); + virtual bool playNote(int channel, int pitch, int velo); + virtual bool sysex(int, const unsigned char*); + virtual bool setController(int, int, int); + void setController(int, int , int, bool); + virtual void getInitData(int*, const unsigned char**) const; + virtual const char* getPatchName(int, int, int, bool) const; + virtual const MidiPatch* getPatchInfo(int i, const MidiPatch* patch) const; + virtual int getControllerInfo(int, const char**, int*, int*, int*, int*) const; + virtual bool processEvent(const MidiPlayEvent&); + + virtual bool hasGui() const { return true; } + virtual bool guiVisible() const; + virtual void showGui(bool val); + + void sendError(const char*); + void sendSoundFontData(); + void sendChannelData(); + void rewriteChannelSettings(); //used because fluidsynth does some very nasty things when loading a font! + bool popSoundfont (int ext_id); + + int getNextAvailableExternalId(); + + fluid_synth_t* fluidsynth; + FluidSynthGui* gui; + pthread_mutex_t *_sfloader_mutex; + int currentlyLoadedFonts; //To know whether or not to run the init-parameters + std::list<FluidSoundFont> stack; + int nrOfSoundfonts; + + void initInternal(); + + static FluidCtrl fluidCtrl[]; + + }; + +struct FS_Helper //Only used to pass parameters when calling the loading thread + { + FluidSynth* fptr; + std::string filename; + int id; + }; + +static void* fontLoadThread(void* t); +#endif /* __MUSE_FLUIDSYNTI_H__ */ |