spu: support multiple pending buffers
[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 #define _IN_XA
20 #include <stdint.h>
21
22 // will be included from spu.c
23 #ifdef _IN_SPU
24
25 ////////////////////////////////////////////////////////////////////////
26 // XA GLOBALS
27 ////////////////////////////////////////////////////////////////////////
28
29 static int gauss_ptr = 0;
30 static int gauss_window[8] = {0, 0, 0, 0, 0, 0, 0, 0};
31
32 #define gvall0 gauss_window[gauss_ptr]
33 #define gvall(x) gauss_window[(gauss_ptr+x)&3]
34 #define gvalr0 gauss_window[4+gauss_ptr]
35 #define gvalr(x) gauss_window[4+((gauss_ptr+x)&3)]
36
37 ////////////////////////////////////////////////////////////////////////
38 // MIX XA & CDDA
39 ////////////////////////////////////////////////////////////////////////
40
41 INLINE void MixXA(int *SSumLR, int ns_to, int decode_pos)
42 {
43  int cursor = decode_pos;
44  int ns;
45  short l, r;
46  uint32_t v;
47
48  if(spu.XAPlay != spu.XAFeed || spu.XARepeat > 0)
49  {
50   if(spu.XAPlay == spu.XAFeed)
51    spu.XARepeat--;
52
53   v = spu.XALastVal;
54   for(ns = 0; ns < ns_to*2; )
55    {
56     if(spu.XAPlay != spu.XAFeed) v=*spu.XAPlay++;
57     if(spu.XAPlay == spu.XAEnd) spu.XAPlay=spu.XAStart;
58
59     l = ((int)(short)v * spu.iLeftXAVol) >> 15;
60     r = ((int)(short)(v >> 16) * spu.iLeftXAVol) >> 15;
61     SSumLR[ns++] += l;
62     SSumLR[ns++] += r;
63
64     spu.spuMem[cursor] = v;
65     spu.spuMem[cursor + 0x400/2] = v >> 16;
66     cursor = (cursor + 1) & 0x1ff;
67    }
68   spu.XALastVal = v;
69  }
70
71  for(ns = 0; ns < ns_to * 2 && spu.CDDAPlay!=spu.CDDAFeed && (spu.CDDAPlay!=spu.CDDAEnd-1||spu.CDDAFeed!=spu.CDDAStart);)
72   {
73    v=*spu.CDDAPlay++;
74    if(spu.CDDAPlay==spu.CDDAEnd) spu.CDDAPlay=spu.CDDAStart;
75
76    l = ((int)(short)v * spu.iLeftXAVol) >> 15;
77    r = ((int)(short)(v >> 16) * spu.iLeftXAVol) >> 15;
78    SSumLR[ns++] += l;
79    SSumLR[ns++] += r;
80
81    spu.spuMem[cursor] = v;
82    spu.spuMem[cursor + 0x400/2] = v >> 16;
83    cursor = (cursor + 1) & 0x1ff;
84   }
85 }
86
87 ////////////////////////////////////////////////////////////////////////
88 // small linux time helper... only used for watchdog
89 ////////////////////////////////////////////////////////////////////////
90
91 static unsigned long timeGetTime_spu()
92 {
93 #if defined(NO_OS)
94  return 0;
95 #elif defined(_WIN32)
96  return GetTickCount();
97 #else
98  struct timeval tv;
99  gettimeofday(&tv, 0);                                 // well, maybe there are better ways
100  return tv.tv_sec * 1000 + tv.tv_usec/1000;            // to do that, but at least it works
101 #endif
102 }
103
104 ////////////////////////////////////////////////////////////////////////
105 // FEED XA 
106 ////////////////////////////////////////////////////////////////////////
107
108 INLINE void FeedXA(xa_decode_t *xap)
109 {
110  int sinc,spos,i,iSize,iPlace,vl,vr;
111
112  if(!spu.bSPUIsOpen) return;
113
114  spu.xapGlobal = xap;                                  // store info for save states
115  spu.XARepeat  = 100;                                  // set up repeat
116
117 #if 0//def XA_HACK
118  iSize=((45500*xap->nsamples)/xap->freq);              // get size
119 #else
120  iSize=((44100*xap->nsamples)/xap->freq);              // get size
121 #endif
122  if(!iSize) return;                                    // none? bye
123
124  if(spu.XAFeed<spu.XAPlay) iPlace=spu.XAPlay-spu.XAFeed; // how much space in my buf?
125  else              iPlace=(spu.XAEnd-spu.XAFeed) + (spu.XAPlay-spu.XAStart);
126
127  if(iPlace==0) return;                                 // no place at all
128
129  //----------------------------------------------------//
130  if(spu_config.iXAPitch)                               // pitch change option?
131   {
132    static DWORD dwLT=0;
133    static DWORD dwFPS=0;
134    static int   iFPSCnt=0;
135    static int   iLastSize=0;
136    static DWORD dwL1=0;
137    DWORD dw=timeGetTime_spu(),dw1,dw2;
138
139    iPlace=iSize;
140
141    dwFPS+=dw-dwLT;iFPSCnt++;
142
143    dwLT=dw;
144                                        
145    if(iFPSCnt>=10)
146     {
147      if(!dwFPS) dwFPS=1;
148      dw1=1000000/dwFPS; 
149      if(dw1>=(dwL1-100) && dw1<=(dwL1+100)) dw1=dwL1;
150      else dwL1=dw1;
151      dw2=(xap->freq*100/xap->nsamples);
152      if((!dw1)||((dw2+100)>=dw1)) iLastSize=0;
153      else
154       {
155        iLastSize=iSize*dw2/dw1;
156        if(iLastSize>iPlace) iLastSize=iPlace;
157        iSize=iLastSize;
158       }
159      iFPSCnt=0;dwFPS=0;
160     }
161    else
162     {
163      if(iLastSize) iSize=iLastSize;
164     }
165   }
166  //----------------------------------------------------//
167
168  spos=0x10000L;
169  sinc = (xap->nsamples << 16) / iSize;                 // calc freq by num / size
170
171  if(xap->stereo)
172 {
173    uint32_t * pS=(uint32_t *)xap->pcm;
174    uint32_t l=0;
175
176    if(spu_config.iXAPitch)
177     {
178      int32_t l1,l2;short s;
179      for(i=0;i<iSize;i++)
180       {
181        if(spu_config.iUseInterpolation==2)
182         {
183          while(spos>=0x10000L)
184           {
185            l = *pS++;
186            gauss_window[gauss_ptr] = (short)LOWORD(l);
187            gauss_window[4+gauss_ptr] = (short)HIWORD(l);
188            gauss_ptr = (gauss_ptr+1) & 3;
189            spos -= 0x10000L;
190           }
191          vl = (spos >> 6) & ~3;
192          vr=(gauss[vl]*gvall0)&~2047;
193          vr+=(gauss[vl+1]*gvall(1))&~2047;
194          vr+=(gauss[vl+2]*gvall(2))&~2047;
195          vr+=(gauss[vl+3]*gvall(3))&~2047;
196          l= (vr >> 11) & 0xffff;
197          vr=(gauss[vl]*gvalr0)&~2047;
198          vr+=(gauss[vl+1]*gvalr(1))&~2047;
199          vr+=(gauss[vl+2]*gvalr(2))&~2047;
200          vr+=(gauss[vl+3]*gvalr(3))&~2047;
201          l |= vr << 5;
202         }
203        else
204         {
205          while(spos>=0x10000L)
206           {
207            l = *pS++;
208            spos -= 0x10000L;
209           }
210         }
211
212        s=(short)LOWORD(l);
213        l1=s;
214        l1=(l1*iPlace)/iSize;
215        ssat32_to_16(l1);
216        s=(short)HIWORD(l);
217        l2=s;
218        l2=(l2*iPlace)/iSize;
219        ssat32_to_16(l2);
220        l=(l1&0xffff)|(l2<<16);
221
222        *spu.XAFeed++=l;
223
224        if(spu.XAFeed==spu.XAEnd) spu.XAFeed=spu.XAStart;
225        if(spu.XAFeed==spu.XAPlay)
226         {
227          if(spu.XAPlay!=spu.XAStart) spu.XAFeed=spu.XAPlay-1;
228          break;
229         }
230
231        spos += sinc;
232       }
233     }
234    else
235     {
236      for(i=0;i<iSize;i++)
237       {
238        if(spu_config.iUseInterpolation==2)
239         {
240          while(spos>=0x10000L)
241           {
242            l = *pS++;
243            gauss_window[gauss_ptr] = (short)LOWORD(l);
244            gauss_window[4+gauss_ptr] = (short)HIWORD(l);
245            gauss_ptr = (gauss_ptr+1) & 3;
246            spos -= 0x10000L;
247           }
248          vl = (spos >> 6) & ~3;
249          vr=(gauss[vl]*gvall0)&~2047;
250          vr+=(gauss[vl+1]*gvall(1))&~2047;
251          vr+=(gauss[vl+2]*gvall(2))&~2047;
252          vr+=(gauss[vl+3]*gvall(3))&~2047;
253          l= (vr >> 11) & 0xffff;
254          vr=(gauss[vl]*gvalr0)&~2047;
255          vr+=(gauss[vl+1]*gvalr(1))&~2047;
256          vr+=(gauss[vl+2]*gvalr(2))&~2047;
257          vr+=(gauss[vl+3]*gvalr(3))&~2047;
258          l |= vr << 5;
259         }
260        else
261         {
262          while(spos>=0x10000L)
263           {
264            l = *pS++;
265            spos -= 0x10000L;
266           }
267         }
268
269        *spu.XAFeed++=l;
270
271        if(spu.XAFeed==spu.XAEnd) spu.XAFeed=spu.XAStart;
272        if(spu.XAFeed==spu.XAPlay)
273         {
274          if(spu.XAPlay!=spu.XAStart) spu.XAFeed=spu.XAPlay-1;
275          break;
276         }
277
278        spos += sinc;
279       }
280     }
281   }
282  else
283   {
284    unsigned short * pS=(unsigned short *)xap->pcm;
285    uint32_t l;short s=0;
286
287    if(spu_config.iXAPitch)
288     {
289      int32_t l1;
290      for(i=0;i<iSize;i++)
291       {
292        if(spu_config.iUseInterpolation==2)
293         {
294          while(spos>=0x10000L)
295           {
296            gauss_window[gauss_ptr] = (short)*pS++;
297            gauss_ptr = (gauss_ptr+1) & 3;
298            spos -= 0x10000L;
299           }
300          vl = (spos >> 6) & ~3;
301          vr=(gauss[vl]*gvall0)&~2047;
302          vr+=(gauss[vl+1]*gvall(1))&~2047;
303          vr+=(gauss[vl+2]*gvall(2))&~2047;
304          vr+=(gauss[vl+3]*gvall(3))&~2047;
305          l1=s= vr >> 11;
306          l1 &= 0xffff;
307         }
308        else
309         {
310          while(spos>=0x10000L)
311           {
312            s = *pS++;
313            spos -= 0x10000L;
314           }
315          l1=s;
316         }
317
318        l1=(l1*iPlace)/iSize;
319        ssat32_to_16(l1);
320        l=(l1&0xffff)|(l1<<16);
321        *spu.XAFeed++=l;
322
323        if(spu.XAFeed==spu.XAEnd) spu.XAFeed=spu.XAStart;
324        if(spu.XAFeed==spu.XAPlay)
325         {
326          if(spu.XAPlay!=spu.XAStart) spu.XAFeed=spu.XAPlay-1;
327          break;
328         }
329
330        spos += sinc;
331       }
332     }
333    else
334     {
335      for(i=0;i<iSize;i++)
336       {
337        if(spu_config.iUseInterpolation==2)
338         {
339          while(spos>=0x10000L)
340           {
341            gauss_window[gauss_ptr] = (short)*pS++;
342            gauss_ptr = (gauss_ptr+1) & 3;
343            spos -= 0x10000L;
344           }
345          vl = (spos >> 6) & ~3;
346          vr=(gauss[vl]*gvall0)&~2047;
347          vr+=(gauss[vl+1]*gvall(1))&~2047;
348          vr+=(gauss[vl+2]*gvall(2))&~2047;
349          vr+=(gauss[vl+3]*gvall(3))&~2047;
350          l=s= vr >> 11;
351         }
352        else
353         {
354          while(spos>=0x10000L)
355           {
356            s = *pS++;
357            spos -= 0x10000L;
358           }
359          l=s;
360         }
361
362        l &= 0xffff;
363        *spu.XAFeed++=(l|(l<<16));
364
365        if(spu.XAFeed==spu.XAEnd) spu.XAFeed=spu.XAStart;
366        if(spu.XAFeed==spu.XAPlay)
367         {
368          if(spu.XAPlay!=spu.XAStart) spu.XAFeed=spu.XAPlay-1;
369          break;
370         }
371
372        spos += sinc;
373       }
374     }
375   }
376 }
377
378 ////////////////////////////////////////////////////////////////////////
379 // FEED CDDA
380 ////////////////////////////////////////////////////////////////////////
381
382 INLINE int FeedCDDA(unsigned char *pcm, int nBytes)
383 {
384  int space;
385  space=(spu.CDDAPlay-spu.CDDAFeed-1)*4 & (CDDA_BUFFER_SIZE - 1);
386  if(space<nBytes)
387   return 0x7761; // rearmed_wait
388
389  while(nBytes>0)
390   {
391    if(spu.CDDAFeed==spu.CDDAEnd) spu.CDDAFeed=spu.CDDAStart;
392    space=(spu.CDDAPlay-spu.CDDAFeed-1)*4 & (CDDA_BUFFER_SIZE - 1);
393    if(spu.CDDAFeed+space/4>spu.CDDAEnd)
394     space=(spu.CDDAEnd-spu.CDDAFeed)*4;
395    if(space>nBytes)
396     space=nBytes;
397
398    memcpy(spu.CDDAFeed,pcm,space);
399    spu.CDDAFeed+=space/4;
400    nBytes-=space;
401    pcm+=space;
402   }
403
404  return 0x676f; // rearmed_go
405 }
406
407 #endif