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