# HG changeset patch # User Michael Pavone # Date 1391753899 28800 # Node ID 2c1679058727fb28056bc6f83998c2a5adb8f26f # Parent a277de8c1a183e3512571b9413bdfe4cbb5f5c0f Add support for VGM 1.50 PCM data blocks diff -r a277de8c1a18 -r 2c1679058727 vgmplay.c --- a/vgmplay.c Thu Feb 06 10:04:31 2014 -0800 +++ b/vgmplay.c Thu Feb 06 22:18:19 2014 -0800 @@ -65,7 +65,18 @@ CMD_DATA, CMD_PCM_WRITE, CMD_WAIT_SHORT = 0x70, - CMD_YM2612_DAC = 0x80 + 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) @@ -93,10 +104,16 @@ uint8_t headless = 0; #define CYCLE_LIMIT MCLKS_NTSC/60 +#define MAX_SOUND_CYCLES 100000 tern_node * config; void wait(ym2612_context * y_context, psg_context * p_context, uint32_t * current_cycle, uint32_t cycles) { + while (cycles > MAX_SOUND_CYCLES) + { + wait(y_context, p_context, current_cycle, MAX_SOUND_CYCLES); + cycles -= MAX_SOUND_CYCLES; + } *current_cycle += cycles; psg_run(p_context, *current_cycle); ym_run(y_context, *current_cycle); @@ -109,8 +126,20 @@ } } +typedef struct { + struct data_block *next; + uint8_t *data; + uint32_t size; + uint8_t type; +} data_block; + int main(int argc, char ** argv) { + data_block *blocks = NULL; + data_block *seek_block = NULL; + uint32_t seek_offset; + uint32_t block_offset; + uint32_t fps = 60; config = load_config(argv[0]); render_init(320, 240, "vgm play", 60, 0, 0); @@ -173,13 +202,71 @@ break; case CMD_END: return 0; + case CMD_DATA: { + 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 (data_type == DATA_YM2612_PCM) { + data_block ** curblock = &blocks; + while(*curblock) + { + curblock = &((*curblock)->next); + } + *curblock = malloc(sizeof(data_block)); + (*curblock)->size = data_size; + (*curblock)->type = data_type; + (*curblock)->data = cur; + (*curblock)->next = NULL; + } else { + fprintf(stderr, "Skipping data block with unrecognized type %X\n", data_type); + } + cur += data_size; + break; + } + case CMD_DATA_SEEK: { + uint32_t new_offset = *(cur++); + new_offset |= *(cur++) << 8; + new_offset |= *(cur++) << 16; + new_offset |= *(cur++) << 24; + if (!seek_block || new_offset < seek_offset) { + seek_block = blocks; + seek_offset = 0; + block_offset = 0; + } + while (seek_block && (seek_offset - block_offset + seek_block->size) < new_offset) + { + seek_offset += seek_block->size - block_offset; + seek_block = seek_block->next; + block_offset = 0; + } + block_offset += new_offset-seek_offset; + seek_offset = new_offset; + break; + } + default: if (cmd >= CMD_WAIT_SHORT && cmd < (CMD_WAIT_SHORT + 0x10)) { uint32_t wait_time = (cmd & 0xF) + 1; wait_time *= mclks_sample; wait(&y_context, &p_context, ¤t_cycle, wait_time); + } else if (cmd >= CMD_YM2612_DAC && cmd < CMD_DAC_STREAM_SETUP) { + if (seek_block) { + ym_address_write_part1(&y_context, 0x2A); + ym_data_write(&y_context, seek_block->data[block_offset]); + } else { + fputs("Encountered DAC write command but data seek pointer is invalid!\n", stderr); + } + uint32_t wait_time = (cmd & 0xF); + if (wait_time) + { + wait_time *= mclks_sample; + wait(&y_context, &p_context, ¤t_cycle, wait_time); + } } else { - printf("unimplemented command: %X at offset %X\n", cmd, cur - data - 1); + printf("unimplemented command: %X at offset %X\n", cmd, (unsigned int)(cur - data - 1)); exit(1); } }