update 4-operator mode enable logic
This commit is contained in:
parent
a6b1293d37
commit
d4e88aebea
|
@ -169,7 +169,7 @@ int synth_render(int16_t* buffer, uint32_t num_samples) {
|
||||||
samples_to_render -= opmctx.delay_count;
|
samples_to_render -= opmctx.delay_count;
|
||||||
|
|
||||||
// parse VGM stream
|
// parse VGM stream
|
||||||
opmplay_tick(&opmctx.opm, &opl3);
|
opmplay_tick(&opmctx.opm);
|
||||||
opmctx.delay_count = (44100.0 / ((double)0x1234DD / opmctx.opm.header.frame_rate));
|
opmctx.delay_count = (44100.0 / ((double)0x1234DD / opmctx.opm.header.frame_rate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -251,7 +251,7 @@ int main(int argc, char* argv[])
|
||||||
|
|
||||||
int rtn;
|
int rtn;
|
||||||
|
|
||||||
if ((rtn = opmplay_init(&opmctx.opm)) != OPMPLAY_ERR_OK) {
|
if ((rtn = opmplay_init(&opmctx.opm, &opl3)) != OPMPLAY_ERR_OK) {
|
||||||
printf("unable to init OPMPlay (error = %d)\n", rtn);
|
printf("unable to init OPMPlay (error = %d)\n", rtn);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,9 +34,9 @@ struct opm_header_t {
|
||||||
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 channels; // must be either 9 or 18!
|
uint8_t channels; // must be either 9 or 18!
|
||||||
uint32_t channel_mask; // used channel mask, LSB-ch 0
|
uint32_t channel_mask; // used channel mask, LSB = ch 0
|
||||||
|
|
||||||
// opm_header_stream_desc_t stream[18 + 1]; // first is control stream
|
// opm_header_stream_desc_t stream[opm_header_t::channels + 1]; // first is control stream
|
||||||
};
|
};
|
||||||
|
|
||||||
// OPM v0 stream data:
|
// OPM v0 stream data:
|
||||||
|
@ -48,6 +48,7 @@ enum {
|
||||||
OPM_STREAM_SET_FRAME_RATE = 0xFB, // word rate (as in opm_header_t::frame_rate)
|
OPM_STREAM_SET_FRAME_RATE = 0xFB, // word rate (as in opm_header_t::frame_rate)
|
||||||
OPM_STREAM_LOOP = 0xFA, // set loop point here
|
OPM_STREAM_LOOP = 0xFA, // set loop point here
|
||||||
|
|
||||||
|
OPM_4OP_TRIGGER = 0xF4, // 4-op mode on/off
|
||||||
OPM_KEY_TRIGGER = 0xF0, // set key on/off + optionally end of frame
|
OPM_KEY_TRIGGER = 0xF0, // set key on/off + optionally end of frame
|
||||||
|
|
||||||
// delay commands
|
// delay commands
|
||||||
|
@ -68,6 +69,9 @@ enum {
|
||||||
OPM_CTRL_REG_SET = 0x80, // 80..BF - set control registers
|
OPM_CTRL_REG_SET = 0x80, // 80..BF - set control registers
|
||||||
|
|
||||||
// flags
|
// flags
|
||||||
|
OPM_4OP_OFF = (0 << 0),
|
||||||
|
OPM_4OP_ON = (1 << 0),
|
||||||
|
|
||||||
OPM_KEY_OFF = (0 << 0),
|
OPM_KEY_OFF = (0 << 0),
|
||||||
OPM_KEY_ON = (1 << 0),
|
OPM_KEY_ON = (1 << 0),
|
||||||
OPM_KEY_END_OF_FRAME = (1 << 1),
|
OPM_KEY_END_OF_FRAME = (1 << 1),
|
||||||
|
|
|
@ -29,10 +29,11 @@ static uint32_t opmplay_file_seek(opmplay_io_t* io, uint32_t pos) {
|
||||||
|
|
||||||
// -------------------------
|
// -------------------------
|
||||||
|
|
||||||
int opmplay_init(opmplay_context_t* ctx) {
|
int opmplay_init(opmplay_context_t* ctx, opl3_chip* chip) {
|
||||||
if (ctx == NULL) return OPMPLAY_ERR_NULLPTR;
|
if ((ctx == NULL) || (chip == NULL)) return OPMPLAY_ERR_NULLPTR;
|
||||||
|
|
||||||
opmplay_memset(ctx, 0, sizeof(opmplay_context_t));
|
opmplay_memset(ctx, 0, sizeof(opmplay_context_t));
|
||||||
|
ctx->chip = chip;
|
||||||
|
|
||||||
return OPMPLAY_ERR_OK;
|
return OPMPLAY_ERR_OK;
|
||||||
}
|
}
|
||||||
|
@ -139,9 +140,35 @@ int opmplay_loop(opmplay_context_t* ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int opmplay_rewind(opmplay_context_t* ctx) {
|
int opmplay_rewind(opmplay_context_t* ctx) {
|
||||||
|
// set chip mode
|
||||||
|
switch (ctx->header.flags & OPM_FLAG_CHIP_TYPE) {
|
||||||
|
case OPM_FLAG_CHIP_OPL3:
|
||||||
|
// set OPL3 mode
|
||||||
|
OPL3_WriteRegBuffered(ctx->chip, 0x105, 0x01);
|
||||||
|
// disable 4-op
|
||||||
|
OPL3_WriteRegBuffered(ctx->chip, 0x104, 0x00);
|
||||||
|
// send key off for every channel
|
||||||
|
for (int ch = 0; ch < 9; ch++) {
|
||||||
|
OPL3_WriteRegBuffered(ctx->chip, 0x0B0 + ch, 0x00);
|
||||||
|
OPL3_WriteRegBuffered(ctx->chip, 0x1B0 + ch, 0x00);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OPM_FLAG_CHIP_OPL2:
|
||||||
|
// enable waveforms
|
||||||
|
OPL3_WriteRegBuffered(ctx->chip, 0x001, 0x20);
|
||||||
|
// send key off for every channel
|
||||||
|
for (int ch = 0; ch < 9; ch++) {
|
||||||
|
OPL3_WriteRegBuffered(ctx->chip, 0x0B0 + ch, 0x00);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: return OPMPLAY_ERR_DEVICE;
|
||||||
|
}
|
||||||
|
|
||||||
for (int ch = 0; ch < ctx->header.channels + 1; ch++) {
|
for (int ch = 0; ch < ctx->header.channels + 1; ch++) {
|
||||||
ctx->channels[ch].stream.loop = ctx->channels[ch].stream.data;
|
ctx->channels[ch].stream.loop = ctx->channels[ch].stream.data;
|
||||||
}
|
}
|
||||||
|
ctx->_4op = 0;
|
||||||
|
|
||||||
opmplay_loop(ctx);
|
opmplay_loop(ctx);
|
||||||
|
|
||||||
|
@ -178,7 +205,7 @@ static uint32_t opmplay_set_delay(uint8_t** data) {
|
||||||
return delay;
|
return delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
// translation tables (TODO: OPL3)
|
// translation tables
|
||||||
static int opmplay_opregs_channel_offset[] = {
|
static int opmplay_opregs_channel_offset[] = {
|
||||||
0x000, 0x001, 0x002, 0x008, 0x009, 0x00A, 0x010, 0x011, 0x012,
|
0x000, 0x001, 0x002, 0x008, 0x009, 0x00A, 0x010, 0x011, 0x012,
|
||||||
0x100, 0x101, 0x102, 0x108, 0x109, 0x10A, 0x110, 0x111, 0x112,
|
0x100, 0x101, 0x102, 0x108, 0x109, 0x10A, 0x110, 0x111, 0x112,
|
||||||
|
@ -187,79 +214,25 @@ static int opmplay_regs_channel_offset[] = {
|
||||||
0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, 0x008,
|
0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, 0x008,
|
||||||
0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108,
|
0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108,
|
||||||
};
|
};
|
||||||
|
static int opmplay_4op_bit_offset[] = {
|
||||||
|
1<<0, 1<<1, 1<<2, 0, 0, 0, 0, 0, 0,
|
||||||
|
1<<3, 1<<4, 1<<5, 0, 0, 0, 0, 0, 0,
|
||||||
|
};
|
||||||
|
static int opmplay_ch_parsing_order[] = {
|
||||||
|
3, 0, 4, 1, 5, 2, 6, 7, 8,
|
||||||
|
12, 9, 13, 10, 14, 11, 15, 16, 17
|
||||||
|
};
|
||||||
|
|
||||||
int opmplay_tick(opmplay_context_t* ctx, opl3_chip* opl3) {
|
int opmplay_tick(opmplay_context_t* ctx) {
|
||||||
|
|
||||||
int ch = 0;
|
int ch = 0;
|
||||||
int rtn = OPMPLAY_ERR_OK;
|
int rtn = OPMPLAY_ERR_OK;
|
||||||
uint32_t newdelay = 0;
|
uint32_t newdelay = 0;
|
||||||
|
|
||||||
// process control stream
|
|
||||||
{
|
|
||||||
opmplay_channel_context_t* chctx = ctx->channels + 0;
|
|
||||||
uint8_t* data = chctx->stream.ptr;
|
|
||||||
bool isRun = true;
|
|
||||||
if (--chctx->stream.delay == 0) {
|
|
||||||
while (isRun) {
|
|
||||||
if ((*(data) & 0xE0) == OPM_CTRL_REG_SET) {
|
|
||||||
int mask = *data;
|
|
||||||
data++;
|
|
||||||
if (mask & OPM_CTRL_SET_REG01) OPL3_WriteRegBuffered(opl3, 0x001, *data++);
|
|
||||||
if (mask & OPM_CTRL_SET_REG08) OPL3_WriteRegBuffered(opl3, 0x008, *data++);
|
|
||||||
if (mask & OPM_CTRL_SET_REG105) OPL3_WriteRegBuffered(opl3, 0x105, *data++);
|
|
||||||
if (mask & OPM_CTRL_SET_REG104) OPL3_WriteRegBuffered(opl3, 0x104, *data++);
|
|
||||||
if (mask & OPM_CTRL_SET_REGBD) OPL3_WriteRegBuffered(opl3, 0x0BD, *data++);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for common stuff
|
|
||||||
switch (*data) {
|
|
||||||
// end of stream - rewind everything
|
|
||||||
case OPM_STREAM_END:
|
|
||||||
opmplay_loop(ctx);
|
|
||||||
isRun = false;
|
|
||||||
return OPMPLAY_ERR_END_OF_STREAM;
|
|
||||||
// just an NOP, break
|
|
||||||
case OPM_STREAM_NEW_ORDER:
|
|
||||||
case OPM_STREAM_NOP:
|
|
||||||
data++;
|
|
||||||
break;
|
|
||||||
case OPM_STREAM_LOOP:
|
|
||||||
// save loop point
|
|
||||||
ctx->pos.looped = ctx->pos.frame;
|
|
||||||
chctx->stream.loop = data;
|
|
||||||
data++;
|
|
||||||
break;
|
|
||||||
// set new frame rate
|
|
||||||
case OPM_STREAM_SET_FRAME_RATE:
|
|
||||||
ctx->header.frame_rate = *(uint16_t*)(data + 1); data += 3;
|
|
||||||
break;
|
|
||||||
case OPM_STREAM_END_FRAME:
|
|
||||||
// end of frame - special case here
|
|
||||||
data++;
|
|
||||||
isRun = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
// test for delay
|
|
||||||
newdelay = opmplay_set_delay(&data);
|
|
||||||
if (newdelay > 0) {
|
|
||||||
chctx->stream.reload = newdelay;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("unknonw token %02x!\n", *data);
|
|
||||||
return OPMPLAY_ERR_BAD_FILE_STRUCTURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chctx->stream.delay = chctx->stream.reload;
|
|
||||||
chctx->stream.ptr = data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse channel streams
|
// parse channel streams
|
||||||
for (int ch = 0; ch < ctx->header.channels; ch++) {
|
for (int i = 0; i < ctx->header.channels; i++) {
|
||||||
|
int ch = opmplay_ch_parsing_order[i]; // mangle channel order to make 4op mode work
|
||||||
|
|
||||||
opmplay_channel_context_t* chctx = ctx->channels + ch + 1;
|
opmplay_channel_context_t* chctx = ctx->channels + ch + 1;
|
||||||
uint8_t* data = chctx->stream.ptr; if (data == NULL) continue;
|
uint8_t* data = chctx->stream.ptr; if (data == NULL) continue;
|
||||||
bool isRun = true;
|
bool isRun = true;
|
||||||
|
@ -270,32 +243,40 @@ int opmplay_tick(opmplay_context_t* ctx, opl3_chip* opl3) {
|
||||||
int mask = *data;
|
int mask = *data;
|
||||||
int op = (mask & OPM_CMD00_SELECT_OPERATOR ? 3 : 0) + opmplay_opregs_channel_offset[ch];
|
int op = (mask & OPM_CMD00_SELECT_OPERATOR ? 3 : 0) + opmplay_opregs_channel_offset[ch];
|
||||||
data++;
|
data++;
|
||||||
if (mask & OPM_CMD00_SET_MULT) OPL3_WriteRegBuffered(opl3, 0x20 + op, *data++);
|
if (mask & OPM_CMD00_SET_MULT) OPL3_WriteRegBuffered(ctx->chip, 0x20 + op, *data++);
|
||||||
if (mask & OPM_CMD00_SET_TL) OPL3_WriteRegBuffered(opl3, 0x40 + op, *data++);
|
if (mask & OPM_CMD00_SET_TL) OPL3_WriteRegBuffered(ctx->chip, 0x40 + op, *data++);
|
||||||
if (mask & OPM_CMD00_SET_AD) OPL3_WriteRegBuffered(opl3, 0x60 + op, *data++);
|
if (mask & OPM_CMD00_SET_AD) OPL3_WriteRegBuffered(ctx->chip, 0x60 + op, *data++);
|
||||||
if (mask & OPM_CMD00_SET_SR) OPL3_WriteRegBuffered(opl3, 0x80 + op, *data++);
|
if (mask & OPM_CMD00_SET_SR) OPL3_WriteRegBuffered(ctx->chip, 0x80 + op, *data++);
|
||||||
if (mask & OPM_CMD00_SET_WAVEFORM) OPL3_WriteRegBuffered(opl3, 0xE0 + op, *data++);
|
if (mask & OPM_CMD00_SET_WAVEFORM) OPL3_WriteRegBuffered(ctx->chip, 0xE0 + op, *data++);
|
||||||
if (mask & OPM_CMD00_END_OF_FRAME) isRun = false;
|
if (mask & OPM_CMD00_END_OF_FRAME) isRun = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ((*(data) & 0xC0) == OPM_SET_FREQ_FB_VOL) {
|
if ((*(data) & 0xC0) == OPM_SET_FREQ_FB_VOL) {
|
||||||
int mask = *data;
|
int mask = *data;
|
||||||
data++;
|
data++;
|
||||||
if (mask & OPM_CMD80_SET_TL0) OPL3_WriteRegBuffered(opl3, 0x40 + opmplay_opregs_channel_offset[ch], *data++);
|
if (mask & OPM_CMD80_SET_TL0) OPL3_WriteRegBuffered(ctx->chip, 0x40 + opmplay_opregs_channel_offset[ch], *data++);
|
||||||
if (mask & OPM_CMD80_SET_TL1) OPL3_WriteRegBuffered(opl3, 0x43 + opmplay_opregs_channel_offset[ch], *data++);
|
if (mask & OPM_CMD80_SET_TL1) OPL3_WriteRegBuffered(ctx->chip, 0x43 + opmplay_opregs_channel_offset[ch], *data++);
|
||||||
if (mask & OPM_CMD80_SET_FEEDBACK) OPL3_WriteRegBuffered(opl3, 0xC0 + opmplay_regs_channel_offset[ch], *data++);
|
if (mask & OPM_CMD80_SET_FEEDBACK) OPL3_WriteRegBuffered(ctx->chip, 0xC0 + opmplay_regs_channel_offset[ch], *data++);
|
||||||
if (mask & OPM_CMD80_SET_FREQ) OPL3_WriteRegBuffered(opl3, 0xA0 + opmplay_regs_channel_offset[ch], *data++);
|
if (mask & OPM_CMD80_SET_FREQ) OPL3_WriteRegBuffered(ctx->chip, 0xA0 + opmplay_regs_channel_offset[ch], *data++);
|
||||||
if (mask & OPM_CMD80_SET_KEYBLOCK) { chctx->block = *data; OPL3_WriteRegBuffered(opl3, 0xB0 + opmplay_regs_channel_offset[ch], *data++); };
|
if (mask & OPM_CMD80_SET_KEYBLOCK) { chctx->block = *data; OPL3_WriteRegBuffered(ctx->chip, 0xB0 + opmplay_regs_channel_offset[ch], *data++); };
|
||||||
if (mask & OPM_CMD80_END_OF_FRAME) isRun = false;
|
if (mask & OPM_CMD80_END_OF_FRAME) isRun = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ((*data & 0xFC) == OPM_KEY_TRIGGER) {
|
if ((*data & 0xFC) == OPM_KEY_TRIGGER) {
|
||||||
if (*data & OPM_KEY_ON) chctx->block |= (1 << 5); else chctx->block &= ~(1 << 5);
|
if (*data & OPM_KEY_ON) chctx->block |= (1 << 5); else chctx->block &= ~(1 << 5);
|
||||||
OPL3_WriteRegBuffered(opl3, 0xB0 + opmplay_regs_channel_offset[ch], chctx->block);
|
OPL3_WriteRegBuffered(ctx->chip, 0xB0 + opmplay_regs_channel_offset[ch], chctx->block);
|
||||||
if (*data & OPM_KEY_END_OF_FRAME) isRun = false;
|
if (*data & OPM_KEY_END_OF_FRAME) isRun = false;
|
||||||
data++;
|
data++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if ((*data & 0xFE) == OPM_4OP_TRIGGER) {
|
||||||
|
if (opmplay_4op_bit_offset[ch] != 0) {
|
||||||
|
if (*data & OPM_4OP_ON) ctx->_4op |= opmplay_4op_bit_offset[ch]; else ctx->_4op &= ~opmplay_4op_bit_offset[ch];
|
||||||
|
OPL3_WriteRegBuffered(ctx->chip, 0x104, ctx->_4op);
|
||||||
|
}
|
||||||
|
data++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if ((*data & 0xF0) == OPM_STREAM_BACKREF) {
|
if ((*data & 0xF0) == OPM_STREAM_BACKREF) {
|
||||||
// back reference, nested call :)
|
// back reference, nested call :)
|
||||||
int distance = ((*(data + 0) & 0x0F) << 8) | (*(data + 1));
|
int distance = ((*(data + 0) & 0x0F) << 8) | (*(data + 1));
|
||||||
|
@ -357,6 +338,88 @@ int opmplay_tick(opmplay_context_t* ctx, opl3_chip* opl3) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// process control stream
|
||||||
|
{
|
||||||
|
opmplay_channel_context_t* chctx = ctx->channels + 0;
|
||||||
|
uint8_t* data = chctx->stream.ptr;
|
||||||
|
bool isRun = true;
|
||||||
|
if (--chctx->stream.delay == 0) {
|
||||||
|
while (isRun) {
|
||||||
|
if ((*(data) & 0xE0) == OPM_CTRL_REG_SET) {
|
||||||
|
int mask = *data;
|
||||||
|
data++;
|
||||||
|
if (mask & OPM_CTRL_SET_REG01) OPL3_WriteRegBuffered(ctx->chip, 0x001, *data++);
|
||||||
|
if (mask & OPM_CTRL_SET_REG08) OPL3_WriteRegBuffered(ctx->chip, 0x008, *data++);
|
||||||
|
if (mask & OPM_CTRL_SET_REG105) OPL3_WriteRegBuffered(ctx->chip, 0x105, *data++);
|
||||||
|
if (mask & OPM_CTRL_SET_REG104) OPL3_WriteRegBuffered(ctx->chip, 0x104, *data++);
|
||||||
|
if (mask & OPM_CTRL_SET_REGBD) OPL3_WriteRegBuffered(ctx->chip, 0x0BD, *data++);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((*data & 0xF0) == OPM_STREAM_BACKREF) {
|
||||||
|
// back reference, nested call :)
|
||||||
|
int distance = ((*(data + 0) & 0x0F) << 8) | (*(data + 1));
|
||||||
|
int frames_to_play = *(data + 2);
|
||||||
|
chctx->stream.ptr = data + 3;
|
||||||
|
opmplay_push_stack(chctx);
|
||||||
|
data -= distance;
|
||||||
|
chctx->stream.samples_to_play = frames_to_play; // hack?
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for common stuff
|
||||||
|
switch (*data) {
|
||||||
|
// end of stream - rewind everything
|
||||||
|
case OPM_STREAM_END:
|
||||||
|
opmplay_loop(ctx);
|
||||||
|
isRun = false;
|
||||||
|
return OPMPLAY_ERR_END_OF_STREAM;
|
||||||
|
// just an NOP, break
|
||||||
|
case OPM_STREAM_NEW_ORDER:
|
||||||
|
case OPM_STREAM_NOP:
|
||||||
|
data++;
|
||||||
|
break;
|
||||||
|
case OPM_STREAM_LOOP:
|
||||||
|
// save loop point
|
||||||
|
ctx->pos.looped = ctx->pos.frame;
|
||||||
|
chctx->stream.loop = data;
|
||||||
|
data++;
|
||||||
|
break;
|
||||||
|
// set new frame rate
|
||||||
|
case OPM_STREAM_SET_FRAME_RATE:
|
||||||
|
ctx->header.frame_rate = *(uint16_t*)(data + 1); data += 3;
|
||||||
|
break;
|
||||||
|
case OPM_STREAM_END_FRAME:
|
||||||
|
// end of frame - special case here
|
||||||
|
data++;
|
||||||
|
isRun = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// test for delay
|
||||||
|
newdelay = opmplay_set_delay(&data);
|
||||||
|
if (newdelay > 0) {
|
||||||
|
chctx->stream.reload = newdelay;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("unknonw token %02x!\n", *data);
|
||||||
|
return OPMPLAY_ERR_BAD_FILE_STRUCTURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chctx->stream.delay = chctx->stream.reload;
|
||||||
|
// decrement samples to play counter
|
||||||
|
if (--chctx->stream.samples_to_play == 0) {
|
||||||
|
// pop context from the stack
|
||||||
|
do opmplay_pop_stack(chctx); while (--chctx->stream.samples_to_play == 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// save data pointer
|
||||||
|
chctx->stream.ptr = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx->pos.frame++;
|
ctx->pos.frame++;
|
||||||
|
|
||||||
return rtn;
|
return rtn;
|
||||||
|
|
|
@ -110,11 +110,17 @@ struct opmplay_context_t {
|
||||||
uint32_t looped;
|
uint32_t looped;
|
||||||
} pos;
|
} pos;
|
||||||
|
|
||||||
|
// internal registers
|
||||||
|
uint8_t _4op; // used to track 4op mode changes
|
||||||
|
|
||||||
|
// opl chip context
|
||||||
|
opl3_chip* chip;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// init context
|
// init context
|
||||||
int opmplay_init(opmplay_context_t* ctx);
|
int opmplay_init(opmplay_context_t* ctx, opl3_chip *chip);
|
||||||
|
|
||||||
// free context
|
// free context
|
||||||
int opmplay_free(opmplay_context_t* ctx);
|
int opmplay_free(opmplay_context_t* ctx);
|
||||||
|
@ -129,8 +135,7 @@ int opmplay_load_module(opmplay_context_t* ctx, opmplay_io_t* io);
|
||||||
int opmplay_rewind(opmplay_context_t* ctx);
|
int opmplay_rewind(opmplay_context_t* ctx);
|
||||||
|
|
||||||
// play one tick, render changes to opl3 device
|
// play one tick, render changes to opl3 device
|
||||||
int opmplay_tick(opmplay_context_t* ctx, opl3_chip *opl3);
|
int opmplay_tick(opmplay_context_t* ctx);
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,8 @@ enum {
|
||||||
OPM_REG_104 = (1 << 18),
|
OPM_REG_104 = (1 << 18),
|
||||||
OPM_REG_BD = (1 << 19),
|
OPM_REG_BD = (1 << 19),
|
||||||
|
|
||||||
|
OPM_4OP = (1 << 20),
|
||||||
|
|
||||||
// opm_serialize_channel_stream() tweaks
|
// opm_serialize_channel_stream() tweaks
|
||||||
OPM_TL0 = (1 << 8),
|
OPM_TL0 = (1 << 8),
|
||||||
OPM_TL1 = (1 << 9),
|
OPM_TL1 = (1 << 9),
|
||||||
|
@ -59,6 +61,7 @@ struct opm_frame_record {
|
||||||
int block;
|
int block;
|
||||||
int feedback;
|
int feedback;
|
||||||
int key;
|
int key;
|
||||||
|
int _4op;
|
||||||
|
|
||||||
// control stream only
|
// control stream only
|
||||||
int keyperc;
|
int keyperc;
|
||||||
|
|
|
@ -48,6 +48,7 @@ enum {
|
||||||
OPM_STREAM_SET_FRAME_RATE = 0xFB, // word rate (as in opm_header_t::frame_rate)
|
OPM_STREAM_SET_FRAME_RATE = 0xFB, // word rate (as in opm_header_t::frame_rate)
|
||||||
OPM_STREAM_LOOP = 0xFA, // set loop point here
|
OPM_STREAM_LOOP = 0xFA, // set loop point here
|
||||||
|
|
||||||
|
OPM_4OP_TRIGGER = 0xF4, // 4-op mode on/off
|
||||||
OPM_KEY_TRIGGER = 0xF0, // set key on/off + optionally end of frame
|
OPM_KEY_TRIGGER = 0xF0, // set key on/off + optionally end of frame
|
||||||
|
|
||||||
// delay commands
|
// delay commands
|
||||||
|
@ -68,6 +69,9 @@ enum {
|
||||||
OPM_CTRL_REG_SET = 0x80, // 80..BF - set control registers
|
OPM_CTRL_REG_SET = 0x80, // 80..BF - set control registers
|
||||||
|
|
||||||
// flags
|
// flags
|
||||||
|
OPM_4OP_OFF = (0 << 0),
|
||||||
|
OPM_4OP_ON = (1 << 0),
|
||||||
|
|
||||||
OPM_KEY_OFF = (0 << 0),
|
OPM_KEY_OFF = (0 << 0),
|
||||||
OPM_KEY_ON = (1 << 0),
|
OPM_KEY_ON = (1 << 0),
|
||||||
OPM_KEY_END_OF_FRAME = (1 << 1),
|
OPM_KEY_END_OF_FRAME = (1 << 1),
|
||||||
|
|
|
@ -147,6 +147,7 @@ int opm_requantize(opm_convert_context_t* ctx) {
|
||||||
// per-channel structures
|
// per-channel structures
|
||||||
std::vector<opm_channel_rawdata_t> oplchans(1 + ctx->max_channels); // TODO
|
std::vector<opm_channel_rawdata_t> oplchans(1 + ctx->max_channels); // TODO
|
||||||
uint32_t currentFrame = 0; uint32_t delay_rate, delay_acc = 0;
|
uint32_t currentFrame = 0; uint32_t delay_rate, delay_acc = 0;
|
||||||
|
int reg_104_old = 0; // KLUDGE!
|
||||||
|
|
||||||
auto it = ctx->vgm.vgmfile.begin() + ctx->vgm.start; bool loopFrame = false;
|
auto it = ctx->vgm.vgmfile.begin() + ctx->vgm.start; bool loopFrame = false;
|
||||||
while (it < ctx->vgm.vgmfile.begin() + ctx->vgm.end) {
|
while (it < ctx->vgm.vgmfile.begin() + ctx->vgm.end) {
|
||||||
|
@ -204,8 +205,28 @@ int opm_requantize(opm_convert_context_t* ctx) {
|
||||||
oplchans[channel+chip_chidx].data.push_back(reg & ~0xF);
|
oplchans[channel+chip_chidx].data.push_back(reg & ~0xF);
|
||||||
oplchans[channel+chip_chidx].data.push_back(data);
|
oplchans[channel+chip_chidx].data.push_back(data);
|
||||||
break;
|
break;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
if ((chip_idx == 1) && (reg == 0x04 || reg == 0x05)) break;
|
if ((chip_idx == 1) && (reg == 0x04)) {
|
||||||
|
// process special case with 4op register (0x104)
|
||||||
|
if (reg_104_old != data) {
|
||||||
|
static int reg104_to_ch[] = {0+1, 1+1, 2+1, 9+1, 10+1, 11+1};
|
||||||
|
int mask = reg_104_old ^ data;
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
if (mask & (1 << i)) {
|
||||||
|
// push data
|
||||||
|
oplchans[channel + chip_chidx].frame = currentFrame;
|
||||||
|
oplchans[reg104_to_ch[i]].data.push_back(0x04);
|
||||||
|
oplchans[reg104_to_ch[i]].data.push_back(data & (1 << i));
|
||||||
|
// mark channels as used
|
||||||
|
ctx->ch[reg104_to_ch[i]].used = ctx->ch[reg104_to_ch[i]+3].used = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reg_104_old = data;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((chip_idx == 0) && (reg == 0x04 || reg == 0x05)) break;
|
||||||
global_reg:
|
global_reg:
|
||||||
oplchans[0].frame = currentFrame;
|
oplchans[0].frame = currentFrame;
|
||||||
oplchans[0].data.push_back(reg);
|
oplchans[0].data.push_back(reg);
|
||||||
|
@ -369,26 +390,26 @@ int opm_group_control_stream(opm_convert_context_t* ctx) {
|
||||||
defrec.reg_104 = data; defrec.flags |= OPM_REG_104;
|
defrec.reg_104 = data; defrec.flags |= OPM_REG_104;
|
||||||
// mark channel pairs as used depending on register value
|
// mark channel pairs as used depending on register value
|
||||||
if ((ctx->chip_type == OPM_FLAG_CHIP_OPL3) && (ctx->max_channels >= 18)) {
|
if ((ctx->chip_type == OPM_FLAG_CHIP_OPL3) && (ctx->max_channels >= 18)) {
|
||||||
if (data & (1 << 0)) ctx->ch[ 0].used = ctx->ch[ 3].used = true;
|
if (data & (1 << 0)) ctx->ch[ 0+1].used = ctx->ch[ 3+1].used = true;
|
||||||
if (data & (1 << 1)) ctx->ch[ 1].used = ctx->ch[ 4].used = true;
|
if (data & (1 << 1)) ctx->ch[ 1+1].used = ctx->ch[ 4+1].used = true;
|
||||||
if (data & (1 << 2)) ctx->ch[ 2].used = ctx->ch[ 5].used = true;
|
if (data & (1 << 2)) ctx->ch[ 2+1].used = ctx->ch[ 5+1].used = true;
|
||||||
if (data & (1 << 3)) ctx->ch[ 9].used = ctx->ch[12].used = true;
|
if (data & (1 << 3)) ctx->ch[ 9+1].used = ctx->ch[12+1].used = true;
|
||||||
if (data & (1 << 4)) ctx->ch[10].used = ctx->ch[13].used = true;
|
if (data & (1 << 4)) ctx->ch[10+1].used = ctx->ch[13+1].used = true;
|
||||||
if (data & (1 << 5)) ctx->ch[11].used = ctx->ch[14].used = true;
|
if (data & (1 << 5)) ctx->ch[11+1].used = ctx->ch[14+1].used = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OPM_REG_105: defrec.reg_105 = data; defrec.flags |= OPM_REG_105; break;
|
case OPM_REG_105: defrec.reg_105 = data; defrec.flags |= OPM_REG_105; break;
|
||||||
case OPM_REG_BD:
|
case OPM_REG_BD:
|
||||||
// test low 6 bits
|
// test low 6 bits
|
||||||
defrec.reg_BD = data; defrec.flags |= OPM_REG_BD; break;
|
defrec.reg_BD = data; defrec.flags |= OPM_REG_BD;
|
||||||
if (((data ^ defrec.reg_BD) & 0x3F) != 0) {
|
if (((data) & 0x3F) != 0) {
|
||||||
// percussion key on/off - serialize records!
|
// percussion key on/off - serialize records!
|
||||||
chanrec.records.push_back(defrec); defrec.flags = 0;
|
chanrec.records.push_back(defrec); defrec.flags = 0;
|
||||||
if (data & (1 << 5)) {
|
if (data & (1 << 5)) {
|
||||||
// percussion mode is used - mark channels 6-8 as used
|
// percussion mode is used - mark channels 6-8 as used
|
||||||
ctx->percussion_mode = true;
|
ctx->percussion_mode = true;
|
||||||
ctx->ch[6].used = ctx->ch[7].used = ctx->ch[8].used = true;
|
ctx->ch[6+1].used = ctx->ch[7+1].used = ctx->ch[8+1].used = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -462,7 +483,7 @@ int opm_group_channel_stream(opm_convert_context_t *ctx, opm_channel_t* chctx, i
|
||||||
defrec.key = data & (1 << 5);
|
defrec.key = data & (1 << 5);
|
||||||
if ((oldblock ^ data) == (1 << 5)) defrec.flags |= OPM_KEY;
|
if ((oldblock ^ data) == (1 << 5)) defrec.flags |= OPM_KEY;
|
||||||
// serialize!
|
// serialize!
|
||||||
chanrec.records.push_back(defrec); defrec.flags = 0;
|
chanrec.records.push_back(defrec); defrec.flags = 0;
|
||||||
if (defrec.key != 0) {
|
if (defrec.key != 0) {
|
||||||
// key on triggered - mark channel as used
|
// key on triggered - mark channel as used
|
||||||
ctx->ch[ch].used = true;
|
ctx->ch[ch].used = true;
|
||||||
|
@ -470,6 +491,13 @@ int opm_group_channel_stream(opm_convert_context_t *ctx, opm_channel_t* chctx, i
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 0x04:
|
||||||
|
if (ctx->chip_type != OPM_FLAG_CHIP_OPL3) break;
|
||||||
|
defrec.flags |= OPM_4OP;
|
||||||
|
defrec._4op = data;
|
||||||
|
// serialize!
|
||||||
|
chanrec.records.push_back(defrec); defrec.flags = 0;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -573,6 +601,7 @@ int opm_serialize_channel_stream(opm_convert_context_t* ctx, int ch) {
|
||||||
int tl_relocated;
|
int tl_relocated;
|
||||||
int freq_fb;
|
int freq_fb;
|
||||||
bool key_only;
|
bool key_only;
|
||||||
|
bool _4op;
|
||||||
int last;
|
int last;
|
||||||
} mask;
|
} mask;
|
||||||
|
|
||||||
|
@ -589,8 +618,10 @@ int opm_serialize_channel_stream(opm_convert_context_t* ctx, int ch) {
|
||||||
}
|
}
|
||||||
mask.freq_fb = (e.flags & (OPM_BLOCK | OPM_FNUM | OPM_FEEDBACK)) | mask.tl_relocated;
|
mask.freq_fb = (e.flags & (OPM_BLOCK | OPM_FNUM | OPM_FEEDBACK)) | mask.tl_relocated;
|
||||||
mask.last |= ((mask.freq_fb ? 4 : 0) << 0);
|
mask.last |= ((mask.freq_fb ? 4 : 0) << 0);
|
||||||
|
mask._4op = (e.flags & OPM_4OP);
|
||||||
|
mask.last |= ((mask._4op ? 8 : 0) << 0);
|
||||||
mask.key_only = ((e.flags & (OPM_BLOCK | OPM_FNUM | OPM_FEEDBACK | OPM_KEY)) | (mask.tl_relocated)) == (OPM_BLOCK | OPM_KEY);
|
mask.key_only = ((e.flags & (OPM_BLOCK | OPM_FNUM | OPM_FEEDBACK | OPM_KEY)) | (mask.tl_relocated)) == (OPM_BLOCK | OPM_KEY);
|
||||||
mask.last |= ((mask.key_only ? 8 : 0) << 0);
|
mask.last |= ((mask.key_only ? 16 : 0) << 0);
|
||||||
if (i < s.records.size() - 1)
|
if (i < s.records.size() - 1)
|
||||||
mask.last |= (1 << 31); // prevent non-last events from emitting eof token
|
mask.last |= (1 << 31); // prevent non-last events from emitting eof token
|
||||||
|
|
||||||
|
@ -628,6 +659,12 @@ int opm_serialize_channel_stream(opm_convert_context_t* ctx, int ch) {
|
||||||
if (mask.freq_fb & OPM_BLOCK) s.rawdata.push_back(e.block);
|
if (mask.freq_fb & OPM_BLOCK) s.rawdata.push_back(e.block);
|
||||||
}
|
}
|
||||||
mask.last >>= 1;
|
mask.last >>= 1;
|
||||||
|
if (mask._4op) {
|
||||||
|
s.rawdata.push_back(
|
||||||
|
OPM_4OP_TRIGGER | (e._4op ? OPM_4OP_ON : OPM_4OP_OFF)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
mask.last >>= 1;
|
||||||
if (mask.key_only) {
|
if (mask.key_only) {
|
||||||
s.rawdata.push_back(
|
s.rawdata.push_back(
|
||||||
OPM_KEY_TRIGGER |
|
OPM_KEY_TRIGGER |
|
||||||
|
|
Loading…
Reference in a new issue