# HG changeset patch # User Michael Pavone # Date 1446411308 28800 # Node ID 7068a9db6dd0c5f8b33509b73d56eaa8744d1304 # Parent 7decd421cdc8f9014ca5d68c4591e3fda5388ed6 Wrote a buggy tool for splitting VGM files by channel diff -r 7decd421cdc8 -r 7068a9db6dd0 vgmplay.c --- a/vgmplay.c Sun Nov 01 00:12:52 2015 -0700 +++ b/vgmplay.c Sun Nov 01 12:55:08 2015 -0800 @@ -21,69 +21,6 @@ #define MCLKS_PER_Z80 15 #define MCLKS_PER_PSG (MCLKS_PER_Z80*16) -#pragma pack(push, 1) -typedef struct { - char ident[4]; - uint32_t eof_offset; - uint32_t version; - uint32_t sn76489_clk; - uint32_t ym2413_clk; - uint32_t gd3_offset; - uint32_t num_samples; - uint32_t loop_offset; - uint32_t loop_samples; - uint32_t rate; - uint16_t sn76489_fb; - uint8_t sn76489_shift; - uint8_t sn76489_flags; - uint32_t ym2612_clk; - uint32_t ym2151_clk; - uint32_t data_offset; - uint32_t sega_pcm_clk; - uint32_t sega_pcm_reg; -} vgm_header; - -enum { - CMD_PSG_STEREO = 0x4F, - CMD_PSG, - CMD_YM2413, - CMD_YM2612_0, - CMD_YM2612_1, - CMD_YM2151, - CMD_YM2203, - CMD_YM2608_0, - CMD_YM2608_1, - CMD_YM2610_0, - CMD_YM2610_1, - CMD_YM3812, - CMD_YM3526, - CMD_Y8950, - CMD_YMZ280B, - CMD_YMF262_0, - CMD_YMF262_1, - CMD_WAIT = 0x61, - CMD_WAIT_60, - CMD_WAIT_50, - CMD_END = 0x66, - CMD_DATA, - CMD_PCM_WRITE, - CMD_WAIT_SHORT = 0x70, - CMD_YM2612_DAC = 0x80, - CMD_DAC_STREAM_SETUP = 0x90, - CMD_DAC_STREAM_DATA, - CMD_DAC_STREAM_FREQ, - CMD_DAC_STREAM_START, - CMD_DAC_STREAM_STOP, - CMD_DAC_STREAM_STARTFAST, - CMD_DATA_SEEK = 0xE0 -}; - -enum { - DATA_YM2612_PCM = 0 -}; - -#pragma pack(pop) - void handle_keydown(int keycode) { } @@ -129,13 +66,6 @@ } } -typedef struct { - struct data_block *next; - uint8_t *data; - uint32_t size; - uint8_t type; -} data_block; - int main(int argc, char ** argv) { set_exe_str(argv[0]); diff -r 7decd421cdc8 -r 7068a9db6dd0 vgmsplit.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vgmsplit.c Sun Nov 01 12:55:08 2015 -0800 @@ -0,0 +1,218 @@ +/* + Copyright 2015 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ +#include "ym2612.h" +#include "vgm.h" +#include +#include +#include +#include + +#define OUT_CHANNELS 10 +#define DAC_CHANNEL 5 +#define PSG_BASE 6 +#define SAMPLE_THRESHOLD 100 + +int main(int argc, char ** argv) +{ + data_block *blocks = NULL; + data_block *seek_block = NULL; + uint32_t seek_offset; + uint32_t block_offset; + + + FILE * f = fopen(argv[1], "rb"); + vgm_header header; + size_t bytes = fread(&header, 1, sizeof(header), f); + if (bytes != sizeof(header)) { + fputs("Error reading file\n", stderr); + exit(1); + } + if (header.version < 0x150 || !header.data_offset) { + header.data_offset = 0xC; + } + fseek(f, header.data_offset + 0x34, SEEK_SET); + uint32_t data_size = header.eof_offset + 4 - (header.data_offset + 0x34); + uint8_t * data = malloc(data_size); + data_size = fread(data, 1, data_size, f); + fclose(f); + uint8_t *buffers[OUT_CHANNELS]; + uint8_t *out_pos[OUT_CHANNELS]; + uint8_t has_real_data[OUT_CHANNELS]; + + buffers[0] = malloc(data_size * OUT_CHANNELS); + out_pos[0] = buffers[0]; + has_real_data[0] = 0; + for (int i = 1; i < OUT_CHANNELS; i++) + { + buffers[i] = buffers[i-1] + data_size; + out_pos[i] = buffers[i]; + has_real_data[i] = 0; + } + + uint8_t * end = data + data_size; + uint8_t * cur = data; + uint32_t current_cycle = 0; + uint8_t psg_latch = 0; + uint8_t param,reg; + uint8_t channel; + uint32_t sample_count = 0; + uint8_t last_cmd; + while (cur < end) { + uint8_t cmd = *(cur++); + switch(cmd) + { + case CMD_PSG_STEREO: + //ignore for now + cur++; + break; + case CMD_PSG: + param = *(cur++); + if (param & 0x80) { + psg_latch = param; + channel = param >> 5 & 3; + } else { + channel = psg_latch >> 5 & 3; + } + *(out_pos[PSG_BASE+channel]++) = cmd; + *(out_pos[PSG_BASE+channel]++) = param; + has_real_data[PSG_BASE+channel] = 1; + break; + case CMD_YM2612_0: + reg = *(cur++); + param = *(cur++); + if (reg < REG_KEY_ONOFF) { + for (int i = 0; i < 6; i++) + { + *(out_pos[i]++) = cmd; + *(out_pos[i]++) = reg; + *(out_pos[i]++) = param; + } + break; + } else if(reg == REG_DAC || reg == REG_DAC_ENABLE) { + if (reg == REG_DAC) { + sample_count++; + } + channel = DAC_CHANNEL; + } else if(reg == REG_KEY_ONOFF) { + channel = param & 7; + if (channel > 2) { + channel--; + } + if (param & 0xF0) { + has_real_data[channel] = 1; + } + } else if (reg >= REG_FNUM_LOW_CH3 && reg < REG_ALG_FEEDBACK) { + channel = 2; + } else { + channel = 255; + } + case CMD_YM2612_1: + if (cmd == CMD_YM2612_1) { + reg = *(cur++); + param = *(cur++); + channel = 255; + } + if (channel >= PSG_BASE) { + if (reg >= REG_DETUNE_MULT && reg < REG_FNUM_LOW) { + channel = (cmd == CMD_YM2612_0 ? 0 : 3) + (reg & 0xC >> 2); + } else if ((reg >= REG_FNUM_LOW && reg < REG_FNUM_LOW_CH3) || (reg >= REG_ALG_FEEDBACK && reg < 0xC0)) { + channel = (cmd == CMD_YM2612_0 ? 0 : 3) + (reg & 0x3); + } else { + fprintf(stderr, "WARNING: Skipping nrecognized write to register %X on part %d\n", reg, (cmd == CMD_YM2612_0 ? 1 : 2)); + } + } + if (channel < PSG_BASE) { + *(out_pos[channel]++) = cmd; + *(out_pos[channel]++) = reg; + *(out_pos[channel]++) = param; + } + break; + case CMD_WAIT: { + reg = *(cur++); + param = *(cur++); + for (int i = 0; i < OUT_CHANNELS; i++) + { + *(out_pos[i]++) = cmd; + *(out_pos[i]++) = reg; + *(out_pos[i]++) = param; + } + break; + } + case CMD_WAIT_60: + case CMD_WAIT_50: + case CMD_END: + for (int i = 0; i < OUT_CHANNELS; i++) + { + *(out_pos[i]++) = cmd; + } + cur = end; + break; + case CMD_DATA: { + uint8_t * start = cur - 1; + cur++; //skip compat command + uint8_t data_type = *(cur++); + uint32_t data_size = *(cur++); + data_size |= *(cur++) << 8; + data_size |= *(cur++) << 16; + data_size |= *(cur++) << 24; + if (cur + data_size > end) { + data_size = end - cur; + } + cur += data_size; + if (data_type == DATA_YM2612_PCM) { + memcpy(out_pos[DAC_CHANNEL], start, cur-start); + out_pos[DAC_CHANNEL] += cur-start; + } else { + fprintf(stderr, "WARNING: Skipping data block with unrecognized type %X\n", data_type); + } + break; + } + case CMD_DATA_SEEK: { + memcpy(out_pos[DAC_CHANNEL], cur-1, 5); + out_pos[DAC_CHANNEL] += 5; + cur += 4; + break; + } + + default: + if (cmd >= CMD_WAIT_SHORT && cmd < (CMD_WAIT_SHORT + 0x10)) { + for (int i = 0; i < OUT_CHANNELS; i++) + { + *(out_pos[i]++) = cmd; + } + } else if (cmd >= CMD_YM2612_DAC && cmd < CMD_DAC_STREAM_SETUP) { + *(out_pos[DAC_CHANNEL]++) = cmd; + sample_count++; + } else { + fprintf(stderr, "unimplemented command: %X at offset %X, last valid command was %X\n", cmd, (unsigned int)(cur - data - 1), last_cmd); + exit(1); + } + } + last_cmd = cmd; + } + if (sample_count > SAMPLE_THRESHOLD) { + has_real_data[DAC_CHANNEL] = 1; + } + for (int i = 0; i < OUT_CHANNELS; i++) + { + if (has_real_data[i]) { + char fname[11]; + sprintf(fname, i < PSG_BASE ? "ym_%d.vgm" : "psg_%d.vgm", i < PSG_BASE ? i : i - PSG_BASE); + f = fopen(fname, "wb"); + if (!f) { + fprintf(stderr, "Failed to open %s for writing\n", fname); + exit(1); + } + data_size = out_pos[i] - buffers[i]; + header.eof_offset = (header.data_offset + 0x34) + data_size - 4; + fwrite(&header, 1, sizeof(header), f); + fseek(f, header.data_offset + 0x34, SEEK_SET); + fwrite(buffers[i], 1, data_size, f); + fclose(f); + } + } + return 0; +} diff -r 7decd421cdc8 -r 7068a9db6dd0 ym2612.c --- a/ym2612.c Sun Nov 01 00:12:52 2015 -0700 +++ b/ym2612.c Sun Nov 01 12:55:08 2015 -0800 @@ -26,31 +26,6 @@ #define BUSY_CYCLES_DATA_HIGH 47 #define OP_UPDATE_PERIOD 144 -enum { - REG_LFO = 0x22, - REG_TIMERA_HIGH = 0x24, - REG_TIMERA_LOW, - REG_TIMERB, - REG_TIME_CTRL, - REG_KEY_ONOFF, - REG_DAC = 0x2A, - REG_DAC_ENABLE, - - REG_DETUNE_MULT = 0x30, - REG_TOTAL_LEVEL = 0x40, - REG_ATTACK_KS = 0x50, - REG_DECAY_AM = 0x60, - REG_SUSTAIN_RATE = 0x70, - REG_S_LVL_R_RATE = 0x80, - - REG_FNUM_LOW = 0xA0, - REG_BLOCK_FNUM_H = 0xA4, - REG_FNUM_LOW_CH3 = 0xA8, - REG_BLOCK_FN_CH3 = 0xAC, - REG_ALG_FEEDBACK = 0xB0, - REG_LR_AMS_PMS = 0xB4 -}; - #define BIT_TIMERA_ENABLE 0x1 #define BIT_TIMERB_ENABLE 0x2 #define BIT_TIMERA_OVEREN 0x4 diff -r 7decd421cdc8 -r 7068a9db6dd0 ym2612.h --- a/ym2612.h Sun Nov 01 00:12:52 2015 -0700 +++ b/ym2612.h Sun Nov 01 12:55:08 2015 -0800 @@ -98,6 +98,31 @@ uint8_t part2_regs[YM_PART2_REGS]; } ym2612_context; +enum { + REG_LFO = 0x22, + REG_TIMERA_HIGH = 0x24, + REG_TIMERA_LOW, + REG_TIMERB, + REG_TIME_CTRL, + REG_KEY_ONOFF, + REG_DAC = 0x2A, + REG_DAC_ENABLE, + + REG_DETUNE_MULT = 0x30, + REG_TOTAL_LEVEL = 0x40, + REG_ATTACK_KS = 0x50, + REG_DECAY_AM = 0x60, + REG_SUSTAIN_RATE = 0x70, + REG_S_LVL_R_RATE = 0x80, + + REG_FNUM_LOW = 0xA0, + REG_BLOCK_FNUM_H = 0xA4, + REG_FNUM_LOW_CH3 = 0xA8, + REG_BLOCK_FN_CH3 = 0xAC, + REG_ALG_FEEDBACK = 0xB0, + REG_LR_AMS_PMS = 0xB4 +}; + void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t sample_limit, uint32_t options); void ym_adjust_master_clock(ym2612_context * context, uint32_t master_clock); void ym_run(ym2612_context * context, uint32_t to_cycle);