bump file format version, revert backref resolve changes

This commit is contained in:
wbcbz7 2024-06-08 02:48:44 +07:00
parent c6af5d02ac
commit e4d8dfe028
6 changed files with 100 additions and 73 deletions

View file

@ -3,11 +3,23 @@
#pragma pack(push, 1) #pragma pack(push, 1)
enum {
OPM_FORMAT_VERSION = 0x0002
};
struct opm_header_stream_desc_t { struct opm_header_stream_desc_t {
//uint16_t ptr; // offset to data stream in paragraphs (bytes*16) //uint16_t ptr; // offset to data stream in paragraphs (bytes*16)
uint16_t size; // stream data size in bytes 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 { struct opm_header_t {
char magic[4]; // "OPM\x1A" char magic[4]; // "OPM\x1A"
@ -18,12 +30,13 @@ struct opm_header_t {
}; };
uint16_t v; uint16_t v;
} version; } version;
uint16_t flags; // reserved uint16_t flags; // see above
uint16_t frame_rate; // [hz] = 0x1234dd/frame_rate uint16_t frame_rate; // [hz] = 0x1234dd/frame_rate
uint8_t callstack_depth; // reserved, 0 at this moment 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: // OPM v0 stream data:

View file

@ -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; if (io->read(io, &ctx->header, sizeof(ctx->header)) != sizeof(ctx->header)) return OPMPLAY_ERR_IO;
// and validate it // 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; return OPMPLAY_ERR_BAD_FILE_STRUCTURE;
// done for now, waiting for opmplay_load_module :) // done for now, waiting for opmplay_load_module :)

View file

@ -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.flags |= OPM_CHAN_BACKREF;
rec.distance_frames = match.distance_frames; rec.distance_frames = match.distance_frames;
rec.frames_to_play = match.logic_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!) // fill dummy rawdata (will resolve this later!)
rec.rawdata.clear(); rec.rawdata.clear();
rec.rawdata.push_back(OPM_STREAM_BACKREF | (backref_dist >> 8)); rec.rawdata.push_back(OPM_STREAM_BACKREF);
rec.rawdata.push_back(backref_dist & 0xFF); rec.rawdata.push_back(0);
rec.rawdata.push_back(rec.frames_to_play); rec.rawdata.push_back(rec.frames_to_play);
ctx->opmpacked[ch].push_back(rec); ctx->opmpacked[ch].push_back(rec);
pos += match.total_frames; // skip matched data pos += match.total_frames; // skip matched data
@ -159,8 +157,11 @@ void opm_compress(opm_convert_context_t* ctx) {
} backref_len; } backref_len;
switch (ctx->flags.compress_level) { switch (ctx->flags.compress_level) {
case 2: backref_len.min = 2; backref_len.max = 16; break; // bruteforced case 2: backref_len.min = 2; backref_len.max = 16; break; // bruteforced
case 1: case 1: backref_len.min = backref_len.max = 4; break; // fixed
default: backref_len.min = backref_len.max = 4; break; // fixed case 0:
default:
ctx->flags.max_stack_depth = 0;
break;
}; };
int ch = 0; int ch = 0;
@ -183,11 +184,26 @@ void opm_compress(opm_convert_context_t* ctx) {
} }
// recompress if current != best // recompress if current != best
if (sz - 1 != cur_backref_len.best) opm_compress_channel(ctx, ch, cur_backref_len.best); if (sz - 1 != cur_backref_len.best) opm_compress_channel(ctx, ch, cur_backref_len.best);
ch++; // calculate byte offsets for each frame
std::vector<uint32_t> ch_bytepos;
if (ctx->flags.verbosity < 2) { uint32_t bytepos_cur = 0;
printf("."); fflush(stdout); 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"); printf("done\n");
} }

View file

