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)
|
#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:
|
||||||
|
|
|
@ -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 :)
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue