diff --git a/CMakeLists.txt b/CMakeLists.txt index 64803e6..2e0fccd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,7 +65,8 @@ if(${RETROWAVE_BUILD_PLAYER} EQUAL 1) CPMAddPackage( NAME TinyVGM GITHUB_REPOSITORY SudoMaker/TinyVGM - VERSION 0.0.2 + #VERSION v1.0.2 + GIT_TAG a122a90f3ebacea7af25b92b183664dc5a3621e4 ) include_directories(${cxxopts_SOURCE_DIR}/include) diff --git a/Player/Metadata.cpp b/Player/Metadata.cpp index a3a1240..b5d80b0 100644 --- a/Player/Metadata.cpp +++ b/Player/Metadata.cpp @@ -67,13 +67,13 @@ // iconv_close(cd); //} -void RetroWavePlayer::char16_to_string(std::string &str, int16_t *c16) { +void RetroWavePlayer::char16_to_string(std::string &str, int16_t *c16, uint32_t memsize) { str.clear(); if (!c16) return; - size_t c16_len = tinyvgm_strlen16(c16); + size_t c16_len = memsize / 2; if (!c16_len) return; @@ -100,18 +100,3 @@ void RetroWavePlayer::char16_to_string(std::string &str, int16_t *c16) { // // str.resize(to_next - &str[0]); } - -void RetroWavePlayer::gd3_to_info(TinyVGMGd3Info *g) { - char16_to_string(metadata.title, g->title); - char16_to_string(metadata.album, g->album); - char16_to_string(metadata.system_name, g->system_name); - char16_to_string(metadata.composer, g->composer); - char16_to_string(metadata.release_date, g->release_date); - char16_to_string(metadata.converter, g->converter); - char16_to_string(metadata.note, g->note); - - char16_to_string(metadata.title_jp, g->title_jp); - char16_to_string(metadata.album_jp, g->album_jp); - char16_to_string(metadata.system_name_jp, g->system_name_jp); - char16_to_string(metadata.composer_jp, g->composer_jp); -} diff --git a/Player/OSD.cpp b/Player/OSD.cpp index f72063d..87b0da1 100644 --- a/Player/OSD.cpp +++ b/Player/OSD.cpp @@ -47,6 +47,7 @@ static std::unordered_map regmap_name = { {0x5a, "OPL2"}, + {0xaa, "OPL2 dual"}, {0x5e, "OPL3 Port0"}, {0x5f, "OPL3 Port1"}, {0x50, "SN76489"}, @@ -333,9 +334,9 @@ void RetroWavePlayer::osd_show() { if (total_samples) { auto [th, tm, ts] = sec2hms(total_samples / sample_rate); - printf("Playing: %02d:%02d:%02d / %02d:%02d:%02d +%011.6lfms (%zu/%zu %s+%05zu\033[0m %06.3lf)\033[K\n", h, m, s, th, tm, ts, last_slept_msecs, played_samples, total_samples, samples_color, last_slept_samples, fps); + printf("Playing: %02zu:%02zu:%02zu / %02zu:%02zu:%02zu +%011.6lfms (%zu/%zu %s+%05zu\033[0m %06.3lf)\033[K\n", h, m, s, th, tm, ts, last_slept_msecs, played_samples, total_samples, samples_color, last_slept_samples, fps); } else { - printf("Playing: %02d:%02d:%02d +%011.6lfms (%zu %s+%05zu\033[0m %06.3lf)\033[K\n", h, m, s, last_slept_msecs, played_samples, samples_color, last_slept_samples, fps); + printf("Playing: %02zu:%02zu:%02zu +%011.6lfms (%zu %s+%05zu\033[0m %06.3lf)\033[K\n", h, m, s, last_slept_msecs, played_samples, samples_color, last_slept_samples, fps); } last_last_slept_samples = last_slept_samples; diff --git a/Player/Player.cpp b/Player/Player.cpp index c3e5613..847ed56 100644 --- a/Player/Player.cpp +++ b/Player/Player.cpp @@ -44,6 +44,7 @@ */ #include "Player.hpp" +#include RetroWavePlayer player; @@ -96,52 +97,129 @@ void RetroWavePlayer::init_retrowave() { usleep(200 * 1000); } -void RetroWavePlayer::init_tinyvgm() { - tinyvgm_init(&tvc); - - std::unordered_map cmd_cb_map = { - {0x5a, callback_opl2}, - {0x5e, callback_opl3_port0}, - {0x5f, callback_opl3_port1}, - {0xbd, callback_saa1099}, - {0x50, callback_sn76489_port0}, - {0x51, callback_ym2413}, - {0x30, callback_sn76489_port1}, - }; +static int tvc_callback_command(void *userp, unsigned int cmd, const void *buf, uint32_t cmd_val_len) +{ + auto t = (RetroWavePlayer *)userp; + + switch (cmd) + { + case 0x61: return RetroWavePlayer::callback_sleep (userp, cmd, buf, cmd_val_len); + case 0x62: return RetroWavePlayer::callback_sleep_62 (userp, cmd, buf, cmd_val_len); + case 0x63: return RetroWavePlayer::callback_sleep_63 (userp, cmd, buf, cmd_val_len); + case 0x70: return RetroWavePlayer::callback_sleep_7n (userp, cmd, buf, cmd_val_len); + case 0x71: return RetroWavePlayer::callback_sleep_7n (userp, cmd, buf, cmd_val_len); + case 0x72: return RetroWavePlayer::callback_sleep_7n (userp, cmd, buf, cmd_val_len); + case 0x73: return RetroWavePlayer::callback_sleep_7n (userp, cmd, buf, cmd_val_len); + case 0x74: return RetroWavePlayer::callback_sleep_7n (userp, cmd, buf, cmd_val_len); + case 0x75: return RetroWavePlayer::callback_sleep_7n (userp, cmd, buf, cmd_val_len); + case 0x76: return RetroWavePlayer::callback_sleep_7n (userp, cmd, buf, cmd_val_len); + case 0x77: return RetroWavePlayer::callback_sleep_7n (userp, cmd, buf, cmd_val_len); + case 0x78: return RetroWavePlayer::callback_sleep_7n (userp, cmd, buf, cmd_val_len); + case 0x79: return RetroWavePlayer::callback_sleep_7n (userp, cmd, buf, cmd_val_len); + case 0x7a: return RetroWavePlayer::callback_sleep_7n (userp, cmd, buf, cmd_val_len); + case 0x7b: return RetroWavePlayer::callback_sleep_7n (userp, cmd, buf, cmd_val_len); + case 0x7c: return RetroWavePlayer::callback_sleep_7n (userp, cmd, buf, cmd_val_len); + case 0x7d: return RetroWavePlayer::callback_sleep_7n (userp, cmd, buf, cmd_val_len); + case 0x7e: return RetroWavePlayer::callback_sleep_7n (userp, cmd, buf, cmd_val_len); + case 0x7f: return RetroWavePlayer::callback_sleep_7n (userp, cmd, buf, cmd_val_len); + } - for (auto &it : disabled_vgm_commands) { - cmd_cb_map.erase(it); - printf("info: disabled VGM command %02x\n", it); + if (t->disabled_vgm_commands.count(cmd)) return TinyVGM_OK; + + switch (cmd) + { + case 0x5a: return RetroWavePlayer::callback_opl2 (userp, cmd, buf, cmd_val_len); + case 0xaa: return RetroWavePlayer::callback_opl2_dual (userp, cmd, buf, cmd_val_len); + case 0x5e: return RetroWavePlayer::callback_opl3_port0 (userp, cmd, buf, cmd_val_len); + case 0x5f: return RetroWavePlayer::callback_opl3_port1 (userp, cmd, buf, cmd_val_len); + case 0xbd: return RetroWavePlayer::callback_saa1099 (userp, cmd, buf, cmd_val_len); + case 0x50: return RetroWavePlayer::callback_sn76489_port0 (userp, cmd, buf, cmd_val_len); + case 0x51: return RetroWavePlayer::callback_ym2413 (userp, cmd, buf, cmd_val_len); + case 0x30: return RetroWavePlayer::callback_sn76489_port1 (userp, cmd, buf, cmd_val_len); + } + + return TinyVGM_OK; // ignore command +} + +static int tvc_callback_header(void *userp, TinyVGMHeaderField field, uint32_t value) +{ + auto t = (RetroWavePlayer *)userp; + + switch (field) + { + case TinyVGM_HeaderField_Total_Samples: return RetroWavePlayer::callback_header_total_samples (userp, value); + case TinyVGM_HeaderField_SN76489_Clock: return RetroWavePlayer::callback_header_sn76489 (userp, value); + case TinyVGM_HeaderField_GD3_Offset: + t->gd3_offset_abs = value + tinyvgm_headerfield_offset(field); + break; + case TinyVGM_HeaderField_Data_Offset: + t->data_offset_abs = value + tinyvgm_headerfield_offset(field); + break; } - for (auto &it : cmd_cb_map) { - tinyvgm_add_command_callback(&tvc, it.first, it.second, this); - printf("debug: VGM cmd %02x handler %p\n", it.first, it.second); + return TinyVGM_OK; // ignore header +} + +static int tvc_callback_metadata(void *userp, TinyVGMMetadataType field, uint32_t pos, uint32_t len) +{ + auto t = (RetroWavePlayer *)userp; + +#define X(F) t->char16_to_string(t->metadata.F, (int16_t *)(t->file_buf.data() + pos), len); + + switch (field) + { + case TinyVGM_MetadataType_Title_EN: X(title); break; + case TinyVGM_MetadataType_Title: X(title_jp); break; + case TinyVGM_MetadataType_Album_EN: X(album); break; + case TinyVGM_MetadataType_Album: X(album_jp); break; + case TinyVGM_MetadataType_SystemName_EN: X(system_name); break; + case TinyVGM_MetadataType_SystemName: X(system_name_jp); break; + case TinyVGM_MetadataType_Composer_EN: X(composer); break; + case TinyVGM_MetadataType_Composer: X(composer_jp); break; + case TinyVGM_MetadataType_ReleaseDate: X(release_date); break; + case TinyVGM_MetadataType_Converter: X(converter); break; + case TinyVGM_MetadataType_Notes: X(note); break; } -// tinyvgm_add_command_callback(&tvc, 0x5a, callback_opl2, this); -// tinyvgm_add_command_callback(&tvc, 0x5e, callback_opl3_port0, this); -// tinyvgm_add_command_callback(&tvc, 0x5f, callback_opl3_port1, this); -// -// tinyvgm_add_command_callback(&tvc, 0xbd, callback_saa1099, this); -// tinyvgm_add_command_callback(&tvc, 0x50, callback_sn76489_port0, this); -// tinyvgm_add_command_callback(&tvc, 0x51, callback_ym2413, this); -// tinyvgm_add_command_callback(&tvc, 0x30, callback_sn76489_port1, this); +#undef X + return TinyVGM_OK; +} +int32_t tvc_callback_read(void *userp, uint8_t *buf, uint32_t len) +{ + auto t = (RetroWavePlayer *)userp; - tinyvgm_add_command_callback(&tvc, 0x61, callback_sleep, this); - tinyvgm_add_command_callback(&tvc, 0x62, callback_sleep_62, this); - tinyvgm_add_command_callback(&tvc, 0x63, callback_sleep_63, this); + if (t->file_pos >= t->file_buf.size()) + { + return 0; + } - for (int i = 0x70; i <= 0x7f; i++) { - tinyvgm_add_command_callback(&tvc, i, callback_sleep_7n, this); + if (t->file_pos + len >= t->file_buf.size()) + { + len = t->file_buf.size() - t->file_pos; } + memcpy (buf, t->file_buf.data() + t->file_pos, len); + t->file_pos+=len; + return len; +} - tinyvgm_add_header_callback(&tvc, 0x18, callback_header_total_samples, this); - tinyvgm_add_header_callback(&tvc, 0x0c, callback_header_sn76489, this); +int tvc_callback_seek(void *userp, uint32_t pos) +{ + auto t = (RetroWavePlayer *)userp; - tinyvgm_add_event_callback(&tvc, TinyVGM_Event_HeaderParseDone, callback_header_done, this); - tinyvgm_add_event_callback(&tvc, TinyVGM_Event_PlaybackDone, callback_playback_done, this); + t->file_pos = pos; + + return 0; +} + +void RetroWavePlayer::init_tinyvgm() { + memset (&tvc, 0, sizeof (tvc)); + tvc.userp = this; + tvc.callback.header = tvc_callback_header; + tvc.callback.metadata = tvc_callback_metadata; + tvc.callback.command = tvc_callback_command; + tvc.callback.seek = tvc_callback_seek; + tvc.callback.read = tvc_callback_read; } void RetroWavePlayer::parse_disabled_vgm_commands(const std::string &str) { @@ -164,6 +242,10 @@ void RetroWavePlayer::parse_disabled_vgm_commands(const std::string &str) { } bool RetroWavePlayer::load_file(const std::string &path) { + gd3_offset_abs = 0; + data_offset_abs = 0; + file_pos = 0; + int fd = open(path.c_str(), O_RDONLY); if (fd < 0) { @@ -278,60 +360,33 @@ void RetroWavePlayer::play(const std::vector &file_list) { continue; } - int32_t rc_tv_parse, rc_gd3_parse; - - rc_tv_parse = tinyvgm_parse(&tvc, file_buf.data(), 32); // GD3 offset must be within first 32 bytes - - if (rc_tv_parse == 32) { - auto gd3_offset = tvc.header_info.gd3_offset; - auto gd3_size = file_buf.size() - gd3_offset; - - if (gd3_offset) { - tinyvgm_init_gd3(&gd3_info); - rc_gd3_parse = tinyvgm_parse_gd3(&gd3_info, file_buf.data()+gd3_offset, gd3_size); - if (rc_gd3_parse) { - gd3_to_info(&gd3_info); - } - } - } else { - printf("TinyVGM error: failed to process file `%s', rc=%d\n", cur_file.c_str(), rc_tv_parse); - tinyvgm_reset(&tvc); + if (tinyvgm_parse_header (&tvc) != TinyVGM_OK) { i++; continue; } - const size_t parse_size_hint = 32; - - for (size_t j=32; j file_buf.size()) { - parse_size = file_buf.size() - j; + if (gd3_offset_abs) { + if (tinyvgm_parse_metadata(&tvc, gd3_offset_abs) != TinyVGM_OK) { + // ignore errors } + } - rc_tv_parse = tinyvgm_parse(&tvc, file_buf.data()+j, parse_size); + callback_header_done(this); + tinyvgm_parse_commands(&tvc, data_offset_abs); - if (rc_tv_parse == INT32_MIN) { - printf("TinyVGM error: failed to process file `%s', rc=%d\n", cur_file.c_str(), rc_tv_parse); + switch (key_command) + { + case PREV: + if (i) + i--; break; - } - - played_bytes += rc_tv_parse; - - if (playback_done) { + case NEXT: + i++; break; - } - - if (key_command & 0x0c) { + case QUIT: + i = file_list.size(); + playback_reset(); break; - } - } - - if (key_command == PREV) { - if (i) - i--; - } else { - i++; } key_command = NONE; @@ -343,7 +398,6 @@ void RetroWavePlayer::play(const std::vector &file_list) { } void RetroWavePlayer::playback_reset() { - playback_done = false; played_samples = 0; last_slept_samples = 0; last_last_slept_samples = 0; @@ -352,9 +406,7 @@ void RetroWavePlayer::playback_reset() { played_bytes = 0; last_secs = 0; - metadata = Metadata(); - tinyvgm_destroy_gd3(&gd3_info); - tinyvgm_reset(&tvc); + metadata = Metadata(); // reset all pointers back to NULL reset_chips(); usleep(200 * 1000); } diff --git a/Player/Player.hpp b/Player/Player.hpp index 001d163..dae29c8 100644 --- a/Player/Player.hpp +++ b/Player/Player.hpp @@ -119,6 +119,7 @@ class RetroWavePlayer { // File I/O std::vector file_buf; + uint32_t file_pos; // Controls struct termios term_state; @@ -144,11 +145,9 @@ class RetroWavePlayer { size_t played_samples = 0, last_slept_samples = 0, last_last_slept_samples = 0, total_samples = 0; size_t played_bytes = 0, last_secs = 0, bytes_per_sec = 0; uint64_t last_slept_usecs = 0; - bool playback_done = false; bool sn76489_dual = false; // Metadata - TinyVGMGd3Info gd3_info = {0}; struct Metadata { std::string title, album, system_name, composer, release_date, converter, note; std::string title_jp, album_jp, system_name_jp, composer_jp; @@ -157,6 +156,8 @@ class RetroWavePlayer { timespec sleep_end; TinyVGMContext tvc; + uint32_t gd3_offset_abs; + uint32_t data_offset_abs; RetroWaveContext rtctx; std::unordered_set disabled_vgm_commands; @@ -194,9 +195,7 @@ class RetroWavePlayer { // Metadata - static void char16_to_string(std::string& str, int16_t *c16); - void gd3_to_info(TinyVGMGd3Info *g); - + static void char16_to_string(std::string& str, int16_t *c16, uint32_t memsize); // Controls static void term_attr_disable_buffering(); @@ -212,13 +211,13 @@ class RetroWavePlayer { void reset_chips(); void flush_chips(); - static int callback_header_total_samples(void *userp, uint8_t value, const void *buf, uint32_t len); - static int callback_header_sn76489(void *userp, uint8_t value, const void *buf, uint32_t len); - static int callback_header_done(void *userp, uint8_t value, const void *buf, uint32_t len); - static int callback_playback_done(void *userp, uint8_t value, const void *buf, uint32_t len); + static int callback_header_total_samples(void *userp, uint32_t value); + static int callback_header_sn76489(void *userp, uint32_t value); + static int callback_header_done(void *userp); static int callback_saa1099(void *userp, uint8_t value, const void *buf, uint32_t len); static int callback_opl2(void *userp, uint8_t value, const void *buf, uint32_t len); + static int callback_opl2_dual(void *userp, uint8_t value, const void *buf, uint32_t len); static int callback_opl3_port0(void *userp, uint8_t value, const void *buf, uint32_t len); static int callback_opl3_port1(void *userp, uint8_t value, const void *buf, uint32_t len); static int callback_sn76489_port0(void *userp, uint8_t value, const void *buf, uint32_t len); diff --git a/Player/RegMap.cpp b/Player/RegMap.cpp index f10b244..b794ba7 100644 --- a/Player/RegMap.cpp +++ b/Player/RegMap.cpp @@ -48,7 +48,7 @@ void RetroWavePlayer::regmap_insert(int idx, uint8_t reg, uint8_t val) { auto &v = reg_map[idx]; if (v.size() < (reg+1)) { - v.resize(reg+1); + v.resize((reg+1+15) & ~15); // round up to nearest 16, so that osd_show_regmaps() does not access invalid memory } v[reg] = val; @@ -61,7 +61,6 @@ void RetroWavePlayer::regmap_sn76489_insert(uint8_t chip_idx, uint8_t data) { cur_regmap.used = true; - cur_regmap.is_latch = (data >> 7) & 0x1; if (cur_regmap.is_latch) { diff --git a/Player/SoundDriver.cpp b/Player/SoundDriver.cpp index 415e291..585d9e2 100644 --- a/Player/SoundDriver.cpp +++ b/Player/SoundDriver.cpp @@ -66,25 +66,23 @@ void RetroWavePlayer::flush_chips() { retrowave_flush(&rtctx); } -int RetroWavePlayer::callback_header_total_samples(void *userp, uint8_t value, const void *buf, uint32_t len) { +int RetroWavePlayer::callback_header_total_samples(void *userp, uint32_t value) { auto *ctx = (RetroWavePlayer *)userp; - assert(len == 4); - ctx->total_samples = *((uint32_t *) buf); + ctx->total_samples = value; return TinyVGM_OK; } -int RetroWavePlayer::callback_header_sn76489(void *userp, uint8_t value, const void *buf, uint32_t len) { +int RetroWavePlayer::callback_header_sn76489(void *userp, uint32_t value) { auto *ctx = (RetroWavePlayer *)userp; - assert(len == 4); - ctx->sn76489_dual = *((uint32_t *) buf) >> 31; + ctx->sn76489_dual = value >> 1; return TinyVGM_OK; } -int RetroWavePlayer::callback_header_done(void *userp, uint8_t value, const void *buf, uint32_t len) { +int RetroWavePlayer::callback_header_done(void *userp) { auto *ctx = (RetroWavePlayer *)userp; clock_gettime(RETROWAVE_PLAYER_TIME_REF, &ctx->sleep_end); @@ -92,14 +90,6 @@ int RetroWavePlayer::callback_header_done(void *userp, uint8_t value, const void return TinyVGM_OK; } -int RetroWavePlayer::callback_playback_done(void *userp, uint8_t value, const void *buf, uint32_t len) { - auto *ctx = (RetroWavePlayer *)userp; - - ctx->playback_done = true; - - return TinyVGM_OK; -} - int RetroWavePlayer::callback_saa1099(void *userp, uint8_t value, const void *buf, uint32_t len) { auto *ctx = (RetroWavePlayer *)userp; @@ -131,6 +121,22 @@ int RetroWavePlayer::callback_opl2(void *userp, uint8_t value, const void *buf, return TinyVGM_OK; } +int RetroWavePlayer::callback_opl2_dual(void *userp, uint8_t value, const void *buf, uint32_t len) { + auto *ctx = (RetroWavePlayer *)userp; + + assert(len == 2); + + uint8_t reg = ((uint8_t *) buf)[0]; + uint8_t val = ((uint8_t *) buf)[1]; + + ctx->regmap_insert(value, reg, val); + retrowave_opl3_queue_port1(&ctx->rtctx, reg, val); + ctx->single_frame_hook(); + + + return TinyVGM_OK; +} + int RetroWavePlayer::callback_opl3_port0(void *userp, uint8_t value, const void *buf, uint32_t len) { auto *ctx = (RetroWavePlayer *)userp; @@ -291,7 +297,7 @@ int RetroWavePlayer::flush_and_sleep(uint32_t sleep_samples) { break; case NEXT: case PREV: - return TinyVGM_NO; + return TinyVGM_ECANCELED; case SINGLE_FRAME: single_step = true; break;