platform ps2, handle audio similar to psp
[picodrive.git] / pico / sound / sn76496.c
CommitLineData
cc68a136 1/***************************************************************************\r
2\r
3 sn76496.c\r
4\r
5 Routines to emulate the Texas Instruments SN76489 / SN76496 programmable\r
6 tone /noise generator. Also known as (or at least compatible with) TMS9919.\r
7\r
8 Noise emulation is not accurate due to lack of documentation. The noise\r
9 generator uses a shift register with a XOR-feedback network, but the exact\r
10 layout is unknown. It can be set for either period or white noise; again,\r
11 the details are unknown.\r
12\r
13 28/03/2005 : Sebastien Chevalier\r
14 Update th SN76496Write func, according to SN76489 doc found on SMSPower.\r
15 - On write with 0x80 set to 0, when LastRegister is other then TONE,\r
16 the function is similar than update with 0x80 set to 1\r
17***************************************************************************/\r
18\r
19#ifndef __GNUC__\r
20#pragma warning (disable:4244)\r
21#endif\r
22\r
23#include "sn76496.h"\r
24\r
af01b1b1 25#define MAX_OUTPUT 0x4800 // was 0x7fff\r
cc68a136 26\r
27#define STEP 0x10000\r
28\r
29\r
30/* Formulas for noise generator */\r
31/* bit0 = output */\r
32\r
33/* noise feedback for white noise mode (verified on real SN76489 by John Kortink) */\r
af01b1b1 34#define FB_WNOISE_T 0x3000 /* (15bits) bit15 = bit1 ^ bit2, TI */\r
35#define FB_WNOISE_S 0x9000 /* (16bits) bit16 = bit0 ^ bit3, Sega PSG */\r
cc68a136 36\r
37/* noise feedback for periodic noise mode */\r
af01b1b1 38#define FB_PNOISE_T 0x4000 /* 15bit rotate for TI */\r
39#define FB_PNOISE_S 0x8000 /* 16bit rotate for Sega PSG */\r
cc68a136 40\r
af01b1b1 41#define FB_WNOISE FB_WNOISE_S /* Sega */\r
42#define FB_PNOISE FB_PNOISE_S\r
cc68a136 43\r
44\r
45struct SN76496\r
46{\r
47 //sound_stream * Channel;\r
48 int SampleRate;\r
49 unsigned int UpdateStep;\r
50 int VolTable[16]; /* volume table */\r
51 int Register[8]; /* registers */\r
52 int LastRegister; /* last register written */\r
53 int Volume[4]; /* volume of voice 0-2 and noise */\r
af01b1b1 54 unsigned int RNG; /* noise generator */\r
cc68a136 55 int NoiseFB; /* noise feedback mask */\r
56 int Period[4];\r
57 int Count[4];\r
58 int Output[4];\r
70efc52d 59 int Panning;\r
cc68a136 60};\r
61\r
62static struct SN76496 ono_sn; // one and only SN76496\r
6396f461 63int *sn76496_regs = ono_sn.Register;\r
cc68a136 64\r
65//static\r
66void SN76496Write(int data)\r
67{\r
68 struct SN76496 *R = &ono_sn;\r
5103774f 69 int n, r, c;\r
cc68a136 70\r
71 /* update the output buffer before changing the registers */\r
72 //stream_update(R->Channel,0);\r
73\r
5103774f 74 r = R->LastRegister;\r
cc68a136 75 if (data & 0x80)\r
5103774f 76 r = R->LastRegister = (data & 0x70) >> 4;\r
77 c = r / 2;\r
cc68a136 78\r
5103774f 79 if (!(data & 0x80) && (r == 0 || r == 2 || r == 4))\r
80 // data byte (tone only)\r
81 R->Register[r] = (R->Register[r] & 0x0f) | ((data & 0x3f) << 4);\r
cc68a136 82 else\r
5103774f 83 R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f);\r
cc68a136 84\r
5103774f 85 data = R->Register[r];\r
86 switch (r)\r
87 {\r
88 case 0: /* tone 0 : frequency */\r
89 case 2: /* tone 1 : frequency */\r
90 case 4: /* tone 2 : frequency */\r
91 R->Period[c] = R->UpdateStep * data;\r
92 if (R->Period[c] == 0) R->Period[c] = R->UpdateStep;\r
6985cdd8 93 if (R->Count[c] > R->Period[c]) R->Count[c] = R->Period[c];\r
5103774f 94 if (r == 4)\r
95 {\r
96 /* update noise shift frequency */\r
97 if ((R->Register[6] & 0x03) == 0x03)\r
98 R->Period[3] = 2 * R->Period[2];\r
99 }\r
100 break;\r
101 case 1: /* tone 0 : volume */\r
102 case 3: /* tone 1 : volume */\r
103 case 5: /* tone 2 : volume */\r
104 case 7: /* noise : volume */\r
105 R->Volume[c] = R->VolTable[data & 0x0f];\r
106 break;\r
107 case 6: /* noise : frequency, mode */\r
108 n = data;\r
109 R->NoiseFB = (n & 4) ? FB_WNOISE : FB_PNOISE;\r
110 n &= 3;\r
111 /* N/512,N/1024,N/2048,Tone #3 output */\r
fa0c5b45 112 R->Period[3] = 2 * (n == 3 ? R->Period[2] : R->UpdateStep << (4 + n));\r
5103774f 113\r
114 /* reset noise shifter */\r
af01b1b1 115 R->RNG = FB_PNOISE;\r
5103774f 116 R->Output[3] = R->RNG & 1;\r
117 break;\r
cc68a136 118 }\r
119}\r
120\r
121/*\r
122WRITE8_HANDLER( SN76496_0_w ) { SN76496Write(0,data); }\r
123WRITE8_HANDLER( SN76496_1_w ) { SN76496Write(1,data); }\r
124WRITE8_HANDLER( SN76496_2_w ) { SN76496Write(2,data); }\r
125WRITE8_HANDLER( SN76496_3_w ) { SN76496Write(3,data); }\r
126WRITE8_HANDLER( SN76496_4_w ) { SN76496Write(4,data); }\r
127*/\r
128\r
129//static\r
4f265db7 130void SN76496Update(short *buffer, int length, int stereo)\r
cc68a136 131{\r
132 int i;\r
133 struct SN76496 *R = &ono_sn;\r
134\r
cc68a136 135 while (length > 0)\r
136 {\r
137 int vol[4];\r
cc68a136 138 int left;\r
139\r
140\r
141 /* vol[] keeps track of how long each square wave stays */\r
142 /* in the 1 position during the sample period. */\r
143 vol[0] = vol[1] = vol[2] = vol[3] = 0;\r
144\r
145 for (i = 0;i < 3;i++)\r
146 {\r
147 if (R->Output[i]) vol[i] += R->Count[i];\r
148 R->Count[i] -= STEP;\r
149 /* Period[i] is the half period of the square wave. Here, in each */\r
150 /* loop I add Period[i] twice, so that at the end of the loop the */\r
151 /* square wave is in the same status (0 or 1) it was at the start. */\r
152 /* vol[i] is also incremented by Period[i], since the wave has been 1 */\r
153 /* exactly half of the time, regardless of the initial position. */\r
154 /* If we exit the loop in the middle, Output[i] has to be inverted */\r
155 /* and vol[i] incremented only if the exit status of the square */\r
156 /* wave is 1. */\r
fa0c5b45 157 if (R->Count[i] < -2*R->Period[i] || R->Volume[i] == 0) {\r
6985cdd8 158 /* Cut off anything above the Nyquist frequency. */\r
159 /* It will only create aliasing anyway. This is actually an */\r
160 /* ideal lowpass filter with Nyquist corner frequency. */\r
af01b1b1 161 vol[i] += STEP/2; // mean value\r
162 R->Count[i] = R->Output[i] = 0;\r
163 }\r
164 while (R->Count[i] < 0)\r
cc68a136 165 {\r
af01b1b1 166 R->Count[i] += R->Period[i];\r
167 if (R->Count[i] >= 0)\r
cc68a136 168 {\r
169 R->Output[i] ^= 1;\r
170 if (R->Output[i]) vol[i] += R->Period[i];\r
171 break;\r
172 }\r
173 R->Count[i] += R->Period[i];\r
174 vol[i] += R->Period[i];\r
175 }\r
176 if (R->Output[i]) vol[i] -= R->Count[i];\r
177 }\r
178\r
179 left = STEP;\r
af01b1b1 180 if (R->Output[3]) vol[3] += R->Count[3];\r
cc68a136 181 do\r
182 {\r
183 int nextevent;\r
184\r
185 if (R->Count[3] < left) nextevent = R->Count[3];\r
186 else nextevent = left;\r
187\r
cc68a136 188 R->Count[3] -= nextevent;\r
189 if (R->Count[3] <= 0)\r
190 {\r
cc68a136 191 R->Output[3] = R->RNG & 1;\r
af01b1b1 192 R->RNG >>= 1;\r
193 if (R->Output[3])\r
194 {\r
195 R->RNG ^= R->NoiseFB;\r
196 vol[3] += R->Period[3];\r
197 }\r
cc68a136 198 R->Count[3] += R->Period[3];\r
cc68a136 199 }\r
cc68a136 200\r
201 left -= nextevent;\r
fa0c5b45 202 } while (left > 0 && R->Volume[3]);\r
af01b1b1 203 if (R->Output[3]) vol[3] -= R->Count[3];\r
cc68a136 204\r
70efc52d 205 length--;\r
206 if (R->Panning == 0xff || !stereo) {\r
207 unsigned int out =\r
208 vol[0] * R->Volume[0] + vol[1] * R->Volume[1] +\r
cc68a136 209 vol[2] * R->Volume[2] + vol[3] * R->Volume[3];\r
210\r
70efc52d 211 if (out > MAX_OUTPUT * STEP) out = MAX_OUTPUT * STEP;\r
212\r
213 out /= STEP; // will be optimized to shift; max 0x4800 = 18432\r
214 *buffer++ += out;\r
215 if (stereo) *buffer++ += out;\r
216 } else {\r
217#define P(n) !!(R->Panning & (1<<(n)))\r
218 unsigned int outl =\r
219 vol[0] * R->Volume[0] * P(4) + vol[1] * R->Volume[1] * P(5) +\r
220 vol[2] * R->Volume[2] * P(6) + vol[3] * R->Volume[3] * P(7);\r
221 unsigned int outr =\r
222 vol[0] * R->Volume[0] * P(0) + vol[1] * R->Volume[1] * P(1) +\r
223 vol[2] * R->Volume[2] * P(2) + vol[3] * R->Volume[3] * P(3);\r
224#undef P\r
225 if (outl > MAX_OUTPUT * STEP) outl = MAX_OUTPUT * STEP;\r
226 if (outr > MAX_OUTPUT * STEP) outr = MAX_OUTPUT * STEP;\r
227\r
228 outl /= STEP; // will be optimized to shift; max 0x4800 = 18432\r
229 outr /= STEP; // will be optimized to shift; max 0x4800 = 18432\r
230 *buffer++ += outl;\r
231 *buffer++ += outr;\r
232 }\r
cc68a136 233 }\r
234}\r
235\r
70efc52d 236void SN76496Config(int panning)\r
237{\r
238 struct SN76496 *R = &ono_sn;\r
239 R->Panning = panning & 0xff;\r
240}\r
241\r
cc68a136 242\r
243static void SN76496_set_clock(struct SN76496 *R,int clock)\r
244{\r
245\r
246 /* the base clock for the tone generators is the chip clock divided by 16; */\r
247 /* for the noise generator, it is clock / 256. */\r
248 /* Here we calculate the number of steps which happen during one sample */\r
249 /* at the given sample rate. No. of events = sample rate / (clock/16). */\r
250 /* STEP is a multiplier used to turn the fraction into a fixed point */\r
251 /* number. */\r
252 R->UpdateStep = ((double)STEP * R->SampleRate * 16) / clock;\r
253}\r
254\r
255\r
256static void SN76496_set_gain(struct SN76496 *R,int gain)\r
257{\r
258 int i;\r
259 double out;\r
260\r
261\r
262 gain &= 0xff;\r
263\r
264 /* increase max output basing on gain (0.2 dB per step) */\r
af01b1b1 265 out = MAX_OUTPUT / 4.0;\r
cc68a136 266 while (gain-- > 0)\r
267 out *= 1.023292992; /* = (10 ^ (0.2/20)) */\r
268\r
269 /* build volume table (2dB per step) */\r
270 for (i = 0;i < 15;i++)\r
271 {\r
272 /* limit volume to avoid clipping */\r
af01b1b1 273 if (out > MAX_OUTPUT / 4) R->VolTable[i] = MAX_OUTPUT / 4;\r
cc68a136 274 else R->VolTable[i] = out;\r
275\r
276 out /= 1.258925412; /* = 10 ^ (2/20) = 2dB */\r
277 }\r
278 R->VolTable[15] = 0;\r
279}\r
280\r
281\r
864ac1d6 282//static\r
283void SN76496_set_clockrate(int clock,int sample_rate)\r
284{\r
285 struct SN76496 *R = &ono_sn;\r
286\r
287 R->SampleRate = sample_rate;\r
288 SN76496_set_clock(R,clock);\r
289}\r
290\r
cc68a136 291//static\r
292int SN76496_init(int clock,int sample_rate)\r
293{\r
294 struct SN76496 *R = &ono_sn;\r
295 int i;\r
296\r
297 //R->Channel = stream_create(0,1, sample_rate,R,SN76496Update);\r
cc68a136 298\r
864ac1d6 299 SN76496_set_clockrate(clock,sample_rate);\r
cc68a136 300\r
301 for (i = 0;i < 4;i++) R->Volume[i] = 0;\r
302\r
303 R->LastRegister = 0;\r
304 for (i = 0;i < 8;i+=2)\r
305 {\r
306 R->Register[i] = 0;\r
307 R->Register[i + 1] = 0x0f; /* volume = 0 */\r
308 }\r
309\r
310 for (i = 0;i < 4;i++)\r
311 {\r
af01b1b1 312 R->Volume[i] = R->Output[i] = R->Count[i] = 0;\r
313 R->Period[i] = R->UpdateStep;\r
cc68a136 314 }\r
af01b1b1 315 R->RNG = FB_PNOISE;\r
cc68a136 316 R->Output[3] = R->RNG & 1;\r
317\r
318 // added\r
319 SN76496_set_gain(R, 0);\r
70efc52d 320 R->Panning = 0xff;\r
cc68a136 321\r
322 return 0;\r
323}\r
324\r