diff --git a/.gitmodules b/.gitmodules index e17fb42..71dc9ae 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "deps/fmgen"] - path = deps/fmgen - url = https://github.com/kichikuou/fmgen.git +[submodule "deps/ymfm"] + path = deps/ymfm + url = https://github.com/aaronsgiles/ymfm.git diff --git a/deps/fmgen b/deps/fmgen deleted file mode 160000 index 7246b59..0000000 --- a/deps/fmgen +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7246b59cac8f9228a842899f208e6b2757f97501 diff --git a/deps/ymfm b/deps/ymfm new file mode 160000 index 0000000..10c72f7 --- /dev/null +++ b/deps/ymfm @@ -0,0 +1 @@ +Subproject commit 10c72f79bea3e0ab66af32d4295519aa17e6ea0f diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e005d14..d275b8a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -17,7 +17,12 @@ if (MSVC) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") endif () -add_subdirectory(../deps/fmgen fmgen) +add_library(ymfm + ../deps/ymfm/src/ymfm_opn.cpp + ../deps/ymfm/src/ymfm_adpcm.cpp + ../deps/ymfm/src/ymfm_ssg.cpp) +target_include_directories(ymfm PUBLIC ../deps/ymfm/src) + if (ANDROID) add_library(system3 SHARED) @@ -27,7 +32,7 @@ if (ANDROID) android/mako.cpp sys/mako_midi.cpp ) - target_link_libraries(system3 PRIVATE SDL2 SDL2_ttf fmgen) + target_link_libraries(system3 PRIVATE SDL2 SDL2_ttf ymfm) elseif (EMSCRIPTEN) add_executable(system3) @@ -39,7 +44,7 @@ elseif (EMSCRIPTEN) set(LIBS "SHELL:-s USE_SDL=2" "SHELL:-s USE_SDL_TTF=2") target_compile_options(system3 PRIVATE ${LIBS}) target_link_options(system3 PRIVATE ${LIBS}) - target_link_libraries(system3 PRIVATE idbfs.js fmgen) + target_link_libraries(system3 PRIVATE idbfs.js ymfm) # Without optimizations, Asyncify generates very large code. list(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG "-O1") @@ -55,7 +60,7 @@ elseif (EMSCRIPTEN) else() # NOT (ANDROID OR EMSCRIPTEN) add_executable(system3) - target_link_libraries(system3 PRIVATE fmgen) + target_link_libraries(system3 PRIVATE ymfm) if (MSVC) if (CMAKE_VS_PLATFORM_NAME STREQUAL "Win32") @@ -151,7 +156,7 @@ target_sources(system3 PRIVATE encoding.cpp texthook.cpp fm/makofm.cpp - fm/mako_fmgen.cpp + fm/mako_ymfm.cpp sys/ags.cpp sys/ags_bmp.cpp sys/ags_cursor.cpp diff --git a/src/android/mako.cpp b/src/android/mako.cpp index f741e1c..65a1107 100644 --- a/src/android/mako.cpp +++ b/src/android/mako.cpp @@ -6,7 +6,7 @@ #include "dri.h" #include "mako.h" #include "mako_midi.h" -#include "fm/mako_fmgen.h" +#include "fm/mako_ymfm.h" namespace { @@ -14,11 +14,11 @@ const int SAMPLE_RATE = 44100; MAKO *g_mako; SDL_mutex* fm_mutex; -std::unique_ptr fm; +std::unique_ptr fm; void audio_callback(void*, Uint8* stream, int len) { SDL_LockMutex(fm_mutex); - fm->Process(reinterpret_cast(stream), len/ 4); + fm->Process(reinterpret_cast(stream), len/ 4); SDL_UnlockMutex(fm_mutex); } @@ -82,7 +82,7 @@ void MAKO::play_music(int page) return; SDL_LockMutex(fm_mutex); - fm = std::make_unique(SAMPLE_RATE, data, true); + fm = std::make_unique(SAMPLE_RATE, data, true); SDL_UnlockMutex(fm_mutex); SDL_PauseAudio(0); } else { diff --git a/src/emscripten/mako.cpp b/src/emscripten/mako.cpp index 8b8c9c5..17f1f02 100644 --- a/src/emscripten/mako.cpp +++ b/src/emscripten/mako.cpp @@ -6,13 +6,13 @@ #include #include #include "mako.h" -#include "fm/mako_fmgen.h" +#include "fm/mako_ymfm.h" #include "dri.h" namespace { MAKO *g_mako; -std::unique_ptr fm; +std::unique_ptr fm; } // namespace @@ -48,7 +48,7 @@ void MAKO::play_music(int page) if (!data) return; int rate = EM_ASM_INT({ return xsystem35.audio.enable_audio_hook(); }); - fm = std::make_unique(rate, data, true); + fm = std::make_unique(rate, data, true); } current_music = page; @@ -101,7 +101,7 @@ EMSCRIPTEN_KEEPALIVE bool audio_callback(Uint8 *stream, int len) { if (!fm) return false; - fm->Process(reinterpret_cast(stream), len); + fm->Process(reinterpret_cast(stream), len); return true; } diff --git a/src/fm/mako_fmgen.cpp b/src/fm/mako_fmgen.cpp deleted file mode 100644 index d5dcd24..0000000 --- a/src/fm/mako_fmgen.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "mako_fmgen.h" -#include -#include -#include - -const int OPNA_CLOCK = 3993600 * 2; - -MakoFMgen::MakoFMgen(int rate, const uint8_t* data, bool free_data) : - MakoFM(data, free_data), - sample_rate(rate), - sync_ms(0), - samples_left(0) { - bool ok = opna.Init(OPNA_CLOCK, sample_rate, false); - assert(ok); - opna.SetVolumePSG(-14); - opna.Reset(); -} - -void MakoFMgen::Process(int16* stream, int len) { - output = stream; - out_samples = len; - - if (samples_left > 0) { - int samples = samples_left; - samples_left = 0; - Mix(samples); - } - while (out_samples > 0) { - MainLoop(); - Sync(); - } -} - -void MakoFMgen::SetReg(RegType type, uint8_t addr, uint8_t val) { - opna.SetReg(addr + (type == OPNA_SLAVE ? 0x100 : 0), val); -} - -void MakoFMgen::Sync() { - int t = static_cast(GetTime()); - if (t == sync_ms) - return; - int ms = t - sync_ms; - sync_ms = t; - Mix(sample_rate * ms / 1000); -} - -void MakoFMgen::Mix(int samples) { - assert(samples_left == 0); - if (samples > out_samples) { - samples_left = samples - out_samples; - samples = out_samples; - } - FM_SAMPLETYPE* buf = (FM_SAMPLETYPE*)alloca(samples * 2 * sizeof(FM_SAMPLETYPE)); - memset(buf, 0, samples * sizeof(FM_SAMPLETYPE) * 2); - opna.Mix(buf, samples); - for (int i = 0; i < samples * 2; i += 2) { - output[i] = std::max(-0x7fff, std::min(0x7fff, buf[i])); - output[i+1] = std::max(-0x7fff, std::min(0x7fff, buf[i+1])); - } - out_samples -= samples; - output += samples * 2; -} - -void MakoFMgen::get_mark(int* mark, int* loop) { - *mark = GetMark(); - *loop = Looped() ? 1 : 0; -} diff --git a/src/fm/mako_fmgen.h b/src/fm/mako_fmgen.h deleted file mode 100644 index c74c576..0000000 --- a/src/fm/mako_fmgen.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef MAKOFMGEN_H_ -#define MAKOFMGEN_H_ - -#include "fmgen/opna.h" -#include "makofm.h" - -class MakoFMgen : private MakoFM { -public: - MakoFMgen(int rate, const uint8_t* data, bool free_data); - void Process(int16* stream, int len); - void get_mark(int* mark, int* loop); - -private: - void SetReg(RegType type, uint8_t addr, uint8_t val) override; - void Sync(); - void Mix(int samples); - - const int sample_rate; - FM::OPNA opna; - int sync_ms; - int16* output; - int out_samples; - int samples_left; -}; - -#endif // MAKOFMGEN_H_ diff --git a/src/fm/mako_ymfm.cpp b/src/fm/mako_ymfm.cpp new file mode 100644 index 0000000..c989c24 --- /dev/null +++ b/src/fm/mako_ymfm.cpp @@ -0,0 +1,71 @@ +#include "mako_ymfm.h" +#include + +const int OPNA_CLOCK = 3993600 * 2; + +int16_t mixSample(int32_t fm, int32_t ssg) { + return ymfm::clamp(fm * 2 + ssg / 2, -32768, 32767); +} + +MakoYmfm::MakoYmfm(int rate, const uint8_t* data, bool free_data) : + MakoFM(data, free_data), + sample_rate(rate), + opna(*this) +{ + opna.set_fidelity(ymfm::OPN_FIDELITY_MIN); + opna.reset(); +} + +void MakoYmfm::Process(int16_t* stream, int len) { + int16_t* buf = stream; + + if (samples_left > 0) { + Generate(buf, samples_left); + buf += samples_left * 2; + len -= samples_left; + samples_left = 0; + } + while (len > 0) { + MainLoop(); + int t = static_cast(GetTime()); + int dt = t - last_sync; + last_sync = t; + int samples = sample_rate * dt / 1000; + if (samples > len) { + samples_left = samples - len; + samples = len; + } + Generate(buf, samples); + buf += samples * 2; + len -= samples; + } +} + +void MakoYmfm::SetReg(RegType type, uint8_t addr, uint8_t val) { + uint32_t offset = type == OPNA_SLAVE ? 2 : 0; + opna.write(offset, addr); + opna.write(offset + 1, val); +} + +void MakoYmfm::Generate(int16_t* buf, int samples) { + const emulated_time output_step = 0x100000000ull / sample_rate; + const emulated_time opna_step = 0x100000000ull / opna.sample_rate(OPNA_CLOCK); + + for (int i = 0; i < samples * 2; i += 2) { + ymfm::ym2608::output_data opna_output; + // generate at the appropriate sample rate + assert(opna_pos <= output_pos); + while (opna_pos <= output_pos) { + opna.generate(&opna_output); + opna_pos += opna_step; + } + buf[i] = mixSample(opna_output.data[0], opna_output.data[2]); + buf[i+1] = mixSample(opna_output.data[1], opna_output.data[2]); + output_pos += output_step; + } +} + +void MakoYmfm::get_mark(int* mark, int* loop) { + *mark = GetMark(); + *loop = Looped() ? 1 : 0; +} diff --git a/src/fm/mako_ymfm.h b/src/fm/mako_ymfm.h new file mode 100644 index 0000000..bc32a82 --- /dev/null +++ b/src/fm/mako_ymfm.h @@ -0,0 +1,28 @@ +#ifndef MAKOYMFM_H_ +#define MAKOYMFM_H_ + +#include "ymfm_opn.h" +#include "makofm.h" + +class MakoYmfm : private MakoFM, private ymfm::ymfm_interface { +public: + MakoYmfm(int rate, const uint8_t* data, bool free_data); + void Process(int16_t* stream, int len); + void get_mark(int* mark, int* loop); + +private: + // we use an int64_t as emulated time, as a 32.32 fixed point value + using emulated_time = int64_t; + + void SetReg(RegType type, uint8_t addr, uint8_t val) override; + void Generate(int16_t* buf, int samples); + + const int sample_rate; + ymfm::ym2608 opna; + emulated_time opna_pos = 0; + emulated_time output_pos = 0; + int last_sync = 0; + int samples_left = 0; +}; + +#endif // MAKOYMFM_H_ diff --git a/src/linux/mako.cpp b/src/linux/mako.cpp index 5d33673..9d5fa5b 100644 --- a/src/linux/mako.cpp +++ b/src/linux/mako.cpp @@ -10,7 +10,7 @@ #include "mako.h" #include "mako_midi.h" -#include "fm/mako_fmgen.h" +#include "fm/mako_ymfm.h" #include "../config.h" #include "dri.h" @@ -27,11 +27,11 @@ std::vector smf; Mix_Music *mix_music; Mix_Chunk *mix_chunk; SDL_mutex* fm_mutex; -std::unique_ptr fm; +std::unique_ptr fm; void FMHook(void *udata, Uint8 *stream, int len) { SDL_LockMutex(fm_mutex); - fm->Process(reinterpret_cast(stream), len / 4); + fm->Process(reinterpret_cast(stream), len / 4); SDL_UnlockMutex(fm_mutex); } @@ -119,7 +119,7 @@ void MAKO::play_music(int page) fm_mutex = SDL_CreateMutex(); SDL_LockMutex(fm_mutex); - fm = std::make_unique(SAMPLE_RATE, data, true); + fm = std::make_unique(SAMPLE_RATE, data, true); Mix_HookMusic(&FMHook, this); SDL_UnlockMutex(fm_mutex); } else { diff --git a/src/win/mako.cpp b/src/win/mako.cpp index 8d9ef39..6a44429 100644 --- a/src/win/mako.cpp +++ b/src/win/mako.cpp @@ -6,13 +6,15 @@ #include #undef ERROR +#undef min +#undef max #include #include #include #include #include "mako.h" #include "mako_midi.h" -#include "fm/mako_fmgen.h" +#include "fm/mako_ymfm.h" #include "../config.h" #include "dri.h" #include "crc32.h" @@ -232,11 +234,11 @@ private: Music* music; uint8* wav_buffer; SDL_mutex* fm_mutex; -std::unique_ptr fm; +std::unique_ptr fm; void audio_callback(void*, Uint8* stream, int len) { SDL_LockMutex(fm_mutex); - fm->Process(reinterpret_cast(stream), len/ 4); + fm->Process(reinterpret_cast(stream), len/ 4); SDL_UnlockMutex(fm_mutex); } @@ -332,7 +334,7 @@ void MAKO::play_music(int page) return; SDL_LockMutex(fm_mutex); - fm = std::make_unique(SAMPLE_RATE, data, true); + fm = std::make_unique(SAMPLE_RATE, data, true); SDL_UnlockMutex(fm_mutex); SDL_PauseAudio(0); } else {