comparison fmgen/psg.c @ 0:c55ea9478c80

Hello Gensokyo!
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Tue, 21 May 2013 10:29:21 +0200
parents
children 8ad174416431
comparison
equal deleted inserted replaced
-1:000000000000 0:c55ea9478c80
1 // FIXME: move ugly-ass legalese somewhere where it won't be seen
2 // by anyone other than lawyers. (/dev/null would be ideal but sadly
3 // we live in an imperfect world).
4 /* Copyright (c) 2012/2013, Peter Barfuss
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9
10 1. Redistributions of source code must retain the above copyright notice, this
11 list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright notice,
13 this list of conditions and the following disclaimer in the documentation
14 and/or other materials provided with the distribution.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
26
27 // Quick, somewhat hacky PSG implementation. Seems to work fine in most cases.
28 // Known bugs: volume *may* still be off for a lot of samples. Importantly,
29 // waveform volume is too quiet but setting it at the correct volume makes
30 // the noise volume too loud and vice-versa. I *think* what I have currently
31 // is mostly correct (I'm basing this mostly on how good Strawberry Crisis
32 // sounds with the given settings), but it's possible that more fine-tuning
33 // is needed. Apart from that, this is probably the sketchiest part of all
34 // of my emulator code, but then again there's a bit-exact VHDL core of
35 // the YM2149F/AY-3-8910, so while I do want to make this as good
36 // as the code in opna.c, it's the lowest-priority of all of the code here.
37 // --bofh
38 #include <stdint.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <math.h>
42 #include <unistd.h>
43 #include "op.h"
44 #include "psg.h"
45 #define Max(a,b) ((a>b)?a:b)
46 #define Min(a,b) ((a<b)?a:b)
47
48 // ---------------------------------------------------------------------------
49 // テーブル
50 //
51 int EmitTable[0x20] = { -1, };
52 uint enveloptable[16][64] = { 0, };
53
54 // ---------------------------------------------------------------------------
55 // PSG reset to power-on defaults
56 //
57 void PSGReset(PSG *psg)
58 {
59 int i;
60 for (i=0; i<14; i++)
61 PSGSetReg(psg, i, 0);
62 PSGSetReg(psg, 7, 0xff);
63 PSGSetReg(psg, 14, 0xff);
64 PSGSetReg(psg, 15, 0xff);
65 }
66
67 // ---------------------------------------------------------------------------
68 // This code is strongly inspired by some random PSG emulator code I found,
69 // and is probably not the optimal way to define periods. It *is* at least
70 // among the fastest, given that it uses the hilarious hack of using the
71 // integer overflow on a 32-bit unsigned integer to compute ""moduli"".
72 //
73 void PSGSetClock(PSG *psg, uint32_t clock, uint32_t rate)
74 {
75 psg->tperiodbase = (uint32_t)((1 << toneshift ) / 4.0f * clock / rate);
76 psg->eperiodbase = (uint32_t)((1 << envshift ) / 4.0f * clock / rate);
77
78 // 各データの更新
79 int tmp;
80 tmp = ((psg->reg[0] + psg->reg[1] * 256) & 0xfff);
81 psg->speriod[0] = tmp ? psg->tperiodbase / tmp : psg->tperiodbase;
82 tmp = ((psg->reg[2] + psg->reg[3] * 256) & 0xfff);
83 psg->speriod[1] = tmp ? psg->tperiodbase / tmp : psg->tperiodbase;
84 tmp = ((psg->reg[4] + psg->reg[5] * 256) & 0xfff);
85 psg->speriod[2] = tmp ? psg->tperiodbase / tmp : psg->tperiodbase;
86 tmp = psg->reg[6] & 0x1f;
87 psg->nperiod = tmp;
88 tmp = ((psg->reg[11] + psg->reg[12] * 256) & 0xffff);
89 psg->eperiod = tmp ? psg->eperiodbase / tmp : psg->eperiodbase * 2;
90 }
91
92 // ---------------------------------------------------------------------------
93 // エンベロープ波形テーブル
94 //
95 static uint8_t table3[4] = { 0, 1, -1, 0 };
96 void MakeEnvelopTable(void)
97 {
98 // 0 lo 1 up 2 down 3 hi
99 static uint8_t table1[16*2] =
100 {
101 2,0, 2,0, 2,0, 2,0, 1,0, 1,0, 1,0, 1,0,
102 2,2, 2,0, 2,1, 2,3, 1,1, 1,3, 1,2, 1,0,
103 };
104 int i, j;
105
106 if (!enveloptable[0][0])
107 {
108 uint* ptr = enveloptable[0];
109
110 for (i=0; i<16*2; i++)
111 {
112 uint8_t v = ((table1[i] & 0x2) ? 31 : 0);
113
114 for (j=0; j<32; j++)
115 {
116 *ptr++ = EmitTable[v];
117 v += table3[table1[i]];
118 }
119 }
120 }
121 }
122
123 // ---------------------------------------------------------------------------
124 // Sets the channel output mask for the PSG device.
125 // c is a bitvector where the 3 LSBs are set to 0 to disable a given
126 // PSG channel and 1 to enable it.
127 // TODO: Possibly allow enabling tone/noise output for each channel independently?
128 //
129 void PSGSetChannelMask(PSG *psg, int c)
130 {
131 int i;
132 psg->mask = c;
133 for (i=0; i<3; i++)
134 psg->olevel[i] = psg->mask & (1 << i) ? EmitTable[(psg->reg[8+i] & 15) * 2 + 1] : 0;
135 }
136
137 // ---------------------------------------------------------------------------
138 // Sets the PSG volume. It's a fairly standard dB -> internal linear scale
139 // conversion, followed by generating a table with volume levels.
140 // The "magic number" 1.189207115 is just sqrt(sqrt(2))
141 // TODO: is this table really needed? We have fast floating-point now.
142 //
143 void SetVolumePSG(PSG *psg, int volume)
144 {
145 int i;
146 float base = 0x4000 / 3.0f * expf((float)M_LN10*(volume / 40.0f));
147 for (i=31; i>=2; i--)
148 {
149 EmitTable[i] = lrintf(base);
150 base /= 1.189207115f;
151 }
152 EmitTable[1] = 0;
153 EmitTable[0] = 0;
154 MakeEnvelopTable();
155
156 PSGSetChannelMask(psg, psg->mask);
157 }
158
159 // ---------------------------------------------------------------------------
160 // PSG register set routine. Mostly just what you'd expect from reading the manual.
161 // Fairly boring code overall. regnum can be 0 - 15, data can be 0x00 - 0xFF.
162 // (This should not be surprising - the YM2149F *did* use an 8-bit bus, after all).
163 // Interesting quirk: the task of register 7 (channel enable/disable) is basically
164 // entirely duplicated by other registers, to the point where you can basically
165 // just ignore any writes to register 7 entirely. I save it here in case some
166 // braindead routine wants to read its value and do something based on that
167 // (Another curiosity: register 7 on the PSG appears to be the only register
168 // between *both* the OPNA and the PSG which is actually *read from* by
169 // pmdwin.cpp and not just written to. Amusingly enough, the only reason
170 // that it is ever read is so that it can then OR something with what it just read
171 // and then write that back to register 7. Hilarity).
172 // HACK ALERT: The output levels for channels 0 and 1 are increased by a factor of 4
173 // to make them match the actual chip in loudness, but without causing the noise channel
174 // to overtake everything in intensity. This is almost certainly wrong, and moreover
175 // it assumes that channel 2 will be playing back Speak Board effects which usually means
176 // drum kit only (for the most part, at least), and not being used as a separate tonal
177 // channel in its own right. To the best of my knowledge, this does hold for all of ZUN's
178 // songs, however, once you step outside that set of music, it's trivial to find
179 // all sorts of counterexamples to that assumption. Therefore, this should be fixed ASAP.
180 //
181 void PSGSetReg(PSG *psg, uint8_t regnum, uint8_t data)
182 {
183 if (regnum < 0x10)
184 {
185 psg->reg[regnum] = data;
186 switch (regnum)
187 {
188 int tmp;
189
190 case 0: // ChA Fine Tune
191 case 1: // ChA Coarse Tune
192 tmp = ((psg->reg[0] + psg->reg[1] * 256) & 0xfff);
193 psg->speriod[0] = tmp ? psg->tperiodbase / tmp : psg->tperiodbase;
194 break;
195
196 case 2: // ChB Fine Tune
197 case 3: // ChB Coarse Tune
198 tmp = ((psg->reg[2] + psg->reg[3] * 256) & 0xfff);
199 psg->speriod[1] = tmp ? psg->tperiodbase / tmp : psg->tperiodbase;
200 break;
201
202 case 4: // ChC Fine Tune
203 case 5: // ChC Coarse Tune
204 tmp = ((psg->reg[4] + psg->reg[5] * 256) & 0xfff);
205 psg->speriod[2] = tmp ? psg->tperiodbase / tmp : psg->tperiodbase;
206 break;
207
208 case 6: // Noise generator control
209 data &= 0x1f;
210 psg->nperiod = data;
211 break;
212
213 case 8:
214 psg->olevel[0] = psg->mask & 1 ? EmitTable[(data & 15) * 2 + 1] : 0;
215 break;
216
217 case 9:
218 psg->olevel[1] = psg->mask & 2 ? EmitTable[(data & 15) * 2 + 1] : 0;
219 break;
220
221 case 10:
222 psg->olevel[2] = psg->mask & 4 ? EmitTable[(data & 15) * 2 + 1] : 0;
223 break;
224
225 case 11: // Envelope period
226 case 12:
227 tmp = ((psg->reg[11] + psg->reg[12] * 256) & 0xffff);
228 psg->eperiod = tmp ? psg->eperiodbase / tmp : psg->eperiodbase * 2;
229 break;
230
231 case 13: // Envelope shape
232 psg->ecount = 0;
233 psg->envelop = enveloptable[data & 15];
234 break;
235 }
236 }
237 }
238
239 // ---------------------------------------------------------------------------
240 // Init code. Set volume to 0, reset the chip, enable all channels, seed the RNG.
241 // RNG seed lifted from MAME's YM2149F emulation routine, appears to be correct.
242 //
243 void PSGInit(PSG *psg)
244 {
245 SetVolumePSG(psg, 0);
246 psg->rng = 14231;
247 psg->ncount = 0;
248 PSGReset(psg);
249 psg->mask = 0x3f;
250 }
251
252 // ---------------------------------------------------------------------------
253 // The main output routine for the PSG emulation.
254 // dest should be an array of size nsamples, and of type Sample
255 // (one of int16_t, int32_t or float - any will work here without causing
256 // clipping/precision problems).
257 // Everything is implemented using some form of fixed-point arithmetic
258 // that currently needs no more than 32-bits for its implementation,
259 // but I'm pretty certain that you can get by with much less than that
260 // and still have mostly correct-to-fully-correct emulation.
261 //
262 // TODO: In the future, test the veracity of the above statement. Moreover,
263 // if it turns out to be correct, rewrite this routine to not use more than
264 // the required precision. This is irrelevant for any PC newer than, well,
265 // a 386DX/68040, but important for efficient hardware implementation.
266 //
267 void PSGMix(PSG *psg, Sample* dest, uint32_t nsamples)
268 {
269 uint8_t chenable[3];
270 uint8_t r7 = ~psg->reg[7];
271 int i;
272
273 if ((r7 & 0x3f) | ((psg->reg[8] | psg->reg[9] | psg->reg[10]) & 0x1f)) {
274 chenable[0] = (r7 & 0x01) && (psg->speriod[0] <= (1 << toneshift));
275 chenable[1] = (r7 & 0x02) && (psg->speriod[1] <= (1 << toneshift));
276 chenable[2] = (r7 & 0x04) && (psg->speriod[2] <= (1 << toneshift));
277
278 int noise, sample;
279 uint env;
280 uint* p1 = ((psg->mask & 1) && (psg->reg[ 8] & 0x10)) ? &env : &psg->olevel[0];
281 uint* p2 = ((psg->mask & 2) && (psg->reg[ 9] & 0x10)) ? &env : &psg->olevel[1];
282 uint* p3 = ((psg->mask & 4) && (psg->reg[10] & 0x10)) ? &env : &psg->olevel[2];
283 #define SCOUNT(ch) (psg->scount[ch] >> toneshift)
284
285 if (p1 != &env && p2 != &env && p3 != &env) {
286 // ノイズ有り
287 for (i=0; i<nsamples; i++) {
288 psg->ncount++;
289 if(psg->ncount >= psg->nperiod) {
290 if(psg->rng & 1)
291 psg->rng ^= 0x24000;
292 psg->rng >>= 1;
293 psg->ncount = 0;
294 }
295 noise = (psg->rng & 1);
296 sample = 0;
297 {
298 int x, y, z;
299 x = ((SCOUNT(0) & chenable[0]) | ((r7 >> 3) & noise)) - 1; // 0 or -1
300 sample += (psg->olevel[0] + x) ^ x;
301 psg->scount[0] += psg->speriod[0];
302 y = ((SCOUNT(1) & chenable[1]) | ((r7 >> 4) & noise)) - 1;
303 sample += (psg->olevel[1] + y) ^ y;
304 psg->scount[1] += psg->speriod[1];
305 z = ((SCOUNT(2) & chenable[2]) | ((r7 >> 5) & noise)) - 1;
306 sample += (psg->olevel[2] + z) ^ z;
307 psg->scount[2] += psg->speriod[2];
308 }
309 dest[0] += sample;
310 dest += 1;
311 }
312
313 // エンベロープの計算をさぼった帳尻あわせ
314 psg->ecount = (psg->ecount >> 8) + (psg->eperiod >> 8) * nsamples;
315 if (psg->ecount >= (1 << (envshift+6-8))) {
316 if ((psg->reg[0x0d] & 0x0b) != 0x0a)
317 psg->ecount |= (1 << (envshift+5-8));
318 psg->ecount &= (1 << (envshift+6-8)) - 1;
319 }
320 psg->ecount <<= 8;
321 } else {
322 // エンベロープあり
323 for (i=0; i<nsamples; i++) {
324 psg->ncount++;
325 if(psg->ncount >= psg->nperiod) {
326 if(psg->rng & 1)
327 psg->rng ^= 0x24000;
328 psg->rng >>= 1;
329 psg->ncount = 0;
330 }
331 noise = (psg->rng & 1);
332 sample = 0;
333 {
334 env = psg->envelop[psg->ecount >> envshift];
335 psg->ecount += psg->eperiod;
336 if (psg->ecount >= (1 << (envshift+6))) {
337 if ((psg->reg[0x0d] & 0x0b) != 0x0a)
338 psg->ecount |= (1 << (envshift+5));
339 psg->ecount &= (1 << (envshift+6)) - 1;
340 }
341 int x, y, z;
342 x = ((SCOUNT(0) & chenable[0]) | ((r7 >> 3) & noise)) - 1; // 0 or -1
343 sample += (*p1 + x) ^ x;
344 psg->scount[0] += psg->speriod[0];
345 y = ((SCOUNT(1) & chenable[1]) | ((r7 >> 4) & noise)) - 1;
346 sample += (*p2 + y) ^ y;
347 psg->scount[1] += psg->speriod[1];
348 z = ((SCOUNT(2) & chenable[2]) | ((r7 >> 5) & noise)) - 1;
349 sample += (*p3 + z) ^ z;
350 psg->scount[2] += psg->speriod[2];
351 }
352 dest[0] += sample;
353 dest += 1;
354 }
355 }
356 }
357 }
358