mirror of
https://github.com/dwhinham/mt32-pi.git
synced 2026-10-26 22:11:05 +00:00
Move SoundFont effects profiles to separate files
Deprecate the [fluidsynth.soundfont.x] sections in mt32-pi.cfg and search for a .cfg file matching the SoundFont file name instead.
This commit is contained in:
1 parent
fa249be971
commit
7d30442bd5
9 files changed
+132
-94
No files matched your search
@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Update to libmt32emu v2.6.3.
|
||||
- Update to FluidSynth v2.2.7.
|
||||
- The `i2c_dac_address` and `i2c_dac_init` configuration options have been deprecated and have no longer have any effect. DACs requiring initialization are now automatically detected.
|
||||
- The `[fluidsynth.soundfont.x]` sections have now been deprecated. SoundFont effects profiles must now be stored in separate .cfg files, with the same file name as the SoundFont (minus extension). This means that file index no longer influences SoundFont settings. See `soundfonts/GeneralUser GS v1.511.cfg` for an example.
|
||||
- The maximum number of SoundFonts has been increased to 512.
|
||||
|
||||
## [0.11.3] - 2022-04-13
|
||||
|
||||
+2
-9
@@ -28,8 +28,6 @@
|
||||
|
||||
#include "control/rotaryencoder.h"
|
||||
#include "lcd/drivers/ssd1306.h"
|
||||
#include "soundfontmanager.h"
|
||||
#include "synth/fxprofile.h"
|
||||
#include "synth/mt32romset.h"
|
||||
#include "synth/mt32synth.h"
|
||||
#include "utility.h"
|
||||
@@ -87,11 +85,6 @@ public:
|
||||
#define CFG(_1, TYPE, MEMBER_NAME, _2, _3...) TYPE MEMBER_NAME;
|
||||
#include "config.def"
|
||||
|
||||
TFXProfile FXProfiles[CSoundFontManager::MaxSoundFonts];
|
||||
|
||||
private:
|
||||
static int INIHandler(void* pUser, const char* pSection, const char* pName, const char* pValue);
|
||||
|
||||
// Overloaded function to parse config options based on their types specified in the definition file
|
||||
static bool ParseOption(const char* pString, bool* pOut);
|
||||
static bool ParseOption(const char* pString, int* pOut, bool bHex = false);
|
||||
@@ -110,8 +103,8 @@ private:
|
||||
static bool ParseOption(const char* pString, TLCDMirror* pOut);
|
||||
static bool ParseOption(const char* pString, TNetworkMode* pOut);
|
||||
|
||||
static bool ParseFXProfileSection(const char* pSection, size_t* pOutFXProfileIndex);
|
||||
static bool ParseFXProfileOption(const char* pString, const char* pValue, TFXProfile* pOutFXProfile);
|
||||
private:
|
||||
static int INIHandler(void* pUser, const char* pSection, const char* pName, const char* pValue);
|
||||
|
||||
static CConfig* s_pThis;
|
||||
};
|
||||
|
||||
+16
-4
@@ -41,11 +41,16 @@ public:
|
||||
operator=(Other);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
TOptional(U&& Value)
|
||||
explicit TOptional(T&& Value)
|
||||
{
|
||||
m_bSet = true;
|
||||
new(reinterpret_cast<T*>(m_Value)) T(static_cast<U&&>(Value));
|
||||
new(reinterpret_cast<T*>(m_Value)) T(static_cast<T&&>(Value));
|
||||
}
|
||||
|
||||
explicit TOptional(TOptional<T>&& Other)
|
||||
{
|
||||
m_bSet = Other.m_bSet;
|
||||
new(reinterpret_cast<T*>(m_Value)) T(*reinterpret_cast<const T*>(Other.m_Value));
|
||||
}
|
||||
|
||||
~TOptional() { Reset(); }
|
||||
@@ -77,11 +82,18 @@ public:
|
||||
|
||||
TOptional<T>& operator =(const TOptional<T>& Other)
|
||||
{
|
||||
m_bSet = true;
|
||||
m_bSet = Other.m_bSet;
|
||||
*reinterpret_cast<T*>(m_Value) = *reinterpret_cast<const T*>(Other.m_Value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
TOptional<T>& operator =(const T& Value)
|
||||
{
|
||||
m_bSet = true;
|
||||
*reinterpret_cast<T*>(m_Value) = Value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr const T& operator *() const { return Value(); }
|
||||
constexpr const T* operator ->() const { return &Value(); }
|
||||
constexpr const T* operator &() const { return &Value(); }
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
#include <circle/string.h>
|
||||
|
||||
#include "synth/fxprofile.h"
|
||||
|
||||
class CSoundFontManager
|
||||
{
|
||||
public:
|
||||
@@ -35,6 +37,7 @@ public:
|
||||
size_t GetSoundFontCount() const { return m_nSoundFonts; }
|
||||
const char* GetSoundFontPath(size_t nIndex) const;
|
||||
const char* GetSoundFontName(size_t nIndex) const;
|
||||
TFXProfile GetSoundFontFXProfile(size_t nIndex) const;
|
||||
const char* GetFirstValidSoundFontPath() const;
|
||||
|
||||
static constexpr size_t MaxSoundFonts = 512;
|
||||
@@ -53,6 +56,7 @@ private:
|
||||
size_t m_nSoundFonts;
|
||||
TSoundFontListEntry m_SoundFontList[MaxSoundFonts];
|
||||
|
||||
static int INIHandler(void* pUser, const char* pSection, const char* pName, const char* pValue);
|
||||
inline static bool SoundFontListComparator(const TSoundFontListEntry& lhs, const TSoundFontListEntry& rhs);
|
||||
};
|
||||
|
||||
|
||||
+4
-23
@@ -303,9 +303,10 @@ polyphony = 200
|
||||
# The following settings set the default parameters for FluidSynth's master
|
||||
# volume gain, reverb and chorus effects.
|
||||
#
|
||||
# Each setting can be overridden on a per-SoundFont basis by adding extra
|
||||
# sections, e.g. [fluidsynth.soundfont.x], where x is the zero-based index of
|
||||
# the SoundFont. See the next section for an example.
|
||||
# Each setting can be overridden on a per-SoundFont basis by creating a .cfg
|
||||
# file adjacent to the SoundFont file with the same name. For example,
|
||||
# when "foo.sf2" is loaded, mt32-pi will look for a file called "foo.cfg" and
|
||||
# apply any effects settings found inside it.
|
||||
#
|
||||
# Full descriptions and valid value ranges for each setting can be found in the
|
||||
# FluidSynth documentation: https://www.fluidsynth.org/api/fluidsettings.xml
|
||||
@@ -323,26 +324,6 @@ chorus_level = 2.0
|
||||
chorus_voices = 3
|
||||
chorus_speed = 0.3
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# FluidSynth effects profile for SoundFont 0 (GeneralUser GS)
|
||||
# -----------------------------------------------------------------------------
|
||||
[fluidsynth.soundfont.0]
|
||||
|
||||
# The following settings are recommended for GeneralUser GS by its author.
|
||||
gain = 0.2
|
||||
|
||||
reverb = on
|
||||
reverb_damping = 0.19
|
||||
reverb_level = 0.5
|
||||
reverb_room_size = 0.7
|
||||
reverb_width = 0.75
|
||||
|
||||
chorus = on
|
||||
chorus_depth = 4.5
|
||||
chorus_level = 0.5
|
||||
chorus_voices = 3
|
||||
chorus_speed = 0.35
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# LCD/OLED display options
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# The following settings are recommended for GeneralUser GS by its author.
|
||||
gain = 0.2
|
||||
|
||||
reverb = on
|
||||
reverb_damping = 0.19
|
||||
reverb_level = 0.5
|
||||
reverb_room_size = 0.7
|
||||
reverb_width = 0.75
|
||||
|
||||
chorus = on
|
||||
chorus_depth = 4.5
|
||||
chorus_level = 0.5
|
||||
chorus_voices = 3
|
||||
chorus_speed = 0.35
|
||||
@@ -113,60 +113,9 @@ bool CConfig::Initialize(const char* pPath)
|
||||
|
||||
}
|
||||
|
||||
bool CConfig::ParseFXProfileSection(const char* pSection, size_t* pOutFXProfileIndex)
|
||||
{
|
||||
constexpr char SectionPrefix[] = "fluidsynth.soundfont.";
|
||||
if (!strstr(pSection, SectionPrefix))
|
||||
return false;
|
||||
|
||||
const char* const pProfileName = &pSection[sizeof(SectionPrefix) - 1];
|
||||
char* pEnd;
|
||||
|
||||
size_t nIndex = strtoull(pProfileName, &pEnd, 10);
|
||||
if (pEnd == pProfileName || *pEnd != '\0' || nIndex >= CSoundFontManager::MaxSoundFonts)
|
||||
return false;
|
||||
|
||||
*pOutFXProfileIndex = nIndex;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CConfig::ParseFXProfileOption(const char* pString, const char* pValue, TFXProfile* pOutFXProfile)
|
||||
{
|
||||
#define MATCH(NAME, TYPE, STRUCT_MEMBER) \
|
||||
if (!strcmp(NAME, pString)) \
|
||||
{ \
|
||||
auto Temp = decltype(*pOutFXProfile->STRUCT_MEMBER){}; \
|
||||
if (ParseOption(pValue, &Temp)) \
|
||||
{ \
|
||||
pOutFXProfile->STRUCT_MEMBER = Temp; \
|
||||
return true; \
|
||||
} \
|
||||
return false; \
|
||||
}
|
||||
|
||||
MATCH("gain", float, nGain);
|
||||
|
||||
MATCH("reverb", bool, bReverbActive);
|
||||
MATCH("reverb_damping", float, nReverbDamping);
|
||||
MATCH("reverb_level", float, nReverbLevel);
|
||||
MATCH("reverb_room_size", float, nReverbRoomSize);
|
||||
MATCH("reverb_width", float, nReverbWidth);
|
||||
|
||||
MATCH("chorus", bool, bChorusActive);
|
||||
MATCH("chorus_depth", float, nChorusDepth);
|
||||
MATCH("chorus_level", float, nChorusLevel);
|
||||
MATCH("chorus_voices", int, nChorusVoices);
|
||||
MATCH("chorus_speed", float, nChorusSpeed);
|
||||
|
||||
#undef MATCH
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int CConfig::INIHandler(void* pUser, const char* pSection, const char* pName, const char* pValue)
|
||||
{
|
||||
CConfig* const pConfig = static_cast<CConfig*>(pUser);
|
||||
size_t nFXProfileIndex;
|
||||
|
||||
//CLogger::Get()->Write(ConfigName, LogDebug, "'%s', '%s', '%s'", pSection, pName, pValue);
|
||||
|
||||
@@ -185,10 +134,6 @@ int CConfig::INIHandler(void* pUser, const char* pSection, const char* pName, co
|
||||
|
||||
#include "config.def"
|
||||
|
||||
// Special handling for SoundFont effects profiles
|
||||
if (ParseFXProfileSection(pSection, &nFXProfileIndex))
|
||||
return ParseFXProfileOption(pName, pValue, &pConfig->FXProfiles[nFXProfileIndex]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
#include <circle/util.h>
|
||||
#include <fatfs/ff.h>
|
||||
|
||||
#include <ini.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "soundfontmanager.h"
|
||||
#include "utility.h"
|
||||
|
||||
@@ -126,6 +129,53 @@ const char* CSoundFontManager::GetSoundFontName(size_t nIndex) const
|
||||
return static_cast<const char*>(m_SoundFontList[nIndex].Name);
|
||||
}
|
||||
|
||||
TFXProfile CSoundFontManager::GetSoundFontFXProfile(size_t nIndex) const
|
||||
{
|
||||
TFXProfile FXProfile;
|
||||
|
||||
const char* const pSoundFontPath = GetSoundFontPath(nIndex);
|
||||
if (!pSoundFontPath)
|
||||
return FXProfile;
|
||||
|
||||
// +5 bytes in case we need to add an extension (4 chars + null terminator)
|
||||
const size_t nPathLength = strlen(pSoundFontPath) + 5;
|
||||
char PathBuffer[nPathLength];
|
||||
strcpy(PathBuffer, pSoundFontPath);
|
||||
|
||||
// Replace file extension if present
|
||||
char* const pExtension = strrchr(PathBuffer, '.');
|
||||
if (pExtension)
|
||||
strcpy(pExtension, ".cfg");
|
||||
else
|
||||
strcat(PathBuffer, ".cfg");
|
||||
|
||||
FIL File;
|
||||
if (f_open(&File, PathBuffer, FA_READ) != FR_OK)
|
||||
return FXProfile;
|
||||
|
||||
// +1 byte for null terminator
|
||||
const UINT nSize = f_size(&File);
|
||||
char Buffer[nSize + 1];
|
||||
UINT nRead;
|
||||
|
||||
if (f_read(&File, Buffer, nSize, &nRead) != FR_OK)
|
||||
{
|
||||
CLogger::Get()->Write(SoundFontManagerName, LogError, "Error reading effects profile");
|
||||
f_close(&File);
|
||||
return FXProfile;
|
||||
}
|
||||
|
||||
// Ensure null-terminated
|
||||
Buffer[nRead] = '\0';
|
||||
|
||||
const int nResult = ini_parse_string(Buffer, INIHandler, &FXProfile);
|
||||
if (nResult > 0)
|
||||
CLogger::Get()->Write(SoundFontManagerName, LogWarning, "Effects profile parse error on line %d", nResult);
|
||||
|
||||
f_close(&File);
|
||||
return FXProfile;
|
||||
}
|
||||
|
||||
const char* CSoundFontManager::GetFirstValidSoundFontPath() const
|
||||
{
|
||||
return m_nSoundFonts > 0 ? static_cast<const char*>(m_SoundFontList[0].Path) : nullptr;
|
||||
@@ -210,3 +260,37 @@ inline bool CSoundFontManager::SoundFontListComparator(const TSoundFontListEntry
|
||||
{
|
||||
return strcasecmp(EntryA.Path, EntryB.Path) < 0;
|
||||
}
|
||||
|
||||
int CSoundFontManager::INIHandler(void* pUser, const char* pSection, const char* pName, const char* pValue)
|
||||
{
|
||||
TFXProfile* const pFXProfile = static_cast<TFXProfile*>(pUser);
|
||||
|
||||
#define MATCH(NAME, TYPE, STRUCT_MEMBER) \
|
||||
if (!strcmp(NAME, pName)) \
|
||||
{ \
|
||||
auto Temp = decltype(*pFXProfile->STRUCT_MEMBER){}; \
|
||||
if (CConfig::ParseOption(pValue, &Temp)) \
|
||||
{ \
|
||||
pFXProfile->STRUCT_MEMBER = Temp; \
|
||||
return 1; \
|
||||
} \
|
||||
return 0; \
|
||||
}
|
||||
|
||||
MATCH("gain", float, nGain);
|
||||
|
||||
MATCH("reverb", bool, bReverbActive);
|
||||
MATCH("reverb_damping", float, nReverbDamping);
|
||||
MATCH("reverb_level", float, nReverbLevel);
|
||||
MATCH("reverb_room_size", float, nReverbRoomSize);
|
||||
MATCH("reverb_width", float, nReverbWidth);
|
||||
|
||||
MATCH("chorus", bool, bChorusActive);
|
||||
MATCH("chorus_depth", float, nChorusDepth);
|
||||
MATCH("chorus_level", float, nChorusLevel);
|
||||
MATCH("chorus_voices", int, nChorusVoices);
|
||||
MATCH("chorus_speed", float, nChorusSpeed);
|
||||
|
||||
#undef MATCH
|
||||
return 0;
|
||||
}
|
||||
@@ -179,6 +179,8 @@ bool CSoundFontSynth::Initialize()
|
||||
if (!pSoundFontPath)
|
||||
return false;
|
||||
|
||||
TFXProfile FXProfile = m_SoundFontManager.GetSoundFontFXProfile(m_nCurrentSoundFontIndex);
|
||||
|
||||
// Install logging handlers
|
||||
fluid_set_log_function(FLUID_PANIC, FluidSynthLogCallback, this);
|
||||
fluid_set_log_function(FLUID_ERR, FluidSynthLogCallback, this);
|
||||
@@ -198,7 +200,7 @@ bool CSoundFontSynth::Initialize()
|
||||
fluid_settings_setnum(m_pSettings, "synth.sample-rate", static_cast<double>(m_nSampleRate));
|
||||
fluid_settings_setint(m_pSettings, "synth.threadsafe-api", false);
|
||||
|
||||
return Reinitialize(pSoundFontPath, &pConfig->FXProfiles[m_nCurrentSoundFontIndex]);
|
||||
return Reinitialize(pSoundFontPath, &FXProfile);
|
||||
}
|
||||
|
||||
void CSoundFontSynth::HandleMIDIShortMessage(u32 nMessage)
|
||||
@@ -355,8 +357,10 @@ bool CSoundFontSynth::SwitchSoundFont(size_t nIndex)
|
||||
if (m_pUI)
|
||||
m_pUI->ShowSystemMessage("Loading SoundFont", true);
|
||||
|
||||
TFXProfile FXProfile = m_SoundFontManager.GetSoundFontFXProfile(nIndex);
|
||||
|
||||
// We can't use fluid_synth_sfunload() as we don't support the lazy SoundFont unload timer, so trash the entire synth and create a new one
|
||||
if (!Reinitialize(pSoundFontPath, &CConfig::Get()->FXProfiles[nIndex]))
|
||||
if (!Reinitialize(pSoundFontPath, &FXProfile))
|
||||
{
|
||||
if (m_pUI)
|
||||
m_pUI->ShowSystemMessage("SF switch failed!");
|
||||
@@ -404,7 +408,7 @@ bool CSoundFontSynth::Reinitialize(const char* pSoundFontPath, const TFXProfile*
|
||||
fluid_synth_set_reverb_group_roomsize(m_pSynth, -1, pFXProfile->nReverbRoomSize.ValueOr(pConfig->FluidSynthDefaultReverbRoomSize));
|
||||
fluid_synth_set_reverb_group_width(m_pSynth, -1, pFXProfile->nReverbWidth.ValueOr(pConfig->FluidSynthDefaultReverbWidth));
|
||||
|
||||
fluid_synth_chorus_on(m_pSynth, -1, pFXProfile->bChorusActive.ValueOr(pConfig->FluidSynthDefaultChorusActive));
|
||||
fluid_synth_chorus_on(m_pSynth, -1, pFXProfile->bChorusActive.ValueOr(pConfig->FluidSynthDefaultChorusActive));
|
||||
fluid_synth_set_chorus_group_depth(m_pSynth, -1, pFXProfile->nChorusDepth.ValueOr(pConfig->FluidSynthDefaultChorusDepth));
|
||||
fluid_synth_set_chorus_group_level(m_pSynth, -1, pFXProfile->nChorusLevel.ValueOr(pConfig->FluidSynthDefaultChorusLevel));
|
||||
fluid_synth_set_chorus_group_nr(m_pSynth, -1, pFXProfile->nChorusVoices.ValueOr(pConfig->FluidSynthDefaultChorusVoices));
|
||||
|
||||
Reference in new issue
Block a user