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