mirror of
https://github.com/dwhinham/mt32-pi.git
synced 2026-10-27 06:21:59 +00:00
Add ability to switch between multiple SoundFonts
This commit is contained in:
1 parent
0068cf648d
commit
2705788391
9 files changed
+262
-6
No files matched your search
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in new issue
Block a user