Add ability to switch between multiple SoundFonts

This commit is contained in:
Dale Whinham committed 2020-11-22 23:16:44 +00:00
1 parent 0068cf648d
commit 2705788391
9 files changed
+262 -6

No files matched your search

+1
View File
@@ -16,6 +16,7 @@ OBJS := src/config.o \
src/mt32pi.o \
src/power.o \
src/rommanager.o \
src/soundfontmanager.o \
src/synth/mt32synth.o \
src/synth/soundfontsynth.o
+1
View File
@@ -39,6 +39,7 @@ CFG(rom_set, TMT32EmuROMSet, MT32EmuROMSet, TMT32EmuROMSet::MT32Old
END_SECTION
BEGIN_SECTION(fluidsynth)
CFG(soundfont, int, FluidSynthSoundFont, 0 )
CFG(polyphony, int, FluidSynthPolyphony, 256 )
END_SECTION
+45
View File
@@ -0,0 +1,45 @@
//
// soundfontmanager.h
//
// mt32-pi - A bare-metal Roland MT-32 emulator for Raspberry Pi
// Copyright (C) 2020 Dale Whinham <daleyo@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef _soundfontmanager_h
#define _soundfontmanager_h
#include <circle/string.h>
class CSoundFontManager
{
public:
CSoundFontManager();
~CSoundFontManager() = default;
bool ScanSoundFonts();
const char* GetSoundFontPath(size_t nIndex) const;
const char* GetFirstValidSoundFontPath() const;
private:
static constexpr size_t MaxSoundFonts = 256;
void CheckSoundFont(const char* pPath);
size_t m_nSoundFonts;
CString m_SoundFontPaths[MaxSoundFonts];
};
#endif
+7
View File
@@ -25,6 +25,7 @@
#include <fluidsynth.h>
#include "soundfontmanager.h"
#include "synth/synthbase.h"
class CSoundFontSynth : public CSynthBase
@@ -42,6 +43,8 @@ public:
virtual size_t Render(s16* pOutBuffer, size_t nFrames) override;
virtual size_t Render(float* pOutBuffer, size_t nFrames) override;
bool SwitchSoundFont(size_t nIndex);
private:
unsigned m_nSampleRate;
@@ -49,6 +52,10 @@ private:
fluid_synth_t* m_pSynth;
u32 m_nPolyphony;
int m_nSoundFontID;
size_t m_nCurrentSoundFontIndex;
CSoundFontManager m_SoundFontManager;
static void FluidSynthLogCallback(int nLevel, const char* pMessage, void* pUser);
};
+17
View File
@@ -170,6 +170,23 @@ rom_set = old
# -----------------------------------------------------------------------------
[fluidsynth]
# Set the initial SoundFont to use.
#
# If multiple SoundFonts are available, this option determines which SoundFont
# to use on startup.
#
# On startup, the "soundfonts" directory is scanned for valid SoundFonts, which
# are added to a list and sorted into alphabetical order.
#
# This setting is a zero-indexed offset into that list (i.e. 0 is the first,
# 1, is the second, and so on).
#
# If the index specified is unavailable, the first available SoundFont will be
# used.
#
# Values: 0-255 (0*)
soundfont = 0
# Set the maximum number of voices that can be played simultaneously.
#
# Depending on the complexity of your SoundFont, you may need to reduce this
@@ -0,0 +1,2 @@
Place your SoundFonts in this directory.
The file names or extensions don't matter; mt32-pi will scan and detect their types automatically.
+20 -2
View File
@@ -403,7 +403,7 @@ bool CMT32Pi::ParseCustomSysEx(const u8* pData, size_t nSize)
return true;
}
// Switch ROM set (F0 7D 01 xx F7)
// Switch MT-32 ROM set (F0 7D 01 xx F7)
else if (pData[2] == 0x01 && nSize == 5)
{
u8 romSet = pData[3];
@@ -414,8 +414,26 @@ bool CMT32Pi::ParseCustomSysEx(const u8* pData, size_t nSize)
return m_pMT32Synth->SwitchROMSet(static_cast<CROMManager::TROMSet>(romSet));
}
// Switch synthesizer (F0 7D 02 xx F7)
// Switch SoundFont (F0 7D 02 xx F7)
else if (pData[2] == 0x02 && nSize == 5)
{
u8 index = pData[3];
CLogger::Get()->Write(MT32PiName, LogNotice, "Switching to SoundFont %d", index);
// Wait for audio to stop
m_pSound->Cancel();
while (m_pSound->IsActive())
;
m_pSoundFontSynth->SwitchSoundFont(index);
m_pSound->Start();
// This could have involved a long period of disk I/O; update power management
Awaken();
}
// Switch synthesizer (F0 7D 03 xx F7)
else if (pData[2] == 0x03 && nSize == 5)
{
u8 synth = pData[3];
if (synth > 1)
+95
View File
@@ -0,0 +1,95 @@
//
// soundfontmanager.cpp
//
// mt32-pi - A bare-metal Roland MT-32 emulator for Raspberry Pi
// Copyright (C) 2020 Dale Whinham <daleyo@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#include <circle/logger.h>
#include <circle/util.h>
#include <fatfs/ff.h>
#include <fluidsynth.h>
#include "soundfontmanager.h"
#include "utility.h"
const char SoundFontManagerName[] = "soundfontmanager";
const char SoundFontPath[] = "soundfonts";
CSoundFontManager::CSoundFontManager()
: m_nSoundFonts(0)
{
}
bool CSoundFontManager::ScanSoundFonts()
{
// Clear existing SoundFont names
for (size_t i = 0; i < m_nSoundFonts; ++i)
m_SoundFontPaths[i] = CString();
DIR dir;
FILINFO fileInfo;
FRESULT result = f_findfirst(&dir, &fileInfo, SoundFontPath, "*");
char path[sizeof(SoundFontPath) + FF_LFN_BUF];
strcpy(path, SoundFontPath);
path[sizeof(SoundFontPath) - 1] = '/';
// Loop over each file in the directory
while (result == FR_OK && *fileInfo.fname && m_nSoundFonts < MaxSoundFonts)
{
// Ensure not directory, hidden, or system file
if (!(fileInfo.fattrib & (AM_DIR | AM_HID | AM_SYS)))
{
// Assemble path
strcpy(path + sizeof(SoundFontPath), fileInfo.fname);
CheckSoundFont(path);
}
result = f_findnext(&dir, &fileInfo);
}
CLogger::Get()->Write(SoundFontManagerName, LogNotice, "%d SoundFonts found:", m_nSoundFonts);
// Sort into alphabetical order
if (m_nSoundFonts > 0)
Utility::QSort(m_SoundFontPaths, Utility::Comparator::CaseInsensitiveAscending, 0, m_nSoundFonts - 1);
for (size_t i = 0; i < m_nSoundFonts; ++i)
CLogger::Get()->Write(SoundFontManagerName, LogNotice, "\t%s", static_cast<const char*>(m_SoundFontPaths[i]));
return m_nSoundFonts > 0;
}
const char* CSoundFontManager::GetSoundFontPath(size_t nIndex) const
{
// Return the path if in-range
return nIndex < m_nSoundFonts ? static_cast<const char*>(m_SoundFontPaths[nIndex]) : nullptr;
}
const char* CSoundFontManager::GetFirstValidSoundFontPath() const
{
return m_nSoundFonts > 0 ? static_cast<const char*>(m_SoundFontPaths[0]) : nullptr;
}
void CSoundFontManager::CheckSoundFont(const char* pPath)
{
// Try to open file
if (fluid_is_soundfont(pPath))
m_SoundFontPaths[m_nSoundFonts++] = pPath;
}
+74 -4
View File
@@ -22,6 +22,7 @@
#include <circle/logger.h>
#include <circle/timer.h>
#include "config.h"
#include "synth/soundfontsynth.h"
#include "utility.h"
@@ -112,7 +113,9 @@ CSoundFontSynth::CSoundFontSynth(unsigned nSampleRate, u32 nPolyphony)
m_pSettings(nullptr),
m_pSynth(nullptr),
m_nPolyphony(nPolyphony)
m_nPolyphony(nPolyphony),
m_nSoundFontID(0),
m_nCurrentSoundFontIndex(0)
{
}
@@ -132,6 +135,21 @@ void CSoundFontSynth::FluidSynthLogCallback(int nLevel, const char* pMessage, vo
bool CSoundFontSynth::Initialize()
{
if (!m_SoundFontManager.ScanSoundFonts())
return false;
// Try to get preferred SoundFont
m_nCurrentSoundFontIndex = CConfig::Get()->FluidSynthSoundFont;
const char* soundFontPath = m_SoundFontManager.GetSoundFontPath(m_nCurrentSoundFontIndex);
// Fall back on first available SoundFont
if (!soundFontPath)
soundFontPath = m_SoundFontManager.GetFirstValidSoundFontPath();
// Give up
if (!soundFontPath)
return false;
// Install logging handlers
fluid_set_log_function(FLUID_PANIC, FluidSynthLogCallback, this);
fluid_set_log_function(FLUID_ERR, FluidSynthLogCallback, this);
@@ -139,8 +157,6 @@ bool CSoundFontSynth::Initialize()
// fluid_set_log_function(FLUID_INFO, FluidSynthLogCallback, this);
// fluid_set_log_function(FLUID_DBG, FluidSynthLogCallback, this);
CLogger::Get()->Write(SoundFontSynthName, LogNotice, "Loading soundfont");
m_pSettings = new_fluid_settings();
if (!m_pSettings)
{
@@ -158,7 +174,8 @@ bool CSoundFontSynth::Initialize()
return false;
}
if (fluid_synth_sfload(m_pSynth, "soundfonts/SC-55.sf2", true) == FLUID_FAILED)
m_nSoundFontID = fluid_synth_sfload(m_pSynth, soundFontPath, true);
if (m_nSoundFontID == FLUID_FAILED)
{
CLogger::Get()->Write(SoundFontSynthName, LogError, "Failed to load SoundFont");
return false;
@@ -245,3 +262,56 @@ size_t CSoundFontSynth::Render(s16* pOutBuffer, size_t nFrames)
assert(fluid_synth_write_s16(m_pSynth, nFrames, pOutBuffer, 0, 2, pOutBuffer, 1, 2) == FLUID_OK);
return nFrames;
}
bool CSoundFontSynth::SwitchSoundFont(size_t nIndex)
{
// Is this SoundFont already active?
if (m_nCurrentSoundFontIndex == nIndex)
{
// if (m_pLCD)
// m_pLCD->OnSystemMessage("Already selected!");
return false;
}
// Get SoundFont if available
const char* soundFontPath = m_SoundFontManager.GetSoundFontPath(nIndex);
if (!soundFontPath)
{
// if (m_pLCD)
// m_pLCD->OnSystemMessage("SoundFont not avail!");
return false;
}
if (m_nSoundFontID >= 0 && fluid_synth_sfunload(m_pSynth, m_nSoundFontID, true) == FLUID_FAILED)
{
// if (m_pLCD)
// m_pLCD->OnSystemMessage("SF unload failed!");
return false;
}
// Invalidate SoundFont ID
m_nSoundFontID = -1;
int result = fluid_synth_sfload(m_pSynth, soundFontPath, true);
if (result == FLUID_FAILED)
{
// if (m_pLCD)
// m_pLCD->OnSystemMessage("SF load failed!");
return false;
}
if (fluid_sfont_t* soundFont = fluid_synth_get_sfont_by_id(m_pSynth, result))
{
if (const char* name = fluid_sfont_get_name(soundFont))
{
CLogger::Get()->Write(SoundFontSynthName, LogNotice, "Loaded \"%s\"", name);
// if (m_pLCD)
// m_pLCD->OnSystemMessage(name);
}
}
m_nCurrentSoundFontIndex = nIndex;
m_nSoundFontID = result;
return true;
}