Mercurial > repos > blastem
comparison vgm.c @ 2053:3414a4423de1 segacd
Merge from default
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 15 Jan 2022 13:15:21 -0800 |
parents | 327332138c5c |
children | 7e1215d17571 |
comparison
equal
deleted
inserted
replaced
1692:5dacaef602a7 | 2053:3414a4423de1 |
---|---|
1 #include <stdlib.h> | |
2 #include <string.h> | |
3 #include <stddef.h> | |
4 #include "vgm.h" | |
5 | |
6 vgm_writer *vgm_write_open(char *filename, uint32_t rate, uint32_t clock, uint32_t cycle) | |
7 { | |
8 FILE *f = fopen(filename, "wb"); | |
9 if (!f) { | |
10 return NULL; | |
11 } | |
12 vgm_writer *writer = calloc(sizeof(vgm_writer), 1); | |
13 memcpy(writer->header.ident, "Vgm ", 4); | |
14 writer->header.version = 0x150; | |
15 writer->header.data_offset = sizeof(writer->header) - offsetof(vgm_header, data_offset); | |
16 writer->header.rate = rate; | |
17 writer->f = f; | |
18 if (1 != fwrite(&writer->header, sizeof(writer->header), 1, f)) { | |
19 free(writer); | |
20 fclose(f); | |
21 return NULL; | |
22 } | |
23 writer->master_clock = clock; | |
24 writer->last_cycle = cycle; | |
25 | |
26 return writer; | |
27 } | |
28 | |
29 void vgm_sn76489_init(vgm_writer *writer, uint32_t clock, uint16_t feedback, uint8_t shift_reg_size, uint8_t flags) | |
30 { | |
31 if (flags && writer->header.version < 0x151) { | |
32 writer->header.version = 0x151; | |
33 } | |
34 writer->header.sn76489_clk = clock, | |
35 writer->header.sn76489_fb = feedback; | |
36 writer->header.sn76489_shift = shift_reg_size; | |
37 writer->header.sn76489_flags = flags; | |
38 } | |
39 | |
40 static void wait_commands(vgm_writer *writer, uint32_t delta) | |
41 { | |
42 if (!delta) { | |
43 return; | |
44 } | |
45 if (delta <= 0x10) { | |
46 fputc(CMD_WAIT_SHORT + (delta - 1), writer->f); | |
47 } else if (delta >= 735 && delta <= (735 + 0x10)) { | |
48 fputc(CMD_WAIT_60, writer->f); | |
49 wait_commands(writer, delta - 735); | |
50 } else if (delta >= 882 && delta <= (882 + 0x10)) { | |
51 fputc(CMD_WAIT_50, writer->f); | |
52 wait_commands(writer, delta - 882); | |
53 } else if (delta > 0xFFFF) { | |
54 uint8_t cmd[3] = {CMD_WAIT, 0xFF, 0xFF}; | |
55 fwrite(cmd, 1, sizeof(cmd), writer->f); | |
56 wait_commands(writer, delta - 0xFFFF); | |
57 } else { | |
58 uint8_t cmd[3] = {CMD_WAIT, delta, delta >> 8}; | |
59 fwrite(cmd, 1, sizeof(cmd), writer->f); | |
60 } | |
61 } | |
62 | |
63 #include "util.h" | |
64 static void add_wait(vgm_writer *writer, uint32_t cycle) | |
65 { | |
66 if (cycle < writer->last_cycle) { | |
67 //This can happen when a YM-2612 write happens immediately after a PSG write | |
68 //due to the relatively low granularity of the PSG's internal clock | |
69 //given that VGM only has a granularity of 44.1 kHz ignoring this is harmless | |
70 return; | |
71 } | |
72 uint64_t last_sample = (uint64_t)writer->last_cycle * (uint64_t)44100; | |
73 last_sample /= (uint64_t)writer->master_clock; | |
74 uint64_t sample = ((uint64_t)cycle + (uint64_t)writer->extra_delta) * (uint64_t)44100; | |
75 sample /= (uint64_t)writer->master_clock; | |
76 uint32_t delta = sample - last_sample; | |
77 | |
78 writer->last_cycle = cycle; | |
79 writer->extra_delta = 0; | |
80 writer->header.num_samples += delta; | |
81 wait_commands(writer, delta); | |
82 } | |
83 | |
84 static uint8_t last_cmd; | |
85 void vgm_sn76489_write(vgm_writer *writer, uint32_t cycle, uint8_t value) | |
86 { | |
87 add_wait(writer, cycle); | |
88 uint8_t cmd[2] = {CMD_PSG, value}; | |
89 last_cmd = CMD_PSG; | |
90 fwrite(cmd, 1, sizeof(cmd), writer->f); | |
91 } | |
92 | |
93 void vgm_ym2612_init(vgm_writer *writer, uint32_t clock) | |
94 { | |
95 writer->header.ym2612_clk = clock; | |
96 } | |
97 | |
98 void vgm_ym2612_part1_write(vgm_writer *writer, uint32_t cycle, uint8_t reg, uint8_t value) | |
99 { | |
100 add_wait(writer, cycle); | |
101 uint8_t cmd[3] = {CMD_YM2612_0, reg, value}; | |
102 last_cmd = CMD_YM2612_0; | |
103 fwrite(cmd, 1, sizeof(cmd), writer->f); | |
104 } | |
105 | |
106 void vgm_ym2612_part2_write(vgm_writer *writer, uint32_t cycle, uint8_t reg, uint8_t value) | |
107 { | |
108 add_wait(writer, cycle); | |
109 uint8_t cmd[3] = {CMD_YM2612_1, reg, value}; | |
110 last_cmd = CMD_YM2612_1; | |
111 fwrite(cmd, 1, sizeof(cmd), writer->f); | |
112 } | |
113 | |
114 void vgm_adjust_cycles(vgm_writer *writer, uint32_t deduction) | |
115 { | |
116 if (deduction > writer->last_cycle) { | |
117 writer->extra_delta += deduction - writer->last_cycle; | |
118 writer->last_cycle = 0; | |
119 } else { | |
120 writer->last_cycle -= deduction; | |
121 } | |
122 } | |
123 | |
124 void vgm_close(vgm_writer *writer) | |
125 { | |
126 uint8_t cmd = 0x66; | |
127 fwrite(&cmd, 1, sizeof(cmd), writer->f); | |
128 writer->header.eof_offset = ftell(writer->f) - offsetof(vgm_header, eof_offset); | |
129 fseek(writer->f, SEEK_SET, 0); | |
130 fwrite(&writer->header, sizeof(writer->header), 1, writer->f); | |
131 fclose(writer->f); | |
132 free(writer); | |
133 } |