mirror of
https://github.com/kichikuou/system3-sdl2.git
synced 2026-09-22 22:58:12 +03:00
Switch FM sound emulator to ymfm
This commit is contained in:
+3
-3
@@ -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
+1
Submodule deps/ymfm added at 10c72f79be
+10
-5
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user