@ -7,15 +7,6 @@
#include <string> #include <string>
#include <map> #include <map>
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 { struct opm_control_track_t {
uint32_t frame; uint32_t frame;
int type, data; int type, data;
@ -127,10 +118,30 @@ struct opm_channel_rawdata_t {
std::vector<uint8_t> data; std::vector<uint8_t> data;
}; };
struct opm_channel_t {
// OPL register stream (parsed from VGM)
std::vector<opm_channel_rawdata_t> opl;
// records
std::vector<opm_channel_record_t> records;
// packed records
std::vector<opm_channel_record_t> records_packed;
// raw stream bytes
std::vector<uint8_t> stream_bytes;
// stream descriptor
opm_header_stream_desc_t streamdesc;
};
struct opm_convert_context_t { struct opm_convert_context_t {
// VGM context // VGM context
vgm_context_t vgm; vgm_context_t vgm;
// chip type
uint32_t chip_type; // OPM_FLAG_CHIP_*
// conversion flags // conversion flags
struct { struct {
int compress_level; int compress_level;
@ -153,15 +164,11 @@ struct opm_convert_context_t {
// channel records // channel records
std::vector<std::vector<opm_channel_record_t>> opmrecords; std::vector<std::vector<opm_channel_record_t>> opmrecords;
std::vector<opm_control_track_t> opmctrl;
// channel bytes per blabla
std::vector<int> opmrecords_bytes;
// compressed streams // compressed streams
std::vector<std::vector<opm_channel_record_t>> opmpacked; std::vector<std::vector<opm_channel_record_t>> opmpacked;
// final OPM stream // final OPM stream
std::vector<std::vector<size_t>> opmstream_pos; // positions for each events, used for backref pos calc
std::vector<std::vector<uint8_t>> opmstream; std::vector<std::vector<uint8_t>> opmstream;
// ----------------------------------- // -----------------------------------

View file

@ -3,11 +3,23 @@
#pragma pack(push, 1) #pragma pack(push, 1)
enum {
OPM_FORMAT_VERSION = 0x0002
};
struct opm_header_stream_desc_t { struct opm_header_stream_desc_t {
//uint16_t ptr; // offset to data stream in paragraphs (bytes*16) //uint16_t ptr; // offset to data stream in paragraphs (bytes*16)
uint16_t size; // stream data size in bytes 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 { struct opm_header_t {
char magic[4]; // "OPM\x1A" char magic[4]; // "OPM\x1A"
@ -18,12 +30,13 @@ struct opm_header_t {
}; };
uint16_t v; uint16_t v;
} version; } version;
uint16_t flags; // reserved uint16_t flags; // see above
uint16_t frame_rate; // [hz] = 0x1234dd/frame_rate uint16_t frame_rate; // [hz] = 0x1234dd/frame_rate
uint8_t callstack_depth; // reserved, 0 at this moment 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: // OPM v0 stream data:

View file

@ -283,41 +283,6 @@ int opm_requantize(opm_convert_context_t* ctx) {
} }
#if 1 #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 // 4th step - write OPM file
@ -328,12 +293,12 @@ int opm_write_file(opm_convert_context_t* ctx) {
// fill write info // fill write info
auto round_to_para = [](uint32_t a) -> pad_info_t { return { (a + 15) & ~15 , ((a + 15) & ~15) - a }; }; 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); memcpy(ctx->opmfile.header.magic, "OPM\x1A", 4);
ctx->opmfile.header.version.v = 0x0001; ctx->opmfile.header.version.v = OPM_FORMAT_VERSION;
ctx->opmfile.header.flags = 0; ctx->opmfile.header.flags = ctx->chip_type;
ctx->opmfile.header.callstack_depth = ctx->flags.max_stack_depth; ctx->opmfile.header.callstack_depth = ctx->flags.max_stack_depth;
ctx->opmfile.header.frame_rate = ((double)0x1234DD / (double)(44100 / ctx->delay)); ctx->opmfile.header.frame_rate = ((double)0x1234DD / (double)(44100 / ctx->delay));
ctx->opmfile.header.reserved = 0;
struct opm_write_file_info_t { struct opm_write_file_info_t {
uint32_t pos; uint32_t pos;
@ -387,6 +352,7 @@ int opm_write_file(opm_convert_context_t* ctx) {
} }
fclose(f); fclose(f);
#endif #endif
return 0;
} }
#endif #endif
@ -851,15 +817,27 @@ int main(int argc, char* argv[]) {
ctx->vgm.end = ctx->vgm.header->eofOffset + offsetof(VGMHeader, eofOffset); 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)); 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); // determine chip type and clock rate
printf("frame rate = %d Hz, OPL clock rate = %d Hz\n", ctx->vgm.header->frameRate, oplClockRate); uint32_t oplClockRate;
if (ctx->vgm.header->YMF262_Clock != 0) {
// check if OPL2/3 is present ctx->chip_type = OPM_FLAG_CHIP_OPL3;
if (oplClockRate == 0) { 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"); printf("OPL2/3 data not found!\n");
return 1; 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 // set estimation parameters
ctx->estimate.max_delay_base = (44100.0 / ((double)0x1234DD / 65536)); ctx->estimate.max_delay_base = (44100.0 / ((double)0x1234DD / 65536));
ctx->estimate.max_delay_freq = 400.0; ctx->estimate.max_delay_freq = 400.0;