diff --git a/.gitmodules b/.gitmodules index c3501c0..4f9d591 100644 --- a/.gitmodules +++ b/.gitmodules @@ -57,3 +57,6 @@ [submodule "subprojects/protobuf-c"] path = subprojects/protobuf-c url = https://github.com/protobuf-c/protobuf-c.git +[submodule "backends/playback/gme/game-music-emu"] + path = backends/playback/gme/game-music-emu + url = https://github.com/libgme/game-music-emu.git diff --git a/CMakeLists.txt b/CMakeLists.txt index c33d4e2..9ed3056 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -529,6 +529,7 @@ playback_backend_subdir(NAME "VGMSTREAM" READABLE_NAME "VgmStream" SUBDIR backen 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) +playback_backend_subdir(NAME "GME" READABLE_NAME "Game Music Emu" SUBDIR backends/playback/gme) 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) diff --git a/backends/playback/gme/CMakeLists.txt b/backends/playback/gme/CMakeLists.txt new file mode 100644 index 0000000..8e287f7 --- /dev/null +++ b/backends/playback/gme/CMakeLists.txt @@ -0,0 +1,8 @@ +set(BACKEND_GME_SRC ${CMAKE_CURRENT_SOURCE_DIR}/gme_backend.cpp) +set(BACKEND_GME_INC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) +add_subdirectory(game-music-emu) +add_playback_backend(gme_backend ${BACKEND_GME_SRC}) +target_include_directories(gme_backend PRIVATE ${BACKEND_GME_INC}) +find_package(OpenMP) +target_include_directories(gme_backend PUBLIC game-music-emu/gme) +target_link_libraries(gme_backend PUBLIC gme OpenMP::OpenMP_CXX) diff --git a/backends/playback/gme/backend.json b/backends/playback/gme/backend.json new file mode 100644 index 0000000..004926d --- /dev/null +++ b/backends/playback/gme/backend.json @@ -0,0 +1,4 @@ +{ + "class_name": "GmeBackend", + "include_path": "gme_backend.hpp" +} diff --git a/backends/playback/gme/cmake_install.cmake b/backends/playback/gme/cmake_install.cmake new file mode 100644 index 0000000..e0608bc --- /dev/null +++ b/backends/playback/gme/cmake_install.cmake @@ -0,0 +1,39 @@ +# Install script for directory: /boot/home/Desktop/looper/backends/playback/zsm + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/boot/system") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "RelWithDebInfo") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Is this installation the result of a crosscompile? +if(NOT DEFINED CMAKE_CROSSCOMPILING) + set(CMAKE_CROSSCOMPILING "FALSE") +endif() + +# Set path to fallback-tool for dependency-resolution. +if(NOT DEFINED CMAKE_OBJDUMP) + set(CMAKE_OBJDUMP "/system/bin/objdump") +endif() + diff --git a/backends/playback/gme/game-music-emu b/backends/playback/gme/game-music-emu new file mode 160000 index 0000000..e76bdc0 --- /dev/null +++ b/backends/playback/gme/game-music-emu @@ -0,0 +1 @@ +Subproject commit e76bdc0cb916e79aa540290e6edd0c445879d3ba diff --git a/backends/playback/gme/gme_backend.cpp b/backends/playback/gme/gme_backend.cpp new file mode 100644 index 0000000..fbaa183 --- /dev/null +++ b/backends/playback/gme/gme_backend.cpp @@ -0,0 +1,123 @@ +#include "gme_backend.hpp" +#include +#include +#include +#include +#include "file_backend.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +void GmeBackend::load(const char *filename) { + memset(&spec, 0, sizeof(spec)); + current_file = filename; + spec.format = AUDIO_S16SYS; + spec.samples = 100; + spec.channels = 2; + spec.freq = 48000; + spec.size = 100 * 2 * sizeof(int16_t); + gme_open_file(filename, &gme_backend, spec.freq); + if (gme_backend == NULL) { + throw std::exception(); + } + int track_count = gme_track_count(gme_backend); + streams.clear(); + for (int i = 0; i < track_count; i++) { + gme_info_t *info; + gme_track_info(gme_backend, &info, i); + PlaybackStream stream; + if (info->song[0] == '\0') { + stream.name = fmt::format("Song {}", i); + } else { + stream.name = info->song; + } + + stream.length = ((double)info->length) / 1000.0; + if (stream.length < 0.0) stream.length = 0.0; + stream.id = i; + streams.push_back(stream); + gme_free_info(info); + } +} +extern SDL_AudioSpec obtained; +void GmeBackend::switch_stream(int idx) { + if (gme_backend == NULL) { + throw std::exception(); + } + if (idx > streams.size()) { + throw std::exception(); + } + gme_start_track(gme_backend, idx); + gme_info_t *info; + gme_track_info(gme_backend, &info, idx); + if (info->length < 0) { + info->length = 0; + } + uint64_t tmp = info->length; + tmp *= spec.freq; + tmp /= 1000; + this->length = tmp; + this->loop_len = info->loop_length; + this->loop_start = info->intro_length; + if (info->song[0] == '\0') { + this->current_title = {}; + } else { + this->current_title = info->song; + } + gme_free_info(info); + gme_ignore_silence(gme_backend, true); + open = true; +} +void GmeBackend::cleanup() { + gme_delete(gme_backend); + gme_backend = NULL; + streams.clear(); +} +size_t GmeBackend::render(void *buf, size_t maxlen) { + if (gme_backend == NULL) return 0; + const char *err; + size_t sample_type_len = 4; + maxlen /= sample_type_len; + if (err = gme_play(gme_backend, maxlen * 2, (short*)buf)) { + ERROR.writefln("Failed to play audio: %s", err); + return 0; + } + int pos_samples = gme_tell_samples(gme_backend); + int loop_end = loop_len + loop_start; + uint64_t loop_end_samples = loop_end; + loop_end_samples *= spec.freq; + loop_end_samples /= 1000; + uint64_t loop_start_samples = loop_start; + loop_start_samples *= spec.freq; + loop_start_samples /= 1000; + if (pos_samples >= loop_end_samples || gme_track_ended(gme_backend)) { + gme_seek_samples(gme_backend, loop_start_samples + (pos_samples - loop_end_samples)); + } + maxlen *= sample_type_len; + return maxlen; +} +uint64_t GmeBackend::get_min_samples() { + return spec.size; +} +std::optional GmeBackend::get_max_samples() { + return get_min_samples(); +} +void GmeBackend::seek_samples(uint64_t position) { + gme_seek_samples(gme_backend, position); +} +uint64_t GmeBackend::get_position_samples() { + return gme_tell_samples(gme_backend); +} +int GmeBackend::get_stream_idx() { + return streamidx; +} +void GmeBackend::add_licenses() { + auto &license_data = get_license_data(); + auto gme = LicenseData("gme", "lgpl-2.1"); + LOAD_LICENSE(gme, lgpl_2_1); + license_data.insert(gme); +} diff --git a/backends/playback/gme/gme_backend.hpp b/backends/playback/gme/gme_backend.hpp new file mode 100644 index 0000000..d4f1f51 --- /dev/null +++ b/backends/playback/gme/gme_backend.hpp @@ -0,0 +1,44 @@ +#pragma once +#include "playback_backend.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "file_backend.hpp" +class GmeBackend : public PlaybackBackend { + File *file; + Fifo audio_buf; + Music_Emu *gme_backend; + int streamidx; + int loop_len, loop_start; + std::map voices; + public: + uint64_t get_min_samples() override; + std::optional get_max_samples() override; + inline std::string get_id() override { + return "gme"; + } + inline std::string get_name() override { + return "Game Music Emu"; + } + void add_licenses() override; + //std::vector get_property_list() override; + void seek_samples(uint64_t position) override; + void load(const char *filename) override; + void switch_stream(int idx) override; + void cleanup() override; + int get_stream_idx() override; + size_t render(void *buf, size_t maxlen) override; + uint64_t get_position_samples() override; + inline uint64_t get_length_samples() override { + return length; + } + inline ~GmeBackend() override { } +}; diff --git a/backends/playback/gme/properties.inc b/backends/playback/gme/properties.inc new file mode 100644 index 0000000..379722b --- /dev/null +++ b/backends/playback/gme/properties.inc @@ -0,0 +1,10 @@ +#define BOOL_PROPERTY(name, default_value) _PROPERTY(name, bool, default_value) +#define DOUBLE_PROPERTY(name, default_value) _PROPERTY(name, double, default_value) +BOOL_PROPERTY(pcm_enable, true) +BOOL_PROPERTY(psg_enable, true) +BOOL_PROPERTY(fm_enable, true) +DOUBLE_PROPERTY(pcm_volume, 1.0) +DOUBLE_PROPERTY(psg_volume, 1.0) +DOUBLE_PROPERTY(fm_volume, 1.0) +#undef BOOL_PROPERTY +#undef DOUBLE_PROPERTY