7d69b43e0149dbf607c0d08707706ec221378045
[picodrive.git] / pico / pico / xpcm.c
1 /*
2  * PicoDrive
3  * (C) notaz, 2008
4  * (C) irixxxx, 2024
5  *
6  * This work is licensed under the terms of MAME license.
7  * See COPYING file in the top-level directory.
8  *
9  * The following ADPCM algorithm was derived from MAME upd7759 driver.
10  *
11  * The Pico is using this chip in slave mode. In this mode there are no ROM
12  * headers, but the first byte sent to the chip is used to start the ADPCM
13  * engine. This byte is discarded, i.e. not processed by the engine.
14  *
15  * Data is fed into the chip through a FIFO. An Interrupt is created if the
16  * FIFO has been drained below the low water mark.
17  *
18  * The Pico has 2 extensions to the standard upd7759 chip:
19  * - gain control, used to control the volume of the ADPCM output
20  * - filtering, used to remove (some of) the ADPCM compression artifacts
21  */
22
23 #include <math.h>
24 #include "../pico_int.h"
25
26 /* limitter */
27 #define Limit(val, max, min) \
28         (val > max ? max : val < min ? min : val)
29
30 #define ADPCM_CLOCK     (1280000/4)
31
32 #define FIFO_IRQ_THRESHOLD 16
33
34 static const int step_deltas[16][16] =
35 {
36   { 0,  0,  1,  2,  3,   5,   7,  10,  0,   0,  -1,  -2,  -3,   -5,   -7,  -10 },
37   { 0,  1,  2,  3,  4,   6,   8,  13,  0,  -1,  -2,  -3,  -4,   -6,   -8,  -13 },
38   { 0,  1,  2,  4,  5,   7,  10,  15,  0,  -1,  -2,  -4,  -5,   -7,  -10,  -15 },
39   { 0,  1,  3,  4,  6,   9,  13,  19,  0,  -1,  -3,  -4,  -6,   -9,  -13,  -19 },
40   { 0,  2,  3,  5,  8,  11,  15,  23,  0,  -2,  -3,  -5,  -8,  -11,  -15,  -23 },
41   { 0,  2,  4,  7, 10,  14,  19,  29,  0,  -2,  -4,  -7, -10,  -14,  -19,  -29 },
42   { 0,  3,  5,  8, 12,  16,  22,  33,  0,  -3,  -5,  -8, -12,  -16,  -22,  -33 },
43   { 1,  4,  7, 10, 15,  20,  29,  43, -1,  -4,  -7, -10, -15,  -20,  -29,  -43 },
44   { 1,  4,  8, 13, 18,  25,  35,  53, -1,  -4,  -8, -13, -18,  -25,  -35,  -53 },
45   { 1,  6, 10, 16, 22,  31,  43,  64, -1,  -6, -10, -16, -22,  -31,  -43,  -64 },
46   { 2,  7, 12, 19, 27,  37,  51,  76, -2,  -7, -12, -19, -27,  -37,  -51,  -76 },
47   { 2,  9, 16, 24, 34,  46,  64,  96, -2,  -9, -16, -24, -34,  -46,  -64,  -96 },
48   { 3, 11, 19, 29, 41,  57,  79, 117, -3, -11, -19, -29, -41,  -57,  -79, -117 },
49   { 4, 13, 24, 36, 50,  69,  96, 143, -4, -13, -24, -36, -50,  -69,  -96, -143 },
50   { 4, 16, 29, 44, 62,  85, 118, 175, -4, -16, -29, -44, -62,  -85, -118, -175 },
51   { 6, 20, 36, 54, 76, 104, 144, 214, -6, -20, -36, -54, -76, -104, -144, -214 },
52 };
53
54 static const int state_deltas[16] = { -1, -1, 0, 0, 1, 2, 2, 3, -1, -1, 0, 0, 1, 2, 2, 3 };
55
56 static s32 stepsamples; // ratio as Q16, host sound rate / chip sample rate
57
58 static struct xpcm_state {
59   s32 samplepos;        // leftover duration for current sample wrt sndrate, Q16
60   int sample;           // current sample
61   short state;          // ADPCM decoder state
62   short samplegain;     // programmable gain
63
64   char startpin;        // value on the !START pin
65   char irqenable;       // IRQ enabled?
66
67   char portstate;       // ADPCM stream state
68   short silence;        // silence blocks still to be played
69   short rate, nibbles;  // ADPCM nibbles still to be played
70   unsigned char highlow, cache; // nibble selector and cache
71
72   char filter;          // filter selector
73   s32 x[3], y[3];       // filter history
74 } xpcm;
75 enum { RESET, START, HDR, COUNT }; // portstate
76
77
78 // SEGA Pico specific filtering
79
80 #define QB      16                      // mantissa bits
81 #define FP(f)   (int)((f)*(1<<QB))      // convert to fixpoint
82
83 static struct iir2 { // 2nd order Butterworth IIR coefficients
84   s32 a[2], gain;       // coefficients
85 } filters[4];
86 static struct iir2 *filter; // currently selected filter
87
88
89 static void PicoPicoFilterCoeff(struct iir2 *iir, int cutoff, int rate)
90 {
91   // no filter if the cutoff is above the Nyquist frequency
92   if (cutoff >= rate/2) {
93     memset(iir, 0, sizeof(*iir));
94     return;
95   }
96
97   // compute 2nd order butterworth filter coefficients
98   double a = 1 / tan(M_PI * cutoff / rate);
99   double axa = a*a;
100   double gain = 1/(1 + M_SQRT2*a + axa);
101   iir->gain = FP(gain);
102   iir->a[0] = FP(2 * (axa-1) * gain);
103   iir->a[1] = FP(-(1 - M_SQRT2*a + axa) * gain);
104 }
105
106 static int PicoPicoFilterApply(struct iir2 *iir, int sample)
107 {
108   if (!iir)
109     return sample;
110
111   // NB Butterworth specific!
112   xpcm.x[0] = xpcm.x[1]; xpcm.x[1] = xpcm.x[2];
113   xpcm.x[2] = sample * iir->gain; // Qb
114   xpcm.y[0] = xpcm.y[1]; xpcm.y[1] = xpcm.y[2];
115   xpcm.y[2] = (xpcm.x[0] + 2*xpcm.x[1] + xpcm.x[2]
116                + xpcm.y[0]*iir->a[1] + xpcm.y[1]*iir->a[0]) >> QB;
117   return xpcm.y[2];
118 }
119
120
121 // pin functions, N designating a negated pin
122
123 PICO_INTERNAL void PicoPicoPCMResetN(int pin)
124 {
125   if (!pin) {
126     xpcm.portstate = RESET;
127     xpcm.sample = xpcm.samplepos = xpcm.state = 0;
128     xpcm.nibbles = xpcm.silence = 0;
129   } else if (xpcm.portstate == RESET)
130     xpcm.portstate = START;
131 }
132
133 PICO_INTERNAL void PicoPicoPCMStartN(int pin)
134 {
135   xpcm.startpin = pin;
136 }
137
138 PICO_INTERNAL int PicoPicoPCMBusyN(void)
139 {
140   return (xpcm.portstate <= START);
141 }
142
143
144 // configuration functions
145
146 PICO_INTERNAL void PicoPicoPCMRerate(void)
147 {
148   s32 nextstep = ((u64)PicoIn.sndRate<<16)/ADPCM_CLOCK;
149
150   // if the sound rate changes, erase filter history to avoid freak behaviour
151   if (stepsamples != nextstep) {
152     memset(xpcm.x, 0, sizeof(xpcm.x));
153     memset(xpcm.y, 0, sizeof(xpcm.y));
154   }
155
156   // output samples per chip clock
157   stepsamples = nextstep;
158
159   // compute filter coefficients, cutoff at half the ADPCM sample rate
160   PicoPicoFilterCoeff(&filters[1],  6000/2, PicoIn.sndRate); // 5-6 KHz
161   PicoPicoFilterCoeff(&filters[2],  9000/2, PicoIn.sndRate); // 8-12 KHz
162   PicoPicoFilterCoeff(&filters[3], 15000/2, PicoIn.sndRate); // 14-16 KHz
163
164   PicoPicoPCMFilter(xpcm.filter);
165 }
166
167 PICO_INTERNAL void PicoPicoPCMGain(int gain)
168 {
169   xpcm.samplegain = gain*4;
170 }
171
172 PICO_INTERNAL void PicoPicoPCMFilter(int index)
173 {
174   // if the filter changes, erase the history to avoid freak behaviour
175   if (index != xpcm.filter) {
176     memset(xpcm.x, 0, sizeof(xpcm.x));
177     memset(xpcm.y, 0, sizeof(xpcm.y));
178   }
179
180   xpcm.filter = index;
181   filter = filters+index;
182   if (filter->a[0] == 0)
183     filter = NULL;
184 }
185
186 PICO_INTERNAL void PicoPicoPCMIrqEn(int enable)
187 {
188   xpcm.irqenable = (enable ? 3 : 0);
189 }
190
191 // TODO need an interupt pending mask?
192 PICO_INTERNAL int PicoPicoIrqAck(int level)
193 {
194   return (PicoPicohw.fifo_bytes < FIFO_IRQ_THRESHOLD && level != xpcm.irqenable
195             ? xpcm.irqenable : 0);
196 }
197
198
199 // adpcm operation
200
201 #define apply_filter(v) PicoPicoFilterApply(filter, v)
202
203 // compute next ADPCM sample
204 #define do_sample(nibble) \
205 { \
206   xpcm.sample += step_deltas[xpcm.state][nibble]; \
207   xpcm.state += state_deltas[nibble]; \
208   xpcm.state = (xpcm.state < 0 ? 0 : xpcm.state > 15 ? 15 : xpcm.state); \
209 }
210
211 // writes samples with sndRate, nearest neighbour resampling, filtering
212 #define write_sample(buffer, length, stereo) \
213 { \
214   while (xpcm.samplepos > 0 && length > 0) { \
215     int val = Limit(xpcm.samplegain*xpcm.sample, 16383, -16384); \
216     xpcm.samplepos -= 1<<16; \
217     length --; \
218     if (buffer) { \
219       int out = apply_filter(val); \
220       *buffer++ += out; \
221       if (stereo) *buffer++ += out; \
222     } \
223   } \
224 }
225
226 PICO_INTERNAL void PicoPicoPCMUpdate(short *buffer, int length, int stereo)
227 {
228   unsigned char *src = PicoPicohw.xpcm_buffer;
229   unsigned char *lim = PicoPicohw.xpcm_ptr;
230   int srcval, irq = 0;
231
232   // leftover partial sample from last run
233   write_sample(buffer, length, stereo);
234
235   // loop over FIFO data, generating ADPCM samples
236   while (length > 0 && src < lim)
237   {
238     // ADPCM state engine
239     if (xpcm.silence > 0) { // generate silence
240       xpcm.silence --;
241       xpcm.sample = 0;
242       xpcm.samplepos += stepsamples*256;
243
244     } else if (xpcm.nibbles > 0) { // produce samples
245       xpcm.nibbles --;
246
247       if (xpcm.highlow)
248         xpcm.cache = *src++;
249       else
250         xpcm.cache <<= 4;
251       xpcm.highlow = !xpcm.highlow;
252
253       do_sample((xpcm.cache & 0xf0) >> 4);
254       xpcm.samplepos += stepsamples*xpcm.rate;
255
256     } else switch (xpcm.portstate) { // handle stream headers
257       case RESET:
258         xpcm.sample = 0;
259         xpcm.samplepos += length<<16;
260         break;
261       case START:
262         if (xpcm.startpin) {
263           if (*src)
264             xpcm.portstate ++;
265           else // kill 0x00 bytes at stream start
266             src ++;
267         } else {
268           xpcm.sample = 0;
269           xpcm.samplepos += length<<16;
270         }
271         break;
272       case HDR:
273         srcval = *src++;
274         xpcm.nibbles = xpcm.silence = xpcm.rate = 0;
275         xpcm.highlow = 1;
276         if (srcval == 0) { // terminator
277           // HACK, kill leftover odd byte to avoid restart (Minna de Odorou)
278           if (lim-src == 1) src++;
279           xpcm.portstate = START;
280         } else switch (srcval >> 6) {
281           case 0: xpcm.silence = (srcval & 0x3f) + 1; break;
282           case 1: xpcm.rate = (srcval & 0x3f) + 1; xpcm.nibbles = 256; break;
283           case 2: xpcm.rate = (srcval & 0x3f) + 1; xpcm.portstate = COUNT; break;
284           case 3: break;
285         }
286         break;
287       case COUNT:
288         xpcm.nibbles = *src++ + 1; xpcm.portstate = HDR;
289         break;
290       }
291
292     write_sample(buffer, length, stereo);
293   }
294
295   // buffer cleanup, generate irq if lowwater reached
296   if (src < lim && src != PicoPicohw.xpcm_buffer) {
297     int di = lim - src;
298     memmove(PicoPicohw.xpcm_buffer, src, di);
299     PicoPicohw.xpcm_ptr = PicoPicohw.xpcm_buffer + di;
300     elprintf(EL_PICOHW, "xpcm update: over %i", di);
301
302     if (!irq && di < FIFO_IRQ_THRESHOLD)
303       irq = xpcm.irqenable;
304     PicoPicohw.fifo_bytes = di;
305   } else if (src == lim && src != PicoPicohw.xpcm_buffer) {
306     PicoPicohw.xpcm_ptr = PicoPicohw.xpcm_buffer;
307     elprintf(EL_PICOHW, "xpcm update: under %i", length);
308
309     if (!irq)
310       irq = xpcm.irqenable;
311     PicoPicohw.fifo_bytes = 0;
312   }
313
314   // TODO need an IRQ mask somewhere to avoid loosing one in cases of HINT/VINT
315   if (irq && SekIrqLevel != irq) {
316     elprintf(EL_PICOHW, "irq%d", irq);
317     if (SekIrqLevel < irq)
318       SekInterrupt(irq);
319   }
320
321   if (buffer && length) {
322     // for underflow, use last sample to avoid clicks
323     int val = Limit(xpcm.samplegain*xpcm.sample, 16383, -16384);
324     while (length--) {
325       int out = apply_filter(val);
326       *buffer++ += out;
327       if (stereo) *buffer++ += out;
328     }
329   }
330 }
331
332 PICO_INTERNAL int PicoPicoPCMSave(void *buffer, int length)
333 {
334   u8 *bp = buffer;
335
336   if (length < sizeof(xpcm)) {
337     elprintf(EL_ANOMALY, "save buffer too small?");
338     return 0;
339   }
340
341   memcpy(bp, &xpcm, sizeof(xpcm));
342   bp += sizeof(xpcm);
343   return (bp - (u8*)buffer);
344 }
345
346 PICO_INTERNAL void PicoPicoPCMLoad(void *buffer, int length)
347 {
348   u8 *bp = buffer;
349
350   if (length >= sizeof(xpcm))
351     memcpy(&xpcm, bp, sizeof(xpcm));
352   bp += sizeof(xpcm);
353 }