1 /***************************************************************************
2 spu.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
20 #define _IN_SPU
21
22 #include "externals.h"
23 #include "cfg.h"
24 #include "dsoundoss.h"
25 #include "regs.h"
26
27 #ifdef _WINDOWS
28 #include "debug.h"
29 #include "record.h"
30 #elif defined(_MACOSX)
31 #include "maccfg.h"
32 #endif
33
34 #ifdef ENABLE_NLS
35 #include <libintl.h>
36 #include <locale.h>
37 #define _(x) gettext(x)
38 #define N_(x) (x)
39 //If running under Mac OS X, use the Localizable.strings file instead.
40 #elif defined(_MACOSX)
41 #ifdef PCSXRCORE
42 __private_extern char* Pcsxr_locale_text(char* toloc);
43 #define _(String) Pcsxr_locale_text(String)
44 #define N_(String) String
45 #else
46 #ifndef PCSXRPLUG
47 #warning please define the plug being built to use Mac OS X localization!
48 #define _(msgid) msgid
49 #define N_(msgid) msgid
50 #else
51 //Kludge to get the preprocessor to accept PCSXRPLUG as a variable.
52 #define PLUGLOC_x(x,y) x ## y
53 #define PLUGLOC_y(x,y) PLUGLOC_x(x,y)
54 #define PLUGLOC PLUGLOC_y(PCSXRPLUG,_locale_text)
55 __private_extern char* PLUGLOC(char* toloc);
56 #define _(String) PLUGLOC(String)
57 #define N_(String) String
58 #endif
59 #endif
60 #else
61 #define _(x) (x)
62 #define N_(x) (x)
63 #endif
64
65 #if defined (_WINDOWS)
66 static char * libraryName = N_("DirectSound Driver");
67 #elif defined (USEMACOSX)
68 static char * libraryName = N_("Mac OS X Sound");
69 #elif defined (USEALSA)
70 static char * libraryName = N_("ALSA Sound");
71 #elif defined (USEOSS)
72 static char * libraryName = N_("OSS Sound");
73 #elif defined (USESDL)
74 static char * libraryName = N_("SDL Sound");
75 #elif defined (USEOPENAL)
76 static char * libraryName = N_("OpenAL Sound");
77 #elif defined (USEPULSEAUDIO)
78 static char * libraryName = N_("PulseAudio Sound");
79 #else
80 static char * libraryName = N_("NULL Sound");
81 #endif
82
83 static char * libraryInfo = N_("P.E.Op.S. Sound Driver V1.7\nCoded by Pete Bernert and the P.E.Op.S. team\n");
84
85 // globals
86
87 // psx buffer / addresses
88
89 unsigned short regArea[10000];
90 unsigned short spuMem[256*1024];
91 unsigned char * spuMemC;
92 unsigned char * pSpuIrq=0;
93 unsigned char * pSpuBuffer;
94 unsigned char * pMixIrq=0;
95
96 // user settings
97
98 int iVolume=3;
99 int iXAPitch=1;
100 int iUseTimer=2;
101 int iSPUIRQWait=1;
102 int iDebugMode=0;
103 int iRecordMode=0;
104 int iUseReverb=2;
105 int iUseInterpolation=2;
106 int iDisStereo=0;
107 int iFreqResponse=0;
108
109 // MAIN infos struct for each channel
110
111 SPUCHAN s_chan[MAXCHAN+1]; // channel + 1 infos (1 is security for fmod handling)
112 REVERBInfo rvb;
113
114 unsigned long dwNoiseVal=1; // global noise generator
115 unsigned long dwNoiseCount; // global noise generator
116 unsigned long dwNoiseClock; // global noise generator
117 int iSpuAsyncWait=0;
118
119 unsigned int decoded_ptr = 0;
120 unsigned int bIrqHit = 0;
121
122 unsigned short spuCtrl=0; // some vars to store psx reg infos
123 unsigned short spuStat=0;
124 unsigned short spuIrq=0;
125 unsigned long spuAddr=0x200; // address into spu mem
126 int bEndThread=0; // thread handlers
127 int bThreadEnded=0;
128 int bSpuInit=0;
129 int bSPUIsOpen=0;
130
131 #ifdef _WINDOWS
132 HWND hWMain=0; // window handle
133 HWND hWDebug=0;
134 HWND hWRecord=0;
135 static HANDLE hMainThread;
136 #else
137 static pthread_t thread = (pthread_t)-1; // thread id (linux)
138 #endif
139
140 uint32_t dwNewChannel=0; // flags for faster testing, if new channel starts
141
142 void (CALLBACK *irqCallback)(void)=0; // func of main emu, called on spu irq
143 void (CALLBACK *cddavCallback)(unsigned short,unsigned short)=0;
144
145 // certain globals (were local before, but with the new timeproc I need em global)
146
147 static const int f[5][2] = { { 0, 0 },
148 { 60, 0 },
149 { 115, -52 },
150 { 98, -55 },
151 { 122, -60 } };
152 int SSumR[NSSIZE];
153 int SSumL[NSSIZE];
154 int iFMod[NSSIZE];
155 int iCycle = 0;
156 short * pS;
157
158 int lastns=0; // last ns pos
159 static int iSecureStart=0; // secure start counter
160
161 ////////////////////////////////////////////////////////////////////////
162 // CODE AREA
163 ////////////////////////////////////////////////////////////////////////
164
165 // dirty inline func includes
166
167 #include "reverb.c"
168 #include "adsr.c"
169
170 ////////////////////////////////////////////////////////////////////////
171 // helpers for simple interpolation
172
173 //
174 // easy interpolation on upsampling, no special filter, just "Pete's common sense" tm
175 //
176 // instead of having n equal sample values in a row like:
177 // ____
178 // |____
179 //
180 // we compare the current delta change with the next delta change.
181 //
182 // if curr_delta is positive,
183 //
184 // - and next delta is smaller (or changing direction):
185 // \.
186 // -__
187 //
188 // - and next delta significant (at least twice) bigger:
189 // --_
190 // \.
191 //
192 // - and next delta is nearly same:
193 // \.
194 // \.
195 //
196 //
197 // if curr_delta is negative,
198 //
199 // - and next delta is smaller (or changing direction):
200 // _--
201 // /
202 //
203 // - and next delta significant (at least twice) bigger:
204 // /
205 // __-
206 //
207 // - and next delta is nearly same:
208 // /
209 // /
210 //
211
212
InterpolateUp(int ch)213 static INLINE void InterpolateUp(int ch)
214 {
215 if(s_chan[ch].SB[32]==1) // flag == 1? calc step and set flag... and don't change the value in this pass
216 {
217 const int id1=s_chan[ch].SB[30]-s_chan[ch].SB[29]; // curr delta to next val
218 const int id2=s_chan[ch].SB[31]-s_chan[ch].SB[30]; // and next delta to next-next val :)
219
220 s_chan[ch].SB[32]=0;
221
222 if(id1>0) // curr delta positive
223 {
224 if(id2<id1)
225 {s_chan[ch].SB[28]=id1;s_chan[ch].SB[32]=2;}
226 else
227 if(id2<(id1<<1))
228 s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x10000L;
229 else
230 s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x20000L;
231 }
232 else // curr delta negative
233 {
234 if(id2>id1)
235 {s_chan[ch].SB[28]=id1;s_chan[ch].SB[32]=2;}
236 else
237 if(id2>(id1<<1))
238 s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x10000L;
239 else
240 s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x20000L;
241 }
242 }
243 else
244 if(s_chan[ch].SB[32]==2) // flag 1: calc step and set flag... and don't change the value in this pass
245 {
246 s_chan[ch].SB[32]=0;
247
248 s_chan[ch].SB[28]=(s_chan[ch].SB[28]*s_chan[ch].sinc)/0x20000L;
249 if(s_chan[ch].sinc<=0x8000)
250 s_chan[ch].SB[29]=s_chan[ch].SB[30]-(s_chan[ch].SB[28]*((0x10000/s_chan[ch].sinc)-1));
251 else s_chan[ch].SB[29]+=s_chan[ch].SB[28];
252 }
253 else // no flags? add bigger val (if possible), calc smaller step, set flag1
254 s_chan[ch].SB[29]+=s_chan[ch].SB[28];
255 }
256
257 //
258 // even easier interpolation on downsampling, also no special filter, again just "Pete's common sense" tm
259 //
260
InterpolateDown(int ch)261 static INLINE void InterpolateDown(int ch)
262 {
263 if(s_chan[ch].sinc>=0x20000L) // we would skip at least one val?
264 {
265 s_chan[ch].SB[29]+=(s_chan[ch].SB[30]-s_chan[ch].SB[29])/2; // add easy weight
266 if(s_chan[ch].sinc>=0x30000L) // we would skip even more vals?
267 s_chan[ch].SB[29]+=(s_chan[ch].SB[31]-s_chan[ch].SB[30])/2;// add additional next weight
268 }
269 }
270
271 ////////////////////////////////////////////////////////////////////////
272 // helpers for gauss interpolation
273
274 #define gval0 (((short*)(&s_chan[ch].SB[29]))[gpos])
275 #define gval(x) (((short*)(&s_chan[ch].SB[29]))[(gpos+x)&3])
276
277 #include "gauss_i.h"
278
279 ////////////////////////////////////////////////////////////////////////
280
281 #include "xa.c"
282
283 ////////////////////////////////////////////////////////////////////////
284 // START SOUND... called by main thread to setup a new sound on a channel
285 ////////////////////////////////////////////////////////////////////////
286
StartSound(int ch)287 static INLINE void StartSound(int ch)
288 {
289 StartADSR(ch);
290 StartREVERB(ch);
291
292 // fussy timing issues - do in VoiceOn
293 //s_chan[ch].pCurr=s_chan[ch].pStart; // set sample start
294 //s_chan[ch].bStop=0;
295 //s_chan[ch].bOn=1;
296
297 s_chan[ch].s_1=0; // init mixing vars
298 s_chan[ch].s_2=0;
299 s_chan[ch].iSBPos=28;
300
301 s_chan[ch].bNew=0; // init channel flags
302
303 s_chan[ch].SB[29]=0; // init our interpolation helpers
304 s_chan[ch].SB[30]=0;
305
306 if(iUseInterpolation>=2) // gauss interpolation?
307 {s_chan[ch].spos=0x30000L;s_chan[ch].SB[28]=0;} // -> start with more decoding
308 else {s_chan[ch].spos=0x10000L;s_chan[ch].SB[31]=0;} // -> no/simple interpolation starts with one 44100 decoding
309
310 dwNewChannel&=~(1<<ch); // clear new channel bit
311 }
312
313 ////////////////////////////////////////////////////////////////////////
314 // ALL KIND OF HELPERS
315 ////////////////////////////////////////////////////////////////////////
316
VoiceChangeFrequency(int ch)317 static INLINE void VoiceChangeFrequency(int ch)
318 {
319 s_chan[ch].iUsedFreq=s_chan[ch].iActFreq; // -> take it and calc steps
320 s_chan[ch].sinc=s_chan[ch].iRawPitch<<4;
321 if(!s_chan[ch].sinc) s_chan[ch].sinc=1;
322 if(iUseInterpolation==1) s_chan[ch].SB[32]=1; // -> freq change in simle imterpolation mode: set flag
323 }
324
325 ////////////////////////////////////////////////////////////////////////
326
FModChangeFrequency(int ch,int ns)327 static INLINE void FModChangeFrequency(int ch,int ns)
328 {
329 int NP=s_chan[ch].iRawPitch;
330
331 NP=((32768L+iFMod[ns])*NP)/32768L;
332
333 if(NP>0x3fff) NP=0x3fff;
334 if(NP<0x1) NP=0x1;
335
336 NP=(44100L*NP)/(4096L); // calc frequency
337
338 s_chan[ch].iActFreq=NP;
339 s_chan[ch].iUsedFreq=NP;
340 s_chan[ch].sinc=(((NP/10)<<16)/4410);
341 if(!s_chan[ch].sinc) s_chan[ch].sinc=1;
342 if(iUseInterpolation==1) // freq change in simple interpolation mode
343 s_chan[ch].SB[32]=1;
344 iFMod[ns]=0;
345 }
346
347 ////////////////////////////////////////////////////////////////////////
348
349 /*
350 Noise Algorithm
351 - Dr.Hell (Xebra PS1 emu)
352 - 100% accurate (waveform + frequency)
353 - http://drhell.web.fc2.com
354
355
356 Level change cycle
357 Freq = 0x8000 >> (NoiseClock >> 2);
358
359 Frequency of half cycle
360 Half = ((NoiseClock & 3) * 2) / (4 + (NoiseClock & 3));
361 - 0 = (0*2)/(4+0) = 0/4
362 - 1 = (1*2)/(4+1) = 2/5
363 - 2 = (2*2)/(4+2) = 4/6
364 - 3 = (3*2)/(4+3) = 6/7
365
366 -------------------------------
367
368 5*6*7 = 210
369 4 - 0*0 = 0
370 5 - 42*2 = 84
371 6 - 35*4 = 140
372 7 - 30*6 = 180
373 */
374
375 // Noise Waveform - Dr. Hell (Xebra)
376 char NoiseWaveAdd [64] = {
377 1, 0, 0, 1, 0, 1, 1, 0,
378 1, 0, 0, 1, 0, 1, 1, 0,
379 1, 0, 0, 1, 0, 1, 1, 0,
380 1, 0, 0, 1, 0, 1, 1, 0,
381 0, 1, 1, 0, 1, 0, 0, 1,
382 0, 1, 1, 0, 1, 0, 0, 1,
383 0, 1, 1, 0, 1, 0, 0, 1,
384 0, 1, 1, 0, 1, 0, 0, 1
385 };
386
387 unsigned short NoiseFreqAdd[5] = {
388 0, 84, 140, 180, 210
389 };
390
NoiseClock()391 static INLINE void NoiseClock()
392 {
393 unsigned int level;
394
395 level = 0x8000 >> (dwNoiseClock >> 2);
396 level <<= 16;
397
398 dwNoiseCount += 0x10000;
399
400 // Dr. Hell - fraction
401 dwNoiseCount += NoiseFreqAdd[ dwNoiseClock & 3 ];
402 if( (dwNoiseCount&0xffff) >= NoiseFreqAdd[4] ) {
403 dwNoiseCount += 0x10000;
404 dwNoiseCount -= NoiseFreqAdd[ dwNoiseClock & 3 ];
405 }
406
407 if( dwNoiseCount >= level )
408 {
409 while( dwNoiseCount >= level )
410 dwNoiseCount -= level;
411
412 // Dr. Hell - form
413 dwNoiseVal = (dwNoiseVal<<1) | NoiseWaveAdd[ (dwNoiseVal>>10) & 63 ];
414 }
415 }
416
iGetNoiseVal(int ch)417 static INLINE int iGetNoiseVal(int ch)
418 {
419 int fa;
420
421 fa = (short) dwNoiseVal;
422
423 // no clip need
424 //if(fa>32767L) fa=32767L;
425 //if(fa<-32767L) fa=-32767L;
426
427 // don't upset VAG decoder
428 //if(iUseInterpolation<2) // no gauss/cubic interpolation?
429 //pChannel->SB[29] = fa; // -> store noise val in "current sample" slot
430
431 // boost volume - no more!
432 //return fa * 3 / 2;
433 return fa;
434 }
435
436 ////////////////////////////////////////////////////////////////////////
437
StoreInterpolationVal(int ch,int fa)438 static INLINE void StoreInterpolationVal(int ch,int fa)
439 {
440 /*
441 // fmod channel = sound output
442 if(s_chan[ch].bFMod==2) // fmod freq channel
443 s_chan[ch].SB[29]=fa;
444 else
445 */
446 {
447 if((spuCtrl&0x4000)==0) fa=0; // muted?
448 else // else adjust
449 {
450 if(fa>32767L) fa=32767L;
451 if(fa<-32767L) fa=-32767L;
452 }
453
454 if(iUseInterpolation>=2) // gauss/cubic interpolation
455 {
456 int gpos = s_chan[ch].SB[28];
457 gval0 = fa;
458 gpos = (gpos+1) & 3;
459 s_chan[ch].SB[28] = gpos;
460 }
461 else
462 if(iUseInterpolation==1) // simple interpolation
463 {
464 s_chan[ch].SB[28] = 0;
465 s_chan[ch].SB[29] = s_chan[ch].SB[30]; // -> helpers for simple linear interpolation: delay real val for two slots, and calc the two deltas, for a 'look at the future behaviour'
466 s_chan[ch].SB[30] = s_chan[ch].SB[31];
467 s_chan[ch].SB[31] = fa;
468 s_chan[ch].SB[32] = 1; // -> flag: calc new interolation
469 }
470 else s_chan[ch].SB[29]=fa; // no interpolation
471 }
472 }
473
474 ////////////////////////////////////////////////////////////////////////
475
iGetInterpolationVal(int ch)476 static INLINE int iGetInterpolationVal(int ch)
477 {
478 int fa;
479
480 // fmod channel = sound output
481 //if(s_chan[ch].bFMod==2) return s_chan[ch].SB[29];
482
483 switch(iUseInterpolation)
484 {
485 //--------------------------------------------------//
486 case 3: // cubic interpolation
487 {
488 long xd;int gpos;
489 xd = ((s_chan[ch].spos) >> 1)+1;
490 gpos = s_chan[ch].SB[28];
491
492 fa = gval(3) - 3*gval(2) + 3*gval(1) - gval0;
493 fa *= (xd - (2<<15)) / 6;
494 fa >>= 15;
495 fa += gval(2) - gval(1) - gval(1) + gval0;
496 fa *= (xd - (1<<15)) >> 1;
497 fa >>= 15;
498 fa += gval(1) - gval0;
499 fa *= xd;
500 fa >>= 15;
501 fa = fa + gval0;
502
503 } break;
504 //--------------------------------------------------//
505 case 2: // gauss interpolation
506 {
507 int vl, vr;int gpos;
508 vl = (s_chan[ch].spos >> 6) & ~3;
509 gpos = s_chan[ch].SB[28];
510 vr=(gauss[vl]*gval0)&~2047;
511 vr+=(gauss[vl+1]*gval(1))&~2047;
512 vr+=(gauss[vl+2]*gval(2))&~2047;
513 vr+=(gauss[vl+3]*gval(3))&~2047;
514 fa = vr>>11;
515 } break;
516 //--------------------------------------------------//
517 case 1: // simple interpolation
518 {
519 if(s_chan[ch].sinc<0x10000L) // -> upsampling?
520 InterpolateUp(ch); // --> interpolate up
521 else InterpolateDown(ch); // --> else down
522 fa=s_chan[ch].SB[29];
523 } break;
524 //--------------------------------------------------//
525 default: // no interpolation
526 {
527 fa=s_chan[ch].SB[29];
528 } break;
529 //--------------------------------------------------//
530 }
531
532 return fa;
533 }
534
535 ////////////////////////////////////////////////////////////////////////
536 // MAIN SPU FUNCTION
537 // here is the main job handler... thread, timer or direct func call
538 // basically the whole sound processing is done in this fat func!
539 ////////////////////////////////////////////////////////////////////////
540
541 // 5 ms waiting phase, if buffer is full and no new sound has to get started
542 // .. can be made smaller (smallest val: 1 ms), but bigger waits give
543 // better performance
544
545 #define PAUSE_W 1
546 #define PAUSE_L 1000
547
548 ////////////////////////////////////////////////////////////////////////
549
550 #ifdef _WINDOWS
MAINProc(UINT nTimerId,UINT msg,DWORD dwUser,DWORD dwParam1,DWORD dwParam2)551 static VOID CALLBACK MAINProc(UINT nTimerId, UINT msg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
552 #else
553 static void *MAINThread(void *arg)
554 #endif
555 {
556 int s_1,s_2,fa,ns;
557 int voldiv = iVolume;
558
559 unsigned char * start;unsigned int nSample;
560 int ch,predict_nr,shift_factor,flags,d,s;
561 int bIRQReturn=0;
562 unsigned int decoded_voice=0;
563
564 // mute output
565 if( voldiv == 5 ) voldiv = 0x7fffffff;
566
567 while(!bEndThread) // until we are shutting down
568 {
569 // ok, at the beginning we are looking if there is
570 // enuff free place in the dsound/oss buffer to
571 // fill in new data, or if there is a new channel to start.
572 // if not, we wait (thread) or return (timer/spuasync)
573 // until enuff free place is available/a new channel gets
574 // started
575
576 if(dwNewChannel) // new channel should start immedately?
577 { // (at least one bit 0 ... MAXCHANNEL is set?)
578 iSecureStart++; // -> set iSecure
579 if(iSecureStart>1) iSecureStart=0; // (if it is set 5 times - that means on 5 tries a new samples has been started - in a row, we will reset it, to give the sound update a chance)
580 }
581 else iSecureStart=0; // 0: no new channel should start
582
583 while(!iSecureStart && !bEndThread && // no new start? no thread end?
584 (SoundGetBytesBuffered()>TESTSIZE)) // and still enuff data in sound buffer?
585 {
586 iSecureStart=0; // reset secure
587
588 #ifdef _WINDOWS
589 if(iUseTimer) // no-thread mode?
590 {
591 if(iUseTimer==1) // -> ok, timer mode 1: setup a oneshot timer of x ms to wait
592 timeSetEvent(PAUSE_W,1,MAINProc,0,TIME_ONESHOT);
593 return; // -> and done this time (timer mode 1 or 2)
594 }
595 // win thread mode:
596 Sleep(PAUSE_W); // sleep for x ms (win)
597 #else
598 if(iUseTimer) return 0; // linux no-thread mode? bye
599 usleep(PAUSE_L); // else sleep for x ms (linux)
600 #endif
601
602 if(dwNewChannel) iSecureStart=1; // if a new channel kicks in (or, of course, sound buffer runs low), we will leave the loop
603 }
604
605 ns=0;
606
607 //--------------------------------------------------// continue from irq handling in timer mode?
608
609 if(lastns>0) // will be 0 if no continue is pending
610 {
611 ns=lastns; // -> setup all kind of vars to continue
612 lastns=0;
613 }
614
615 //--------------------------------------------------//
616 //- main channel loop -//
617 //--------------------------------------------------//
618 {
619 decoded_voice = decoded_ptr;
620
621 while(ns<NSSIZE) // loop until 1 ms of data is reached
622 {
623 SSumL[ns]=0;
624 SSumR[ns]=0;
625
626
627 // decoded buffer values - dummy
628 spuMem[ (0x000 + decoded_voice) / 2 ] = (short) 0;
629 spuMem[ (0x400 + decoded_voice) / 2 ] = (short) 0;
630 spuMem[ (0x800 + decoded_voice) / 2 ] = (short) 0;
631 spuMem[ (0xc00 + decoded_voice) / 2 ] = (short) 0;
632
633
634 NoiseClock();
635
636 for(ch=0;ch<MAXCHAN;ch++) // loop em all... we will collect 1 ms of sound of each playing channel
637 {
638 if(s_chan[ch].bNew) {
639 #if 1
640 StartSound(ch); // start new sound
641 dwNewChannel&=~(1<<ch); // clear new channel bit
642 #else
643 if( s_chan[ch].ADSRX.StartDelay == 0 ) {
644 StartSound(ch); // start new sound
645 dwNewChannel&=~(1<<ch); // clear new channel bit
646 } else {
647 s_chan[ch].ADSRX.StartDelay--;
648 }
649 #endif
650 }
651 if(!s_chan[ch].bOn) continue; // channel not playing? next
652
653 if(s_chan[ch].iActFreq!=s_chan[ch].iUsedFreq) // new psx frequency?
654 VoiceChangeFrequency(ch);
655
656 if(s_chan[ch].bFMod==1 && iFMod[ns]) // fmod freq channel
657 FModChangeFrequency(ch,ns);
658
659 while(s_chan[ch].spos>=0x10000L)
660 {
661 if(s_chan[ch].iSBPos==28) // 28 reached?
662 {
663 // Xenogears - Anima Relic dungeon (exp gain)
664 if( s_chan[ch].bLoopJump == 1 )
665 s_chan[ch].pCurr = s_chan[ch].pLoop;
666
667 s_chan[ch].bLoopJump = 0;
668
669
670 start=s_chan[ch].pCurr; // set up the current pos
671
672 if (start == spuMemC)
673 s_chan[ch].bOn = 0;
674
675 if (s_chan[ch].iSilent==1 )
676 {
677 // silence = let channel keep running (IRQs)
678 //s_chan[ch].bOn=0; // -> turn everything off
679 s_chan[ch].iSilent=2;
680
681 s_chan[ch].ADSRX.lVolume=0;
682 s_chan[ch].ADSRX.EnvelopeVol=0;
683 }
684
685 s_chan[ch].iSBPos=0;
686
687 //////////////////////////////////////////// spu irq handler here? mmm... do it later
688
689 s_1=s_chan[ch].s_1;
690 s_2=s_chan[ch].s_2;
691
692 predict_nr=(int)*start;start++;
693 shift_factor=predict_nr&0xf;
694 predict_nr >>= 4;
695 flags=(int)*start;start++;
696
697 // Silhouette Mirage - Serah fight
698 if( predict_nr > 4 ) predict_nr = 0;
699
700 // -------------------------------------- //
701
702 for (nSample=0;nSample<28;start++)
703 {
704 d=(int)*start;
705 s=((d&0xf)<<12);
706 if(s&0x8000) s|=0xffff0000;
707
708 fa=(s >> shift_factor);
709 fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6);
710
711 // snes brr clamps
712 fa = CLAMP16(fa);
713
714 s_2=s_1;s_1=fa;
715 s=((d & 0xf0) << 8);
716
717 s_chan[ch].SB[nSample++]=fa;
718
719
720 if(s&0x8000) s|=0xffff0000;
721 fa=(s>>shift_factor);
722 fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6);
723
724 // snes brr clamps
725 fa = CLAMP16(fa);
726
727 s_2=s_1;s_1=fa;
728
729 s_chan[ch].SB[nSample++]=fa;
730 }
731
732 //////////////////////////////////////////// irq check
733
734 #if 1
735 // Check channel/loop IRQs (e.g. Castlevania Chronicles) and at pos-8 for unknown reason
736 if( Check_IRQ( (s_chan[ch].pCurr)-spuMemC, 0 ) ||
737 Check_IRQ( (start-spuMemC)-0, 0 ) ||
738 Check_IRQ( (start-spuMemC)-8, 0 ) )
739 {
740 #else
741 if(irqCallback && (spuCtrl&0x40)) // some callback and irq active?
742 {
743 if((pSpuIrq > start-16 && // irq address reached?
744 pSpuIrq <= start) ||
745 ((flags&1) && // special: irq on looping addr, when stop/loop flag is set
746 (pSpuIrq > s_chan[ch].pLoop-16 &&
747 pSpuIrq <= s_chan[ch].pLoop)))
748 #endif
749 {
750 s_chan[ch].iIrqDone=1; // -> debug flag
751 //irqCallback(); // -> call main emu (checked & called on Check_IRQ)
752
753 if(iSPUIRQWait) // -> option: wait after irq for main emu
754 {
755 iSpuAsyncWait=1;
756 bIRQReturn=1;
757 }
758 }
759 }
760
761 //////////////////////////////////////////// flag handler
762
763 /*
764 SPU2-X:
765 $4 = set loop to current block
766 $2 = keep envelope on (no mute)
767 $1 = jump to loop address
768
769 silence means no volume (ADSR keeps playing!!)
770 */
771
772 if(flags&4)
773 s_chan[ch].pLoop=start-16;
774
775
776 // Jungle Book - Rhythm 'n Groove - don't reset ignore status
777 // - fixes gameplay speed (IRQ hits)
778 //s_chan[ch].bIgnoreLoop = 0;
779
780
781 if(flags&1)
782 {
783 // ...?
784 //s_chan[ch].bIgnoreLoop = 0;
785
786 // Xenogears - 7 = play missing sounds
787 // set jump flag
788 s_chan[ch].bLoopJump = 1;
789
790
791 // silence = keep playing..?
792 if( (flags&2) == 0 ) {
793 s_chan[ch].iSilent = 1;
794
795 // silence = don't start release phase
796 //s_chan[ch].bStop = 1;
797
798 //start = (unsigned char *) -1;
799 }
800 }
801
802 #if 0
803 // crash check
804 if( start == 0 )
805 start = (unsigned char *) -1;
806 if( start >= spuMemC + 0x80000 )
807 start = spuMemC - 0x80000;
808 #endif
809
810
811 // Silhouette Mirage - ending mini-game
812
813 // ??
814 if( start - spuMemC >= 0x80000 ) {
815 start -= 16;
816
817 s_chan[ch].iSilent = 1;
818 s_chan[ch].bStop = 1;
819 }
820
821
822 s_chan[ch].pCurr=start; // store values for next cycle
823 s_chan[ch].s_1=s_1;
824 s_chan[ch].s_2=s_2;
825 }
826
827 fa=s_chan[ch].SB[s_chan[ch].iSBPos++]; // get sample data
828
829 StoreInterpolationVal(ch,fa); // store val for later interpolation
830
831 s_chan[ch].spos -= 0x10000L;
832 }
833
834 if(s_chan[ch].bNoise)
835 fa=iGetNoiseVal(ch); // get noise val
836 else fa=iGetInterpolationVal(ch); // get sample val
837
838
839 // Voice 1/3 decoded buffer
840 if( ch == 0 ) {
841 spuMem[ (0x800 + decoded_voice) / 2 ] = (short) fa;
842 } else if( ch == 2 ) {
843 spuMem[ (0xc00 + decoded_voice) / 2 ] = (short) fa;
844 }
845
846
847 s_chan[ch].sval = (MixADSR(ch) * fa) / 1023; // mix adsr
848
849 if(s_chan[ch].bFMod==2) // fmod freq channel
850 iFMod[ns]=s_chan[ch].sval; // -> store 1T sample data, use that to do fmod on next channel
851
852 // mix fmod channel into output
853 // - Xenogears save icon (high pitch)
854 {
855 //////////////////////////////////////////////
856 // ok, left/right sound volume (psx volume goes from 0 ... 0x3fff)
857
858 if(s_chan[ch].iMute)
859 s_chan[ch].sval=0; // debug mute
860 else
861 {
862 SSumL[ns]+=(s_chan[ch].sval*s_chan[ch].iLeftVolume)/0x4000L;
863 SSumR[ns]+=(s_chan[ch].sval*s_chan[ch].iRightVolume)/0x4000L;
864 }
865
866 //////////////////////////////////////////////
867 // now let us store sound data for reverb
868
869 if(s_chan[ch].bRVBActive) StoreREVERB(ch,ns);
870 }
871
872 s_chan[ch].spos += s_chan[ch].sinc;
873 }
874
875 ////////////////////////////////////////////////
876 // ok, go on until 1 ms data of this channel is collected
877
878 // decoded buffer - voice
879 decoded_voice += 2;
880 decoded_voice &= 0x3ff;
881
882
883 // status flag
884 if( decoded_voice >= 0x200 ) {
885 spuStat |= STAT_DECODED;
886 } else {
887 spuStat &= ~STAT_DECODED;
888 }
889
890
891 // IRQ work
892 {
893 unsigned char *old_irq;
894 unsigned int old_ptr;
895
896 old_irq = pSpuIrq;
897 old_ptr = decoded_voice;
898
899 #if 0
900 // align to boundaries ($0, $200, $400, $600)
901 pSpuIrq = ((pSpuIrq - spuMemC) & (~0x1ff)) + spuMemC;
902 decoded_voice = decoded_voice & (~0x1ff);
903 #endif
904
905 // check all decoded buffer IRQs - timing issue
906 Check_IRQ( decoded_voice + 0x000, 0 );
907 Check_IRQ( decoded_voice + 0x400, 0 );
908 Check_IRQ( decoded_voice + 0x800, 0 );
909 Check_IRQ( decoded_voice + 0xc00, 0 );
910
911 pSpuIrq = old_irq;
912 decoded_voice = old_ptr;
913 }
914
915 if(bIRQReturn) // special return for "spu irq - wait for cpu action"
916 {
917 bIRQReturn=0;
918 if(iUseTimer!=2)
919 {
920 DWORD dwWatchTime=timeGetTime_spu()+2500;
921
922 while(iSpuAsyncWait && !bEndThread &&
923 timeGetTime_spu()<dwWatchTime)
924 #ifdef _WINDOWS
925 Sleep(1);
926 #else
927 usleep(1000L);
928 #endif
929 }
930 else
931 {
932 lastns=ns+1;
933
934 #ifdef _WINDOWS
935 return;
936 #else
937 return 0;
938 #endif
939 }
940 }
941
942 ns++;
943 } // end ns
944 }
945
946
947 //---------------------------------------------------//
948 //- here we have another 1 ms of sound data
949 //---------------------------------------------------//
950 // mix XA infos (if any)
951
952 MixXA();
953
954
955 // now safe to update decoded buffer ptr
956 decoded_ptr += ns * 2;
957 decoded_ptr &= 0x3ff;
958
959
960 ///////////////////////////////////////////////////////
961 // mix all channels (including reverb) into one buffer
962
963 if(iDisStereo) // no stereo?
964 {
965 int dl, dr;
966 for (ns = 0; ns < NSSIZE; ns++)
967 {
968 SSumL[ns] += MixREVERBLeft(ns);
969
970 dl = SSumL[ns] / voldiv; SSumL[ns] = 0;
971 if (dl < -32767) dl = -32767; if (dl > 32767) dl = 32767;
972
973 SSumR[ns] += MixREVERBRight();
974
975 dr = SSumR[ns] / voldiv; SSumR[ns] = 0;
976 if (dr < -32767) dr = -32767; if (dr > 32767) dr = 32767;
977 *pS++ = (dl + dr) / 2;
978 }
979 }
980 else // stereo:
981 for (ns = 0; ns < NSSIZE; ns++)
982 {
983 static double _interpolation_coefficient = 3.759285613;
984
985 if(iFreqResponse) {
986 int sl,sr;
987 double ldiff, rdiff, avg, tmp;
988
989 SSumL[ns]+=MixREVERBLeft(ns);
990 SSumR[ns]+=MixREVERBRight();
991
992 sl = SSumL[ns]; SSumL[ns]=0;
993 sr = SSumR[ns]; SSumR[ns]=0;
994
995
996 /*
997 Frequency Response
998 - William Pitcock (nenolod) (UPSE PSF player)
999 - accurate (!)
1000 - http://nenolod.net
1001 */
1002
1003 avg = ((sl + sr) / 2);
1004 ldiff = sl - avg;
1005 rdiff = sr - avg;
1006
1007 tmp = avg + ldiff * _interpolation_coefficient;
1008 if (tmp < -32768)
1009 tmp = -32768;
1010 if (tmp > 32767)
1011 tmp = 32767;
1012 sl = (int)tmp;
1013
1014 tmp = avg + rdiff * _interpolation_coefficient;
1015 if (tmp < -32768)
1016 tmp = -32768;
1017 if (tmp > 32767)
1018 tmp = 32767;
1019 sr = (int)tmp;
1020
1021
1022 *pS++=sl/voldiv;
1023 *pS++=sr/voldiv;
1024 } else {
1025 SSumL[ns]+=MixREVERBLeft(ns);
1026
1027 d=SSumL[ns]/voldiv;SSumL[ns]=0;
1028 if(d<-32767) d=-32767;if(d>32767) d=32767;
1029 *pS++=d;
1030
1031 SSumR[ns]+=MixREVERBRight();
1032
1033 d=SSumR[ns]/voldiv;SSumR[ns]=0;
1034 if(d<-32767) d=-32767;if(d>32767) d=32767;
1035 *pS++=d;
1036 }
1037 }
1038
1039 //////////////////////////////////////////////////////
1040 // special irq handling in the decode buffers (0x0000-0x1000)
1041 // we know:
1042 // the decode buffers are located in spu memory in the following way:
1043 // 0x0000-0x03ff CD audio left
1044 // 0x0400-0x07ff CD audio right
1045 // 0x0800-0x0bff Voice 1
1046 // 0x0c00-0x0fff Voice 3
1047 // and decoded data is 16 bit for one sample
1048 // we assume:
1049 // even if voices 1/3 are off or no cd audio is playing, the internal
1050 // play positions will move on and wrap after 0x400 bytes.
1051 // Therefore: we just need a pointer from spumem+0 to spumem+3ff, and
1052 // increase this pointer on each sample by 2 bytes. If this pointer
1053 // (or 0x400 offsets of this pointer) hits the spuirq address, we generate
1054 // an IRQ. Only problem: the "wait for cpu" option is kinda hard to do here
1055 // in some of Peops timer modes. So: we ignore this option here (for now).
1056
1057 #if 0
1058 if(pMixIrq && irqCallback)
1059 {
1060 for(ns=0;ns<NSSIZE;ns++)
1061 {
1062 if((spuCtrl&0x40) && pSpuIrq && pSpuIrq<spuMemC+0x1000)
1063 {
1064 for(ch=0;ch<4;ch++)
1065 {
1066 if(pSpuIrq>=pMixIrq+(ch*0x400) && pSpuIrq<pMixIrq+(ch*0x400)+2)
1067 {irqCallback();s_chan[ch].iIrqDone=1;}
1068 }
1069 }
1070 pMixIrq+=2;if(pMixIrq>spuMemC+0x3ff) pMixIrq=spuMemC;
1071 }
1072 }
1073 #endif
1074
1075 InitREVERB();
1076
1077 //////////////////////////////////////////////////////
1078 // feed the sound
1079 // latency = 25 ms (less pops, crackles, smoother)
1080
1081 //if(iCycle++>=20)
1082 iCycle += APU_CYCLES_UPDATE;
1083 if(iCycle > 44000/1000*LATENCY + 100*LATENCY/1000)
1084 {
1085 SoundFeedStreamData((unsigned char *)pSpuBuffer,
1086 ((unsigned char *)pS) - ((unsigned char *)pSpuBuffer));
1087 pS = (short *)pSpuBuffer;
1088 iCycle = 0;
1089 }
1090
1091
1092 if( iUseTimer == 2 )
1093 break;
1094 }
1095
1096 // end of big main loop...
1097
1098 bThreadEnded = 1;
1099
1100 #ifndef _WINDOWS
1101 return 0;
1102 #endif
1103 }
1104
1105 ////////////////////////////////////////////////////////////////////////
1106 // WINDOWS THREAD... simply calls the timer func and stays forever :)
1107 ////////////////////////////////////////////////////////////////////////
1108
1109 #ifdef _WINDOWS
1110
1111 DWORD WINAPI MAINThreadEx(LPVOID lpParameter)
1112 {
1113 MAINProc(0,0,0,0,0);
1114 return 0;
1115 }
1116
1117 #endif
1118
1119 // SPU ASYNC... even newer epsxe func
1120 // 1 time every 'cycle' cycles... harhar
1121
1122 long cpu_cycles;
1123 void CALLBACK SPUasync(unsigned long cycle)
1124 {
1125 cpu_cycles += cycle;
1126
1127 if(iSpuAsyncWait)
1128 {
1129 iSpuAsyncWait++;
1130 if(iSpuAsyncWait<=64) return;
1131 iSpuAsyncWait=0;
1132 }
1133
1134 #ifdef _WINDOWS
1135 if(iDebugMode==2)
1136 {
1137 if(IsWindow(hWDebug)) DestroyWindow(hWDebug);
1138 hWDebug=0;iDebugMode=0;
1139 }
1140 if(iRecordMode==2)
1141 {
1142 if(IsWindow(hWRecord)) DestroyWindow(hWRecord);
1143 hWRecord=0;iRecordMode=0;
1144 }
1145 #endif
1146
1147 if(iUseTimer==2) // special mode, only used in Linux by this spu (or if you enable the experimental Windows mode)
1148 {
1149 if(!bSpuInit) return; // -> no init, no call
1150
1151 // note: usable precision difference (not using interval_time)
1152 while( cpu_cycles >= CPU_CLOCK / 44100 * NSSIZE )
1153 {
1154 #ifdef _WINDOWS
1155 MAINProc(0,0,0,0,0); // -> experimental win mode... not really tested... don't like the drawbacks
1156 #else
1157 MAINThread(0); // -> linux high-compat mode
1158 #endif
1159
1160 if (iSpuAsyncWait)
1161 break;
1162 cpu_cycles -= CPU_CLOCK / 44100 * NSSIZE;
1163 }
1164 }
1165 }
1166
1167 // SPU UPDATE... new epsxe func
1168 // 1 time every 32 hsync lines
1169 // (312/32)x50 in pal
1170 // (262/32)x60 in ntsc
1171
1172 // since epsxe 1.5.2 (linux) uses SPUupdate, not SPUasync, I will
1173 // leave that func in the linux port, until epsxe linux is using
1174 // the async function as well
1175
1176 void CALLBACK SPUupdate(void)
1177 {
1178 SPUasync(0);
1179 }
1180
1181 // XA AUDIO
1182
1183 void CALLBACK SPUplayADPCMchannel(xa_decode_t *xap)
1184 {
1185 if(!xap) return;
1186 if(!xap->freq) return; // no xa freq ? bye
1187
1188 FeedXA(xap); // call main XA feeder
1189 }
1190
1191 // CDDA AUDIO
1192 void CALLBACK SPUplayCDDAchannel(short *pcm, int nbytes)
1193 {
1194 if (!pcm) return;
1195 if (nbytes<=0) return;
1196
1197 FeedCDDA((unsigned char *)pcm, nbytes);
1198 }
1199
1200 // SETUPTIMER: init of certain buffers and threads/timers
1201 void SetupTimer(void)
1202 {
1203 memset(SSumR,0,NSSIZE*sizeof(int)); // init some mixing buffers
1204 memset(SSumL,0,NSSIZE*sizeof(int));
1205 memset(iFMod,0,NSSIZE*sizeof(int));
1206 pS=(short *)pSpuBuffer; // setup soundbuffer pointer
1207
1208 bEndThread=0; // init thread vars
1209 bThreadEnded=0;
1210 bSpuInit=1; // flag: we are inited
1211
1212 #ifdef _WINDOWS
1213
1214 if(iUseTimer==1) // windows: use timer
1215 {
1216 timeBeginPeriod(1);
1217 timeSetEvent(1,1,MAINProc,0,TIME_ONESHOT);
1218 }
1219 else
1220 if(iUseTimer==0) // windows: use thread
1221 {
1222 //_beginthread(MAINThread,0,NULL);
1223 DWORD dw;
1224 hMainThread=CreateThread(NULL,0,MAINThreadEx,0,0,&dw);
1225 SetThreadPriority(hMainThread,
1226 //THREAD_PRIORITY_TIME_CRITICAL);
1227 THREAD_PRIORITY_HIGHEST);
1228 }
1229
1230 #else
1231
1232 if(!iUseTimer) // linux: use thread
1233 {
1234 pthread_create(&thread, NULL, MAINThread, NULL);
1235 }
1236
1237 #endif
1238 }
1239
1240 // REMOVETIMER: kill threads/timers
1241 void RemoveTimer(void)
1242 {
1243 bEndThread=1; // raise flag to end thread
1244
1245 #ifdef _WINDOWS
1246
1247 if(iUseTimer!=2) // windows thread?
1248 {
1249 while(!bThreadEnded) {Sleep(5L);} // -> wait till thread has ended
1250 Sleep(5L);
1251 }
1252 if(iUseTimer==1) timeEndPeriod(1); // windows timer? stop it
1253
1254 #else
1255 if(!iUseTimer) // linux tread?
1256 {
1257 int i=0;
1258 while(!bThreadEnded && i<2000) {usleep(1000L);i++;} // -> wait until thread has ended
1259 if(thread!=(pthread_t)-1) {pthread_cancel(thread);thread=(pthread_t)-1;} // -> cancel thread anyway
1260 }
1261
1262 #endif
1263
1264 bThreadEnded=0; // no more spu is running
1265 bSpuInit=0;
1266 }
1267
1268 // SETUPSTREAMS: init most of the spu buffers
1269 void SetupStreams(void)
1270 {
1271 int i;
1272
1273 pSpuBuffer=(unsigned char *)malloc(32768); // alloc mixing buffer
1274
1275 if(iUseReverb==1) i=88200*2;
1276 else i=NSSIZE*2;
1277
1278 sRVBStart = (int *)malloc(i*4); // alloc reverb buffer
1279 memset(sRVBStart,0,i*4);
1280 sRVBEnd = sRVBStart + i;
1281 sRVBPlay = sRVBStart;
1282
1283 XAStart = // alloc xa buffer
1284 (uint32_t *)malloc(44100 * sizeof(uint32_t));
1285 XAEnd = XAStart + 44100;
1286 XAPlay = XAStart;
1287 XAFeed = XAStart;
1288
1289 CDDAStart = // alloc cdda buffer
1290 (uint32_t *)malloc(44100 * sizeof(uint32_t));
1291 CDDAEnd = CDDAStart + 44100;
1292 CDDAPlay = CDDAStart;
1293 CDDAFeed = CDDAStart;
1294
1295 for(i=0;i<MAXCHAN;i++) // loop sound channels
1296 {
1297 // we don't use mutex sync... not needed, would only
1298 // slow us down:
1299 // s_chan[i].hMutex=CreateMutex(NULL,FALSE,NULL);
1300 s_chan[i].ADSRX.SustainLevel = 1024; // -> init sustain
1301 s_chan[i].iMute=0;
1302 s_chan[i].iIrqDone=0;
1303 s_chan[i].pLoop=spuMemC;
1304 s_chan[i].pStart=spuMemC;
1305 s_chan[i].pCurr=spuMemC;
1306 }
1307
1308 pMixIrq=spuMemC; // enable decoded buffer irqs by setting the address
1309 }
1310
1311 // REMOVESTREAMS: free most buffer
1312 void RemoveStreams(void)
1313 {
1314 free(pSpuBuffer); // free mixing buffer
1315 pSpuBuffer = NULL;
1316 free(sRVBStart); // free reverb buffer
1317 sRVBStart = NULL;
1318 free(XAStart); // free XA buffer
1319 XAStart = NULL;
1320 free(CDDAStart); // free CDDA buffer
1321 CDDAStart = NULL;
1322 }
1323
1324 // INIT/EXIT STUFF
1325
1326 // SPUINIT: this func will be called first by the main emu
1327 long CALLBACK SPUinit(void)
1328 {
1329 spuMemC = (unsigned char *)spuMem; // just small setup
1330 memset((void *)&rvb, 0, sizeof(REVERBInfo));
1331 InitADSR();
1332
1333 iVolume = 3;
1334 iReverbOff = -1;
1335 spuIrq = 0;
1336 spuAddr = 0x200;
1337 bEndThread = 0;
1338 bThreadEnded = 0;
1339 spuMemC = (unsigned char *)spuMem;
1340 pMixIrq = 0;
1341 memset((void *)s_chan, 0, (MAXCHAN + 1) * sizeof(SPUCHAN));
1342 pSpuIrq = 0;
1343 iSPUIRQWait = 1;
1344 lastns = 0;
1345
1346 ReadConfig(); // read user stuff
1347 SetupStreams(); // prepare streaming
1348
1349 return 0;
1350 }
1351
1352 // SPUOPEN: called by main emu after init
1353 #ifdef _WINDOWS
1354 long CALLBACK SPUopen(HWND hW)
1355 #else
1356 long SPUopen(void)
1357 #endif
1358 {
1359 if (bSPUIsOpen) return 0; // security for some stupid main emus
1360
1361 #ifdef _WINDOWS
1362 LastWrite=0xffffffff;LastPlay=0; // init some play vars
1363 if(!IsWindow(hW)) hW=GetActiveWindow();
1364 hWMain = hW; // store hwnd
1365 #endif
1366
1367 SetupSound(); // setup sound (before init!)
1368 SetupTimer(); // timer for feeding data
1369
1370 bSPUIsOpen = 1;
1371
1372 #ifdef _WINDOWS
1373 if(iDebugMode) // windows debug dialog
1374 {
1375 hWDebug=CreateDialog(hInst,MAKEINTRESOURCE(IDD_DEBUG),
1376 NULL,(DLGPROC)DebugDlgProc);
1377 SetWindowPos(hWDebug,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW|SWP_NOACTIVATE);
1378 UpdateWindow(hWDebug);
1379 SetFocus(hWMain);
1380 }
1381
1382 if(iRecordMode) // windows recording dialog
1383 {
1384 hWRecord=CreateDialog(hInst,MAKEINTRESOURCE(IDD_RECORD),
1385 NULL,(DLGPROC)RecordDlgProc);
1386 SetWindowPos(hWRecord,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW|SWP_NOACTIVATE);
1387 UpdateWindow(hWRecord);
1388 SetFocus(hWMain);
1389 }
1390 #endif
1391
1392 return PSE_SPU_ERR_SUCCESS;
1393 }
1394
1395 // SPUCLOSE: called before shutdown
1396 long CALLBACK SPUclose(void)
1397 {
1398 if (!bSPUIsOpen) return 0; // some security
1399
1400 bSPUIsOpen = 0; // no more open
1401
1402 #ifdef _WINDOWS
1403 if(IsWindow(hWDebug)) DestroyWindow(hWDebug);
1404 hWDebug=0;
1405 if(IsWindow(hWRecord)) DestroyWindow(hWRecord);
1406 hWRecord=0;
1407 #endif
1408
1409 RemoveTimer(); // no more feeding
1410 RemoveSound(); // no more sound handling
1411
1412 return 0;
1413 }
1414
1415 // SPUSHUTDOWN: called by main emu on final exit
1416 long CALLBACK SPUshutdown(void)
1417 {
1418 SPUclose();
1419 RemoveStreams(); // no more streaming
1420
1421 return 0;
1422 }
1423
1424 // SPUTEST: we don't test, we are always fine ;)
1425 long CALLBACK SPUtest(void)
1426 {
1427 return 0;
1428 }
1429
1430 // SPUCONFIGURE: call config dialog
1431 long CALLBACK SPUconfigure(void)
1432 {
1433 #if defined (_WINDOWS)
1434 DialogBox(hInst,MAKEINTRESOURCE(IDD_CFGDLG),
1435 GetActiveWindow(),(DLGPROC)DSoundDlgProc);
1436 #elif defined (_MACOSX)
1437 return DoConfiguration();
1438 #else
1439 StartCfgTool("configure");
1440 #endif
1441
1442 return 0;
1443 }
1444
1445 // SPUABOUT: show about window
1446 void CALLBACK SPUabout(void)
1447 {
1448 #if defined (_WINDOWS)
1449 DialogBox(hInst,MAKEINTRESOURCE(IDD_ABOUT),
1450 GetActiveWindow(),(DLGPROC)AboutDlgProc);
1451 #elif defined (_MACOSX)
1452 DoAbout();
1453 #else
1454 StartCfgTool("about");
1455 #endif
1456 }
1457
1458 // SETUP CALLBACKS
1459 // this functions will be called once,
1460 // passes a callback that should be called on SPU-IRQ/cdda volume change
1461 void CALLBACK SPUregisterCallback(void (CALLBACK *callback)(void))
1462 {
1463 irqCallback = callback;
1464 }
1465
1466 void CALLBACK SPUregisterCDDAVolume(void (CALLBACK *CDDAVcallback)(unsigned short,unsigned short))
1467 {
1468 cddavCallback = CDDAVcallback;
1469 }
1470
1471 // COMMON PLUGIN INFO FUNCS
1472 char * CALLBACK PSEgetLibName(void)
1473 {
1474 return _(libraryName);
1475 }
1476
1477 unsigned long CALLBACK PSEgetLibType(void)
1478 {
1479 return PSE_LT_SPU;
1480 }
1481
1482 unsigned long CALLBACK PSEgetLibVersion(void)
1483 {
1484 return (1 << 16) | (1 << 8);
1485 }
1486
1487 char * SPUgetLibInfos(void)
1488 {
1489 return _(libraryInfo);
1490 }
1491