281 lines
8.7 KiB
C++
281 lines
8.7 KiB
C++
/*********************************************************************************/
|
|
/*!
|
|
@file MidiDeviceFluidSynth.cpp
|
|
|
|
@brief MidiDeviceFluidSynth talks to the MidiRt Real Time Version.
|
|
|
|
@author L. J. Barman
|
|
|
|
Copyright (c) 2008-2020, L. J. Barman, all rights reserved
|
|
|
|
This file is part of the PianoBooster application
|
|
|
|
PianoBooster is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
PianoBooster 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 PianoBooster. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
/*********************************************************************************/
|
|
|
|
#include "MidiDeviceFluidSynth.h"
|
|
|
|
static fluid_settings_t* s_debug_fluid_settings;
|
|
|
|
static void debug_settings_foreach_func (void *data,
|
|
#if FLUIDSYNTH_VERSION_MAJOR >= 2
|
|
const char *name,
|
|
#else
|
|
char *name,
|
|
#endif
|
|
int type)
|
|
{
|
|
Q_UNUSED(data)
|
|
Q_UNUSED(type)
|
|
char buffer[300];
|
|
fluid_settings_copystr(s_debug_fluid_settings, name, buffer, sizeof (buffer));
|
|
ppLogDebug("settings_foreach_func %s : %s", name , buffer );
|
|
}
|
|
|
|
CMidiDeviceFluidSynth::CMidiDeviceFluidSynth()
|
|
{
|
|
m_synth = nullptr;
|
|
m_fluidSettings = nullptr;
|
|
m_audioDriver = nullptr;
|
|
m_rawDataIndex = 0;
|
|
m_validConnection = false;
|
|
}
|
|
|
|
CMidiDeviceFluidSynth::~CMidiDeviceFluidSynth()
|
|
{
|
|
CMidiDeviceFluidSynth::closeMidiPort(MIDI_OUTPUT, -1); // not possible to make virtual call in d'tor
|
|
}
|
|
|
|
void CMidiDeviceFluidSynth::init()
|
|
{
|
|
}
|
|
|
|
QStringList CMidiDeviceFluidSynth::getMidiPortList(midiType_t type)
|
|
{
|
|
if (type != MIDI_OUTPUT) // Only has an output
|
|
return QStringList();
|
|
|
|
if (qsettings==nullptr)
|
|
return QStringList();
|
|
|
|
QStringList fontList = qsettings->value("FluidSynth/SoundFont").toStringList();
|
|
|
|
if (fontList.size() > 0){
|
|
return QStringList(getFluidInternalName());
|
|
}
|
|
return fontList;
|
|
}
|
|
|
|
bool CMidiDeviceFluidSynth::openMidiPort(midiType_t type, const QString &portName)
|
|
{
|
|
closeMidiPort(MIDI_OUTPUT, -1);
|
|
|
|
if (portName.isEmpty())
|
|
return false;
|
|
|
|
if (type == MIDI_INPUT)
|
|
return false;
|
|
|
|
if (!portName.endsWith(FLUID_NAME)) {return false;}
|
|
|
|
if (getMidiPortList(type).size()==0) {return false;}
|
|
|
|
// Load a SoundFont
|
|
QStringList fontList = qsettings->value("FluidSynth/SoundFont").toStringList();
|
|
if (fontList.size() == 0) {return false;}
|
|
|
|
// Create the settings.
|
|
m_fluidSettings = new_fluid_settings();
|
|
|
|
// Change the settings if necessary
|
|
fluid_settings_setnum(m_fluidSettings, "synth.sample-rate", qsettings->value("FluidSynth/sampleRateCombo",22050).toInt());
|
|
fluid_settings_setint(m_fluidSettings, "audio.period-size", qsettings->value("FluidSynth/bufferSizeCombo", 128).toInt());
|
|
fluid_settings_setint(m_fluidSettings, "audio.periods", qsettings->value("FluidSynth/bufferCountCombo", 4).toInt());
|
|
|
|
#if !defined (Q_OS_WINDOWS)
|
|
fluid_settings_setstr(m_fluidSettings, "audio.driver", qsettings->value("FluidSynth/audioDriverCombo", "pulseaudio").toString().toStdString().c_str());
|
|
#endif
|
|
|
|
// Create the synthesizer.
|
|
m_synth = new_fluid_synth(m_fluidSettings);
|
|
#if (FLUIDSYNTH_VERSION_MAJOR >= 2) && (FLUIDSYNTH_VERSION_MINOR >= 2)
|
|
fluid_synth_reverb_on(m_synth, -1, 0);
|
|
fluid_synth_chorus_on(m_synth, -1, 0);
|
|
#else
|
|
fluid_synth_set_reverb_on(m_synth, 0);
|
|
fluid_synth_set_chorus_on(m_synth, 0);
|
|
#endif
|
|
|
|
// Create the audio driver.
|
|
m_audioDriver = new_fluid_audio_driver(m_fluidSettings, m_synth);
|
|
|
|
QString pathName = fontList.at(0);
|
|
ppLogDebug("Sound font %s", qPrintable(pathName));
|
|
m_soundFontId = fluid_synth_sfload(m_synth, qPrintable(pathName), 0);
|
|
if (m_soundFontId == -1)
|
|
return false;
|
|
|
|
for (int channel = 0; channel < MAX_MIDI_CHANNELS ; channel++)
|
|
{
|
|
fluid_synth_program_change(m_synth, channel, GM_PIANO_PATCH);
|
|
}
|
|
fluid_synth_set_gain(m_synth, qsettings->value("FluidSynth/masterGainSpin", FLUID_DEFAULT_GAIN).toFloat()/100.0f );
|
|
m_validConnection = true;
|
|
if (Cfg::logLevel >= LOG_LEVEL_DEBUG) {
|
|
s_debug_fluid_settings = m_fluidSettings;
|
|
fluid_settings_foreach(m_fluidSettings, 0, debug_settings_foreach_func);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CMidiDeviceFluidSynth::closeMidiPort(midiType_t type, int index)
|
|
{
|
|
Q_UNUSED(index)
|
|
m_validConnection = false;
|
|
|
|
if (type != MIDI_OUTPUT)
|
|
return;
|
|
|
|
if (m_fluidSettings == nullptr)
|
|
return;
|
|
|
|
/* Clean up */
|
|
delete_fluid_audio_driver(m_audioDriver);
|
|
delete_fluid_synth(m_synth);
|
|
delete_fluid_settings(m_fluidSettings);
|
|
m_fluidSettings = nullptr;
|
|
m_rawDataIndex = 0;
|
|
|
|
}
|
|
|
|
//! add a midi event to be played immediately
|
|
void CMidiDeviceFluidSynth::playMidiEvent(const CMidiEvent & event)
|
|
{
|
|
if (m_synth == nullptr)
|
|
return;
|
|
|
|
int channel = event.channel() & 0x0f;
|
|
switch(event.type())
|
|
{
|
|
case MIDI_NOTE_OFF: // NOTE_OFF
|
|
fluid_synth_noteoff(m_synth, channel, event.note());
|
|
break;
|
|
case MIDI_NOTE_ON: // NOTE_ON
|
|
fluid_synth_noteon(m_synth, channel, event.note(), event.velocity());
|
|
break;
|
|
|
|
case MIDI_NOTE_PRESSURE: //POLY_AFTERTOUCH: 3 bytes
|
|
//fluid_synth_key_pressure(m_synth, channel, event.data1(), event.data2());
|
|
break;
|
|
|
|
case MIDI_CONTROL_CHANGE: //CONTROL_CHANGE:
|
|
fluid_synth_cc(m_synth, channel, event.data1(), event.data2());
|
|
//ppLogTrace("MIDI_CONTROL_CHANGE %d %d %d", channel, event.data1(), event.data2());
|
|
break;
|
|
|
|
case MIDI_PROGRAM_CHANGE: //PROGRAM_CHANGE:
|
|
fluid_synth_program_change(m_synth, channel, event.programme());
|
|
break;
|
|
|
|
case MIDI_CHANNEL_PRESSURE: //AFTERTOUCH: 2 bytes only
|
|
fluid_synth_channel_pressure(m_synth, channel, event.programme());
|
|
break;
|
|
|
|
case MIDI_PITCH_BEND: //PITCH_BEND:
|
|
// a 14 bit number LSB first 0x4000 is the off positions
|
|
fluid_synth_pitch_bend(m_synth, channel, (event.data2() << 7) | event.data1());
|
|
break;
|
|
|
|
case MIDI_PB_collateRawMidiData: //used for a SYSTEM_EVENT
|
|
if (m_rawDataIndex < arraySizeAs<unsigned int>(m_savedRawBytes))
|
|
m_savedRawBytes[m_rawDataIndex++] = static_cast<unsigned char>(event.data1());
|
|
return; // Don't output any thing yet so just return
|
|
|
|
case MIDI_PB_outputRawMidiData: //used for a SYSTEM_EVENT
|
|
//for (size_t i = 0; i < m_rawDataIndex; i++)
|
|
//message.push_back( m_savedRawBytes[i]);
|
|
m_rawDataIndex = 0;
|
|
// FIXME missing
|
|
break;
|
|
}
|
|
//event.printDetails(); // useful for debugging
|
|
}
|
|
|
|
// Return the number of events waiting to be read from the midi device
|
|
int CMidiDeviceFluidSynth::checkMidiInput()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// reads the real midi event
|
|
CMidiEvent CMidiDeviceFluidSynth::readMidiInput()
|
|
{
|
|
CMidiEvent midiEvent;
|
|
return midiEvent;
|
|
}
|
|
|
|
int CMidiDeviceFluidSynth::midiSettingsSetStr(const QString &name, const QString &str)
|
|
{
|
|
if (!m_fluidSettings)
|
|
return 0;
|
|
|
|
return fluid_settings_setstr(m_fluidSettings, (char *)qPrintable(name), (char *)qPrintable(str));
|
|
}
|
|
|
|
int CMidiDeviceFluidSynth::midiSettingsSetNum(const QString &name, double val)
|
|
{
|
|
if (!m_fluidSettings)
|
|
return 0;
|
|
return fluid_settings_setnum(m_fluidSettings, (char *)qPrintable(name), val);
|
|
}
|
|
|
|
int CMidiDeviceFluidSynth::midiSettingsSetInt(const QString &name, int val)
|
|
{
|
|
if (!m_fluidSettings)
|
|
return 0;
|
|
|
|
return fluid_settings_setint(m_fluidSettings, (char *)qPrintable(name), val);
|
|
}
|
|
|
|
QString CMidiDeviceFluidSynth::midiSettingsGetStr(const QString &name)
|
|
{
|
|
Q_UNUSED(name)
|
|
//char buffer[200];
|
|
//if (!m_fluidSettings)
|
|
// return QString();
|
|
//fluid_settings_getstr(m_fluidSettings, (char *)qPrintable(name), buffer );
|
|
//return QString( buffer );
|
|
return QString(); // FIXME: buffer is never initialized here, let's just return QString() for now
|
|
}
|
|
|
|
double CMidiDeviceFluidSynth::midiSettingsGetNum(const QString &name)
|
|
{
|
|
if (!m_fluidSettings)
|
|
return 0.0;
|
|
double val;
|
|
fluid_settings_getnum(m_fluidSettings, (char *)qPrintable(name), &val);
|
|
return val;
|
|
}
|
|
|
|
int CMidiDeviceFluidSynth::midiSettingsGetInt(const QString &name)
|
|
{
|
|
if (!m_fluidSettings)
|
|
return 0;
|
|
int val = 0;
|
|
fluid_settings_getint(m_fluidSettings, (char *)qPrintable(name),&val);
|
|
return val;
|
|
}
|