diff options
Diffstat (limited to 'muse_qt4_evolution/synti/fluidsynth')
-rw-r--r-- | muse_qt4_evolution/synti/fluidsynth/CMakeLists.txt | 51 | ||||
-rw-r--r-- | muse_qt4_evolution/synti/fluidsynth/README.txt | 45 | ||||
-rw-r--r-- | muse_qt4_evolution/synti/fluidsynth/TODO | 13 | ||||
-rw-r--r-- | muse_qt4_evolution/synti/fluidsynth/fluidsynthgui.cpp | 596 | ||||
-rw-r--r-- | muse_qt4_evolution/synti/fluidsynth/fluidsynthgui.h | 136 | ||||
-rw-r--r-- | muse_qt4_evolution/synti/fluidsynth/fluidsynthgui.ui | 641 | ||||
-rw-r--r-- | muse_qt4_evolution/synti/fluidsynth/fluidsynti.cpp | 1174 | ||||
-rw-r--r-- | muse_qt4_evolution/synti/fluidsynth/fluidsynti.h | 141 |
8 files changed, 2797 insertions, 0 deletions
diff --git a/muse_qt4_evolution/synti/fluidsynth/CMakeLists.txt b/muse_qt4_evolution/synti/fluidsynth/CMakeLists.txt new file mode 100644 index 00000000..5fdc3533 --- /dev/null +++ b/muse_qt4_evolution/synti/fluidsynth/CMakeLists.txt @@ -0,0 +1,51 @@ +#============================================================================= +# MusE +# Linux Music Editor +# $Id:$ +# +# Copyright (C) 2002-2006 by Werner Schweer and others +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +#============================================================================= + +QT4_WRAP_CPP ( fluidsynth_mocs fluidsynthgui.h ) +QT4_WRAP_UI ( fluidsynth_uis fluidsynthgui.ui ) + +add_library ( fluidsynth SHARED + fluidsynti.cpp + fluidsynthgui.cpp + fluidsynthgui.h + ${fluidsynth_mocs} + ${fluidsynth_uis} + ) + +target_link_libraries( fluidsynth synti ) + +# - tell cmake to name target fluidsynth.so instead of +# libfluidsynth.so +# - use precompiled header files +# +set_target_properties ( fluidsynth + PROPERTIES PREFIX "" + COMPILE_FLAGS "-include ${PROJECT_BINARY_DIR}/all-pic.h" + LINK_FLAGS "-L${FLUID_LIBDIR} ${FLUID_LIB}" + ) + +target_link_libraries(fluidsynth + synti + ${QT_LIBRARIES} + ${FLUID_LIB} + ) + +install_targets ( /${CMAKE_INSTALL_LIBDIR}/${MusE_INSTALL_NAME}/synthi/ fluidsynth ) + diff --git a/muse_qt4_evolution/synti/fluidsynth/README.txt b/muse_qt4_evolution/synti/fluidsynth/README.txt new file mode 100644 index 00000000..7764edb6 --- /dev/null +++ b/muse_qt4_evolution/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/muse_qt4_evolution/synti/fluidsynth/TODO b/muse_qt4_evolution/synti/fluidsynth/TODO new file mode 100644 index 00000000..e941e1e9 --- /dev/null +++ b/muse_qt4_evolution/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/muse_qt4_evolution/synti/fluidsynth/fluidsynthgui.cpp b/muse_qt4_evolution/synti/fluidsynth/fluidsynthgui.cpp new file mode 100644 index 00000000..42f812b5 --- /dev/null +++ b/muse_qt4_evolution/synti/fluidsynth/fluidsynthgui.cpp @@ -0,0 +1,596 @@ +/* + * MusE FLUID Synth softsynth plugin + * + * Copyright (C) 2004 Mathias Lundgren (lunar_shuttle@users.sourcforge.net) + * + * $Id: fluidsynthgui.cpp,v 1.19 2006/01/06 22:48:09 wschweer Exp $ + * + */ + +#include "fluidsynthgui.h" +#include "fluidsynti.h" +#include <iostream> +#include "muse/midi.h" +#include "xpm/buttondown.xpm" + + +//#define MUSE_FLUID_DEBUG false + +FluidSynthGui::FluidSynthGui() + : MessGui() + { + setupUi(this); + //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())); + + pendingFont = ""; + + //channelListView->setColumnWidthMode(FS_CHANNEL_COL, Q3ListView::Maximum); + //channelListView->setColumnWidthMode(FS_SF_ID_COL,Q3ListView::Maximum); + ReverbFrame->setEnabled(true); + ChorusFrame->setEnabled(true); + + if (!FS_DEBUG) + dumpInfoButton->hide(); + + connect(Gain, SIGNAL(valueChanged(int)), SLOT(changeGain(int))); + connect(dumpInfoButton , SIGNAL(clicked()), SLOT(dumpInfo())); + + connect(channelTreeWidget, SIGNAL(itemPressed(QTreeWidgetItem*,int)), + this, SLOT(channelItemClicked(QTreeWidgetItem*,int))); + connect(sfTreeWidget, SIGNAL(itemPressed(QTreeWidgetItem*,int)), + this, SLOT(sfItemClicked(QTreeWidgetItem*,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(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))); + + //Clear channels + for (int i=0; i<FS_MAX_NR_OF_CHANNELS; i++) + channels[i] = FS_UNSPECIFIED_ID; + } + +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::loadClicked() + { + QString filename = QFileDialog::getOpenFileName( + this, + tr("Choose soundfont"), + lastdir, + QString("*.[Ss][Ff]2") + ); + if (!filename.isEmpty()) { + QFileInfo fi(filename); + lastdir = fi.absolutePath(); + sendLastdir(lastdir); + sendLoadFont(filename); + pendingFont = filename; + } + } + +//--------------------------------------------------------- +// sendLastdir +// Send the last dir-value to the client +//--------------------------------------------------------- + +void FluidSynthGui::sendLastdir(QString dir) + { + int l = dir.size() + 2; + byte data[l]; + data[0] = FS_LASTDIR_CHANGE; + memcpy(data+1, dir.toLatin1().data(), dir.size()+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.toLatin1().data(), filename.length()+1); + sendSysex(data,l); + } + +//--------------------------------------------------------- +// processEvent +//--------------------------------------------------------- + +void FluidSynthGui::processEvent(const MidiEvent& 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); + QMessageBox::critical(this, "Fluidsynth",QString(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 + sfTreeWidget->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--; + } + updateSoundfontTreeWidget(); + updateChannelTreeWidget(); + 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; + } + updateChannelTreeWidget(); + + break; + } + case FS_SEND_DRUMCHANNELINFO: { + byte* drumchptr = (data+1); + for (int i=0; i<FS_MAX_NR_OF_CHANNELS; i++) { + drumchannels[i] = *drumchptr; + drumchptr++; + } + updateChannelTreeWidget(); + break; + } + case FS_FONT_SUCCESSFULLY_LOADED: { + byte extid = *(data+1); + QString fn = (const char*) (data+2); + if (FS_DEBUG) { + printf("Font successfully loaded: %s, extid: %d\n", fn.toLatin1().data(), extid); + } + // Try to add last loaded font (if any) to first available channel: + if (pendingFont == fn) { + if (FS_DEBUG) + printf("Pending font successfully loaded. Add it to first available channel.\n"); + + for (int i=0; i < FS_MAX_NR_OF_CHANNELS; i++) { + if (channels[i] == FS_UNSPECIFIED_ID) { + if (FS_DEBUG) + printf ("sendChannelChange: %d %d\n", extid, i); + sendChannelChange(extid, i); + channels[i] = extid; + updateChannelTreeWidget(); + break; + } + } + } + pendingFont = ""; + 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->setCurrentIndex(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::updateChannelTreeWidget() + { + if (FS_DEBUG) + printf("FluidSynthGui::updateChannelListView\n"); + channelTreeWidget->clear(); + QIcon btndown = QIcon(buttondown_xpm); + + QTreeWidgetItem* header = new QTreeWidgetItem(); + header->setText(FS_CHANNEL_COL, "Channel"); + header->setText(FS_SF_ID_COL, "Soundfont"); + header->setText(FS_DRUM_CHANNEL_COL, "Drum Chnl"); + channelTreeWidget->setHeaderItem(header); + + 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"; + QTreeWidgetItem* qlvNewItem = new QTreeWidgetItem(channelTreeWidget); + qlvNewItem->setText(FS_CHANNEL_COL, chanstr); + qlvNewItem->setIcon(FS_SF_ID_COL, btndown); + qlvNewItem->setText(FS_SF_ID_COL, sfidstr); + qlvNewItem->setIcon(FS_DRUM_CHANNEL_COL, btndown); + qlvNewItem->setText(FS_DRUM_CHANNEL_COL, drumchanstr); + } + } + +//--------------------------------------------------------- +// updateSoundfontTreeWidget +//--------------------------------------------------------- +void FluidSynthGui::updateSoundfontTreeWidget() + { + sfTreeWidget->clear(); //Clear the listview + + QTreeWidgetItem* header = new QTreeWidgetItem(); + header->setText(FS_ID_COL, "ID"); + header->setText(FS_SFNAME_COL, "Fontname"); + sfTreeWidget->setHeaderItem(header); + + for (std::list<FluidGuiSoundFont>::iterator it = stack.begin(); it != stack.end(); it++) { + QTreeWidgetItem* qlvNewItem; + + qlvNewItem = new QTreeWidgetItem(sfTreeWidget); + QString qsid = QString("%1").arg(it->id); + qlvNewItem->setText(FS_ID_COL, qsid); + qlvNewItem->setText(FS_SFNAME_COL, QString(it->name)); + } + //sfTreeWidget->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(QTreeWidgetItem* item, int col) + { + // + // Soundfont ID column + // + if (col == FS_SF_ID_COL) { + QMenu* popup = new QMenu(this); + QPoint ppt = channelTreeWidget->visualItemRect(item).bottomLeft(); + QTreeWidget* treeWidget = item->treeWidget(); + ppt += QPoint(treeWidget->header()->sectionPosition(col), treeWidget->header()->height()); + ppt = treeWidget->mapToGlobal(ppt); + + int i = 0; + for (std::list<FluidGuiSoundFont>::reverse_iterator it = stack.rbegin(); it != stack.rend(); it++) { + i++; + QAction* a = popup->addAction(it->name); + a->setData(i); + } + + int lastindex = i+1; + QAction* a = popup->addAction("unspecified"); + a->setData(lastindex); + a = popup->exec(ppt, 0); + if (a) { + int index = a->data().toInt(); + 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(a->text()); + fontname = getSoundFontName(sfid); + } + byte channel = atoi(item->text(FS_CHANNEL_COL).toLatin1().data()) - 1; + sendChannelChange(sfid, channel); + item->setText(FS_SF_ID_COL, fontname); + } + delete popup; + } + // + // Drumchannel column: + // + else if (col == FS_DRUM_CHANNEL_COL) { + QMenu* popup = new QMenu(this); + QPoint ppt = channelTreeWidget->visualItemRect(item).bottomLeft(); + QTreeWidget* treeWidget = item->treeWidget(); + ppt += QPoint(treeWidget->header()->sectionPosition(col), treeWidget->header()->height()); + ppt = treeWidget->mapToGlobal(ppt); + QAction* a = popup->addAction("Yes"); + a->setData(1); + a = popup->addAction("No"); + a->setData(0); + byte channel = atoi(item->text(FS_CHANNEL_COL).toLatin1().data()) - 1; + + a = popup->exec(ppt, 0); + if (a) { + int index = a->data().toInt(); + if (index != drumchannels[channel]) { + sendDrumChannelChange(index, channel); + drumchannels[channel] = index; + item->setText(FS_DRUM_CHANNEL_COL, index == 0 ? "No" : "Yes" ); + } + } + } +#if 0 + else if (col == FS_DRUM_CHANNEL_COL) { + Q3PopupMenu* popup = new Q3PopupMenu(this); + QPoint ppt = channelListView->itemRect(item).bottomLeft(); + Q3ListView* 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).toLatin1().data()) - 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" ); + } + } +#endif + } + +//--------------------------------------------------------- +// 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(QTreeWidgetItem* item, int /*column*/) + { + if (item != 0) { + currentlySelectedFont = atoi(item->text(FS_ID_COL).toLatin1().data()); + 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); + } + +#endif diff --git a/muse_qt4_evolution/synti/fluidsynth/fluidsynthgui.h b/muse_qt4_evolution/synti/fluidsynth/fluidsynthgui.h new file mode 100644 index 00000000..9884e636 --- /dev/null +++ b/muse_qt4_evolution/synti/fluidsynth/fluidsynthgui.h @@ -0,0 +1,136 @@ +/* + * MusE FLUID Synth softsynth plugin + * + * Copyright (C) 2004 Mathias Lundgren (lunar_shuttle@users.sourcforge.net) + * + * $Id: fluidsynthgui.h,v 1.15 2005/10/05 21:51:04 lunar_shuttle Exp $ + * + */ + +#ifndef __MUSE_FLUIDSYNTHGUI_H__ +#define __MUSE_FLUIDSYNTHGUI_H__ + +#include "ui_fluidsynthgui.h" +#include "libsynti/gui.h" +#include <list> + +struct FluidChannel; +#define FS_DEBUG 0 //Turn on/off debug + +#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.2 +#define FS_PREDEF_REVERB_LEVEL 0.25 +#define FS_PREDEF_REVERB_ROOMSIZE 0.3 +#define FS_PREDEF_REVERB_DAMPING 0.3 +#define FS_PREDEF_REVERB_WIDTH 0.2 +#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; + + +//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 + FS_FONT_SUCCESSFULLY_LOADED // synth tells gui it loaded a font successfully, and gives it it's external id + }; + +enum + { + FS_DUMP_INFO = 240, + FS_ERROR, + FS_INIT_DATA + }; + +struct FluidGuiSoundFont + { + QString filename; + QString name; + byte id; + }; + +//--------------------------------------------------------- +// FluidSynthGui +//--------------------------------------------------------- + +class FluidSynthGui : public QDialog, Ui::FLUIDSynthGuiBase, public MessGui + { + Q_OBJECT + private: + virtual void processEvent(const MidiEvent& ev); + void sendLastdir(QString); + void sendLoadFont(QString); + void sendChannelChange(byte font_id, byte channel); + void sendDrumChannelChange(byte onoff, byte channel); + void updateSoundfontTreeWidget(); + void updateChannelTreeWidget(); + + 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 + QString pendingFont; + + private slots: + void loadClicked(); + void readMessage(int); + void changeGain(int); + void dumpInfo(); + void channelItemClicked(QTreeWidgetItem* item, int column); + 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(QTreeWidgetItem* item, int column); + + public: +// virtual void sysexReceived (const unsigned char *, int); +// virtual void controllerReceived(int, int, int); + + FluidSynthGui(); + ~FluidSynthGui(); +}; + + +#endif /* __MUSE_FLUIDSYNTHGUI_H__ */ diff --git a/muse_qt4_evolution/synti/fluidsynth/fluidsynthgui.ui b/muse_qt4_evolution/synti/fluidsynth/fluidsynthgui.ui new file mode 100644 index 00000000..558f4133 --- /dev/null +++ b/muse_qt4_evolution/synti/fluidsynth/fluidsynthgui.ui @@ -0,0 +1,641 @@ +<ui version="4.0" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>FLUIDSynthGuiBase</class> + <widget class="QDialog" name="FLUIDSynthGuiBase" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>656</width> + <height>514</height> + </rect> + </property> + <property name="windowTitle" > + <string>FLUID Synth</string> + </property> + <property name="windowIcon" > + <iconset/> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>8</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="0" column="0" > + <widget class="QFrame" name="frame_2" > + <property name="frameShape" > + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Raised</enum> + </property> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>8</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLabel" name="loadedFontsLabel" > + <property name="text" > + <string><html><head><meta name="qrichtext" content="1" /></head><body style=" white-space: pre-wrap; font-family:Sans Serif; font-weight:400; font-style:normal; text-decoration:none;"><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Loaded Soundfonts</span></p></body></html></string> + </property> + <property name="textFormat" > + <enum>Qt::AutoText</enum> + </property> + <property name="alignment" > + <set>Qt::AlignHCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QTreeWidget" name="sfTreeWidget" > + <property name="columnCount" > + <number>2</number> + </property> + <property name="sortingEnabled" > + <bool>true</bool> + </property> + <column> + <property name="text" > + <string>0</string> + </property> + </column> + <column> + <property name="text" > + <string>1</string> + </property> + </column> + </widget> + </item> + </layout> + </widget> + </item> + <item row="0" column="1" > + <widget class="QFrame" name="frame_3" > + <property name="frameShape" > + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Raised</enum> + </property> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>8</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLabel" name="fontSetupLabel" > + <property name="text" > + <string><html><head><meta name="qrichtext" content="1" /></head><body style=" white-space: pre-wrap; font-family:Sans Serif; font-weight:400; font-style:normal; text-decoration:none;"><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Channel Setup</span></p></body></html></string> + </property> + <property name="alignment" > + <set>Qt::AlignHCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QTreeWidget" name="channelTreeWidget" > + <property name="columnCount" > + <number>3</number> + </property> + <property name="sortingEnabled" > + <bool>true</bool> + </property> + <column> + <property name="text" > + <string>0</string> + </property> + </column> + <column> + <property name="text" > + <string>1</string> + </property> + </column> + <column> + <property name="text" > + <string>2</string> + </property> + </column> + </widget> + </item> + </layout> + </widget> + </item> + <item rowspan="2" row="1" column="1" > + <widget class="QFrame" name="ChorusFrame" > + <property name="frameShape" > + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Raised</enum> + </property> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>8</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QCheckBox" name="Chorus" > + <property name="text" > + <string>Chorus</string> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLabel" name="ChorusNumberLabel" > + <property name="text" > + <string>Number</string> + </property> + <property name="alignment" > + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="ChorusNumber" > + <property name="maximum" > + <number>127</number> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="ChorusTypeLabel" > + <property name="text" > + <string>Type</string> + </property> + <property name="alignment" > + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="ChorusType" > + <item> + <property name="text" > + <string>Sine</string> + </property> + </item> + <item> + <property name="text" > + <string>Triangle</string> + </property> + </item> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLabel" name="ChorusSpeedLabel" > + <property name="text" > + <string>Speed</string> + </property> + <property name="alignment" > + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QSlider" name="ChorusSpeed" > + <property name="maximum" > + <number>16383</number> + </property> + <property name="singleStep" > + <number>16</number> + </property> + <property name="pageStep" > + <number>1638</number> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="tickInterval" > + <number>1638</number> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLabel" name="ChorusDepthLabel" > + <property name="text" > + <string>Depth</string> + </property> + <property name="alignment" > + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QSlider" name="ChorusDepth" > + <property name="maximum" > + <number>16383</number> + </property> + <property name="singleStep" > + <number>16</number> + </property> + <property name="pageStep" > + <number>1638</number> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="tickInterval" > + <number>1638</number> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLabel" name="ChorusLevelLabel" > + <property name="text" > + <string>Level</string> + </property> + <property name="alignment" > + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QSlider" name="ChorusLevel" > + <property name="maximum" > + <number>16383</number> + </property> + <property name="singleStep" > + <number>16</number> + </property> + <property name="pageStep" > + <number>1638</number> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="tickInterval" > + <number>1638</number> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item row="1" column="0" > + <widget class="QFrame" name="frame" > + <property name="frameShape" > + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Raised</enum> + </property> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>8</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QPushButton" name="Push" > + <property name="text" > + <string>Load</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="Pop" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>Delete</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="dumpInfoButton" > + <property name="text" > + <string>Dump Info</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item row="3" column="0" colspan="2" > + <widget class="QGroupBox" name="GainBox" > + <property name="title" > + <string/> + </property> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>8</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLabel" name="label" > + <property name="text" > + <string><html><head><meta name="qrichtext" content="1" /></head><body style=" white-space: pre-wrap; font-family:Sans Serif; font-weight:400; font-style:normal; text-decoration:none;"><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Gain</p></body></html></string> + </property> + </widget> + </item> + <item> + <widget class="QSlider" name="Gain" > + <property name="minimum" > + <number>0</number> + </property> + <property name="maximum" > + <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>Qt::Horizontal</enum> + </property> + <property name="tickInterval" > + <number>7</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="2" column="0" > + <widget class="QFrame" name="ReverbFrame" > + <property name="frameShape" > + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Raised</enum> + </property> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>8</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QCheckBox" name="Reverb" > + <property name="text" > + <string>Reverb</string> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLabel" name="ReverbRoomSizeLabel" > + <property name="text" > + <string>Room Size</string> + </property> + <property name="alignment" > + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="ReverbDampingLabel" > + <property name="text" > + <string>Damping</string> + </property> + <property name="alignment" > + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="ReverbWidthLabel" > + <property name="text" > + <string>Width</string> + </property> + <property name="alignment" > + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="ReverbLevelLabel" > + <property name="text" > + <string>Level</string> + </property> + <property name="alignment" > + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QSlider" name="ReverbRoomSize" > + <property name="maximum" > + <number>16383</number> + </property> + <property name="singleStep" > + <number>16</number> + </property> + <property name="pageStep" > + <number>1638</number> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="tickInterval" > + <number>1638</number> + </property> + </widget> + </item> + <item> + <widget class="QSlider" name="ReverbDamping" > + <property name="maximum" > + <number>16383</number> + </property> + <property name="singleStep" > + <number>16</number> + </property> + <property name="pageStep" > + <number>1638</number> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="tickInterval" > + <number>1638</number> + </property> + </widget> + </item> + <item> + <widget class="QSlider" name="ReverbWidth" > + <property name="maximum" > + <number>16383</number> + </property> + <property name="singleStep" > + <number>16</number> + </property> + <property name="pageStep" > + <number>1638</number> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="tickInterval" > + <number>1638</number> + </property> + </widget> + </item> + <item> + <widget class="QSlider" name="ReverbLevel" > + <property name="maximum" > + <number>16383</number> + </property> + <property name="singleStep" > + <number>16</number> + </property> + <property name="pageStep" > + <number>1638</number> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="tickInterval" > + <number>1638</number> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + </layout> + <widget class="QWidget" name="widget_2" > + <property name="geometry" > + <rect> + <x>11</x> + <y>36</y> + <width>260</width> + <height>17</height> + </rect> + </property> + </widget> + <widget class="QWidget" name="widget_3" > + <property name="geometry" > + <rect> + <x>11</x> + <y>59</y> + <width>260</width> + <height>17</height> + </rect> + </property> + </widget> + <widget class="QWidget" name="widget_4" > + <property name="geometry" > + <rect> + <x>11</x> + <y>82</y> + <width>260</width> + <height>17</height> + </rect> + </property> + </widget> + <widget class="QWidget" name="widget_5" > + <property name="geometry" > + <rect> + <x>11</x> + <y>105</y> + <width>260</width> + <height>17</height> + </rect> + </property> + </widget> + </widget> + </item> + </layout> + </widget> + <layoutdefault spacing="3" margin="8" /> + <pixmapfunction></pixmapfunction> + <resources/> + <connections/> +</ui> diff --git a/muse_qt4_evolution/synti/fluidsynth/fluidsynti.cpp b/muse_qt4_evolution/synti/fluidsynth/fluidsynti.cpp new file mode 100644 index 00000000..57e72262 --- /dev/null +++ b/muse_qt4_evolution/synti/fluidsynth/fluidsynti.cpp @@ -0,0 +1,1174 @@ +/* + * MusE FLUID Synth softsynth plugin + * + * Copyright (C) 2004 Mathias Lundgren (lunar_shuttle@users.sourcforge.net) + * + * $Id: fluidsynti.cpp,v 1.28 2005/12/18 14:09:54 wschweer Exp $ + * + */ + +#include "fluidsynti.h" +#include "muse/midi.h" + +#include <list> +#include <iostream> + + +FluidCtrl FluidSynth::fluidCtrl[] = { + { "Gain", FS_GAIN ,0, 127}, + { "Master reverb on/off", FS_REVERB_ON , 0, 1}, + { "Master reverb level", FS_REVERB_LEVEL, 0, 16384 }, + { "Master reverb size", FS_REVERB_ROOMSIZE, 0, 16384 }, // Interval: [0,1] + { "Master reverb damping", FS_REVERB_DAMPING, 0, 16384 }, // Interval: [0,1] + { "Master reverb width", FS_REVERB_WIDTH, 0, 16384 }, // Interval: [0,100] + { "Master chorus on/off", FS_CHORUS_ON, 0, 1}, + { "Master chorus num delay lines", FS_CHORUS_NUM, 0, 10 }, //Default: 3 + { "Master chorus type", FS_CHORUS_TYPE, 0, 1 }, + { "Master chorus speed", FS_CHORUS_SPEED, 0, 16384 }, // (0.291,5) Hz + { "Master chorus depth", FS_CHORUS_DEPTH, 0, 16384 }, // [0,40] + { "Master chorus level", FS_CHORUS_LEVEL, 0, 16384 }, // [0,1] + { "Modulation", CTRL_MODULATION, 0, 127 }, + { "Portamento time", CTRL_PORTAMENTO_TIME, 0, 127 }, + { "Volume", CTRL_VOLUME, 0, 127 }, + { "Pan", CTRL_PANPOT, 0, 127 }, + { "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 } + }; + +static int NUM_CONTROLLER = sizeof(FluidSynth::fluidCtrl)/sizeof(*(FluidSynth::fluidCtrl)); + +// +// Fluidsynth +// +FluidSynth::FluidSynth(int sr) : Mess(2) + { + setSampleRate(sr); + fluid_settings_t* s = new_fluid_settings(); + fluid_settings_setnum(s, "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); + +/* + buffer = 0; + bufferlen = 0; + */ + } + +FluidSynth::~FluidSynth() + { + /* + int err = delete_fluid_synth (_fluidsynth); + 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->setWindowTitle(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; + } + +//--------------------------------------------------------- +// process +// called from host +//--------------------------------------------------------- + +void FluidSynth::process(float** ports, int offset, int len) + { + //Process messages from the gui + while (gui->fifoSize()) { + MidiEvent 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) + { + // 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++) { + len+=strlen(it->filename.c_str()) + 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++) { + memcpy(chptr, it->filename.c_str(), strlen(it->filename.c_str())+1); + chptr = chptr + 1 + strlen(it->filename.c_str()); + } + + //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) + { + 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 (*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 MidiEvent& 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()); + } + return false; + } + +//--------------------------------------------------------- +// sysex +//--------------------------------------------------------- + +bool FluidSynth::sysex(int n, const unsigned char* d) + { + if (n == 0 || d == 0) + return false; + 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) + { + MidiEvent ev(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, FluidSynth::fontLoadThread, (void*) helper)) + perror("creating thread failed:"); + + pthread_attr_destroy(attributes); + return true; + } + +//--------------------------------------------------------- +// fontLoadThread +// helper thread to load soundfont in the +// background +//--------------------------------------------------------- + +void* FluidSynth::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 + QFileInfo fi(filename); + font.name = fi.baseName().toLatin1().data(); + 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(); + fptr->sendFontSuccessfullyLoaded(filename, font.extid); + } + + pthread_exit(0); + } + +//--------------------------------------------------------- +// playNote +// called from host +//--------------------------------------------------------- + +bool FluidSynth::playNote(int channel, int pitch, int velo) + { + if (channels[channel].font_intid == FS_UNSPECIFIED_FONT) + 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) { + MidiEvent ev(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) { + MidiEvent ev(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) { + MidiEvent ev(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) { + MidiEvent ev(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) { + MidiEvent ev(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) { + MidiEvent ev(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) { + MidiEvent ev(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) { + MidiEvent ev(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) { + MidiEvent ev(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) { + MidiEvent ev(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) { + MidiEvent ev(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) { + MidiEvent ev(0, 0, ME_CONTROLLER, FS_CHORUS_LEVEL, val); + gui->writeEvent(ev); + } + break; + } + // + // Controllers that depend on channels + // + case CTRL_PITCH: + err = fluid_synth_pitch_bend (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) + { + if (id >= NUM_CONTROLLER) + return 0; + *controller = fluidCtrl[id].num; + *name = fluidCtrl[id].name; + *min = fluidCtrl[id].min; + *max = fluidCtrl[id].max; + if (FS_DEBUG) + printf("FluidSynth::getControllerInfo() id: %d name: %s controller: %d min: %d max: %d\n",id,*name,*controller,*min,*max); + 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); + + MidiEvent ev(0, ME_SYSEX, d, n); + gui->writeEvent(ev); + } + +//--------------------------------------------------------- +// sendLastDir +//--------------------------------------------------------- +void FluidSynth::sendFontSuccessfullyLoaded(const char* filename, byte extid) + { + // extid first, then filename: + int n = strlen(filename) + 3; + byte d[n]; + d[0] = FS_FONT_SUCCESSFULLY_LOADED; + d[1] = extid; + memcpy(d+2, filename, strlen(filename)+1); + MidiEvent ev(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 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) const + { + if (channels[i].font_intid == FS_UNSPECIFIED_FONT) + return "no preset"; + else if (channels[i].preset == FS_UNSPECIFIED_PRESET) + return "no preset"; + else { + //printf("Getpatchname, channel: %d\n",channel); + fluid_preset_t *preset = fluid_synth_get_channel_preset(fluidsynth, i); + if (!preset) return "no preset"; + return preset->get_name(preset); + } + } + +//--------------------------------------------------------- +// getPatchInfo +//--------------------------------------------------------- +const MidiPatch* FluidSynth::getPatchInfo(int i, const MidiPatch* patch) const + { + if (channels[i].font_intid == FS_UNSPECIFIED_FONT) + 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) + 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) + return 0; + if (patch == 0) + return getFirstPatch(channel); + + midiPatch.typ = 0; + midiPatch.lbank = 0; + + if (font_id == FS_UNSPECIFIED_FONT) + return 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) { + 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 Mess* instantiate(int sr, const char* name) + { + printf("fluidsynth sampleRate %d\n", sr); + FluidSynth* synth = new FluidSynth(sr); + if (synth->init(name)) { + delete synth; + synth = 0; + } + return synth; + } + +extern "C" + { + static MESS descriptor = { + "FluidSynth", + "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/muse_qt4_evolution/synti/fluidsynth/fluidsynti.h b/muse_qt4_evolution/synti/fluidsynth/fluidsynti.h new file mode 100644 index 00000000..c66b1faf --- /dev/null +++ b/muse_qt4_evolution/synti/fluidsynth/fluidsynti.h @@ -0,0 +1,141 @@ +/* + * MusE FLUID Synth softsynth plugin + * + * Copyright (C) 2004 Mathias Lundgren (lunar_shuttle@users.sourcforge.net) + * + * $Id: fluidsynti.h,v 1.20 2006/01/06 22:48:09 wschweer Exp $ + * + */ + +#ifndef __MUSE_FLUIDSYNTI_H__ +#define __MUSE_FLUIDSYNTI_H__ + +#include <fluidsynth.h> +#include <pthread.h> +#include <string> +#include "fluidsynthgui.h" +#include "libsynti/mess.h" +#include "muse/debug.h" +#include "libsynti/midievent.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; + }; + +// 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; + +// 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 + }; + +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); + ~FluidSynth(); + bool init(const char*); + 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**); + virtual const char* getPatchName(int, int, int) const; + virtual const MidiPatch* getPatchInfo(int i, const MidiPatch* patch) const; + virtual int getControllerInfo(int, const char**, int*, int*, int*); + virtual bool processEvent(const MidiEvent&); + + virtual bool hasGui() const { return true; } + virtual bool guiVisible() const; + virtual void showGui(bool val); + + void sendError(const char*); + void sendSoundFontData(); + void sendChannelData(); + void sendFontSuccessfullyLoaded(const char* filename, byte extid); + 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[]; + static void* fontLoadThread(void* t); + + }; + +struct FS_Helper //Only used to pass parameters when calling the loading thread + { + FluidSynth* fptr; + std::string filename; + int id; + }; + + +#endif /* __MUSE_FLUIDSYNTI_H__ */ |