From e4d8dfe0282d2d7356b262a8e4cda6d5dd5ea4d6 Mon Sep 17 00:00:00 2001 From: wbcbz7 Date: Sat, 8 Jun 2024 02:48:44 +0700 Subject: [PATCH] bump file format version, revert backref resolve changes --- oplplay/lxmplay/opmfile.h | 19 ++++++- oplplay/lxmplay/opmplay.cpp | 2 +- vgm2opl_next/vgm2opl_next/compressor.cpp | 36 ++++++++---- vgm2opl_next/vgm2opl_next/opmctx.h | 33 ++++++----- vgm2opl_next/vgm2opl_next/opmfile.h | 19 ++++++- vgm2opl_next/vgm2opl_next/vgm2opl_next.cpp | 64 +++++++--------------- 6 files changed, 100 insertions(+), 73 deletions(-) diff --git a/oplplay/lxmplay/opmfile.h b/oplplay/lxmplay/opmfile.h index 3006a1f..a965721 100644 --- a/oplplay/lxmplay/opmfile.h +++ b/oplplay/lxmplay/opmfile.h @@ -3,11 +3,23 @@ #pragma pack(push, 1) +enum { + OPM_FORMAT_VERSION = 0x0002 +}; + struct opm_header_stream_desc_t { //uint16_t ptr; // offset to data stream in paragraphs (bytes*16) uint16_t size; // stream data size in bytes }; +enum { + OPM_FLAG_CHIP_OPL2 = (0 << 0), + OPM_FLAG_CHIP_OPL3 = (1 << 0), + + OPM_FLAG_CHIP_TYPE = (3 << 0), + + OPM_FLAG_PERCUSSION_MODE = (1 << 2), +}; struct opm_header_t { char magic[4]; // "OPM\x1A" @@ -18,12 +30,13 @@ struct opm_header_t { }; uint16_t v; } version; - uint16_t flags; // reserved + uint16_t flags; // see above uint16_t frame_rate; // [hz] = 0x1234dd/frame_rate uint8_t callstack_depth; // reserved, 0 at this moment - uint8_t reserved; + uint8_t channels; // must be either 9 or 18! + uint32_t channel_mask; // used channel mask, LSB-ch 0 - // opm_header_stream_desc_t stream[9 + 1]; // first is control stream + // opm_header_stream_desc_t stream[18 + 1]; // first is control stream }; // OPM v0 stream data: diff --git a/oplplay/lxmplay/opmplay.cpp b/oplplay/lxmplay/opmplay.cpp index 5cc37f8..930c386 100644 --- a/oplplay/lxmplay/opmplay.cpp +++ b/oplplay/lxmplay/opmplay.cpp @@ -75,7 +75,7 @@ int opmplay_load_header(opmplay_context_t* ctx, opmplay_io_t* io) { if (io->read(io, &ctx->header, sizeof(ctx->header)) != sizeof(ctx->header)) return OPMPLAY_ERR_IO; // and validate it - if ((opmplay_memcmp(ctx->header.magic, "OPM\x1A", sizeof(ctx->header.magic)))) + if ((opmplay_memcmp(ctx->header.magic, "OPM\x1A", sizeof(ctx->header.magic))) || (ctx->header.version.v != OPM_FORMAT_VERSION)) return OPMPLAY_ERR_BAD_FILE_STRUCTURE; // done for now, waiting for opmplay_load_module :) diff --git a/vgm2opl_next/vgm2opl_next/compressor.cpp b/vgm2opl_next/vgm2opl_next/compressor.cpp index 89eb653..34e2453 100644 --- a/vgm2opl_next/vgm2opl_next/compressor.cpp +++ b/vgm2opl_next/vgm2opl_next/compressor.cpp @@ -130,12 +130,10 @@ uint32_t opm_compress_channel(opm_convert_context_t* ctx, int ch, int min_backre rec.flags |= OPM_CHAN_BACKREF; rec.distance_frames = match.distance_frames; rec.frames_to_play = match.logic_frames; - - uint32_t backref_dist = rec.byte_stamp - src_ch[pos - rec.distance_frames].byte_stamp; // fill dummy rawdata (will resolve this later!) rec.rawdata.clear(); - rec.rawdata.push_back(OPM_STREAM_BACKREF | (backref_dist >> 8)); - rec.rawdata.push_back(backref_dist & 0xFF); + rec.rawdata.push_back(OPM_STREAM_BACKREF); + rec.rawdata.push_back(0); rec.rawdata.push_back(rec.frames_to_play); ctx->opmpacked[ch].push_back(rec); pos += match.total_frames; // skip matched data @@ -159,8 +157,11 @@ void opm_compress(opm_convert_context_t* ctx) { } backref_len; switch (ctx->flags.compress_level) { case 2: backref_len.min = 2; backref_len.max = 16; break; // bruteforced - case 1: - default: backref_len.min = backref_len.max = 4; break; // fixed + case 1: backref_len.min = backref_len.max = 4; break; // fixed + case 0: + default: + ctx->flags.max_stack_depth = 0; + break; }; int ch = 0; @@ -183,11 +184,26 @@ void opm_compress(opm_convert_context_t* ctx) { } // recompress if current != best if (sz - 1 != cur_backref_len.best) opm_compress_channel(ctx, ch, cur_backref_len.best); - ch++; - - if (ctx->flags.verbosity < 2) { - printf("."); fflush(stdout); + // calculate byte offsets for each frame + std::vector ch_bytepos; + uint32_t bytepos_cur = 0; + for (int f = 0; f < ctx->opmpacked[ch].size(); f++) { + ch_bytepos.push_back(bytepos_cur); + bytepos_cur += ctx->opmpacked[ch][f].rawdata.size(); } + // resolve back references + for (int f = 0; f < ctx->opmpacked[ch].size(); f++) { + // fixup back reference (if any) + if (ctx->opmpacked[ch][f].flags & OPM_CHAN_BACKREF) { + uint32_t backref_dist = ch_bytepos[f] - ch_bytepos[f - ctx->opmpacked[ch][f].distance_frames]; + ctx->opmpacked[ch][f].rawdata[0] = OPM_STREAM_BACKREF | (backref_dist >> 8); + ctx->opmpacked[ch][f].rawdata[1] = (backref_dist & 0xFF); + } + } + ch++; +#ifndef ULTRA_DEBUG + printf("."); fflush(stdout); +#endif } printf("done\n"); } diff --git a/vgm2opl_next/vgm2opl_next/opmctx.h b/vgm2opl_next/vgm2opl_next/opmctx.h index f4dc3a0..e6705a9 100644 --- a/vgm2opl_next/vgm2opl_next/opmctx.h +++ b/vgm2opl_next/vgm2opl_next/opmctx.h @@ -7,15 +7,6 @@ #include #include -enum { - LXM_CTRL_START = 0, - LXM_CTRL_END, - LXM_CTRL_SET_FRAME_RATE, - LXM_CTRL_EVENT_MASK = (1 << 16) - 1, - - LXM_CTRL_LOOP = (1 << 16), -}; - struct opm_control_track_t { uint32_t frame; int type, data; @@ -127,10 +118,30 @@ struct opm_channel_rawdata_t { std::vector data; }; +struct opm_channel_t { + // OPL register stream (parsed from VGM) + std::vector opl; + + // records + std::vector records; + + // packed records + std::vector records_packed; + + // raw stream bytes + std::vector stream_bytes; + + // stream descriptor + opm_header_stream_desc_t streamdesc; +}; + struct opm_convert_context_t { // VGM context vgm_context_t vgm; + // chip type + uint32_t chip_type; // OPM_FLAG_CHIP_* + // conversion flags struct { int compress_level; @@ -153,15 +164,11 @@ struct opm_convert_context_t { // channel records std::vector> opmrecords; - std::vector opmctrl; - // channel bytes per blabla - std::vector opmrecords_bytes; // compressed streams std::vector> opmpacked; // final OPM stream - std::vector> opmstream_pos; // positions for each events, used for backref pos calc std::vector> opmstream; // ----------------------------------- diff --git a/vgm2opl_next/vgm2opl_next/opmfile.h b/vgm2opl_next/vgm2opl_next/opmfile.h index 3006a1f..a965721 100644 --- a/vgm2opl_next/vgm2opl_next/opmfile.h +++ b/vgm2opl_next/vgm2opl_next/opmfile.h @@ -3,11 +3,23 @@ #pragma pack(push, 1) +enum { + OPM_FORMAT_VERSION = 0x0002 +}; + struct opm_header_stream_desc_t { //uint16_t ptr; // offset to data stream in paragraphs (bytes*16) uint16_t size; // stream data size in bytes }; +enum { + OPM_FLAG_CHIP_OPL2 = (0 << 0), + OPM_FLAG_CHIP_OPL3 = (1 << 0), + + OPM_FLAG_CHIP_TYPE = (3 << 0), + + OPM_FLAG_PERCUSSION_MODE = (1 << 2), +}; struct opm_header_t { char magic[4]; // "OPM\x1A" @@ -18,12 +30,13 @@ struct opm_header_t { }; uint16_t v; } version; - uint16_t flags; // reserved + uint16_t flags; // see above uint16_t frame_rate; // [hz] = 0x1234dd/frame_rate uint8_t callstack_depth; // reserved, 0 at this moment - uint8_t reserved; + uint8_t channels; // must be either 9 or 18! + uint32_t channel_mask; // used channel mask, LSB-ch 0 - // opm_header_stream_desc_t stream[9 + 1]; // first is control stream + // opm_header_stream_desc_t stream[18 + 1]; // first is control stream }; // OPM v0 stream data: diff --git a/vgm2opl_next/vgm2opl_next/vgm2opl_next.cpp b/vgm2opl_next/vgm2opl_next/vgm2opl_next.cpp index 387a881..0a511c3 100644 --- a/vgm2opl_next/vgm2opl_next/vgm2opl_next.cpp +++ b/vgm2opl_next/vgm2opl_next/vgm2opl_next.cpp @@ -283,41 +283,6 @@ int opm_requantize(opm_convert_context_t* ctx) { } #if 1 -// -------------------------- -// 3rd step - output very very simple and uncompressed format (just for testing :) -// FF - end of frame -// FE - end of stream -// FD nn - wait N+2 frames -// rr dd - write dd to register rr (adjusted for current channel of course) -int opm_dump_simple(opm_convert_context_t* ctx) { - ctx->oplchan_out.resize(1 + 9); // TODO: FIXME for OPL3 support!! - for (int ch = 0; ch < (1 + 9); ch++) { - // dequeue the streams - int delay_old = 1; - for (int f = 0; f < ctx->oplchan[ch].size(); f++) { - // copy raw register data - ctx->oplchan_out[ch].insert(ctx->oplchan_out[ch].end(), ctx->oplchan[ch][f].data.begin(), ctx->oplchan[ch][f].data.end()); - // post either delay or end of stream - if ((f == ctx->oplchan[ch].size() - 1)) { - // post end of steram - if (ch > 0) ctx->oplchan_out[ch].push_back(0xFE); - } else { - int delay = ctx->oplchan[ch][f + 1].frame - ctx->oplchan[ch][f].frame; - if (delay_old != delay) { - delay_old = delay; - do { - ctx->oplchan_out[ch].push_back(OPM_STREAM_DELAY_INT16); - ctx->oplchan_out[ch].push_back(delay & 0xFF); - ctx->oplchan_out[ch].push_back(delay >> 8); - delay -= 65536; - } while (delay > 0); - } else ctx->oplchan_out[ch].push_back(0xFF); - } - } - } - - return 0; -} // -------------------------- // 4th step - write OPM file @@ -328,12 +293,12 @@ int opm_write_file(opm_convert_context_t* ctx) { // fill write info auto round_to_para = [](uint32_t a) -> pad_info_t { return { (a + 15) & ~15 , ((a + 15) & ~15) - a }; }; + memset(&ctx->opmfile.header, 0, sizeof(ctx->opmfile.header)); memcpy(ctx->opmfile.header.magic, "OPM\x1A", 4); - ctx->opmfile.header.version.v = 0x0001; - ctx->opmfile.header.flags = 0; + ctx->opmfile.header.version.v = OPM_FORMAT_VERSION; + ctx->opmfile.header.flags = ctx->chip_type; ctx->opmfile.header.callstack_depth = ctx->flags.max_stack_depth; ctx->opmfile.header.frame_rate = ((double)0x1234DD / (double)(44100 / ctx->delay)); - ctx->opmfile.header.reserved = 0; struct opm_write_file_info_t { uint32_t pos; @@ -387,6 +352,7 @@ int opm_write_file(opm_convert_context_t* ctx) { } fclose(f); #endif + return 0; } #endif @@ -851,14 +817,26 @@ int main(int argc, char* argv[]) { ctx->vgm.end = ctx->vgm.header->eofOffset + offsetof(VGMHeader, eofOffset); ctx->vgm.start = ((ctx->vgm.header->version < 0x150) ? 0x40 : ctx->vgm.header->dataOffset + offsetof(VGMHeader, dataOffset)); - auto oplClockRate = std::max(ctx->vgm.header->YM3812_Clock, ctx->vgm.header->YMF262_Clock); - printf("frame rate = %d Hz, OPL clock rate = %d Hz\n", ctx->vgm.header->frameRate, oplClockRate); - - // check if OPL2/3 is present - if (oplClockRate == 0) { + // determine chip type and clock rate + uint32_t oplClockRate; + if (ctx->vgm.header->YMF262_Clock != 0) { + ctx->chip_type = OPM_FLAG_CHIP_OPL3; + oplClockRate = ctx->vgm.header->YMF262_Clock; + } + else if (ctx->vgm.header->YM3812_Clock != 0) { + ctx->chip_type = OPM_FLAG_CHIP_OPL2; + oplClockRate = ctx->vgm.header->YM3812_Clock; + } + else { printf("OPL2/3 data not found!\n"); return 1; } + + printf("frame rate = %d Hz, OPL type = %s, OPL clock rate = %d Hz\n", + ctx->vgm.header->frameRate, + ctx->chip_type == OPM_FLAG_CHIP_OPL3 ? "OPL3" : ctx->chip_type == OPM_FLAG_CHIP_OPL2 ? "OPL2" : "unknown", + oplClockRate + ); // set estimation parameters ctx->estimate.max_delay_base = (44100.0 / ((double)0x1234DD / 65536));