0c774f03aebbdad8458f3b82a57ea7e41bbe8b7c
[pcsx_rearmed.git] / plugins / dfsound / xa.c
1 /***************************************************************************
2                             xa.c  -  description
3                              -------------------
4     begin                : Wed May 15 2002
5     copyright            : (C) 2002 by Pete Bernert
6     email                : BlackDove@addcom.de
7  ***************************************************************************/
8 /***************************************************************************
9  *                                                                         *
10  *   This program is free software; you can redistribute it and/or modify  *
11  *   it under the terms of the GNU General Public License as published by  *
12  *   the Free Software Foundation; either version 2 of the License, or     *
13  *   (at your option) any later version. See also the license.txt file for *
14  *   additional informations.                                              *
15  *                                                                         *
16  ***************************************************************************/
17
18 #include "stdafx.h"
19 #include "spu.h"
20 #define _IN_XA
21 #include <stdint.h>
22 #include "psemu_plugin_defs.h"
23
24 // will be included from spu.c
25 #ifdef _IN_SPU
26
27 ////////////////////////////////////////////////////////////////////////
28 // XA GLOBALS
29 ////////////////////////////////////////////////////////////////////////
30
31 static int gauss_ptr = 0;
32 static int gauss_window[8] = {0, 0, 0, 0, 0, 0, 0, 0};
33
34 #define gvall0 gauss_window[gauss_ptr]
35 #define gvall(x) gauss_window[(gauss_ptr+x)&3]
36 #define gvalr0 gauss_window[4+gauss_ptr]
37 #define gvalr(x) gauss_window[4+((gauss_ptr+x)&3)]
38
39 ////////////////////////////////////////////////////////////////////////
40 // MIX XA & CDDA
41 ////////////////////////////////////////////////////////////////////////
42
43 INLINE void SkipCD(int ns_to, int decode_pos)
44 {
45  int cursor = decode_pos;
46  int ns;
47
48  if(spu.XAPlay != spu.XAFeed)
49  {
50   for(ns = 0; ns < ns_to*2; ns += 2)
51    {
52     if(spu.XAPlay != spu.XAFeed) spu.XAPlay++;
53     if(spu.XAPlay == spu.XAEnd) spu.XAPlay=spu.XAStart;
54
55     spu.spuMem[cursor] = 0;
56     spu.spuMem[cursor + 0x400/2] = 0;
57     cursor = (cursor + 1) & 0x1ff;
58    }
59  }
60  else if(spu.CDDAPlay != spu.CDDAFeed)
61  {
62   for(ns = 0; ns < ns_to*2; ns += 2)
63    {
64     if(spu.CDDAPlay != spu.CDDAFeed) spu.CDDAPlay++;
65     if(spu.CDDAPlay == spu.CDDAEnd) spu.CDDAPlay=spu.CDDAStart;
66
67     spu.spuMem[cursor] = 0;
68     spu.spuMem[cursor + 0x400/2] = 0;
69     cursor = (cursor + 1) & 0x1ff;
70    }
71  }
72  spu.XALastVal = 0;
73 }
74
75 INLINE void MixCD(int *SSumLR, int *RVB, int ns_to, int decode_pos)
76 {
77  int vll = spu.iLeftXAVol * spu.cdv.ll >> 7;
78  int vrl = spu.iLeftXAVol * spu.cdv.rl >> 7;
79  int vlr = spu.iRightXAVol * spu.cdv.lr >> 7;
80  int vrr = spu.iRightXAVol * spu.cdv.rr >> 7;
81  int cursor = decode_pos;
82  int l1, r1, l, r;
83  int ns;
84  uint32_t v = spu.XALastVal;
85
86  // note: spu volume doesn't affect cd capture
87  if ((spu.cdv.ll | spu.cdv.lr | spu.cdv.rl | spu.cdv.rr) == 0)
88  {
89   SkipCD(ns_to, decode_pos);
90   return;
91  }
92
93  if(spu.XAPlay != spu.XAFeed || spu.XARepeat > 0)
94  {
95   if(spu.XAPlay == spu.XAFeed)
96    spu.XARepeat--;
97
98   for(ns = 0; ns < ns_to*2; ns += 2)
99    {
100     if(spu.XAPlay != spu.XAFeed) v=*spu.XAPlay++;
101     if(spu.XAPlay == spu.XAEnd) spu.XAPlay=spu.XAStart;
102
103     l1 = (short)v, r1 = (short)(v >> 16);
104     l = (l1 * vll + r1 * vrl) >> 15;
105     r = (r1 * vrr + l1 * vlr) >> 15;
106     ssat32_to_16(l);
107     ssat32_to_16(r);
108     if (spu.spuCtrl & CTRL_CD)
109     {
110      SSumLR[ns+0] += l;
111      SSumLR[ns+1] += r;
112     }
113     if (unlikely(spu.spuCtrl & CTRL_CDREVERB))
114     {
115      RVB[ns+0] += l;
116      RVB[ns+1] += r;
117     }
118
119     spu.spuMem[cursor] = HTOLE16(v);
120     spu.spuMem[cursor + 0x400/2] = HTOLE16(v >> 16);
121     cursor = (cursor + 1) & 0x1ff;
122    }
123   spu.XALastVal = v;
124  }
125  // occasionally CDDAFeed underflows by a few samples due to poor timing,
126  // hence this 'ns_to < 8'
127  else if(spu.CDDAPlay != spu.CDDAFeed || ns_to < 8)
128  {
129   for(ns = 0; ns < ns_to*2; ns += 2)
130    {
131     if(spu.CDDAPlay != spu.CDDAFeed) v=*spu.CDDAPlay++;
132     if(spu.CDDAPlay == spu.CDDAEnd) spu.CDDAPlay=spu.CDDAStart;
133
134     l1 = (short)v, r1 = (short)(v >> 16);
135     l = (l1 * vll + r1 * vrl) >> 15;
136     r = (r1 * vrr + l1 * vlr) >> 15;
137     ssat32_to_16(l);
138     ssat32_to_16(r);
139     if (spu.spuCtrl & CTRL_CD)
140     {
141      SSumLR[ns+0] += l;
142      SSumLR[ns+1] += r;
143     }
144     if (unlikely(spu.spuCtrl & CTRL_CDREVERB))
145     {
146      RVB[ns+0] += l;
147      RVB[ns+1] += r;
148     }
149
150     spu.spuMem[cursor] = HTOLE16(v);
151     spu.spuMem[cursor + 0x400/2] = HTOLE16(v >> 16);
152     cursor = (cursor + 1) & 0x1ff;
153    }
154   spu.XALastVal = v;
155  }
156  else if (spu.cdClearSamples > 0)
157  {
158   for(ns = 0; ns < ns_to; ns++)
159    {
160     spu.spuMem[cursor] = spu.spuMem[cursor + 0x400/2] = 0;
161     cursor = (cursor + 1) & 0x1ff;
162    }
163   spu.cdClearSamples -= ns_to;
164   spu.XALastVal = 0;
165  }
166 }
167
168 ////////////////////////////////////////////////////////////////////////
169 // small linux time helper... only used for watchdog
170 ////////////////////////////////////////////////////////////////////////
171
172 #if 0
173 static unsigned long timeGetTime_spu()
174 {
175 #if defined(NO_OS)
176  return 0;
177 #elif defined(_WIN32)
178  return GetTickCount();
179 #else
180  struct timeval tv;
181  gettimeofday(&tv, 0);                                 // well, maybe there are better ways
182  return tv.tv_sec * 1000 + tv.tv_usec/1000;            // to do that, but at least it works
183 #endif
184 }
185 #endif
186
187 ////////////////////////////////////////////////////////////////////////
188 // FEED XA 
189 ////////////////////////////////////////////////////////////////////////
190
191 void FeedXA(const xa_decode_t *xap)
192 {
193  int sinc,spos,i,iSize,iPlace,vl,vr;
194
195  if(!spu.bSPUIsOpen) return;
196
197  spu.XARepeat  = 3;                                    // set up repeat
198
199 #if 0//def XA_HACK
200  iSize=((45500*xap->nsamples)/xap->freq);              // get size
201 #else
202  iSize=((44100*xap->nsamples)/xap->freq);              // get size
203 #endif
204  if(!iSize) return;                                    // none? bye
205
206  if(spu.XAFeed<spu.XAPlay) iPlace=spu.XAPlay-spu.XAFeed; // how much space in my buf?
207  else              iPlace=(spu.XAEnd-spu.XAFeed) + (spu.XAPlay-spu.XAStart);
208
209  if(iPlace==0) return;                                 // no place at all
210
211  //----------------------------------------------------//
212 #if 0
213  if(spu_config.iXAPitch)                               // pitch change option?
214   {
215    static DWORD dwLT=0;
216    static DWORD dwFPS=0;
217    static int   iFPSCnt=0;
218    static int   iLastSize=0;
219    static DWORD dwL1=0;
220    DWORD dw=timeGetTime_spu(),dw1,dw2;
221
222    iPlace=iSize;
223
224    dwFPS+=dw-dwLT;iFPSCnt++;
225
226    dwLT=dw;
227                                        
228    if(iFPSCnt>=10)
229     {
230      if(!dwFPS) dwFPS=1;
231      dw1=1000000/dwFPS; 
232      if(dw1>=(dwL1-100) && dw1<=(dwL1+100)) dw1=dwL1;
233      else dwL1=dw1;
234      dw2=(xap->freq*100/xap->nsamples);
235      if((!dw1)||((dw2+100)>=dw1)) iLastSize=0;
236      else
237       {
238        iLastSize=iSize*dw2/dw1;
239        if(iLastSize>iPlace) iLastSize=iPlace;
240        iSize=iLastSize;
241       }
242      iFPSCnt=0;dwFPS=0;
243     }
244    else
245     {
246      if(iLastSize) iSize=iLastSize;
247     }
248   }
249 #endif
250  //----------------------------------------------------//
251
252  spos=0x10000L;
253  sinc = (xap->nsamples << 16) / iSize;                 // calc freq by num / size
254
255  if(xap->stereo)
256 {
257    uint32_t * pS=(uint32_t *)xap->pcm;
258    uint32_t l=0;
259
260 #if 0
261    if(spu_config.iXAPitch)
262     {
263      int32_t l1,l2;short s;
264      for(i=0;i<iSize;i++)
265       {
266        if(spu_config.iUseInterpolation==2)
267         {
268          while(spos>=0x10000L)
269           {
270            l = *pS++;
271            gauss_window[gauss_ptr] = (short)LOWORD(l);
272            gauss_window[4+gauss_ptr] = (short)HIWORD(l);
273            gauss_ptr = (gauss_ptr+1) & 3;
274            spos -= 0x10000L;
275           }
276          vl = (spos >> 6) & ~3;
277          vr=(gauss[vl]*gvall0) >> 15;
278          vr+=(gauss[vl+1]*gvall(1)) >> 15;
279          vr+=(gauss[vl+2]*gvall(2)) >> 15;
280          vr+=(gauss[vl+3]*gvall(3)) >> 15;
281          l= vr & 0xffff;
282          vr=(gauss[vl]*gvalr0) >> 15;
283          vr+=(gauss[vl+1]*gvalr(1)) >> 15;
284          vr+=(gauss[vl+2]*gvalr(2)) >> 15;
285          vr+=(gauss[vl+3]*gvalr(3)) >> 15;
286          l |= vr << 16;
287         }
288        else
289         {
290          while(spos>=0x10000L)
291           {
292            l = *pS++;
293            spos -= 0x10000L;
294           }
295         }
296
297        s=(short)LOWORD(l);
298        l1=s;
299        l1=(l1*iPlace)/iSize;
300        ssat32_to_16(l1);
301        s=(short)HIWORD(l);
302        l2=s;
303        l2=(l2*iPlace)/iSize;
304        ssat32_to_16(l2);
305        l=(l1&0xffff)|(l2<<16);
306
307        *spu.XAFeed++=l;
308
309        if(spu.XAFeed==spu.XAEnd) spu.XAFeed=spu.XAStart;
310        if(spu.XAFeed==spu.XAPlay)
311         {
312          if(spu.XAPlay!=spu.XAStart) spu.XAFeed=spu.XAPlay-1;
313          break;
314         }
315
316        spos += sinc;
317       }
318     }
319    else
320 #endif
321     {
322      for(i=0;i<iSize;i++)
323       {
324        if(spu_config.iUseInterpolation==2)
325         {
326          while(spos>=0x10000L)
327           {
328            l = *pS++;
329            gauss_window[gauss_ptr] = (short)LOWORD(l);
330            gauss_window[4+gauss_ptr] = (short)HIWORD(l);
331            gauss_ptr = (gauss_ptr+1) & 3;
332            spos -= 0x10000L;
333           }
334          vl = (spos >> 6) & ~3;
335          vr=(gauss[vl]*gvall0) >> 15;
336          vr+=(gauss[vl+1]*gvall(1)) >> 15;
337          vr+=(gauss[vl+2]*gvall(2)) >> 15;
338          vr+=(gauss[vl+3]*gvall(3)) >> 15;
339          l= vr & 0xffff;
340          vr=(gauss[vl]*gvalr0) >> 15;
341          vr+=(gauss[vl+1]*gvalr(1)) >> 15;
342          vr+=(gauss[vl+2]*gvalr(2)) >> 15;
343          vr+=(gauss[vl+3]*gvalr(3)) >> 15;
344          l |= vr << 16;
345         }
346        else
347         {
348          while(spos>=0x10000L)
349           {
350            l = *pS++;
351            spos -= 0x10000L;
352           }
353         }
354
355        *spu.XAFeed++=l;
356
357        if(spu.XAFeed==spu.XAEnd) spu.XAFeed=spu.XAStart;
358        if(spu.XAFeed==spu.XAPlay)
359         {
360          if(spu.XAPlay!=spu.XAStart) spu.XAFeed=spu.XAPlay-1;
361          break;
362         }
363
364        spos += sinc;
365       }
366     }
367   }
368  else
369   {
370    unsigned short * pS=(unsigned short *)xap->pcm;
371    uint32_t l;short s=0;
372
373 #if 0
374    if(spu_config.iXAPitch)
375     {
376      int32_t l1;
377      for(i=0;i<iSize;i++)
378       {
379        if(spu_config.iUseInterpolation==2)
380         {
381          while(spos>=0x10000L)
382           {
383            gauss_window[gauss_ptr] = (short)*pS++;
384            gauss_ptr = (gauss_ptr+1) & 3;
385            spos -= 0x10000L;
386           }
387          vl = (spos >> 6) & ~3;
388          vr=(gauss[vl]*gvall0) >> 15;
389          vr+=(gauss[vl+1]*gvall(1)) >> 15;
390          vr+=(gauss[vl+2]*gvall(2)) >> 15;
391          vr+=(gauss[vl+3]*gvall(3)) >> 15;
392          l1=s= vr;
393          l1 &= 0xffff;
394         }
395        else
396         {
397          while(spos>=0x10000L)
398           {
399            s = *pS++;
400            spos -= 0x10000L;
401           }
402          l1=s;
403         }
404
405        l1=(l1*iPlace)/iSize;
406        ssat32_to_16(l1);
407        l=(l1&0xffff)|(l1<<16);
408        *spu.XAFeed++=l;
409
410        if(spu.XAFeed==spu.XAEnd) spu.XAFeed=spu.XAStart;
411        if(spu.XAFeed==spu.XAPlay)
412         {
413          if(spu.XAPlay!=spu.XAStart) spu.XAFeed=spu.XAPlay-1;
414          break;
415         }
416
417        spos += sinc;
418       }
419     }
420    else
421 #endif
422     {
423      for(i=0;i<iSize;i++)
424       {
425        if(spu_config.iUseInterpolation==2)
426         {
427          while(spos>=0x10000L)
428           {
429            gauss_window[gauss_ptr] = (short)*pS++;
430            gauss_ptr = (gauss_ptr+1) & 3;
431            spos -= 0x10000L;
432           }
433          vl = (spos >> 6) & ~3;
434          vr=(gauss[vl]*gvall0) >> 15;
435          vr+=(gauss[vl+1]*gvall(1)) >> 15;
436          vr+=(gauss[vl+2]*gvall(2)) >> 15;
437          vr+=(gauss[vl+3]*gvall(3)) >> 15;
438          l=s= vr;
439         }
440        else
441         {
442          while(spos>=0x10000L)
443           {
444            s = *pS++;
445            spos -= 0x10000L;
446           }
447          l=s;
448         }
449
450        l &= 0xffff;
451        *spu.XAFeed++=(l|(l<<16));
452
453        if(spu.XAFeed==spu.XAEnd) spu.XAFeed=spu.XAStart;
454        if(spu.XAFeed==spu.XAPlay)
455         {
456          if(spu.XAPlay!=spu.XAStart) spu.XAFeed=spu.XAPlay-1;
457          break;
458         }
459
460        spos += sinc;
461       }
462     }
463   }
464 }
465
466 ////////////////////////////////////////////////////////////////////////
467 // FEED CDDA
468 ////////////////////////////////////////////////////////////////////////
469
470 void FeedCDDA(unsigned char *pcm, int nBytes)
471 {
472  int space;
473  space=(spu.CDDAPlay-spu.CDDAFeed-1)*4 & (CDDA_BUFFER_SIZE - 1);
474  if (space < nBytes) {
475   log_unhandled("FeedCDDA: %d/%d\n", nBytes, space);
476   return;
477  }
478
479  while(nBytes>0)
480   {
481    if(spu.CDDAFeed==spu.CDDAEnd) spu.CDDAFeed=spu.CDDAStart;
482    space=(spu.CDDAPlay-spu.CDDAFeed-1)*4 & (CDDA_BUFFER_SIZE - 1);
483    if(spu.CDDAFeed+space/4>spu.CDDAEnd)
484     space=(spu.CDDAEnd-spu.CDDAFeed)*4;
485    if(space>nBytes)
486     space=nBytes;
487
488    memcpy(spu.CDDAFeed,pcm,space);
489    spu.CDDAFeed+=space/4;
490    nBytes-=space;
491    pcm+=space;
492   }
493 }
494
495 #endif
496 // vim:shiftwidth=1:expandtab