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)
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:

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;
// 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 :)

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.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<uint32_t> 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");
}

View file

@ -7,15 +7,6 @@
#include <string>
#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 {
uint32_t frame;
int type, data;
@ -127,10 +118,30 @@ struct opm_channel_rawdata_t {
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 {
// 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<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
std::vector<std::vector<opm_channel_record_t>> opmpacked;
// 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;
// -----------------------------------

View file

@ -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:

View file

@ -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));