/[pcsx2_0.9.7]/trunk/plugins/zerospu2/zerospu2.cpp
ViewVC logotype

Contents of /trunk/plugins/zerospu2/zerospu2.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 280 - (show annotations) (download)
Thu Dec 23 12:02:12 2010 UTC (9 years, 2 months ago) by william
File size: 31167 byte(s)
re-commit (had local access denied errors when committing)
1 /* ZeroSPU2
2 * Copyright (C) 2006-2010 zerofrog
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #include "zerospu2.h"
20
21 #ifdef _WIN32
22 #include "svnrev.h"
23 #endif
24
25 #include "Targets/SoundTargets.h"
26
27 #include <assert.h>
28 #include <stdlib.h>
29
30 #include "soundtouch/SoundTouch.h"
31 #ifdef __LINUX__
32 #include "WavFile.h"
33 #else
34 #include "soundtouch/WavFile.h"
35 #endif
36
37 char libraryName[256];
38
39 FILE *spu2Log;
40 Config conf;
41
42 ADMA adma[2];
43
44 u32 MemAddr[2];
45 u32 g_nSpuInit = 0;
46 u16 interrupt = 0;
47 s8 *spu2regs = NULL;
48 u16* spu2mem = NULL;
49 u16* pSpuIrq[2] = {NULL};
50 u32 dwNewChannel2[2] = {0}; // keeps track of what channels that have been turned on
51 u32 dwEndChannel2[2] = {0}; // keeps track of what channels have ended
52 u32 dwNoiseVal=1; // global noise generator
53 bool g_bPlaySound = true; // if true, will output sound, otherwise no
54 s32 iFMod[NSSIZE];
55 s32 s_buffers[NSSIZE][2]; // left and right buffers
56
57 // mixer thread variables
58 bool s_bThreadExit = true;
59 s32 s_nDropPacket = 0;
60 string s_strIniPath( "inis/" );
61
62 #ifdef _WIN32
63 LARGE_INTEGER g_counterfreq;
64 extern HWND hWMain;
65 HANDLE s_threadSPU2 = NULL;
66 DWORD WINAPI SPU2ThreadProc(LPVOID);
67 #else
68 #include <pthread.h>
69 pthread_t s_threadSPU2;
70 void* SPU2ThreadProc(void*);
71 #endif
72
73 AUDIOBUFFER s_pAudioBuffers[NSPACKETS];
74 s32 s_nCurBuffer = 0, s_nQueuedBuffers = 0;
75 s16* s_pCurOutput = NULL;
76 u32 g_startcount=0xffffffff;
77 u32 g_packetcount=0;
78
79 // time stretch variables
80 soundtouch::SoundTouch* pSoundTouch=NULL;
81 extern WavOutFile* g_pWavRecord; // used for recording
82
83 u64 s_GlobalTimeStamp = 0;
84 s32 s_nDurations[64]={0};
85 s32 s_nCurDuration=0;
86 s32 s_nTotalDuration=0;
87
88 s32 SPUCycles = 0, SPUWorkerCycles = 0;
89 s32 SPUStartCycle[2];
90 s32 SPUTargetCycle[2];
91
92 int ADMASWrite(int c);
93
94 void InitADSR();
95
96 // functions of main emu, called on spu irq
97 void (*irqCallbackSPU2)()=0;
98 #ifndef ENABLE_NEW_IOPDMA_SPU2
99 void (*irqCallbackDMA4)()=0;
100 void (*irqCallbackDMA7)()=0;
101 #endif
102
103 uptr g_pDMABaseAddr=0;
104 u32 RateTable[160];
105
106 // channels and voices
107 VOICE_PROCESSED voices[SPU_NUMBER_VOICES+1]; // +1 for modulation
108
109 static void InitLibraryName()
110 {
111 #ifdef _WIN32
112 #ifdef PUBLIC
113
114 // Public Release!
115 // Output a simplified string that's just our name:
116
117 strcpy( libraryName, "ZeroSPU2" );
118
119 #elif defined( SVN_REV_UNKNOWN )
120
121 // Unknown revision.
122 // Output a name that includes devbuild status but not
123 // subversion revision tags:
124
125 strcpy( libraryName, "ZeroSPU2"
126 # ifdef PCSX2_DEBUG
127 "-Debug"
128 # elif defined( ZEROSPU2_DEVBUILD )
129 "-Dev"
130 # endif
131 );
132 #else
133
134 // Use TortoiseSVN's SubWCRev utility's output
135 // to label the specific revision:
136
137 sprintf_s( libraryName, "ZeroSPU2 r%d%s"
138 # ifdef PCSX2_DEBUG
139 "-Debug"
140 # elif defined( ZEROSPU2_DEVBUILD )
141 "-Dev"
142 # endif
143 ,SVN_REV,
144 SVN_MODS ? "m" : ""
145 );
146 #endif
147 #else
148 // I'll hook in svn version code later. --arcum42
149
150 strcpy( libraryName, "ZeroSPU2 Playground"
151 # ifdef PCSX2_DEBUG
152 "-Debug"
153 # elif defined( ZEROSPU2_DEVBUILD )
154 "-Dev"
155 # endif
156 );
157 # endif
158
159 }
160
161 u32 CALLBACK PS2EgetLibType()
162 {
163 return PS2E_LT_SPU2;
164 }
165
166 char* CALLBACK PS2EgetLibName()
167 {
168 InitLibraryName();
169 return libraryName;
170 }
171
172 u32 CALLBACK PS2EgetLibVersion2(u32 type)
173 {
174 return (SPU2_MINOR<<24) | (SPU2_VERSION<<16) | (SPU2_REVISION<<8) | SPU2_BUILD;
175 }
176
177 void __Log(char *fmt, ...)
178 {
179 va_list list;
180
181 if (!conf.Log || spu2Log == NULL) return;
182
183 va_start(list, fmt);
184 vfprintf(spu2Log, fmt, list);
185 va_end(list);
186 }
187
188 void __LogToConsole(const char *fmt, ...)
189 {
190 va_list list;
191
192 va_start(list, fmt);
193
194 if (!conf.Log || spu2Log == NULL)
195 vfprintf(spu2Log, fmt, list);
196
197 printf("ZeroSPU2: ");
198 vprintf(fmt, list);
199 va_end(list);
200 }
201
202 void CALLBACK SPU2setSettingsDir(const char* dir)
203 {
204 s_strIniPath = (dir==NULL) ? "inis/" : dir;
205 }
206
207 void InitApi()
208 {
209 // This is a placeholder till there is actually some way to choose Apis. For the moment, just
210 // Modify this so it does whichever one you want to use.
211 #ifdef _WIN32
212 InitDSound();
213 #else
214 #if defined(ZEROSPU2_ALSA)
215 InitAlsa();
216 #elif defined(ZEROSPU2_OSS)
217 InitOSS();
218 #elif defined(ZEROSPU2_PORTAUDIO)
219 InitPortAudio();
220 #endif
221 #endif
222 }
223
224 s32 CALLBACK SPU2init()
225 {
226 LOG_CALLBACK("SPU2init()\n");
227 spu2Log = fopen("logs/spu2.txt", "w");
228 if (spu2Log) setvbuf(spu2Log, NULL, _IONBF, 0);
229
230 SPU2_LOG("Spu2 null version %d,%d\n",SPU2_REVISION,SPU2_BUILD);
231 SPU2_LOG("SPU2init\n");
232
233 #ifdef _WIN32
234 QueryPerformanceFrequency(&g_counterfreq);
235 #endif
236
237 InitApi();
238
239 spu2regs = (s8*)malloc(0x10000);
240 spu2mem = (u16*)malloc(0x200000); // 2Mb
241 memset(spu2regs, 0, 0x10000);
242 memset(spu2mem, 0, 0x200000);
243 if ((spu2mem == NULL) || (spu2regs == NULL))
244 {
245 SysMessage("Error allocating Memory\n");
246 return -1;
247 }
248
249 memset(dwEndChannel2, 0, sizeof(dwEndChannel2));
250 memset(dwNewChannel2, 0, sizeof(dwNewChannel2));
251 memset(iFMod, 0, sizeof(iFMod));
252 memset(s_buffers, 0, sizeof(s_buffers));
253
254 InitADSR();
255
256 memset(voices, 0, sizeof(voices));
257 // last 24 channels have higher mem offset
258 for (s32 i = 0; i < 24; ++i)
259 voices[i+24].memoffset = 0x400;
260
261 // init each channel
262 for (u32 i = 0; i < ArraySize(voices); ++i)
263 {
264 voices[i].init(i);
265 }
266
267 return 0;
268 }
269
270 s32 CALLBACK SPU2open(void *pDsp)
271 {
272 LOG_CALLBACK("SPU2open()\n");
273 #ifdef _WIN32
274 hWMain = pDsp == NULL ? NULL : *(HWND*)pDsp;
275 if (!IsWindow(hWMain))
276 hWMain=GetActiveWindow();
277 #endif
278
279 LoadConfig();
280
281 SPUCycles = SPUWorkerCycles = 0;
282 interrupt = 0;
283 SPUStartCycle[0] = SPUStartCycle[1] = 0;
284 SPUTargetCycle[0] = SPUTargetCycle[1] = 0;
285 s_nDropPacket = 0;
286
287 if ( conf.options & OPTION_TIMESTRETCH )
288 {
289 pSoundTouch = new soundtouch::SoundTouch();
290 pSoundTouch->setSampleRate(SAMPLE_RATE);
291 pSoundTouch->setChannels(2);
292 pSoundTouch->setTempoChange(0);
293
294 pSoundTouch->setSetting(SETTING_USE_QUICKSEEK, 0);
295 pSoundTouch->setSetting(SETTING_USE_AA_FILTER, 1);
296 }
297
298 //conf.Log = 1;
299
300 g_bPlaySound = !(conf.options&OPTION_MUTE);
301
302 if ( g_bPlaySound && SetupSound() != 0 )
303 {
304 SysMessage("ZeroSPU2: Failed to initialize sound");
305 g_bPlaySound = false;
306 }
307
308 if ( g_bPlaySound ) {
309 // initialize the audio buffers
310 for (u32 i = 0; i < ArraySize(s_pAudioBuffers); ++i)
311 {
312 s_pAudioBuffers[i].pbuf = (u8*)_aligned_malloc(4 * NS_TOTAL_SIZE, 16); // 4 bytes for each sample
313 s_pAudioBuffers[i].len = 0;
314 }
315
316 s_nCurBuffer = 0;
317 s_nQueuedBuffers = 0;
318 s_pCurOutput = (s16*)s_pAudioBuffers[0].pbuf;
319 assert( s_pCurOutput != NULL);
320
321 for (s32 i = 0; i < ArraySize(s_nDurations); ++i)
322 {
323 s_nDurations[i] = NSFRAMES*1000;
324 }
325 s_nTotalDuration = ArraySize(s_nDurations)*NSFRAMES*1000;
326 s_nCurDuration = 0;
327
328 // launch the thread
329 s_bThreadExit = false;
330 #ifdef _WIN32
331 s_threadSPU2 = CreateThread(NULL, 0, SPU2ThreadProc, NULL, 0, NULL);
332 if ( s_threadSPU2 == NULL )
333 {
334 return -1;
335 }
336 #else
337 if ( pthread_create(&s_threadSPU2, NULL, SPU2ThreadProc, NULL) != 0 )
338 {
339 SysMessage("ZeroSPU2: Failed to create spu2thread\n");
340 return -1;
341 }
342 #endif
343 }
344
345 g_nSpuInit = 1;
346 return 0;
347 }
348
349 void CALLBACK SPU2close()
350 {
351 LOG_CALLBACK("SPU2close()\n");
352 g_nSpuInit = 0;
353
354 if ( g_bPlaySound && !s_bThreadExit ) {
355 s_bThreadExit = true;
356 printf("ZeroSPU2: Waiting for thread... ");
357 #ifdef _WIN32
358 WaitForSingleObject(s_threadSPU2, INFINITE);
359 CloseHandle(s_threadSPU2); s_threadSPU2 = NULL;
360 #else
361 pthread_join(s_threadSPU2, NULL);
362 #endif
363 printf("done\n");
364 }
365
366 RemoveSound();
367
368 delete g_pWavRecord; g_pWavRecord = NULL;
369 delete pSoundTouch; pSoundTouch = NULL;
370
371 for (u32 i = 0; i < ArraySize(s_pAudioBuffers); ++i)
372 {
373 _aligned_free(s_pAudioBuffers[i].pbuf);
374 }
375 memset(s_pAudioBuffers, 0, sizeof(s_pAudioBuffers));
376 }
377
378 void CALLBACK SPU2shutdown()
379 {
380 LOG_CALLBACK("SPU2shutdown()\n");
381 free(spu2regs); spu2regs = NULL;
382 free(spu2mem); spu2mem = NULL;
383
384 if (spu2Log) fclose(spu2Log);
385 }
386
387 void CALLBACK SPU2async(u32 cycle)
388 {
389 //LOG_CALLBACK("SPU2async()\n");
390 SPUCycles += cycle;
391
392 if (interrupt & (1<<2))
393 {
394 if (SPUCycles - SPUStartCycle[1] >= SPUTargetCycle[1])
395 {
396 interrupt &= ~(1<<2);
397 #ifndef ENABLE_NEW_IOPDMA_SPU2
398 irqCallbackDMA7();
399 #endif
400 }
401
402 }
403
404 if (interrupt & (1<<1))
405 {
406 if (SPUCycles - SPUStartCycle[0] >= SPUTargetCycle[0])
407 {
408 interrupt &= ~(1<<1);
409 #ifndef ENABLE_NEW_IOPDMA_SPU2
410 irqCallbackDMA4();
411 #endif
412 }
413 }
414
415 if ( g_nSpuInit )
416 {
417 while((SPUCycles-SPUWorkerCycles > 0) && (CYCLES_PER_MS < (SPUCycles - SPUWorkerCycles)))
418 {
419 SPU2Worker();
420 SPUWorkerCycles += CYCLES_PER_MS;
421 }
422 }
423 else
424 SPUWorkerCycles = SPUCycles;
425 }
426
427 void InitADSR() // INIT ADSR
428 {
429 u32 r,rs,rd;
430 s32 i;
431 memset(RateTable,0,sizeof(u32)*160); // build the rate table according to Neill's rules (see at bottom of file)
432
433 r=3;rs=1;rd=0;
434
435 for (i=32;i<160;i++) // we start at pos 32 with the real values... everything before is 0
436 {
437 if (r<0x3FFFFFFF)
438 {
439 r+=rs;
440 rd++;
441
442 if (rd==5)
443 {
444 rd=1;
445 rs*=2;
446 }
447 }
448
449 if (r>0x3FFFFFFF) r=0x3FFFFFFF;
450 RateTable[i]=r;
451 }
452 }
453
454 s32 MixADSR(VOICE_PROCESSED* pvoice) // MIX ADSR
455 {
456 u32 rateadd[8] = { 0, 4, 6, 8, 9, 10, 11, 12 };
457
458 if (pvoice->bStop) // should be stopped:
459 {
460 if (pvoice->ADSRX.ReleaseModeExp) // do release
461 {
462 s32 temp = ((pvoice->ADSRX.EnvelopeVol>>28)&0x7);
463 pvoice->ADSRX.EnvelopeVol-=RateTable[(4*(pvoice->ADSRX.ReleaseRate^0x1F)) - 0x18 + rateadd[temp] + 32];
464 }
465 else
466 {
467 pvoice->ADSRX.EnvelopeVol-=RateTable[(4*(pvoice->ADSRX.ReleaseRate^0x1F)) - 0x0C + 32];
468 }
469
470 // bIgnoreLoop sets EnvelopeVol to 0 anyways, so we can use one if statement rather then two.
471 if ((pvoice->ADSRX.EnvelopeVol<0) || (pvoice->bIgnoreLoop == 0))
472 {
473 pvoice->ADSRX.EnvelopeVol=0;
474 pvoice->bOn=false;
475 pvoice->pStart= (u8*)(spu2mem+pvoice->iStartAddr);
476 pvoice->pLoop= (u8*)(spu2mem+pvoice->iStartAddr);
477 pvoice->pCurr= (u8*)(spu2mem+pvoice->iStartAddr);
478 pvoice->bStop = true;
479 pvoice->bIgnoreLoop = false;
480 //pvoice->bReverb=0;
481 //pvoice->bNoise=0;
482 }
483
484 pvoice->ADSRX.lVolume=pvoice->ADSRX.EnvelopeVol>>21;
485
486 return pvoice->ADSRX.lVolume;
487 }
488 else // not stopped yet?
489 {
490 s32 temp = ((pvoice->ADSRX.EnvelopeVol>>28)&0x7);
491
492 switch (pvoice->ADSRX.State)
493 {
494 case 0: // -> attack
495 if (pvoice->ADSRX.AttackModeExp)
496 {
497 if (pvoice->ADSRX.EnvelopeVol<0x60000000)
498 pvoice->ADSRX.EnvelopeVol += RateTable[(pvoice->ADSRX.AttackRate^0x7F) - 0x10 + 32];
499 else
500 pvoice->ADSRX.EnvelopeVol += RateTable[(pvoice->ADSRX.AttackRate^0x7F) - 0x18 + 32];
501 }
502 else
503 {
504 pvoice->ADSRX.EnvelopeVol += RateTable[(pvoice->ADSRX.AttackRate^0x7F) - 0x10 + 32];
505 }
506
507 if (pvoice->ADSRX.EnvelopeVol<0)
508 {
509 pvoice->ADSRX.EnvelopeVol=0x7FFFFFFF;
510 pvoice->ADSRX.State=1;
511 }
512 break;
513
514 case 1: // -> decay
515 pvoice->ADSRX.EnvelopeVol-=RateTable[(4*(pvoice->ADSRX.DecayRate^0x1F)) - 0x18+ rateadd[temp] + 32];
516
517 if (pvoice->ADSRX.EnvelopeVol<0) pvoice->ADSRX.EnvelopeVol=0;
518
519 if (((pvoice->ADSRX.EnvelopeVol>>27)&0xF) <= pvoice->ADSRX.SustainLevel)
520 pvoice->ADSRX.State=2;
521 break;
522
523 case 2: // -> sustain
524 if (pvoice->ADSRX.SustainIncrease)
525 {
526 if ((pvoice->ADSRX.SustainModeExp) && (pvoice->ADSRX.EnvelopeVol>=0x60000000))
527 pvoice->ADSRX.EnvelopeVol+=RateTable[(pvoice->ADSRX.SustainRate^0x7F) - 0x18 + 32];
528 else
529 pvoice->ADSRX.EnvelopeVol+=RateTable[(pvoice->ADSRX.SustainRate^0x7F) - 0x10 + 32];
530
531 if (pvoice->ADSRX.EnvelopeVol<0) pvoice->ADSRX.EnvelopeVol=0x7FFFFFFF;
532 }
533 else
534 {
535 if (pvoice->ADSRX.SustainModeExp)
536 pvoice->ADSRX.EnvelopeVol-=RateTable[((pvoice->ADSRX.SustainRate^0x7F)) - 0x1B +rateadd[temp] + 32];
537 else
538 pvoice->ADSRX.EnvelopeVol-=RateTable[((pvoice->ADSRX.SustainRate^0x7F)) - 0x0F + 32];
539
540 if (pvoice->ADSRX.EnvelopeVol<0) pvoice->ADSRX.EnvelopeVol=0;
541 }
542 break;
543
544 default:
545 // This should never happen.
546 return 0;
547 }
548
549 pvoice->ADSRX.lVolume=pvoice->ADSRX.EnvelopeVol>>21;
550 return pvoice->ADSRX.lVolume;
551 }
552
553 return 0;
554 }
555
556 void MixChannels(s32 channel)
557 {
558 // mix all channels
559 s32 left_vol, right_vol;
560 ADMA *Adma;
561
562 if (channel == 0)
563 {
564 Adma = &adma[0];
565 left_vol = REG_C0_BVOLL;
566 right_vol = REG_C0_BVOLR;
567 }
568 else
569 {
570 Adma = &adma[1];
571 left_vol = REG_C1_BVOLL;
572 right_vol = REG_C1_BVOLR;
573 }
574
575 if ((spu2mmix(channel) & 0xF0) && spu2admas(channel))
576 {
577 for (u32 ns = 0; ns < NSSIZE; ns++)
578 {
579 u16 left_reg = 0x2000 + c_offset(channel) + Adma->Index;
580 u16 right_reg = left_reg + 0x200;
581
582 if ((spu2mmix(channel) & 0x80))
583 s_buffers[ns][0] += (((s16*)spu2mem)[left_reg]*(s32)spu2Ru16(left_vol))>>16;
584 if ((spu2mmix(channel) & 0x40))
585 s_buffers[ns][1] += (((s16*)spu2mem)[right_reg]*(s32)spu2Ru16(right_vol))>>16;
586
587 Adma->Index +=1;
588 MemAddr[channel] += 4;
589
590 if (Adma->Index == 128 || Adma->Index == 384)
591 {
592 if (ADMASWrite(channel))
593 {
594 if (interrupt & (0x2 * (channel + 1)))
595 {
596 interrupt &= ~(0x2 * (channel + 1));
597 WARN_LOG("Stopping double interrupt DMA7\n");
598 }
599 #ifndef ENABLE_NEW_IOPDMA_SPU2
600 if (channel == 0)
601 irqCallbackDMA4();
602 else
603 irqCallbackDMA7();
604 #endif
605
606 }
607 if (channel == 1) Adma->Enabled = 2;
608 }
609
610 if (Adma->Index == 512)
611 {
612 if ( Adma->Enabled == 2 ) Adma->Enabled = 0;
613 Adma->Index = 0;
614 }
615 }
616 }
617 }
618
619 // resamples pStereoSamples
620 void ResampleLinear(s16* pStereoSamples, s32 oldsamples, s16* pNewSamples, s32 newsamples)
621 {
622 for (s32 i = 0; i < newsamples; ++i)
623 {
624 s32 io = i * oldsamples;
625 s32 old = io / newsamples;
626 s32 rem = io - old * newsamples;
627
628 old *= 2;
629 s32 newsampL = pStereoSamples[old] * (newsamples - rem) + pStereoSamples[old+2] * rem;
630 s32 newsampR = pStereoSamples[old+1] * (newsamples - rem) + pStereoSamples[old+3] * rem;
631 pNewSamples[2 * i] = newsampL / newsamples;
632 pNewSamples[2 * i + 1] = newsampR / newsamples;
633 }
634 }
635
636 static __aligned16 s16 s_ThreadBuffer[NS_TOTAL_SIZE * 2 * 5];
637
638 // SoundTouch's INTEGER system is broken these days, so we'll need this to do float conversions...
639 static __aligned16 float s_floatBuffer[NS_TOTAL_SIZE * 2 * 5];
640
641 static __forceinline u32 GetNewSamples(s32 nReadBuf)
642 {
643 u32 NewSamples = s_pAudioBuffers[nReadBuf].avgtime / 1000;
644 s32 bytesbuf = SoundGetBytesBuffered() * 10;
645
646 if (bytesbuf < MaxBuffer)
647 {
648 NewSamples += 1;
649 }
650 else if (bytesbuf > MaxBuffer * 5)
651 {
652 // check the current timestamp, if too far apart, speed up audio
653 //WARN_LOG("making faster %d\n", timeGetTime() - s_pAudioBuffers[nReadBuf].timestamp);
654 NewSamples = NewSamples - ((bytesbuf - 400000) / 100000);
655 }
656
657 if (s_nDropPacket > 0)
658 {
659 s_nDropPacket--;
660 NewSamples -= 1;
661 }
662
663 return min(NewSamples * NSSIZE, NS_TOTAL_SIZE * 3);
664 }
665
666 static __forceinline void ExtractSamples()
667 {
668 // extract 2*NSFRAMES ms at a time
669 s32 nOutSamples;
670
671 do
672 {
673 nOutSamples = pSoundTouch->receiveSamples(s_floatBuffer, NS_TOTAL_SIZE * 5);
674 if ( nOutSamples > 0 )
675 {
676 for( s32 sx=0; sx<nOutSamples*2; sx++ )
677 s_ThreadBuffer[sx] = (s16)(s_floatBuffer[sx]*65536.0f);
678
679 SoundFeedVoiceData((u8*)s_ThreadBuffer, nOutSamples * 4);
680 }
681 } while (nOutSamples != 0);
682 }
683
684 // communicates with the audio hardware
685 #ifdef _WIN32
686 DWORD WINAPI SPU2ThreadProc(LPVOID)
687 #else
688 void* SPU2ThreadProc(void* lpParam)
689 #endif
690 {
691 s32 nReadBuf = 0;
692
693 while (!s_bThreadExit)
694 {
695
696 if (!(conf.options&OPTION_REALTIME))
697 {
698 while(s_nQueuedBuffers< 3 && !s_bThreadExit)
699 {
700 //Sleeping
701 Sleep(1);
702 if (s_bThreadExit) return NULL;
703 }
704
705 while( SoundGetBytesBuffered() > 72000 )
706 {
707 //Bytes buffered
708 Sleep(1);
709
710 if (s_bThreadExit) return NULL;
711 }
712 }
713 else
714 {
715 while(s_nQueuedBuffers< 1 && !s_bThreadExit)
716 {
717 //Sleeping
718 Sleep(1);
719 }
720 }
721
722 if (conf.options & OPTION_TIMESTRETCH)
723 {
724 u32 NewSamples = GetNewSamples(nReadBuf);
725 s32 OldSamples = s_pAudioBuffers[nReadBuf].len / 4;
726
727 if ((nReadBuf & 3) == 0) // wow, this if statement makes the whole difference
728 {
729 //SPU2_LOG("OldSamples = %d; NewSamples = %d/n", OldSamples, NewSamples);
730 float percent = ((float)OldSamples/(float)NewSamples - 1.0f) * 100.0f;
731 pSoundTouch->setTempoChange(percent);
732 }
733
734 for( s32 sx = 0; sx < OldSamples * 2; sx++ )
735 s_floatBuffer[sx] = ((s16*)s_pAudioBuffers[nReadBuf].pbuf)[sx]/65536.0f;
736
737 pSoundTouch->putSamples(s_floatBuffer, OldSamples);
738 ExtractSamples();
739 }
740 else
741 {
742 SoundFeedVoiceData(s_pAudioBuffers[nReadBuf].pbuf, s_pAudioBuffers[nReadBuf].len);
743 }
744
745 // don't go to the next buffer unless there is more data buffered
746 nReadBuf = (nReadBuf+1)%ArraySize(s_pAudioBuffers);
747 InterlockedExchangeAdd((long*)&s_nQueuedBuffers, -1);
748
749 if ( s_bThreadExit ) break;
750 }
751
752 return NULL;
753 }
754
755 // turn channels on
756 void SoundOn(s32 start,s32 end,u16 val) // SOUND ON PSX COMAND
757 {
758 for (s32 ch=start;ch<end;ch++,val>>=1) // loop channels
759 {
760 if ((val&1) && voices[ch].pStart) // mmm... start has to be set before key on !?!
761 {
762 voices[ch].bNew=true;
763 voices[ch].bIgnoreLoop = false;
764 dwNewChannel2[ch/24]|=(1<<(ch%24)); // clear end channel bit
765 }
766 }
767 }
768
769 // turn channels off
770 void SoundOff(s32 start,s32 end,u16 val) // SOUND OFF PSX COMMAND
771 {
772 for (s32 ch=start;ch<end;ch++,val>>=1) // loop channels
773 {
774 if (val&1) voices[ch].bStop=true; // && s_chan[i].bOn) mmm...
775 }
776 }
777
778 void FModOn(s32 start,s32 end,u16 val) // FMOD ON PSX COMMAND
779 {
780 s32 ch;
781
782 for (ch=start;ch<end;ch++,val>>=1) // loop channels
783 {
784 if (val&1)
785 { // -> fmod on/off
786 if (ch>0)
787 {
788 voices[ch].bFMod=1; // --> sound channel
789 voices[ch-1].bFMod=2; // --> freq channel
790 }
791 }
792 else
793 voices[ch].bFMod=0; // --> turn off fmod
794 }
795 }
796
797 void VolumeOn(s32 start,s32 end,u16 val,s32 iRight) // VOLUME ON PSX COMMAND
798 {
799 s32 ch;
800
801 for (ch=start;ch<end;ch++,val>>=1) // loop channels
802 {
803 if (val&1)
804 { // -> reverb on/off
805 if (iRight)
806 voices[ch].bVolumeR = true;
807 else
808 voices[ch].bVolumeL = true;
809 }
810 else
811 {
812 if (iRight)
813 voices[ch].bVolumeR = false;
814 else
815 voices[ch].bVolumeL = false;
816 }
817 }
818 }
819
820 void CALLBACK SPU2write(u32 mem, u16 value)
821 {
822 LOG_CALLBACK("SPU2write()\n");
823 u32 spuaddr;
824 SPU2_LOG("SPU2 write mem %x value %x\n", mem, value);
825
826 assert(C0_SPUADDR() < 0x100000);
827 assert(C1_SPUADDR() < 0x100000);
828
829 spu2Ru16(mem) = value;
830 u32 r = mem & 0xffff;
831
832 // channel info
833 if ((r<0x0180) || (r>=0x0400 && r<0x0580)) // u32s are always >= 0.
834 {
835 s32 ch=0;
836 if (r >= 0x400)
837 ch = ((r - 0x400) >> 4) + 24;
838 else
839 ch = (r >> 4);
840
841 VOICE_PROCESSED* pvoice = &voices[ch];
842
843 switch(r & 0x0f)
844 {
845 case 0:
846 case 2:
847 pvoice->SetVolume(mem & 0x2);
848 break;
849 case 4:
850 {
851 s32 NP;
852 if (value> 0x3fff)
853 NP=0x3fff; // get pitch val
854 else
855 NP=value;
856
857 pvoice->pvoice->pitch = NP;
858
859 NP = (SAMPLE_RATE * NP) / 4096L; // calc frequency
860 if (NP<1) NP = 1; // some security
861 pvoice->iActFreq = NP; // store frequency
862 break;
863 }
864 case 6:
865 {
866 pvoice->ADSRX.AttackModeExp=(value&0x8000)?1:0;
867 pvoice->ADSRX.AttackRate = ((value>>8) & 0x007f);
868 pvoice->ADSRX.DecayRate = (((value>>4) & 0x000f));
869 pvoice->ADSRX.SustainLevel = (value & 0x000f);
870 break;
871 }
872 case 8:
873 pvoice->ADSRX.SustainModeExp = (value&0x8000)?1:0;
874 pvoice->ADSRX.SustainIncrease= (value&0x4000)?0:1;
875 pvoice->ADSRX.SustainRate = ((value>>6) & 0x007f);
876 pvoice->ADSRX.ReleaseModeExp = (value&0x0020)?1:0;
877 pvoice->ADSRX.ReleaseRate = ((value & 0x001f));
878 break;
879 }
880
881 return;
882 }
883
884 // more channel info
885 if ((r>=0x01c0 && r<0x02E0)||(r>=0x05c0 && r<0x06E0))
886 {
887 s32 ch=0;
888 u32 rx=r;
889 if (rx>=0x400)
890 {
891 ch=24;
892 rx-=0x400;
893 }
894
895 ch += ((rx-0x1c0)/12);
896 rx -= (ch%24)*12;
897 VOICE_PROCESSED* pvoice = &voices[ch];
898
899 switch(rx)
900 {
901 case REG_VA_SSA:
902 pvoice->iStartAddr=(((u32)value&0x3f)<<16)|(pvoice->iStartAddr&0xFFFF);
903 pvoice->pStart=(u8*)(spu2mem+pvoice->iStartAddr);
904 break;
905 case REG_VA_SSA + 2:
906 pvoice->iStartAddr=(pvoice->iStartAddr & 0x3f0000) | (value & 0xFFFF);
907 pvoice->pStart=(u8*)(spu2mem+pvoice->iStartAddr);
908 break;
909 case REG_VA_LSAX:
910 pvoice->iLoopAddr =(((u32)value&0x3f)<<16)|(pvoice->iLoopAddr&0xFFFF);
911 pvoice->pLoop=(u8*)(spu2mem+pvoice->iLoopAddr);
912 pvoice->bIgnoreLoop=pvoice->iLoopAddr>0;
913 break;
914 case REG_VA_LSAX + 2:
915 pvoice->iLoopAddr=(pvoice->iLoopAddr& 0x3f0000) | (value & 0xFFFF);
916 pvoice->pLoop=(u8*)(spu2mem+pvoice->iLoopAddr);
917 pvoice->bIgnoreLoop=pvoice->iLoopAddr>0;
918 break;
919 case REG_VA_NAX:
920 // unused... check if it gets written as well
921 pvoice->iNextAddr=(((u32)value&0x3f)<<16)|(pvoice->iNextAddr&0xFFFF);
922 break;
923 case REG_VA_NAX + 2:
924 // unused... check if it gets written as well
925 pvoice->iNextAddr=(pvoice->iNextAddr & 0x3f0000) | (value & 0xFFFF);
926 break;
927 }
928
929 return;
930 }
931
932 // process non-channel data
933 switch(mem & 0xffff)
934 {
935 case REG_C0_SPUDATA:
936 spuaddr = C0_SPUADDR();
937 spu2mem[spuaddr] = value;
938 spuaddr++;
939
940 if (spu2attr0.irq && (C0_IRQA() == spuaddr))
941 {
942 IRQINFO |= 4;
943 SPU2_LOG("SPU2write:C0_CPUDATA interrupt\n");
944 irqCallbackSPU2();
945 }
946
947 if (spuaddr>0xFFFFE) spuaddr = 0x2800;
948
949 C0_SPUADDR_SET(spuaddr);
950 spu2stat_clear_80(0);
951 spu2attr1.irq = 0;
952 break;
953
954 case REG_C1_SPUDATA:
955 spuaddr = C1_SPUADDR();
956 spu2mem[spuaddr] = value;
957 spuaddr++;
958
959 if (spu2attr1.irq && (C1_IRQA() == spuaddr))
960 {
961 IRQINFO |= 8;
962 SPU2_LOG("SPU2write:C1_CPUDATA interrupt\n");
963 irqCallbackSPU2();
964 }
965
966 if (spuaddr>0xFFFFE) spuaddr = 0x2800;
967
968 C1_SPUADDR_SET(spuaddr);
969 spu2stat_clear_80(1);
970 spu2attr0.irq = 0;
971 break;
972
973 case REG_C0_IRQA_HI:
974 case REG_C0_IRQA_LO:
975 pSpuIrq[0] = spu2mem + C0_IRQA();
976 break;
977
978 case REG_C1_IRQA_HI:
979 case REG_C1_IRQA_LO:
980 pSpuIrq[1] = spu2mem + C1_IRQA();
981 break;
982
983 case REG_C0_SPUADDR_HI:
984 case REG_C1_SPUADDR_HI:
985 spu2Ru16(mem) = value&0xf;
986 break;
987
988 case REG_C0_CTRL:
989 spu2Ru16(mem) = value;
990 // clear interrupt
991 if (!(value & 0x40)) IRQINFO &= ~0x4;
992 break;
993
994 case REG_C1_CTRL:
995 spu2Ru16(mem) = value;
996 // clear interrupt
997 if (!(value & 0x40)) IRQINFO &= ~0x8;
998 break;
999
1000 // Could probably simplify
1001 case REG_C0_SPUON1: SoundOn(0,16,value); break;
1002 case REG_C0_SPUON2: SoundOn(16,24,value); break;
1003 case REG_C1_SPUON1: SoundOn(24,40,value); break;
1004 case REG_C1_SPUON2: SoundOn(40,48,value); break;
1005 case REG_C0_SPUOFF1: SoundOff(0,16,value); break;
1006 case REG_C0_SPUOFF2: SoundOff(16,24,value); break;
1007 case REG_C1_SPUOFF1: SoundOff(24,40,value); break;
1008 case REG_C1_SPUOFF2: SoundOff(40,48,value); break;
1009
1010 // According to manual all bits are cleared by writing an arbitary value
1011 case REG_C0_END1: dwEndChannel2[0] &= 0x00ff0000; break;
1012 case REG_C0_END2: dwEndChannel2[0] &= 0x0000ffff; break;
1013 case REG_C1_END1: dwEndChannel2[1] &= 0x00ff0000; break;
1014 case REG_C1_END2: dwEndChannel2[1] &= 0x0000ffff; break;
1015 case REG_C0_FMOD1: FModOn(0,16,value); break;
1016 case REG_C0_FMOD2: FModOn(16,24,value); break;
1017 case REG_C1_FMOD1: FModOn(24,40,value); break;
1018 case REG_C1_FMOD2: FModOn(40,48,value); break;
1019 case REG_C0_VMIXL1: VolumeOn(0,16,value,0); break;
1020 case REG_C0_VMIXL2: VolumeOn(16,24,value,0); break;
1021 case REG_C1_VMIXL1: VolumeOn(24,40,value,0); break;
1022 case REG_C1_VMIXL2: VolumeOn(40,48,value,0); break;
1023 case REG_C0_VMIXR1: VolumeOn(0,16,value,1); break;
1024 case REG_C0_VMIXR2: VolumeOn(16,24,value,1); break;
1025 case REG_C1_VMIXR1: VolumeOn(24,40,value,1); break;
1026 case REG_C1_VMIXR2: VolumeOn(40,48,value,1); break;
1027 }
1028
1029 assert( C0_SPUADDR() < 0x100000);
1030 assert( C1_SPUADDR() < 0x100000);
1031 }
1032
1033 u16 CALLBACK SPU2read(u32 mem)
1034 {
1035 LOG_CALLBACK("SPU2read()\n");
1036 u32 spuaddr;
1037 u16 ret = 0;
1038 u32 r = mem & 0xffff; // register
1039
1040 // channel info
1041 // if the register is any of the regs before core 0, or is somewhere between core 0 and 1...
1042 if ((r < 0x0180) || (r >= 0x0400 && r < 0x0580)) // u32s are always >= 0.
1043 {
1044 s32 ch = 0;
1045
1046 if (r >= 0x400)
1047 ch=((r - 0x400) >> 4) + 24;
1048 else
1049 ch = (r >> 4);
1050
1051 VOICE_PROCESSED* pvoice = &voices[ch];
1052
1053 if ((r&0x0f) == 10) return (u16)(pvoice->ADSRX.EnvelopeVol >> 16);
1054 }
1055
1056
1057 if ((r>=REG_VA_SSA && r<REG_A_ESA) || (r>=0x05c0 && r<0x06E0)) // some channel info?
1058 {
1059 s32 ch = 0;
1060 u32 rx = r;
1061
1062 if (rx >= 0x400)
1063 {
1064 ch = 24;
1065 rx -= 0x400;
1066 }
1067
1068 ch += ((rx - 0x1c0) / 12);
1069 rx -= (ch % 24) * 12;
1070 VOICE_PROCESSED* pvoice = &voices[ch];
1071
1072 switch(rx)
1073 {
1074 case REG_VA_SSA:
1075 ret = ((((uptr)pvoice->pStart-(uptr)spu2mem)>>17)&0x3F);
1076 break;
1077 case REG_VA_SSA + 2:
1078 ret = ((((uptr)pvoice->pStart-(uptr)spu2mem)>>1)&0xFFFF);
1079 break;
1080 case REG_VA_LSAX:
1081 ret = ((((uptr)pvoice->pLoop-(uptr)spu2mem)>>17)&0x3F);
1082 break;
1083 case REG_VA_LSAX + 2:
1084 ret = ((((uptr)pvoice->pLoop-(uptr)spu2mem)>>1)&0xFFFF);
1085 break;
1086 case REG_VA_NAX:
1087 ret = ((((uptr)pvoice->pCurr-(uptr)spu2mem)>>17)&0x3F);
1088 break;
1089 case REG_VA_NAX + 2:
1090 ret = ((((uptr)pvoice->pCurr-(uptr)spu2mem)>>1)&0xFFFF);
1091 break;
1092 }
1093
1094 SPU2_LOG("SPU2 channel read mem %x: %x\n", mem, ret);
1095 return ret;
1096 }
1097
1098 switch(mem & 0xffff)
1099 {
1100 case REG_C0_SPUDATA:
1101 spuaddr = C0_SPUADDR();
1102 ret =spu2mem[spuaddr];
1103 spuaddr++;
1104 if (spuaddr > 0xfffff) spuaddr=0;
1105 C0_SPUADDR_SET(spuaddr);
1106 break;
1107
1108 case REG_C1_SPUDATA:
1109 spuaddr = C1_SPUADDR();
1110 ret = spu2mem[spuaddr];
1111 spuaddr++;
1112 if (spuaddr > 0xfffff) spuaddr=0;
1113 C1_SPUADDR_SET(spuaddr);
1114 break;
1115
1116 case REG_C0_END1: ret = (dwEndChannel2[0]&0xffff); break;
1117 case REG_C1_END1: ret = (dwEndChannel2[1]&0xffff); break;
1118 case REG_C0_END2: ret = (dwEndChannel2[0]>>16); break;
1119 case REG_C1_END2: ret = (dwEndChannel2[1]>>16); break;
1120
1121 case REG_IRQINFO:
1122 ret = IRQINFO;
1123 break;
1124
1125 default:
1126 ret = spu2Ru16(mem);
1127 }
1128
1129 SPU2_LOG("SPU2 read mem %x: %x\n", mem, ret);
1130
1131 return ret;
1132 }
1133
1134 void CALLBACK SPU2WriteMemAddr(int core, u32 value)
1135 {
1136 LOG_CALLBACK("SPU2WriteMemAddr(%d, %d)\n", core, value);
1137 MemAddr[core] = g_pDMABaseAddr + value;
1138 }
1139
1140 u32 CALLBACK SPU2ReadMemAddr(int core)
1141 {
1142 LOG_CALLBACK("SPU2ReadMemAddr(%d)\n", core);
1143 return MemAddr[core] - g_pDMABaseAddr;
1144 }
1145
1146 void CALLBACK SPU2setDMABaseAddr(uptr baseaddr)
1147 {
1148 LOG_CALLBACK("SPU2setDMABaseAddr()\n");
1149 g_pDMABaseAddr = baseaddr;
1150 }
1151
1152 #ifdef ENABLE_NEW_IOPDMA_SPU2
1153 void CALLBACK SPU2irqCallback(void (*SPU2callback)())
1154 {
1155 LOG_CALLBACK("SPU2irqCallback()\n");
1156 irqCallbackSPU2 = SPU2callback;
1157 }
1158 #else
1159 void CALLBACK SPU2irqCallback(void (*SPU2callback)(),void (*DMA4callback)(),void (*DMA7callback)())
1160 {
1161 LOG_CALLBACK("SPU2irqCallback()\n");
1162 irqCallbackSPU2 = SPU2callback;
1163 irqCallbackDMA4 = DMA4callback;
1164 irqCallbackDMA7 = DMA7callback;
1165 }
1166 #endif
1167
1168 s32 CALLBACK SPU2test()
1169 {
1170 LOG_CALLBACK("SPU2test()\n");
1171 return 0;
1172 }
1173
1174 int CALLBACK SPU2setupRecording(int start, void* pData)
1175 {
1176 LOG_CALLBACK("SPU2setupRecording()\n");
1177 if ( start )
1178 {
1179 conf.options |= OPTION_RECORDING;
1180 WARN_LOG("ZeroSPU2: started recording at %s\n", RECORD_FILENAME);
1181 }
1182 else
1183 {
1184 conf.options &= ~OPTION_RECORDING;
1185 WARN_LOG("ZeroSPU2: stopped recording\n");
1186 }
1187
1188 return 1;
1189 }
1190
1191 void save_data(freezeData *data)
1192 {
1193 SPU2freezeData *spud;
1194
1195 spud = (SPU2freezeData*)data->data;
1196 spud->version = 0x70000002;
1197
1198 memcpy(spud->spu2regs, spu2regs, 0x10000);
1199 memcpy(spud->spu2mem, spu2mem, 0x200000);
1200
1201 spud->nSpuIrq[0] = (s32)(pSpuIrq[0] - spu2mem);
1202 spud->nSpuIrq[1] = (s32)(pSpuIrq[1] - spu2mem);
1203
1204 memcpy(spud->dwNewChannel2, dwNewChannel2, sizeof(dwNewChannel2));
1205 memcpy(spud->dwEndChannel2, dwEndChannel2, sizeof(dwEndChannel2));
1206
1207 spud->dwNoiseVal = dwNoiseVal;
1208 spud->interrupt = interrupt;
1209
1210 memcpy(spud->iFMod, iFMod, sizeof(iFMod));
1211 memcpy(spud->MemAddr, MemAddr, sizeof(MemAddr));
1212
1213 spud->adma[0] = adma[0];
1214 spud->adma[1] = adma[1];
1215 spud->AdmaMemAddr[0] = (u32)((uptr)adma[0].MemAddr - g_pDMABaseAddr);
1216 spud->AdmaMemAddr[1] = (u32)((uptr)adma[1].MemAddr - g_pDMABaseAddr);
1217
1218 spud->SPUCycles = SPUCycles;
1219 spud->SPUWorkerCycles = SPUWorkerCycles;
1220
1221 memcpy(spud->SPUStartCycle, SPUStartCycle, sizeof(SPUStartCycle));
1222 memcpy(spud->SPUTargetCycle, SPUTargetCycle, sizeof(SPUTargetCycle));
1223
1224 for (u32 i = 0; i < ArraySize(s_nDurations); ++i)
1225 {
1226 s_nDurations[i] = NSFRAMES*1000;
1227 }
1228
1229 s_nTotalDuration = ArraySize(s_nDurations)*NSFRAMES*1000;
1230 s_nCurDuration = 0;
1231
1232 spud->voicesize = SPU_VOICE_STATE_SIZE;
1233 for (u32 i = 0; i < ArraySize(voices); ++i)
1234 {
1235 memcpy(&spud->voices[i], &voices[i], SPU_VOICE_STATE_SIZE);
1236 spud->voices[i].pStart = (u8*)((uptr)voices[i].pStart-(uptr)spu2mem);
1237 spud->voices[i].pLoop = (u8*)((uptr)voices[i].pLoop-(uptr)spu2mem);
1238 spud->voices[i].pCurr = (u8*)((uptr)voices[i].pCurr-(uptr)spu2mem);
1239 }
1240
1241 g_startcount=0xffffffff;
1242 s_GlobalTimeStamp = 0;
1243 s_nDropPacket = 0;
1244 }
1245
1246 void load_data(freezeData *data)
1247 {
1248 SPU2freezeData *spud;
1249
1250 spud = (SPU2freezeData*)data->data;
1251
1252 if (spud->version != 0x70000002)
1253 {
1254 ERROR_LOG("zerospu2: Sound data either corrupted or from another plugin. Ignoring.\n");
1255 return;
1256 }
1257
1258 memcpy(spu2regs, spud->spu2regs, 0x10000);
1259 memcpy(spu2mem, spud->spu2mem, 0x200000);
1260
1261 pSpuIrq[0] = spu2mem + spud->nSpuIrq[0];
1262 pSpuIrq[1] = spu2mem + spud->nSpuIrq[1];
1263
1264 memcpy(dwNewChannel2, spud->dwNewChannel2, sizeof(dwNewChannel2));
1265 memcpy(dwEndChannel2, spud->dwEndChannel2, sizeof(dwEndChannel2));
1266
1267 dwNoiseVal = spud->dwNoiseVal;
1268 interrupt = spud->interrupt;
1269
1270 memcpy(iFMod, spud->iFMod, sizeof(iFMod));
1271 memcpy(MemAddr, spud->MemAddr, sizeof(MemAddr));
1272
1273 adma[0] = spud->adma[0];
1274 adma[1] = spud->adma[1];
1275 adma[0].MemAddr = (u16*)(g_pDMABaseAddr+spud->AdmaMemAddr[0]);
1276 adma[1].MemAddr = (u16*)(g_pDMABaseAddr+spud->AdmaMemAddr[1]);
1277
1278 SPUCycles = spud->SPUCycles;
1279 SPUWorkerCycles = spud->SPUWorkerCycles;
1280
1281 memcpy(SPUStartCycle, spud->SPUStartCycle, sizeof(SPUStartCycle));
1282 memcpy(SPUTargetCycle, spud->SPUTargetCycle, sizeof(SPUTargetCycle));
1283
1284 for (u32 i = 0; i < ArraySize(voices); ++i)
1285 {
1286 memcpy(&voices[i], &spud->voices[i], min((s32)SPU_VOICE_STATE_SIZE, spud->voicesize));
1287 voices[i].pStart = (u8*)((uptr)spud->voices[i].pStart+(uptr)spu2mem);
1288 voices[i].pLoop = (u8*)((uptr)spud->voices[i].pLoop+(uptr)spu2mem);
1289 voices[i].pCurr = (u8*)((uptr)spud->voices[i].pCurr+(uptr)spu2mem);
1290 }
1291
1292 s_GlobalTimeStamp = 0;
1293 g_startcount = 0xffffffff;
1294
1295 for (u32 i = 0; i < ArraySize(s_nDurations); ++i)
1296 {
1297 s_nDurations[i] = NSFRAMES*1000;
1298 }
1299
1300 s_nTotalDuration = ArraySize(s_nDurations)*NSFRAMES*1000;
1301 s_nCurDuration = 0;
1302 s_nQueuedBuffers = 0;
1303 s_nDropPacket = 0;
1304 }
1305
1306 s32 CALLBACK SPU2freeze(int mode, freezeData *data)
1307 {
1308 LOG_CALLBACK("SPU2freeze()\n");
1309 assert( g_pDMABaseAddr != 0 );
1310
1311 switch (mode)
1312 {
1313 case FREEZE_LOAD:
1314 load_data(data);
1315 break;
1316 case FREEZE_SAVE:
1317 save_data(data);
1318 break;
1319 case FREEZE_SIZE:
1320 data->size = sizeof(SPU2freezeData);
1321 break;
1322 default:
1323 break;
1324 }
1325
1326 return 0;
1327 }

  ViewVC Help
Powered by ViewVC 1.1.22