Switch FM sound emulator to ymfm

This commit is contained in:
kichikuou
2022-08-07 15:55:07 +09:00
parent 2cb0b2a2a2
commit 0c8903920a
12 changed files with 131 additions and 118 deletions
+3 -3
View File
@@ -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
Vendored
-1
Submodule deps/fmgen deleted from 7246b59cac
Vendored Submodule
+1
Submodule deps/ymfm added at 10c72f79be
+10 -5
View File
@@ -17,7 +17,12 @@ if (MSVC)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>: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
+4 -4
View File
@@ -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<MakoFMgen> fm;
std::unique_ptr<MakoYmfm> fm;
void audio_callback(void*, Uint8* stream, int len) {
SDL_LockMutex(fm_mutex);
fm->Process(reinterpret_cast<int16*>(stream), len/ 4);
fm->Process(reinterpret_cast<int16_t*>(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<MakoFMgen>(SAMPLE_RATE, data, true);
fm = std::make_unique<MakoYmfm>(SAMPLE_RATE, data, true);
SDL_UnlockMutex(fm_mutex);
SDL_PauseAudio(0);
} else {
+4 -4
View File
@@ -6,13 +6,13 @@
#include <memory>
#include <emscripten.h>
#include "mako.h"
#include "fm/mako_fmgen.h"
#include "fm/mako_ymfm.h"
#include "dri.h"
namespace {
MAKO *g_mako;
std::unique_ptr<MakoFMgen> fm;
std::unique_ptr<MakoYmfm> 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<MakoFMgen>(rate, data, true);
fm = std::make_unique<MakoYmfm>(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<int16*>(stream), len);
fm->Process(reinterpret_cast<int16_t*>(stream), len);
return true;
}
-67
View File
@@ -1,67 +0,0 @@
#include "mako_fmgen.h"
#include <algorithm>
#include <assert.h>
#include <string.h>
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<int>(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;
}
-26
View File
@@ -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_
+71
View File
@@ -0,0 +1,71 @@
#include "mako_ymfm.h"
#include <algorithm>
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<int>(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;
}
+28
View File
@@ -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_
+4 -4
View File
@@ -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<uint8> smf;
Mix_Music *mix_music;
Mix_Chunk *mix_chunk;
SDL_mutex* fm_mutex;
std::unique_ptr<MakoFMgen> fm;
std::unique_ptr<MakoYmfm> fm;
void FMHook(void *udata, Uint8 *stream, int len) {
SDL_LockMutex(fm_mutex);
fm->Process(reinterpret_cast<int16*>(stream), len / 4);
fm->Process(reinterpret_cast<int16_t*>(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<MakoFMgen>(SAMPLE_RATE, data, true);
fm = std::make_unique<MakoYmfm>(SAMPLE_RATE, data, true);
Mix_HookMusic(&FMHook, this);
SDL_UnlockMutex(fm_mutex);
} else {
+6 -4
View File
@@ -6,13 +6,15 @@
#include <windows.h>
#undef ERROR
#undef min
#undef max
#include <memory>
#include <string>
#include <SDL.h>
#include <SDL_syswm.h>
#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<MakoFMgen> fm;
std::unique_ptr<MakoYmfm> fm;
void audio_callback(void*, Uint8* stream, int len) {
SDL_LockMutex(fm_mutex);
fm->Process(reinterpret_cast<int16*>(stream), len/ 4);
fm->Process(reinterpret_cast<int16_t*>(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<MakoFMgen>(SAMPLE_RATE, data, true);
fm = std::make_unique<MakoYmfm>(SAMPLE_RATE, data, true);
SDL_UnlockMutex(fm_mutex);
SDL_PauseAudio(0);
} else {