From cafc4a011b3ae897cdc76546cd377987a18ea704 Mon Sep 17 00:00:00 2001 From: wbcbz7 Date: Sat, 6 Apr 2024 15:12:38 +0700 Subject: [PATCH] implement 80/A0/B0 events --- oplplay/lxmplay/lxmfile.h | 107 --------------- oplplay/lxmplay/lxmplay.h | 152 --------------------- oplplay/lxmplay/lxmplay.vcxproj | 2 - oplplay/lxmplay/lxmplay.vcxproj.filters | 6 - oplplay/lxmplay/opmfile.h | 17 ++- oplplay/lxmplay/opmplay.cpp | 74 +++++----- vgm2opl_next/vgm2opl_next/opmfile.h | 14 +- vgm2opl_next/vgm2opl_next/vgm2opl_next.cpp | 18 +++ 8 files changed, 83 insertions(+), 307 deletions(-) delete mode 100644 oplplay/lxmplay/lxmfile.h delete mode 100644 oplplay/lxmplay/lxmplay.h diff --git a/oplplay/lxmplay/lxmfile.h b/oplplay/lxmplay/lxmfile.h deleted file mode 100644 index 570d625..0000000 --- a/oplplay/lxmplay/lxmfile.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once -#include - -#pragma pack(push, 1) - -struct lxm_header_stream_desc_t { - uint32_t ptr; // offset to data stream - uint32_t size; // stream data size in bytes -}; - -enum { - LXM_HEADER_LOG_VOLUME = (1 << 0), - LXM_HEADER_PITCH_TABLE = (1 << 1), - - LXM_HEADER_DESYNC_TEST = (1 << 31), -}; - -struct lxm_header_t { - char magic[4]; // "LXM\x1A" - union { - struct { - uint8_t minor; - uint8_t major; - }; - uint16_t v; - } version; - uint16_t flags; // reserved - uint16_t frame_rate; // in Hz, 8.8fx - uint16_t num_samples; - uint8_t num_channels; // 0..32 - uint8_t callstack_depth; // max nested backrefs - uint16_t amplification; // 8.8fx, for software mixer - uint32_t samples_ptr; // offset to sample descriptors - - //lxm_header_stream_desc_t stream[num_channels + 1]; // first is control stream - - /* - if (flags & LXM_HEADER_PITCH_TABLE) { - uint16_t pitchtab_size; // 0..127 - uint16_t pitchtab[pitchtab_size]; - } - */ -}; - -// sample descriptors, stored sequentially -enum { - LXM_SAMPLE_8BIT = (0 << 0), - LXM_SAMPLE_16BIT = (1 << 0), - LXM_SAMPLE_FORMAT_MASK = (7 << 0), - - LXM_SAMPLE_ONESHOT = (0 << 3), - LXM_SAMPLE_LOOP_FORWARD = (1 << 3), - LXM_SAMPLE_LOOP_BIDIR = (2 << 3), - LXM_SAMPLE_LOOP_MASK = (3 << 3), - LXM_SAMPLE_LOOP_SHIFT = 3, -}; - -struct lxm_sample_t { - uint16_t flags; - uint16_t compression; // currently 0 - uint32_t sample_rate; // in hz, 24.8fx - int32_t length; // in samples, sustain loops not supported - int32_t loop_start; // in samples - uint32_t ofs_data; // offset in file - - // optional, contains 0 if ignored - uint32_t max_sample_rate; // maximum sample rate occured in the file, used for target device sample rate optimization (emu8k :) - uint32_t opt_data; // sample optimization data (priority, etc) - uint32_t reserved; -}; - -// LXM v0 stream data: -enum { - LXM_STREAM_END_FRAME = 0xFF, // end of frame, set channel 0 - LXM_STREAM_END = 0xFE, // end of stream, stop here or loop to LXM_STREAM_LOOP stream point - LXM_STREAM_NOP = 0xFD, - LXM_STREAM_NEW_ORDER = 0xFC, // nop, marks new order - LXM_STREAM_SET_FRAME_RATE = 0xFB, // word rate (as in lxm_header_t::frame_rate) - LXM_STREAM_LOOP = 0xFA, // set loop point here - - // delay commands - LXM_STREAM_DELAY_INT32 = 0xF9, // dword delay - LXM_STREAM_DELAY_INT16 = 0xF8, // word delay - LXM_STREAM_DELAY_INT12 = 0xD0, // D0..DF - 0..4095 frames delay (hibyte in low 4 bits of command) - LXM_STERAM_DELAY_SHORT = 0xC0, // C0..CF - 1..16 frames delay - - // back reference - LXM_STREAM_BACKREF = 0xE0, // E0..EF - word backrefpos (12 bit), byte frames - - // setter commands - LXM_STREAM_VOLUME = 0x00, // 00..7F - volume is 7bit log compressed, end the frame - LXM_STREAM_SET = 0x80, // 80..9F - set all params - LXM_STREAM_RETRIG = 0xA0, // A0..AF - retrig note (ofs = 0), set all params - LXM_STREAM_SET_EXT = 0xB0, // B0..BF - reserved - - // channel flags - LXM_STREAM_SET_SAMPLE = (1 << 0), // byte - LXM_STREAM_SET_PITCH = (1 << 1), // byte/word pitch/note compressed (see below) - LXM_STREAM_SET_PAN = (1 << 2), // byte left 00 .. 80 .. FF right - LXM_STREAM_SET_OFS = (1 << 3), // byte (sample_pos >> 8) - - LXM_STREAM_RETRIG_END = (1 << 3), // end the frame - LXM_STREAM_SET_END = (1 << 4), // end the frame -}; - -#pragma pack(pop) - diff --git a/oplplay/lxmplay/lxmplay.h b/oplplay/lxmplay/lxmplay.h deleted file mode 100644 index cc68e71..0000000 --- a/oplplay/lxmplay/lxmplay.h +++ /dev/null @@ -1,152 +0,0 @@ -#pragma once -#include -#include "lxmfile.h" - -// forward declarations -struct lxm_channel_context_t; -struct lxm_sample_context_t; - -union lxm_frac_t { - struct { - uint32_t f; - int32_t i; - }; - int64_t p; -}; - -struct lxm_stream_stack_t { - uint8_t* stream; - uint32_t samples_to_play; - uint32_t reload; -}; - -struct lxm_channel_context_t { - lxm_sample_context_t* sample; // current sample - - uint32_t pitch; // current pitch - uint32_t volume; // current volume - uint32_t pan; // current panning - - // mixer context - struct { - int32_t vol_l, vol_r; // 16.16fx - - lxm_frac_t pos; // 32.32fx - lxm_frac_t delta; // 32.32fx - - bool play_backwards; - - int stopped; - } mixer; - - // stream stack - lxm_stream_stack_t stack[16]; - int stack_pos; - - // stream data - struct { - uint32_t samples_to_play; - uint32_t delay; - uint32_t reload; - - uint8_t* data; - uint8_t* ptr; - uint8_t* loop; // if active - } stream; - - bool updated; -}; - -struct lxm_sample_context_t { - lxm_sample_t header; - - // mixer.delta = (channel.pitch * sample.rate) / global.rate = channel.pitch * rate_factor; - uint32_t rate_factor; // 16.16fx - - // data unions - union { - int8_t* data8; - int16_t* data16; - }; -}; - -struct lxm_mixer_context_t { - // next row delay count/reload - int32_t spt_count; // 16.16fx - int32_t spt_reload; // 16.16fx - - uint32_t out_channels; // 1 or 2 - uint32_t sample_rate; // sample rate - - // amplify - uint32_t chan_amplify; // 16.16fx -}; - -struct lxm_context_t { - lxm_header_t header; - - // sample contexts - lxm_sample_context_t* samples; - - // channel context - lxm_channel_context_t channels[32]; - - // mixer context - lxm_mixer_context_t mixer; - - // pitch table data - struct { - int size; - uint16_t* data; - } pitchtab; - - // control stream data - struct { - uint32_t delay; - uint32_t reload; - - uint8_t* data; - uint8_t* ptr; - } stream; - - // position data - struct { - uint32_t order; - uint32_t frame; - uint64_t samples; - - uint32_t looped; - } pos; - - // loop data - struct { - uint8_t* data; - uint32_t frames; - uint64_t samples; - } loop; - -}; - -// init context -int lxm_init(lxm_context_t* ctx, uint32_t out_channels, uint32_t sample_rate); - -// free context -int lxm_free(lxm_context_t* ctx); - -// load from file -int lxm_load(lxm_context_t* ctx, const char *filename); - -// load from memory -int lxm_load_mem(lxm_context_t* ctx, const void *data, uint32_t size); - -// reset to start -int lxm_rewind(lxm_context_t* ctx); - -// play one tick -int lxm_tick(lxm_context_t* ctx); - -// render int16_t stereo samples to buffer -// returns sample frames rendered or 0 if error -int lxm_render(lxm_context_t* ctx, int16_t* buf, int32_t count); - - diff --git a/oplplay/lxmplay/lxmplay.vcxproj b/oplplay/lxmplay/lxmplay.vcxproj index fcd477a..b7c51ab 100644 --- a/oplplay/lxmplay/lxmplay.vcxproj +++ b/oplplay/lxmplay/lxmplay.vcxproj @@ -153,8 +153,6 @@ - - diff --git a/oplplay/lxmplay/lxmplay.vcxproj.filters b/oplplay/lxmplay/lxmplay.vcxproj.filters index cf1fa5f..4987912 100644 --- a/oplplay/lxmplay/lxmplay.vcxproj.filters +++ b/oplplay/lxmplay/lxmplay.vcxproj.filters @@ -26,12 +26,6 @@ - - Файлы заголовков - - - Файлы заголовков - Файлы заголовков diff --git a/oplplay/lxmplay/opmfile.h b/oplplay/lxmplay/opmfile.h index 57e5af1..e6803a5 100644 --- a/oplplay/lxmplay/opmfile.h +++ b/oplplay/lxmplay/opmfile.h @@ -4,7 +4,7 @@ #pragma pack(push, 1) 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 }; @@ -52,6 +52,9 @@ enum { OPM_SET_ADSR = 0xA0, // A0..AF - set attack/sustain/decay/release for operator OPM_SET_FREQ_FB = 0xB0, // B0..BF - set frequency + feedback + // control register set + OPM_CTRL_KEY_PERC = 0x00, // 00..7F - set key on/off for percussion, end of frame + OPM_CTRL_REG_SET = 0x80, // 80..BF - set control registers // flags OPM_KEY_OFF = (0 << 0), @@ -71,10 +74,16 @@ enum { OPM_CMDA0_SELECT_OPERATOR = (1 << 2), OPM_CMDA0_END_OF_FRAME = (1 << 3), - OPM_CMDB0_SET_FREQ_LOW = (1 << 0), - OPM_CMDB0_SET_FREQ_HIGH = (1 << 1), - OPM_CMDB0_SET_FEEDBACK = (1 << 2), + OPM_CMDB0_SET_FEEDBACK = (1 << 0), + OPM_CMDB0_SET_FREQ_LOW = (1 << 1), + OPM_CMDB0_SET_FREQ_HIGH = (1 << 2), OPM_CMDB0_END_OF_FRAME = (1 << 3), + + OPM_CTRL_SET_REG01 = (1 << 0), + OPM_CTRL_SET_REG08 = (1 << 1), + OPM_CTRL_SET_REG105 = (1 << 2), + OPM_CTRL_SET_REG104 = (1 << 3), + OPM_CTRL_SET_REGBD = (1 << 4), }; #pragma pack(pop) diff --git a/oplplay/lxmplay/opmplay.cpp b/oplplay/lxmplay/opmplay.cpp index 8dd31ab..5b3c255 100644 --- a/oplplay/lxmplay/opmplay.cpp +++ b/oplplay/lxmplay/opmplay.cpp @@ -98,7 +98,6 @@ int opmplay_load_module(opmplay_context_t* ctx, opmplay_io_t* io) { for (int ch = 0; ch < OPMPLAY_MAX_CHANNLES; ch++) { ctx->channels[ch].stream.data = (uint8_t*)opmplay_alloc(sizeof(uint8_t*) * (streamdesc[ch].size)); if (ctx->channels[ch].stream.data == NULL) return OPMPLAY_ERR_MEMALLOC; - if (io->seek(io, streamdesc[ch].ptr << 4)) return OPMPLAY_ERR_IO; if (io->read(io, ctx->channels[ch].stream.data, streamdesc[ch].size) != streamdesc[ch].size) return OPMPLAY_ERR_IO; ctx->channels[ch].stream.delay = 1; } @@ -180,6 +179,17 @@ int opmplay_tick(opmplay_context_t* ctx, opl3_chip* opl3) { 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 @@ -209,18 +219,15 @@ int opmplay_tick(opmplay_context_t* ctx, opl3_chip* opl3) { break; default: -#if 0 // test for delay newdelay = opmplay_set_delay(&data); if (newdelay > 0) { chctx->stream.reload = newdelay; } else -#endif { - // register:data pair - OPL3_WriteRegBuffered(opl3, *(data + 0), *(data + 1)); - data += 2; + printf("unknonw token %02x!\n", *data); + return OPMPLAY_ERR_BAD_FILE_STRUCTURE; } } } @@ -236,6 +243,33 @@ int opmplay_tick(opmplay_context_t* ctx, opl3_chip* opl3) { bool isRun = true; if (--chctx->stream.delay == 0) { while (isRun) { + // get streams + if ((*(data) & 0xE0) == OPM_SET_MULT_WAVE_TL) { + int mask = *data; + int op = (mask & OPM_CMD80_SELECT_OPERATOR ? 3 : 0) + opmplay_opregs_channel_offset[ch]; + data++; + if (mask & OPM_CMD80_SET_MULT) OPL3_WriteRegBuffered(opl3, 0x20+op, *data++); + if (mask & OPM_CMD80_SET_TL) OPL3_WriteRegBuffered(opl3, 0x40+op, *data++); + if (mask & OPM_CMD80_SET_WAVEFORM) OPL3_WriteRegBuffered(opl3, 0xE0+op, *data++); + continue; + } + if ((*(data) & 0xF0) == OPM_SET_ADSR) { + int mask = *data; + int op = (mask & OPM_CMDA0_SELECT_OPERATOR ? 3 : 0) + opmplay_opregs_channel_offset[ch]; + data++; + if (mask & OPM_CMDA0_SET_AD) OPL3_WriteRegBuffered(opl3, 0x60 + op, *data++); + if (mask & OPM_CMDA0_SET_SR) OPL3_WriteRegBuffered(opl3, 0x80 + op, *data++); + continue; + } + if ((*(data) & 0xF0) == OPM_SET_FREQ_FB) { + int mask = *data; + data++; + if (mask & OPM_CMDB0_SET_FEEDBACK) OPL3_WriteRegBuffered(opl3, 0xC0 + ch, *data++); + if (mask & OPM_CMDB0_SET_FREQ_LOW) OPL3_WriteRegBuffered(opl3, 0xA0 + ch, *data++); + if (mask & OPM_CMDB0_SET_FREQ_HIGH) OPL3_WriteRegBuffered(opl3, 0xB0 + ch, *data++); + continue; + } + // check for common stuff switch (*data) { case OPM_STREAM_END: @@ -258,41 +292,17 @@ int opmplay_tick(opmplay_context_t* ctx, opl3_chip* opl3) { data++; isRun = false; break; - case OPM_STREAM_DELAY_INT16: - // delay (temporary) - chctx->stream.reload = *(data + 1) | (*(data + 2) << 8); - data += 3; - isRun = false; - break; default: -#if 0 // test for delay newdelay = opmplay_set_delay(&data); if (newdelay > 0) { chctx->stream.reload = newdelay; } else -#endif { - switch (*(data + 0) & 0xF0) { - case 0x20: - case 0x40: - case 0x60: - case 0x80: - case 0xE0: - OPL3_WriteRegBuffered(opl3, *(data + 0) + opmplay_opregs_channel_offset[ch], *(data + 1)); - break; - case 0xA0: - case 0xB0: - case 0xC0: - OPL3_WriteRegBuffered(opl3, *(data + 0) + ch, *(data + 1)); - break; - default: - OPL3_WriteRegBuffered(opl3, *(data + 0), *(data + 1)); - break; - } - data += 2; + printf("unknonw token %02x!\n", *data); + return OPMPLAY_ERR_BAD_FILE_STRUCTURE; } } } diff --git a/vgm2opl_next/vgm2opl_next/opmfile.h b/vgm2opl_next/vgm2opl_next/opmfile.h index 23f2f42..92ec34f 100644 --- a/vgm2opl_next/vgm2opl_next/opmfile.h +++ b/vgm2opl_next/vgm2opl_next/opmfile.h @@ -4,7 +4,7 @@ #pragma pack(push, 1) 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 }; @@ -74,10 +74,16 @@ enum { OPM_CMDA0_SELECT_OPERATOR = (1 << 2), OPM_CMDA0_END_OF_FRAME = (1 << 3), - OPM_CMDB0_SET_FREQ_LOW = (1 << 0), - OPM_CMDB0_SET_FREQ_HIGH = (1 << 1), - OPM_CMDB0_SET_FEEDBACK = (1 << 2), + OPM_CMDB0_SET_FEEDBACK = (1 << 0), + OPM_CMDB0_SET_FREQ_LOW = (1 << 1), + OPM_CMDB0_SET_FREQ_HIGH = (1 << 2), OPM_CMDB0_END_OF_FRAME = (1 << 3), + + OPM_CTRL_SET_REG01 = (1 << 0), + OPM_CTRL_SET_REG08 = (1 << 1), + OPM_CTRL_SET_REG105 = (1 << 2), + OPM_CTRL_SET_RE104 = (1 << 3), + OPM_CTRL_SET_REGBD = (1 << 4), }; #pragma pack(pop) diff --git a/vgm2opl_next/vgm2opl_next/vgm2opl_next.cpp b/vgm2opl_next/vgm2opl_next/vgm2opl_next.cpp index d9ee6a0..7f0ff2a 100644 --- a/vgm2opl_next/vgm2opl_next/vgm2opl_next.cpp +++ b/vgm2opl_next/vgm2opl_next/vgm2opl_next.cpp @@ -337,6 +337,7 @@ int opm_write_file(opm_convert_context_t* ctx) { std::vector writeinfo(9 + 1); ctx->opmfile.streamdesc.resize(9 + 1); +#if 0 // calculate offsets auto fsize = round_to_para(sizeof(ctx->opmfile.header) + (9 + 1) * sizeof(opm_header_stream_desc_t)); for (int i = 0; i < 9 + 1; i++) { @@ -363,6 +364,23 @@ int opm_write_file(opm_convert_context_t* ctx) { fwrite(ctx->oplchan_out[i].data(), sizeof(uint8_t), ctx->oplchan_out[i].size(), f); } fclose(f); +#else + for (int i = 0; i < 9 + 1; i++) { + ctx->opmfile.streamdesc[i].size = ctx->oplchan_out[i].size(); + } + // dump to OPM file + FILE* f = fopen(ctx->opmfile.filename.c_str(), "wb"); + if (!f) return 1; + + // write header + fwrite(&ctx->opmfile.header, sizeof(opm_header_t), 1, f); + fwrite(ctx->opmfile.streamdesc.data(), sizeof(decltype(ctx->opmfile.streamdesc)::value_type), ctx->opmfile.streamdesc.size(), f); + // write channel streams + for (int i = 0; i < 1 + 9; i++) { + fwrite(ctx->oplchan_out[i].data(), sizeof(uint8_t), ctx->oplchan_out[i].size(), f); + } + fclose(f); +#endif } #endif