Mercurial > repos > blastem
comparison ym_common.c @ 2558:3f58fec775df
Initial work on YMF262 (aka OPL3) emulation
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sun, 19 Jan 2025 00:31:16 -0800 |
parents | |
children | eb588f22ec76 |
comparison
equal
deleted
inserted
replaced
2557:75dd7536c467 | 2558:3f58fec775df |
---|---|
1 #include <math.h> | |
2 #include "ym_common.h" | |
3 | |
4 #ifdef __ANDROID__ | |
5 #define log2(x) (log(x)/log(2)) | |
6 #endif | |
7 | |
8 //According to Nemesis, real hardware only uses a 256 entry quarter sine table; however, | |
9 //memory is cheap so using a half sine table will probably save some cycles | |
10 //a full sine table would be nice, but negative numbers don't get along with log2 | |
11 #define SINE_TABLE_SIZE 512 | |
12 static uint16_t sine_table[SINE_TABLE_SIZE]; | |
13 //Similar deal here with the power table for log -> linear conversion | |
14 //According to Nemesis, real hardware only uses a 256 entry table for the fractional part | |
15 //and uses the whole part as a shift amount. | |
16 #define POW_TABLE_SIZE (1 << 13) | |
17 static uint16_t pow_table[POW_TABLE_SIZE]; | |
18 | |
19 static uint16_t rate_table_base[] = { | |
20 //main portion | |
21 0,1,0,1,0,1,0,1, | |
22 0,1,0,1,1,1,0,1, | |
23 0,1,1,1,0,1,1,1, | |
24 0,1,1,1,1,1,1,1, | |
25 //top end | |
26 1,1,1,1,1,1,1,1, | |
27 1,1,1,2,1,1,1,2, | |
28 1,2,1,2,1,2,1,2, | |
29 1,2,2,2,1,2,2,2, | |
30 }; | |
31 | |
32 uint16_t rate_table[64*8]; | |
33 | |
34 static uint8_t lfo_pm_base[][8] = { | |
35 {0, 0, 0, 0, 0, 0, 0, 0}, | |
36 {0, 0, 0, 0, 4, 4, 4, 4}, | |
37 {0, 0, 0, 4, 4, 4, 8, 8}, | |
38 {0, 0, 4, 4, 8, 8, 0xc, 0xc}, | |
39 {0, 0, 4, 8, 8, 8, 0xc,0x10}, | |
40 {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, | |
41 {0, 0,0x10,0x18,0x20,0x20,0x28,0x30}, | |
42 {0, 0,0x20,0x30,0x40,0x40,0x50,0x60} | |
43 }; | |
44 int16_t lfo_pm_table[128 * 32 * 8]; | |
45 | |
46 static uint16_t round_fixed_point(double value, int dec_bits) | |
47 { | |
48 return value * (1 << dec_bits) + 0.5; | |
49 } | |
50 | |
51 void ym_init_tables(void) | |
52 { | |
53 static uint8_t did_tbl_init; | |
54 if (did_tbl_init) { | |
55 return; | |
56 } | |
57 did_tbl_init = 1; | |
58 //populate sine table | |
59 for (int32_t i = 0; i < 512; i++) { | |
60 double sine = sin( ((double)(i*2+1) / SINE_TABLE_SIZE) * M_PI_2 ); | |
61 | |
62 //table stores 4.8 fixed pointed representation of the base 2 log | |
63 sine_table[i] = round_fixed_point(-log2(sine), 8); | |
64 } | |
65 //populate power table | |
66 for (int32_t i = 0; i < POW_TABLE_SIZE; i++) { | |
67 double linear = pow(2, -((double)((i & 0xFF)+1) / 256.0)); | |
68 int32_t tmp = round_fixed_point(linear, 11); | |
69 int32_t shift = (i >> 8) - 2; | |
70 if (shift < 0) { | |
71 tmp <<= 0-shift; | |
72 } else { | |
73 tmp >>= shift; | |
74 } | |
75 pow_table[i] = tmp; | |
76 } | |
77 //populate envelope generator rate table, from small base table | |
78 for (int rate = 0; rate < 64; rate++) { | |
79 for (int cycle = 0; cycle < 8; cycle++) { | |
80 uint16_t value; | |
81 if (rate < 2) { | |
82 value = 0; | |
83 } else if (rate >= 60) { | |
84 value = 8; | |
85 } else if (rate < 8) { | |
86 value = rate_table_base[((rate & 6) == 6 ? 16 : 0) + cycle]; | |
87 } else if (rate < 48) { | |
88 value = rate_table_base[(rate & 0x3) * 8 + cycle]; | |
89 } else { | |
90 value = rate_table_base[32 + (rate & 0x3) * 8 + cycle] << ((rate - 48) >> 2); | |
91 } | |
92 rate_table[rate * 8 + cycle] = value; | |
93 } | |
94 } | |
95 //populate LFO PM table from small base table | |
96 //seems like there must be a better way to derive this | |
97 for (int freq = 0; freq < 128; freq++) { | |
98 for (int pms = 0; pms < 8; pms++) { | |
99 for (int step = 0; step < 32; step++) { | |
100 int16_t value = 0; | |
101 for (int bit = 0x40, shift = 0; bit > 0; bit >>= 1, shift++) { | |
102 if (freq & bit) { | |
103 value += lfo_pm_base[pms][(step & 0x8) ? 7-step & 7 : step & 7] >> shift; | |
104 } | |
105 } | |
106 if (step & 0x10) { | |
107 value = -value; | |
108 } | |
109 lfo_pm_table[freq * 256 + pms * 32 + step] = value; | |
110 } | |
111 } | |
112 } | |
113 } | |
114 | |
115 int16_t ym_sine(uint16_t phase, int16_t mod, uint16_t env) | |
116 { | |
117 phase += mod; | |
118 if (env > MAX_ENVELOPE) { | |
119 env = MAX_ENVELOPE; | |
120 } | |
121 int16_t output = pow_table[sine_table[phase & 0x1FF] + env]; | |
122 if (phase & 0x200) { | |
123 output = -output; | |
124 } | |
125 return output; | |
126 } | |
127 | |
128 int16_t ym_opl_wave(uint16_t phase, int16_t mod, uint16_t env, uint8_t waveform) | |
129 { | |
130 if (env > MAX_OPL_ENVELOPE) { | |
131 env = MAX_OPL_ENVELOPE; | |
132 } | |
133 | |
134 int16_t output; | |
135 switch (waveform) | |
136 { | |
137 default: | |
138 case 0: | |
139 output = pow_table[sine_table[phase & 0x1FF] + env]; | |
140 if (phase & 0x200) { | |
141 output = -output; | |
142 } | |
143 break; | |
144 case 1: | |
145 if (phase & 0x200) { | |
146 output = 0; | |
147 } else { | |
148 output = pow_table[sine_table[phase & 0x1FF] + env]; | |
149 } | |
150 break; | |
151 case 2: | |
152 output = pow_table[sine_table[phase & 0x1FF] + env]; | |
153 break; | |
154 case 3: | |
155 if (phase & 0x100) { | |
156 output = 0; | |
157 } else { | |
158 output = pow_table[sine_table[phase & 0xFF] + env]; | |
159 } | |
160 break; | |
161 case 4: | |
162 if (phase & 0x200) { | |
163 output = 0; | |
164 } else { | |
165 output = pow_table[sine_table[(phase & 0xFF) << 1] + env]; | |
166 if (phase & 0x100) { | |
167 output = -output; | |
168 } | |
169 } | |
170 break; | |
171 case 5: | |
172 if (phase & 0x200) { | |
173 output = 0; | |
174 } else { | |
175 output = pow_table[sine_table[(phase & 0xFF) << 1] + env]; | |
176 } | |
177 break; | |
178 case 6: | |
179 output = pow_table[env]; | |
180 if (phase & 0x200) { | |
181 output = -output; | |
182 } | |
183 break; | |
184 case 7: | |
185 if (phase & 0x200) { | |
186 output = -pow_table[((~phase) & 0x1FF) << 3 + env]; | |
187 } else { | |
188 output = pow_table[(phase & 0x1FF) << 3 + env]; | |
189 } | |
190 break; | |
191 } | |
192 return output; | |
193 } |