Mercurial > audlegacy-plugins
comparison src/psf2/peops/spu.c @ 2737:62cc6d667119
Import a bunch of stuff for new psf2 plugin.
author | William Pitcock <nenolod@atheme.org> |
---|---|
date | Mon, 30 Jun 2008 20:20:53 -0500 |
parents | |
children | f1482af6384c |
comparison
equal
deleted
inserted
replaced
2731:324f950774cb | 2737:62cc6d667119 |
---|---|
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 * * | |
11 * This program is free software; you can redistribute it and/or modify * | |
12 * it under the terms of the GNU General Public License as published by * | |
13 * the Free Software Foundation; either version 2 of the License, or * | |
14 * (at your option) any later version. See also the license.txt file for * | |
15 * additional informations. * | |
16 * * | |
17 ***************************************************************************/ | |
18 | |
19 //*************************************************************************// | |
20 // History of changes: | |
21 // | |
22 // 2003/03/01 - linuzappz | |
23 // - libraryName changes using ALSA | |
24 // | |
25 // 2003/02/28 - Pete | |
26 // - added option for type of interpolation | |
27 // - adjusted spu irqs again (Thousant Arms, Valkyrie Profile) | |
28 // - added MONO support for MSWindows DirectSound | |
29 // | |
30 // 2003/02/20 - kode54 | |
31 // - amended interpolation code, goto GOON could skip initialization of gpos and cause segfault | |
32 // | |
33 // 2003/02/19 - kode54 | |
34 // - moved SPU IRQ handler and changed sample flag processing | |
35 // | |
36 // 2003/02/18 - kode54 | |
37 // - moved ADSR calculation outside of the sample decode loop, somehow I doubt that | |
38 // ADSR timing is relative to the frequency at which a sample is played... I guess | |
39 // this remains to be seen, and I don't know whether ADSR is applied to noise channels... | |
40 // | |
41 // 2003/02/09 - kode54 | |
42 // - one-shot samples now process the end block before stopping | |
43 // - in light of removing fmod hack, now processing ADSR on frequency channel as well | |
44 // | |
45 // 2003/02/08 - kode54 | |
46 // - replaced easy interpolation with gaussian | |
47 // - removed fmod averaging hack | |
48 // - changed .sinc to be updated from .iRawPitch, no idea why it wasn't done this way already (<- Pete: because I sometimes fail to see the obvious, haharhar :) | |
49 // | |
50 // 2003/02/08 - linuzappz | |
51 // - small bugfix for one usleep that was 1 instead of 1000 | |
52 // - added iDisStereo for no stereo (Linux) | |
53 // | |
54 // 2003/01/22 - Pete | |
55 // - added easy interpolation & small noise adjustments | |
56 // | |
57 // 2003/01/19 - Pete | |
58 // - added Neill's reverb | |
59 // | |
60 // 2003/01/12 - Pete | |
61 // - added recording window handlers | |
62 // | |
63 // 2003/01/06 - Pete | |
64 // - added Neill's ADSR timings | |
65 // | |
66 // 2002/12/28 - Pete | |
67 // - adjusted spu irq handling, fmod handling and loop handling | |
68 // | |
69 // 2002/08/14 - Pete | |
70 // - added extra reverb | |
71 // | |
72 // 2002/06/08 - linuzappz | |
73 // - SPUupdate changed for SPUasync | |
74 // | |
75 // 2002/05/15 - Pete | |
76 // - generic cleanup for the Peops release | |
77 // | |
78 //*************************************************************************// | |
79 | |
80 #define _IN_SPU | |
81 | |
82 #include "../peops/stdafx.h" | |
83 #include "../peops/externals.h" | |
84 #include "../peops/regs.h" | |
85 #include "../peops/registers.h" | |
86 #include "../peops/spu.h" | |
87 | |
88 void SPUirq(void) ; | |
89 | |
90 //#include "PsxMem.h" | |
91 //#include "driver.h" | |
92 | |
93 //////////////////////////////////////////////////////////////////////// | |
94 // globals | |
95 //////////////////////////////////////////////////////////////////////// | |
96 | |
97 // psx buffer / addresses | |
98 | |
99 static u16 regArea[0x200]; | |
100 static u16 spuMem[256*1024]; | |
101 static u8 * spuMemC; | |
102 static u8 * pSpuIrq=0; | |
103 static u8 * pSpuBuffer; | |
104 | |
105 // user settings | |
106 static int iVolume; | |
107 | |
108 // MAIN infos struct for each channel | |
109 | |
110 static SPUCHAN s_chan[MAXCHAN+1]; // channel + 1 infos (1 is security for fmod handling) | |
111 static REVERBInfo rvb; | |
112 | |
113 static u32 dwNoiseVal=1; // global noise generator | |
114 | |
115 static u16 spuCtrl=0; // some vars to store psx reg infos | |
116 static u16 spuStat=0; | |
117 static u16 spuIrq=0; | |
118 static u32 spuAddr=0xffffffff; // address into spu mem | |
119 static int bSPUIsOpen=0; | |
120 | |
121 static const int f[5][2] = { | |
122 { 0, 0 }, | |
123 { 60, 0 }, | |
124 { 115, -52 }, | |
125 { 98, -55 }, | |
126 { 122, -60 } }; | |
127 s16 * pS; | |
128 static s32 ttemp; | |
129 | |
130 //////////////////////////////////////////////////////////////////////// | |
131 // CODE AREA | |
132 //////////////////////////////////////////////////////////////////////// | |
133 | |
134 // dirty inline func includes | |
135 | |
136 #include "../peops/reverb.c" | |
137 #include "../peops/adsr.c" | |
138 | |
139 // Try this to increase speed. | |
140 #include "../peops/registers.c" | |
141 #include "../peops/dma.c" | |
142 | |
143 //////////////////////////////////////////////////////////////////////// | |
144 // helpers for so-called "gauss interpolation" | |
145 | |
146 #define gval0 (((int *)(&s_chan[ch].SB[29]))[gpos]) | |
147 #define gval(x) (((int *)(&s_chan[ch].SB[29]))[(gpos+x)&3]) | |
148 | |
149 #include "gauss_i.h" | |
150 | |
151 //////////////////////////////////////////////////////////////////////// | |
152 | |
153 //////////////////////////////////////////////////////////////////////// | |
154 // START SOUND... called by main thread to setup a new sound on a channel | |
155 //////////////////////////////////////////////////////////////////////// | |
156 | |
157 static INLINE void StartSound(int ch) | |
158 { | |
159 StartADSR(ch); | |
160 | |
161 s_chan[ch].pCurr=s_chan[ch].pStart; // set sample start | |
162 | |
163 s_chan[ch].s_1=0; // init mixing vars | |
164 s_chan[ch].s_2=0; | |
165 s_chan[ch].iSBPos=28; | |
166 | |
167 s_chan[ch].bNew=0; // init channel flags | |
168 s_chan[ch].bStop=0; | |
169 s_chan[ch].bOn=1; | |
170 | |
171 s_chan[ch].SB[29]=0; // init our interpolation helpers | |
172 s_chan[ch].SB[30]=0; | |
173 | |
174 s_chan[ch].spos=0x40000L;s_chan[ch].SB[28]=0; // -> start with more decoding | |
175 } | |
176 | |
177 //////////////////////////////////////////////////////////////////////// | |
178 // MAIN SPU FUNCTION | |
179 // here is the main job handler... thread, timer or direct func call | |
180 // basically the whole sound processing is done in this fat func! | |
181 //////////////////////////////////////////////////////////////////////// | |
182 | |
183 static u32 sampcount; | |
184 static u32 decaybegin; | |
185 static u32 decayend; | |
186 | |
187 // Counting to 65536 results in full volume offage. | |
188 void setlength(s32 stop, s32 fade) | |
189 { | |
190 if(stop==~0) | |
191 { | |
192 decaybegin=~0; | |
193 } | |
194 else | |
195 { | |
196 stop=(stop*441)/10; | |
197 fade=(fade*441)/10; | |
198 | |
199 decaybegin=stop; | |
200 decayend=stop+fade; | |
201 } | |
202 } | |
203 | |
204 #define CLIP(_x) {if(_x>32767) _x=32767; if(_x<-32767) _x=-32767;} | |
205 int SPUasync(u32 cycles) | |
206 { | |
207 int volmul=iVolume; | |
208 static s32 dosampies; | |
209 s32 temp; | |
210 | |
211 ttemp+=cycles; | |
212 dosampies=ttemp/384; | |
213 if(!dosampies) return(1); | |
214 ttemp-=dosampies*384; | |
215 temp=dosampies; | |
216 | |
217 while(temp) | |
218 { | |
219 s32 revLeft=0, revRight=0; | |
220 s32 sl=0, sr=0; | |
221 int ch,fa; | |
222 | |
223 temp--; | |
224 //--------------------------------------------------// | |
225 //- main channel loop -// | |
226 //--------------------------------------------------// | |
227 { | |
228 for(ch=0;ch<MAXCHAN;ch++) // loop em all. | |
229 { | |
230 if(s_chan[ch].bNew) StartSound(ch); // start new sound | |
231 if(!s_chan[ch].bOn) continue; // channel not playing? next | |
232 | |
233 | |
234 if(s_chan[ch].iActFreq!=s_chan[ch].iUsedFreq) // new psx frequency? | |
235 { | |
236 s_chan[ch].iUsedFreq=s_chan[ch].iActFreq; // -> take it and calc steps | |
237 s_chan[ch].sinc=s_chan[ch].iRawPitch<<4; | |
238 if(!s_chan[ch].sinc) s_chan[ch].sinc=1; | |
239 } | |
240 | |
241 while(s_chan[ch].spos>=0x10000L) | |
242 { | |
243 if(s_chan[ch].iSBPos==28) // 28 reached? | |
244 { | |
245 int predict_nr,shift_factor,flags,d,s; | |
246 u8* start;unsigned int nSample; | |
247 int s_1,s_2; | |
248 | |
249 start=s_chan[ch].pCurr; // set up the current pos | |
250 | |
251 if (start == (u8*)-1) // special "stop" sign | |
252 { | |
253 s_chan[ch].bOn=0; // -> turn everything off | |
254 s_chan[ch].ADSRX.lVolume=0; | |
255 s_chan[ch].ADSRX.EnvelopeVol=0; | |
256 goto ENDX; // -> and done for this channel | |
257 } | |
258 | |
259 s_chan[ch].iSBPos=0; // Reset buffer play index. | |
260 | |
261 //////////////////////////////////////////// spu irq handler here? mmm... do it later | |
262 | |
263 s_1=s_chan[ch].s_1; | |
264 s_2=s_chan[ch].s_2; | |
265 | |
266 predict_nr=(int)*start;start++; | |
267 shift_factor=predict_nr&0xf; | |
268 predict_nr >>= 4; | |
269 flags=(int)*start;start++; | |
270 | |
271 // -------------------------------------- // | |
272 // Decode new samples into s_chan[ch].SB[0 through 27] | |
273 for (nSample=0;nSample<28;start++) | |
274 { | |
275 d=(int)*start; | |
276 s=((d&0xf)<<12); | |
277 if(s&0x8000) s|=0xffff0000; | |
278 | |
279 fa=(s >> shift_factor); | |
280 fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6); | |
281 s_2=s_1;s_1=fa; | |
282 s=((d & 0xf0) << 8); | |
283 | |
284 s_chan[ch].SB[nSample++]=fa; | |
285 | |
286 if(s&0x8000) s|=0xffff0000; | |
287 fa=(s>>shift_factor); | |
288 fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6); | |
289 s_2=s_1;s_1=fa; | |
290 | |
291 s_chan[ch].SB[nSample++]=fa; | |
292 } | |
293 | |
294 //////////////////////////////////////////// irq check | |
295 | |
296 if(spuCtrl&0x40) // irq active? | |
297 { | |
298 if((pSpuIrq > start-16 && // irq address reached? | |
299 pSpuIrq <= start) || | |
300 ((flags&1) && // special: irq on looping addr, when stop/loop flag is set | |
301 (pSpuIrq > s_chan[ch].pLoop-16 && | |
302 pSpuIrq <= s_chan[ch].pLoop))) | |
303 { | |
304 //extern s32 spuirqvoodoo; | |
305 s_chan[ch].iIrqDone=1; // -> debug flag | |
306 SPUirq(); | |
307 //puts("IRQ"); | |
308 //if(spuirqvoodoo!=-1) | |
309 //{ | |
310 // spuirqvoodoo=temp*384; | |
311 // temp=0; | |
312 //} | |
313 } | |
314 } | |
315 | |
316 //////////////////////////////////////////// flag handler | |
317 | |
318 if((flags&4) && (!s_chan[ch].bIgnoreLoop)) | |
319 s_chan[ch].pLoop=start-16; // loop adress | |
320 | |
321 if(flags&1) // 1: stop/loop | |
322 { | |
323 // We play this block out first... | |
324 //if(!(flags&2)) // 1+2: do loop... otherwise: stop | |
325 if(flags!=3 || s_chan[ch].pLoop==NULL) // PETE: if we don't check exactly for 3, loop hang ups will happen (DQ4, for example) | |
326 { // and checking if pLoop is set avoids crashes, yeah | |
327 start = (u8*)-1; | |
328 } | |
329 else | |
330 { | |
331 start = s_chan[ch].pLoop; | |
332 } | |
333 } | |
334 | |
335 s_chan[ch].pCurr=start; // store values for next cycle | |
336 s_chan[ch].s_1=s_1; | |
337 s_chan[ch].s_2=s_2; | |
338 | |
339 //////////////////////////////////////////// | |
340 } | |
341 | |
342 fa=s_chan[ch].SB[s_chan[ch].iSBPos++]; // get sample data | |
343 | |
344 if((spuCtrl&0x4000)==0) fa=0; // muted? | |
345 else CLIP(fa); | |
346 | |
347 { | |
348 int gpos; | |
349 gpos = s_chan[ch].SB[28]; | |
350 gval0 = fa; | |
351 gpos = (gpos+1) & 3; | |
352 s_chan[ch].SB[28] = gpos; | |
353 } | |
354 s_chan[ch].spos -= 0x10000L; | |
355 } | |
356 | |
357 //////////////////////////////////////////////// | |
358 // noise handler... just produces some noise data | |
359 // surely wrong... and no noise frequency (spuCtrl&0x3f00) will be used... | |
360 // and sometimes the noise will be used as fmod modulation... pfff | |
361 | |
362 if(s_chan[ch].bNoise) | |
363 { | |
364 //puts("Noise"); | |
365 if((dwNoiseVal<<=1)&0x80000000L) | |
366 { | |
367 dwNoiseVal^=0x0040001L; | |
368 fa=((dwNoiseVal>>2)&0x7fff); | |
369 fa=-fa; | |
370 } | |
371 else fa=(dwNoiseVal>>2)&0x7fff; | |
372 | |
373 // mmm... depending on the noise freq we allow bigger/smaller changes to the previous val | |
374 fa=s_chan[ch].iOldNoise+((fa-s_chan[ch].iOldNoise)/((0x001f-((spuCtrl&0x3f00)>>9))+1)); | |
375 if(fa>32767L) fa=32767L; | |
376 if(fa<-32767L) fa=-32767L; | |
377 s_chan[ch].iOldNoise=fa; | |
378 | |
379 } //---------------------------------------- | |
380 else // NO NOISE (NORMAL SAMPLE DATA) HERE | |
381 { | |
382 int vl, vr, gpos; | |
383 vl = (s_chan[ch].spos >> 6) & ~3; | |
384 gpos = s_chan[ch].SB[28]; | |
385 vr=(gauss[vl]*gval0)>>9; | |
386 vr+=(gauss[vl+1]*gval(1))>>9; | |
387 vr+=(gauss[vl+2]*gval(2))>>9; | |
388 vr+=(gauss[vl+3]*gval(3))>>9; | |
389 fa = vr>>2; | |
390 } | |
391 | |
392 s_chan[ch].sval = (MixADSR(ch) * fa)>>10; // / 1023; // add adsr | |
393 if(s_chan[ch].bFMod==2) // fmod freq channel | |
394 { | |
395 int NP=s_chan[ch+1].iRawPitch; | |
396 NP=((32768L+s_chan[ch].sval)*NP)>>15; ///32768L; | |
397 | |
398 if(NP>0x3fff) NP=0x3fff; | |
399 if(NP<0x1) NP=0x1; | |
400 | |
401 // mmmm... if I do this, all is screwed | |
402 // s_chan[ch+1].iRawPitch=NP; | |
403 | |
404 NP=(44100L*NP)/(4096L); // calc frequency | |
405 | |
406 s_chan[ch+1].iActFreq=NP; | |
407 s_chan[ch+1].iUsedFreq=NP; | |
408 s_chan[ch+1].sinc=(((NP/10)<<16)/4410); | |
409 if(!s_chan[ch+1].sinc) s_chan[ch+1].sinc=1; | |
410 | |
411 // mmmm... set up freq decoding positions? | |
412 // s_chan[ch+1].iSBPos=28; | |
413 // s_chan[ch+1].spos=0x10000L; | |
414 } | |
415 else | |
416 { | |
417 ////////////////////////////////////////////// | |
418 // ok, left/right sound volume (psx volume goes from 0 ... 0x3fff) | |
419 int tmpl,tmpr; | |
420 | |
421 if (1) //ao_channel_enable[ch+PSF_1]) { | |
422 { | |
423 tmpl=(s_chan[ch].sval*s_chan[ch].iLeftVolume)>>14; | |
424 tmpr=(s_chan[ch].sval*s_chan[ch].iRightVolume)>>14; | |
425 } else { | |
426 tmpl = 0; | |
427 tmpr = 0; | |
428 } | |
429 sl+=tmpl; | |
430 sr+=tmpr; | |
431 | |
432 if(((rvb.Enabled>>ch)&1) && (spuCtrl&0x80)) | |
433 { | |
434 revLeft+=tmpl; | |
435 revRight+=tmpr; | |
436 } | |
437 } | |
438 | |
439 s_chan[ch].spos += s_chan[ch].sinc; | |
440 ENDX: ; | |
441 } | |
442 } | |
443 | |
444 /////////////////////////////////////////////////////// | |
445 // mix all channels (including reverb) into one buffer | |
446 MixREVERBLeftRight(&sl,&sr,revLeft,revRight); | |
447 // printf("sampcount %d decaybegin %d decayend %d\n", sampcount, decaybegin, decayend); | |
448 if(sampcount>=decaybegin) | |
449 { | |
450 s32 dmul; | |
451 if(decaybegin!=~0) // Is anyone REALLY going to be playing a song | |
452 // for 13 hours? | |
453 { | |
454 if(sampcount>=decayend) | |
455 { | |
456 // ao_song_done = 1; | |
457 return(0); | |
458 } | |
459 dmul=256-(256*(sampcount-decaybegin)/(decayend-decaybegin)); | |
460 sl=(sl*dmul)>>8; | |
461 sr=(sr*dmul)>>8; | |
462 } | |
463 } | |
464 | |
465 sampcount++; | |
466 sl=(sl*volmul)>>8; | |
467 sr=(sr*volmul)>>8; | |
468 | |
469 //{ | |
470 // static double asl=0; | |
471 // static double asr=0; | |
472 | |
473 // asl+=(sl-asl)/5; | |
474 // asr+=(sl-asr)/5; | |
475 | |
476 //sl-=asl; | |
477 //sr-=asr; | |
478 | |
479 // if(sl>32767 || sl < -32767) printf("Left: %d, %f\n",sl,asl); | |
480 // if(sr>32767 || sr < -32767) printf("Right: %d, %f\n",sl,asl); | |
481 //} | |
482 | |
483 if(sl>32767) sl=32767; if(sl<-32767) sl=-32767; | |
484 if(sr>32767) sr=32767; if(sr<-32767) sr=-32767; | |
485 | |
486 *pS++=sl; | |
487 *pS++=sr; | |
488 } | |
489 | |
490 return(1); | |
491 } | |
492 | |
493 void SPU_flushboot(void) | |
494 { | |
495 if((u8*)pS>((u8*)pSpuBuffer+1024)) | |
496 { | |
497 spu_update((u8*)pSpuBuffer,(u8*)pS-(u8*)pSpuBuffer); | |
498 pS=(s16 *)pSpuBuffer; | |
499 } | |
500 } | |
501 | |
502 #ifdef TIMEO | |
503 static u64 begintime; | |
504 static u64 gettime64(void) | |
505 { | |
506 struct timeval tv; | |
507 u64 ret; | |
508 | |
509 gettimeofday(&tv,0); | |
510 ret=tv.tv_sec; | |
511 ret*=1000000; | |
512 ret+=tv.tv_usec; | |
513 return(ret); | |
514 } | |
515 #endif | |
516 //////////////////////////////////////////////////////////////////////// | |
517 // INIT/EXIT STUFF | |
518 //////////////////////////////////////////////////////////////////////// | |
519 | |
520 //////////////////////////////////////////////////////////////////////// | |
521 // SPUINIT: this func will be called first by the main emu | |
522 //////////////////////////////////////////////////////////////////////// | |
523 | |
524 int SPUinit(void) | |
525 { | |
526 spuMemC=(u8*)spuMem; // just small setup | |
527 memset((void *)s_chan,0,MAXCHAN*sizeof(SPUCHAN)); | |
528 memset((void *)&rvb,0,sizeof(REVERBInfo)); | |
529 memset(regArea,0,sizeof(regArea)); | |
530 memset(spuMem,0,sizeof(spuMem)); | |
531 InitADSR(); | |
532 sampcount=ttemp=0; | |
533 #ifdef TIMEO | |
534 begintime=gettime64(); | |
535 #endif | |
536 return 0; | |
537 } | |
538 | |
539 //////////////////////////////////////////////////////////////////////// | |
540 // SETUPSTREAMS: init most of the spu buffers | |
541 //////////////////////////////////////////////////////////////////////// | |
542 | |
543 void SetupStreams(void) | |
544 { | |
545 int i; | |
546 | |
547 pSpuBuffer=(u8*)malloc(32768); // alloc mixing buffer | |
548 pS=(s16 *)pSpuBuffer; | |
549 | |
550 for(i=0;i<MAXCHAN;i++) // loop sound channels | |
551 { | |
552 s_chan[i].ADSRX.SustainLevel = 1024; // -> init sustain | |
553 s_chan[i].iIrqDone=0; | |
554 s_chan[i].pLoop=spuMemC; | |
555 s_chan[i].pStart=spuMemC; | |
556 s_chan[i].pCurr=spuMemC; | |
557 } | |
558 } | |
559 | |
560 //////////////////////////////////////////////////////////////////////// | |
561 // REMOVESTREAMS: free most buffer | |
562 //////////////////////////////////////////////////////////////////////// | |
563 | |
564 void RemoveStreams(void) | |
565 { | |
566 free(pSpuBuffer); // free mixing buffer | |
567 pSpuBuffer=NULL; | |
568 | |
569 #ifdef TIMEO | |
570 { | |
571 u64 tmp; | |
572 tmp=gettime64(); | |
573 tmp-=begintime; | |
574 if(tmp) | |
575 tmp=(u64)sampcount*1000000/tmp; | |
576 printf("%lld samples per second\n",tmp); | |
577 } | |
578 #endif | |
579 } | |
580 | |
581 | |
582 //////////////////////////////////////////////////////////////////////// | |
583 // SPUOPEN: called by main emu after init | |
584 //////////////////////////////////////////////////////////////////////// | |
585 | |
586 int SPUopen(void) | |
587 { | |
588 if(bSPUIsOpen) return 0; // security for some stupid main emus | |
589 spuIrq=0; | |
590 | |
591 spuStat=spuCtrl=0; | |
592 spuAddr=0xffffffff; | |
593 dwNoiseVal=1; | |
594 | |
595 spuMemC=(u8*)spuMem; | |
596 memset((void *)s_chan,0,(MAXCHAN+1)*sizeof(SPUCHAN)); | |
597 pSpuIrq=0; | |
598 | |
599 iVolume=255; //85; | |
600 SetupStreams(); // prepare streaming | |
601 | |
602 bSPUIsOpen=1; | |
603 | |
604 return 1; | |
605 } | |
606 | |
607 //////////////////////////////////////////////////////////////////////// | |
608 | |
609 //////////////////////////////////////////////////////////////////////// | |
610 // SPUCLOSE: called before shutdown | |
611 //////////////////////////////////////////////////////////////////////// | |
612 | |
613 int SPUclose(void) | |
614 { | |
615 if(!bSPUIsOpen) return 0; // some security | |
616 | |
617 bSPUIsOpen=0; // no more open | |
618 | |
619 RemoveStreams(); // no more streaming | |
620 | |
621 return 0; | |
622 } | |
623 | |
624 //////////////////////////////////////////////////////////////////////// | |
625 // SPUSHUTDOWN: called by main emu on final exit | |
626 //////////////////////////////////////////////////////////////////////// | |
627 | |
628 int SPUshutdown(void) | |
629 { | |
630 return 0; | |
631 } | |
632 | |
633 void SPUinjectRAMImage(u16 *pIncoming) | |
634 { | |
635 int i; | |
636 | |
637 for (i = 0; i < (256*1024); i++) | |
638 { | |
639 spuMem[i] = pIncoming[i]; | |
640 } | |
641 } |