Add Fluidsynth backend and stop using SDL Mixer X for that.
Some checks failed
Build / build-gentoo (push) Failing after 2m4s
Build / download-system-deps (push) Successful in 4m1s
Build / get-source-code (push) Successful in 12m47s
Build / build-appimage (push) Successful in 4m11s
Build / build-android (push) Failing after 3m1s
Build / build-windows (push) Failing after 7m25s
Some checks failed
Build / build-gentoo (push) Failing after 2m4s
Build / download-system-deps (push) Successful in 4m1s
Build / get-source-code (push) Successful in 12m47s
Build / build-appimage (push) Successful in 4m11s
Build / build-android (push) Failing after 3m1s
Build / build-windows (push) Failing after 7m25s
This commit is contained in:
parent
ee94b97d19
commit
b886e6a585
8 changed files with 306 additions and 22 deletions
|
@ -17,14 +17,14 @@ set(SDL_MIXER_X_SHARED OFF CACHE BOOL "")
|
|||
set(DOWNLOAD_AUDIO_CODECS_DEPENDENCY OFF CACHE BOOL "")
|
||||
set(AUDIO_CODECS_BUILD_LOCAL_SDL2 OFF CACHE BOOL "" FORCE)
|
||||
set(MIXERX_LGPL ON CACHE BOOL "" FORCE)
|
||||
set(USE_MIDI ON CACHE BOOL "" FORCE)
|
||||
set(USE_MIDI OFF CACHE BOOL "" FORCE)
|
||||
set(USE_MIDI_NATIVE_ALT OFF CACHE BOOL "" FORCE)
|
||||
set(USE_MIDI_NATIVE OFF CACHE BOOL "" FORCE)
|
||||
set(USE_MIDI_TIMIDITY OFF CACHE BOOL "" FORCE)
|
||||
set(USE_MIDI_FLUIDLITE OFF CACHE BOOL "" FORCE)
|
||||
set(USE_MIDI_OPNMIDI OFF CACHE BOOL "" FORCE)
|
||||
set(USE_MIDI_ADLMIDI OFF CACHE BOOL "" FORCE)
|
||||
set(USE_MIDI_FLUIDSYNTH ON CACHE BOOL "" FORCE)
|
||||
set(USE_MIDI_FLUIDSYNTH OFF CACHE BOOL "" FORCE)
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(BUILD_FB2K OFF CACHE BOOL "" FORCE)
|
||||
|
@ -524,6 +524,7 @@ endif()
|
|||
playback_backend_subdir(NAME "VGMSTREAM" READABLE_NAME "VgmStream" SUBDIR backends/playback/vgmstream)
|
||||
playback_backend_subdir(NAME "SDL_MIXER_X" READABLE_NAME "SDL Mixer X" SUBDIR backends/playback/sdl_mixer_x)
|
||||
playback_backend_subdir(NAME "ZSM" READABLE_NAME "ZSM" SUBDIR backends/playback/zsm)
|
||||
playback_backend_subdir(NAME "FLUIDSYNTH" READABLE_NAME "Fluidsynth" SUBDIR backends/playback/fluidsynth)
|
||||
execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/gen_ui_backend_inc.py ${CMAKE_CURRENT_BINARY_DIR} --ui ${ENABLED_UIS} --playback ${ENABLED_PLAYBACK_BACKENDS})
|
||||
prefix_all(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/ main.cpp daemon_backend.cpp daemon_backend.hpp proxy_backend.cpp proxy_backend.hpp)
|
||||
list(APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/backend_glue.cpp)
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
#include <string.h>
|
||||
#include <file_backend.hpp>
|
||||
#include <util.hpp>
|
||||
#include <filesystem>
|
||||
#include <limits.h>
|
||||
namespace fs = std::filesystem;
|
||||
void FluidSynthBackend::fluidsynth_get_property_list_wrapper(void *udata, const char *name, int type) {
|
||||
((FluidSynthBackend*)udata)->fluidsynth_get_property_list(name, type);
|
||||
}
|
||||
|
@ -16,15 +19,31 @@ void FluidSynthBackend::fluidsynth_get_property_list(const char *name, int type)
|
|||
property.set_path(fmt::format("fluidsynth/{0}", name));
|
||||
property.set_name(name);
|
||||
switch (type) {
|
||||
case FLUID_NO_TYPE:
|
||||
case FLUID_NUM_TYPE: {
|
||||
property.set_type(PropertyType::Double);
|
||||
double min, max;
|
||||
if (fluid_settings_getnum_range(settings, name, &min, &max) == FLUID_OK) {
|
||||
auto *range = property.mutable_hint()->mutable_range();
|
||||
range->set_min(min);
|
||||
range->set_max(max);
|
||||
}
|
||||
} break;
|
||||
case FLUID_INT_TYPE: {
|
||||
property.set_type(PropertyType::Int);
|
||||
int min, max;
|
||||
if (fluid_settings_getint_range(settings, name, &min, &max) == FLUID_OK) {
|
||||
auto *range = property.mutable_hint()->mutable_range();
|
||||
range->set_min(min);
|
||||
range->set_max(max);
|
||||
}
|
||||
} break;
|
||||
case FLUID_STR_TYPE: {
|
||||
property.set_type(PropertyType::String);
|
||||
} break;
|
||||
default: {
|
||||
throw std::exception();
|
||||
} break;
|
||||
}
|
||||
property.set_id(PropertyId::BackendSpecific);
|
||||
fluidsynth_properties.push_back(property);
|
||||
|
@ -38,31 +57,126 @@ void FluidSynthBackend::load(const char *filename) {
|
|||
spec.format = AUDIO_F32SYS;
|
||||
spec.samples = 100;
|
||||
spec.channels = 2;
|
||||
spec.freq = 48000;
|
||||
spec.freq = 96000;
|
||||
spec.size = 100 * 2 * sizeof(int16_t);
|
||||
file = open_file(filename);
|
||||
this->settings = new_fluid_settings();
|
||||
File *file = open_file(filename);
|
||||
settings = new_fluid_settings();
|
||||
fluid_settings_setnum(settings, "synth.sample-rate", 96000.0);
|
||||
fluid_settings_foreach(settings, (void*)this, &fluidsynth_get_property_list_wrapper);
|
||||
synth = new_fluid_synth(settings);
|
||||
player = new_fluid_player(synth);
|
||||
fs::path fpath(filename);
|
||||
fpath = fpath.parent_path() / fpath.stem();
|
||||
|
||||
std::string fpath_str = fpath.string();
|
||||
std::vector<std::string> try_paths = std::vector<std::string>({
|
||||
fpath_str + ".sf2", fpath_str + ".dls"
|
||||
});
|
||||
bool found = false;
|
||||
for (auto &path : try_paths) {
|
||||
const char *path_c = path.c_str();
|
||||
if (fluid_is_soundfont(path_c)) {
|
||||
fluid_synth_sfload(synth, path_c, 1);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
WARNING.writeln("Could not find a valid companion file to use as a sound font. Make sure the extension is all-lowercase, if you're on a case sensitive filesystem (Such as on Linux).");
|
||||
throw std::exception();
|
||||
}
|
||||
file_data = malloc(file->get_len());
|
||||
file_len = file->read(file_data, 1, file->get_len());
|
||||
delete file;
|
||||
file_data = realloc(file_data, file_len);
|
||||
fluid_player_add_mem(player, file_data, file_len);
|
||||
}
|
||||
extern SDL_AudioSpec obtained;
|
||||
void FluidSynthBackend::switch_stream(int idx) {
|
||||
fluid_player_play(player);
|
||||
}
|
||||
void FluidSynthBackend::cleanup() {
|
||||
delete file;
|
||||
file = nullptr;
|
||||
delete_fluid_player(player);
|
||||
player = nullptr;
|
||||
delete_fluid_synth(synth);
|
||||
synth = nullptr;
|
||||
delete_fluid_settings(settings);
|
||||
settings = nullptr;
|
||||
file_len = 0;
|
||||
free(file_data);
|
||||
file_data = nullptr;
|
||||
}
|
||||
size_t FluidSynthBackend::render(void *buf, size_t maxlen) {
|
||||
size_t sample_type_len = 2;
|
||||
maxlen /= sample_type_len;
|
||||
maxlen *= sample_type_len;
|
||||
return copied;
|
||||
size_t sample_type_len = sizeof(float);
|
||||
maxlen /= sample_type_len * 2;
|
||||
maxlen *= sample_type_len * 2;
|
||||
if (fluid_synth_write_float(synth, maxlen / 2 / sample_type_len, buf, 0, 2, buf, 1, 2) == FLUID_FAILED) {
|
||||
return 0;
|
||||
}
|
||||
return maxlen;
|
||||
}
|
||||
bool FluidSynthBackend::is_fluidsynth_setting(std::string path) {
|
||||
const char *prefix = "fluidsynth/";
|
||||
if (path.length() < strlen(prefix)) return false;
|
||||
return path.substr(0, strlen(prefix)) == std::string(prefix);
|
||||
}
|
||||
void FluidSynthBackend::seek(double position) {
|
||||
|
||||
fluid_player_seek(player, position);
|
||||
}
|
||||
double FluidSynthBackend::get_position() {
|
||||
return position;
|
||||
return fluid_player_get_current_tick(player);
|
||||
}
|
||||
int FluidSynthBackend::get_stream_idx() {
|
||||
return 0;
|
||||
}
|
||||
void FluidSynthBackend::set_fluidsynth_property_str(std::string path, std::string val) {
|
||||
fluid_settings_setstr(settings, path.c_str(), val.c_str());
|
||||
}
|
||||
void FluidSynthBackend::set_fluidsynth_property_num(std::string path, double val) {
|
||||
fluid_settings_setnum(settings, path.c_str(), val);
|
||||
}
|
||||
void FluidSynthBackend::set_fluidsynth_property_int(std::string path, int val) {
|
||||
fluid_settings_setint(settings, path.c_str(), val);
|
||||
}
|
||||
std::optional<std::string> FluidSynthBackend::get_fluidsynth_property_str(std::string path) {
|
||||
char *tmp;
|
||||
if (fluid_settings_dupstr(settings, path.c_str(), &tmp) == FLUID_OK) {
|
||||
std::string output = tmp;
|
||||
free((void*)tmp);
|
||||
return output;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
std::optional<double> FluidSynthBackend::get_fluidsynth_property_num(std::string path) {
|
||||
double output = NAN;
|
||||
if (fluid_settings_getnum(settings, path.c_str(), &output) == FLUID_OK) return output;
|
||||
else return {};
|
||||
}
|
||||
std::optional<int> FluidSynthBackend::get_fluidsynth_property_int(std::string path) {
|
||||
int output = 0;
|
||||
if (fluid_settings_getint(settings, path.c_str(), &output) == FLUID_OK) return output;
|
||||
else return {};
|
||||
}
|
||||
void FluidSynthBackend::reset_fluidsynth_property(std::string path) {
|
||||
switch (fluid_settings_get_type(settings, path.c_str())) {
|
||||
case FLUID_INT_TYPE: {
|
||||
int output;
|
||||
if (fluid_settings_getint_default(settings, path.c_str(), &output) == FLUID_OK) {
|
||||
fluid_settings_setint(settings, path.c_str(), output);
|
||||
}
|
||||
} break;
|
||||
case FLUID_STR_TYPE: {
|
||||
char *val;
|
||||
if (fluid_settings_getstr_default(settings, path.c_str(), &val) == FLUID_OK) {
|
||||
fluid_settings_setstr(settings, path.c_str(), val);
|
||||
}
|
||||
} break;
|
||||
case FLUID_NUM_TYPE: {
|
||||
double val;
|
||||
if (fluid_settings_getnum_default(settings, path.c_str(), &val) == FLUID_OK) {
|
||||
fluid_settings_setnum(settings, path.c_str(), val);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
|
@ -13,18 +13,111 @@
|
|||
#include "file_backend.hpp"
|
||||
#include <fluidsynth.h>
|
||||
class FluidSynthBackend : public PlaybackBackend {
|
||||
File *file;
|
||||
void *file_data;
|
||||
size_t file_len;
|
||||
static void fluidsynth_get_property_list_wrapper(void *udata, const char *name, int type);
|
||||
std::vector<Property> fluidsynth_properties;
|
||||
fluid_settings_t *settings;
|
||||
fluid_synth_t *synth;
|
||||
fluid_player_t *player;
|
||||
void fluidsynth_get_property_list(const char *name, int type);
|
||||
public:
|
||||
void set_fluidsynth_property_str(std::string path, std::string val);
|
||||
void set_fluidsynth_property_num(std::string path, double val);
|
||||
void set_fluidsynth_property_int(std::string path, int val);
|
||||
std::string get_fluidsynth_property_str(std::string path);
|
||||
double get_fluidsynth_property_num(std::string path);
|
||||
int get_fluidsynth_property_int(std::string path);
|
||||
std::optional<std::string> get_fluidsynth_property_str(std::string path);
|
||||
std::optional<double> get_fluidsynth_property_num(std::string path);
|
||||
std::optional<int> get_fluidsynth_property_int(std::string path);
|
||||
void reset_fluidsynth_property(std::string path);
|
||||
bool is_fluidsynth_setting(std::string path);
|
||||
inline std::optional<std::string> get_property_string(google::protobuf::Any value) {
|
||||
try {
|
||||
StringProperty * property = resolve_any<StringProperty>(value);
|
||||
std::string output = property->value();
|
||||
delete property;
|
||||
return output;
|
||||
} catch (std::exception e) {
|
||||
}
|
||||
return {};
|
||||
}
|
||||
inline std::optional<double> get_property_double(google::protobuf::Any value) {
|
||||
try {
|
||||
DoubleProperty *property = resolve_any<DoubleProperty>(value);
|
||||
double output = property->value();
|
||||
delete property;
|
||||
return output;
|
||||
} catch (std::exception e) {
|
||||
}
|
||||
return {};
|
||||
}
|
||||
inline std::optional<int> get_property_int(google::protobuf::Any value) {
|
||||
try {
|
||||
IntProperty *property = resolve_any<IntProperty>(value);
|
||||
int output = property->value();
|
||||
delete property;
|
||||
return output;
|
||||
} catch (std::exception e) {
|
||||
}
|
||||
return {};
|
||||
}
|
||||
inline bool set(std::string path, google::protobuf::Any value) override {
|
||||
if (!is_fluidsynth_setting(path)) {
|
||||
return PlaybackBackend::set(path, value);
|
||||
}
|
||||
std::string fluidsynth_path = path.substr(strlen("fluidsynth/"));
|
||||
auto maybe_num = get_property_double(value);
|
||||
if (maybe_num.has_value()) {
|
||||
set_fluidsynth_property_num(fluidsynth_path, maybe_num.value());
|
||||
return true;
|
||||
}
|
||||
auto maybe_int = get_property_int(value);
|
||||
if (maybe_int.has_value()) {
|
||||
set_fluidsynth_property_int(fluidsynth_path, maybe_int.value());
|
||||
return true;
|
||||
}
|
||||
auto maybe_string = get_property_string(value);
|
||||
if (maybe_string.has_value()) {
|
||||
set_fluidsynth_property_str(fluidsynth_path, maybe_string.value());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
inline std::optional<google::protobuf::Any> get(std::string path) override {
|
||||
if (!is_fluidsynth_setting(path)) return PlaybackBackend::get(path);
|
||||
std::string fluidsynth_path = path.substr(strlen("fluidsynth/"));
|
||||
google::protobuf::Any output;
|
||||
switch (fluid_settings_get_type(settings, fluidsynth_path.c_str())) {
|
||||
case FLUID_INT_TYPE: {
|
||||
IntProperty prop;
|
||||
auto val = get_fluidsynth_property_int(fluidsynth_path);
|
||||
if (!val.has_value()) return {};
|
||||
prop.set_value(val.value());
|
||||
output.PackFrom(prop);
|
||||
} break;
|
||||
case FLUID_STR_TYPE: {
|
||||
StringProperty prop;
|
||||
auto val = get_fluidsynth_property_str(fluidsynth_path);
|
||||
if (!val.has_value()) return {};
|
||||
prop.set_value(val.value());
|
||||
output.PackFrom(prop);
|
||||
} break;
|
||||
case FLUID_NUM_TYPE: {
|
||||
DoubleProperty prop;
|
||||
auto val = get_fluidsynth_property_num(fluidsynth_path);
|
||||
if (!val.has_value()) return {};
|
||||
prop.set_value(val.value());
|
||||
output.PackFrom(prop);
|
||||
} break;
|
||||
default: return {};
|
||||
}
|
||||
return output;
|
||||
}
|
||||
inline std::optional<google::protobuf::Any> reset(std::string path) override {
|
||||
if (!is_fluidsynth_setting(path)) return PlaybackBackend::reset(path);
|
||||
std::string fluidsynth_path = path.substr(strlen("fluidsynth/"));
|
||||
reset_fluidsynth_property(fluidsynth_path);
|
||||
return get(path);
|
||||
}
|
||||
inline std::string get_id() override {
|
||||
return "fluidsynth";
|
||||
}
|
||||
|
@ -40,7 +133,7 @@ class FluidSynthBackend : public PlaybackBackend {
|
|||
size_t render(void *buf, size_t maxlen) override;
|
||||
double get_position() override;
|
||||
inline double get_length() override {
|
||||
return length;
|
||||
return 0.0;
|
||||
}
|
||||
inline ~FluidSynthBackend() override { }
|
||||
};
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "imgui/imgui.h"
|
||||
#include "ui_backend.hpp"
|
||||
#include "thirdparty/CLI11.hpp"
|
||||
#include "imgui/misc/cpp/imgui_stdlib.h"
|
||||
#include <web_functions.hpp>
|
||||
using namespace Looper::Options;
|
||||
void MainLoop::Init() {
|
||||
|
@ -312,6 +313,58 @@ void MainLoop::GuiFunction() {
|
|||
ImGui::PushID(property.path().c_str());
|
||||
bool valid = false;
|
||||
switch (property.type()) {
|
||||
case PropertyType::String: {
|
||||
std::string value = "";
|
||||
if (string_properties.contains(property.path())) {
|
||||
value = string_properties[property.path()];
|
||||
} else {
|
||||
auto value_to_resolve = playback->get_property(property.path());
|
||||
if (value_to_resolve.has_value()) {
|
||||
value = resolve_value<std::string>(value_to_resolve.value());
|
||||
}
|
||||
string_properties[property.path()] = value;
|
||||
}
|
||||
ImGui::InputText(property.path().c_str(), &value);
|
||||
string_properties[property.path()] = value;
|
||||
valid = true;
|
||||
} break;
|
||||
case PropertyType::Int: {
|
||||
std::optional<double> min;
|
||||
std::optional<double> max;
|
||||
int value = 0;
|
||||
if (int_properties.contains(property.path())) {
|
||||
value = int_properties[property.path()];
|
||||
} else {
|
||||
auto value_to_resolve = playback->get_property(property.path());
|
||||
if (value_to_resolve.has_value()) {
|
||||
value = resolve_value<int>(value_to_resolve.value());
|
||||
}
|
||||
int_properties[property.path()] = value;
|
||||
}
|
||||
if (property.has_hint() && property.hint().has_range()) {
|
||||
auto range = property.hint().range();
|
||||
if (range.has_min() && range.has_max()) {
|
||||
if (ImGui::SliderInt(property.path().c_str(), &value, (int)range.min(), (int)range.max())) {
|
||||
int_properties[property.path()] = value;
|
||||
}
|
||||
valid = true;
|
||||
} else {
|
||||
if (range.has_min()) min = range.min();
|
||||
if (range.has_max()) max = range.max();
|
||||
}
|
||||
}
|
||||
if (!valid) {
|
||||
ImGui::InputInt(property.path().c_str(), &value);
|
||||
if (min.has_value() && value < min) {
|
||||
value = min.value();
|
||||
}
|
||||
if (max.has_value() && value > max) {
|
||||
value = max.value();
|
||||
}
|
||||
int_properties[property.path()] = value;
|
||||
valid = true;
|
||||
}
|
||||
} break;
|
||||
case PropertyType::Double: {
|
||||
std::optional<double> min;
|
||||
std::optional<double> max;
|
||||
|
@ -371,6 +424,20 @@ void MainLoop::GuiFunction() {
|
|||
ImGui::SameLine();
|
||||
if (ImGui::Button("Set")) {
|
||||
switch (property.type()) {
|
||||
case PropertyType::String: {
|
||||
StringProperty property_s;
|
||||
property_s.set_value(string_properties[property.path()]);
|
||||
google::protobuf::Any value;
|
||||
value.PackFrom(property_s);
|
||||
playback->set_property(property.path(), value);
|
||||
} break;
|
||||
case PropertyType::Int: {
|
||||
IntProperty property_i;
|
||||
property_i.set_value(int_properties[property.path()]);
|
||||
google::protobuf::Any value;
|
||||
value.PackFrom(property_i);
|
||||
playback->set_property(property.path(), value);
|
||||
} break;
|
||||
case PropertyType::Double: {
|
||||
DoubleProperty property_d;
|
||||
property_d.set_value(double_properties[property.path()]);
|
||||
|
|
|
@ -55,6 +55,8 @@ class MainLoop : public RendererBackend {
|
|||
std::atomic_bool exit_flag;
|
||||
std::map<std::string, bool> boolean_properties;
|
||||
std::map<std::string, double> double_properties;
|
||||
std::map<std::string, int> int_properties;
|
||||
std::map<std::string, std::string> string_properties;
|
||||
std::vector<Property> properties;
|
||||
std::vector<PlaybackStream> streams;
|
||||
public:
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 720da57baba83b3b1829e20133575e57aa1a8a4f
|
||||
Subproject commit 0379bf3a5d52d8542aec1874677c9df5ff9ba5f9
|
|
@ -1 +1 @@
|
|||
Subproject commit d144031940543e15423a25ae5a8a74141044862f
|
||||
Subproject commit 6dae7eb4a5c3a169f3e298392bff4680224aa94a
|
9
util.hpp
9
util.hpp
|
@ -519,6 +519,13 @@ inline std::string resolve_value<std::string>(google::protobuf::Any value) {
|
|||
return output;
|
||||
}
|
||||
template<>
|
||||
inline int resolve_value<int>(google::protobuf::Any value) {
|
||||
IntProperty *property = resolve_any<IntProperty>(value);
|
||||
int output = property->value();
|
||||
delete property;
|
||||
return output;
|
||||
}
|
||||
template<>
|
||||
inline DynPtr resolve_value<DynPtr>(google::protobuf::Any value) {
|
||||
BytesProperty *property = resolve_any<BytesProperty>(value);
|
||||
std::string output = property->value();
|
||||
|
@ -527,7 +534,7 @@ inline DynPtr resolve_value<DynPtr>(google::protobuf::Any value) {
|
|||
}
|
||||
|
||||
template<>
|
||||
inline double resolve_value(google::protobuf::Any value) {
|
||||
inline double resolve_value<double>(google::protobuf::Any value) {
|
||||
DoubleProperty *property = resolve_any<DoubleProperty>(value);
|
||||
double output = property->value();
|
||||
delete property;
|
||||
|
|
Loading…
Reference in a new issue