bump file format version, revert backref resolve changes
This commit is contained in:
parent
c6af5d02ac
commit
e4d8dfe028
|
@ -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:
|
||||
|
|
|
@ -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 :)
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
// -----------------------------------
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,15 +817,27 @@ 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));
|
||||
ctx->estimate.max_delay_freq = 400.0;
|
||||
|
|
Loading…
Reference in a new issue