spu: parametrize sample generation, interpolation
[pcsx_rearmed.git] / plugins / dfsound / registers.c
CommitLineData
ef79bbde
P
1/***************************************************************************\r
2 registers.c - description\r
3 -------------------\r
4 begin : Wed May 15 2002\r
5 copyright : (C) 2002 by Pete Bernert\r
6 email : BlackDove@addcom.de\r
7 ***************************************************************************/\r
8/***************************************************************************\r
9 * *\r
10 * This program is free software; you can redistribute it and/or modify *\r
11 * it under the terms of the GNU General Public License as published by *\r
12 * the Free Software Foundation; either version 2 of the License, or *\r
13 * (at your option) any later version. See also the license.txt file for *\r
14 * additional informations. *\r
15 * *\r
16 ***************************************************************************/\r
17\r
18#include "stdafx.h"\r
19\r
20#define _IN_REGISTERS\r
21\r
22#include "externals.h"\r
23#include "registers.h"\r
24#include "regs.h"\r
25#include "reverb.h"\r
26\r
27/*\r
28// adsr time values (in ms) by James Higgs ... see the end of\r
29// the adsr.c source for details\r
30\r
31#define ATTACK_MS 514L\r
32#define DECAYHALF_MS 292L\r
33#define DECAY_MS 584L\r
34#define SUSTAIN_MS 450L\r
35#define RELEASE_MS 446L\r
36*/\r
37\r
38// we have a timebase of 1.020408f ms, not 1 ms... so adjust adsr defines\r
39#define ATTACK_MS 494L\r
40#define DECAYHALF_MS 286L\r
41#define DECAY_MS 572L\r
42#define SUSTAIN_MS 441L\r
43#define RELEASE_MS 437L\r
44\r
45////////////////////////////////////////////////////////////////////////\r
46// WRITE REGISTERS: called by main emu\r
47////////////////////////////////////////////////////////////////////////\r
48\r
49void CALLBACK SPUwriteRegister(unsigned long reg, unsigned short val)\r
50{\r
51 const unsigned long r=reg&0xfff;\r
52 regArea[(r-0xc00)>>1] = val;\r
53\r
54 if(r>=0x0c00 && r<0x0d80) // some channel info?\r
55 {\r
56 int ch=(r>>4)-0xc0; // calc channel\r
57 switch(r&0x0f)\r
58 {\r
59 //------------------------------------------------// r volume\r
60 case 0: \r
61 SetVolumeL((unsigned char)ch,val);\r
62 break;\r
63 //------------------------------------------------// l volume\r
64 case 2: \r
65 SetVolumeR((unsigned char)ch,val);\r
66 break;\r
67 //------------------------------------------------// pitch\r
68 case 4: \r
69 SetPitch(ch,val);\r
70 break;\r
71 //------------------------------------------------// start\r
72 case 6: \r
b00afb77 73 // Brain Dead 13 - align to 16 boundary\r
74 s_chan[ch].pStart= spuMemC+(unsigned long)((val<<3)&~0xf);\r
ef79bbde
P
75 break;\r
76 //------------------------------------------------// level with pre-calcs\r
77 case 8:\r
78 {\r
6d866bb7 79 const unsigned long lval=val;\r
ef79bbde
P
80 //---------------------------------------------//\r
81 s_chan[ch].ADSRX.AttackModeExp=(lval&0x8000)?1:0; \r
82 s_chan[ch].ADSRX.AttackRate=(lval>>8) & 0x007f;\r
83 s_chan[ch].ADSRX.DecayRate=(lval>>4) & 0x000f;\r
84 s_chan[ch].ADSRX.SustainLevel=lval & 0x000f;\r
85 //---------------------------------------------//\r
6d866bb7 86#if 0\r
ef79bbde
P
87 if(!iDebugMode) break;\r
88 //---------------------------------------------// stuff below is only for debug mode\r
89\r
90 s_chan[ch].ADSR.AttackModeExp=(lval&0x8000)?1:0; //0x007f\r
91\r
92 lx=(((lval>>8) & 0x007f)>>2); // attack time to run from 0 to 100% volume\r
93 lx=min(31,lx); // no overflow on shift!\r
94 if(lx) \r
95 { \r
96 lx = (1<<lx);\r
97 if(lx<2147483) lx=(lx*ATTACK_MS)/10000L; // another overflow check\r
98 else lx=(lx/10000L)*ATTACK_MS;\r
99 if(!lx) lx=1;\r
100 }\r
101 s_chan[ch].ADSR.AttackTime=lx; \r
102\r
103 s_chan[ch].ADSR.SustainLevel= // our adsr vol runs from 0 to 1024, so scale the sustain level\r
104 (1024*((lval) & 0x000f))/15;\r
105\r
106 lx=(lval>>4) & 0x000f; // decay:\r
107 if(lx) // our const decay value is time it takes from 100% to 0% of volume\r
108 {\r
109 lx = ((1<<(lx))*DECAY_MS)/10000L;\r
110 if(!lx) lx=1;\r
111 }\r
112 s_chan[ch].ADSR.DecayTime = // so calc how long does it take to run from 100% to the wanted sus level\r
113 (lx*(1024-s_chan[ch].ADSR.SustainLevel))/1024;\r
6d866bb7 114#endif\r
ef79bbde
P
115 }\r
116 break;\r
117 //------------------------------------------------// adsr times with pre-calcs\r
118 case 10:\r
119 {\r
6d866bb7 120 const unsigned long lval=val;\r
ef79bbde
P
121\r
122 //----------------------------------------------//\r
123 s_chan[ch].ADSRX.SustainModeExp = (lval&0x8000)?1:0;\r
124 s_chan[ch].ADSRX.SustainIncrease= (lval&0x4000)?0:1;\r
125 s_chan[ch].ADSRX.SustainRate = (lval>>6) & 0x007f;\r
126 s_chan[ch].ADSRX.ReleaseModeExp = (lval&0x0020)?1:0;\r
127 s_chan[ch].ADSRX.ReleaseRate = lval & 0x001f;\r
128 //----------------------------------------------//\r
6d866bb7 129#if 0\r
ef79bbde
P
130 if(!iDebugMode) break;\r
131 //----------------------------------------------// stuff below is only for debug mode\r
132\r
133 s_chan[ch].ADSR.SustainModeExp = (lval&0x8000)?1:0;\r
134 s_chan[ch].ADSR.ReleaseModeExp = (lval&0x0020)?1:0;\r
135 \r
136 lx=((((lval>>6) & 0x007f)>>2)); // sustain time... often very high\r
137 lx=min(31,lx); // values are used to hold the volume\r
138 if(lx) // until a sound stop occurs\r
139 { // the highest value we reach (due to \r
140 lx = (1<<lx); // overflow checking) is: \r
141 if(lx<2147483) lx=(lx*SUSTAIN_MS)/10000L; // 94704 seconds = 1578 minutes = 26 hours... \r
142 else lx=(lx/10000L)*SUSTAIN_MS; // should be enuff... if the stop doesn't \r
143 if(!lx) lx=1; // come in this time span, I don't care :)\r
144 }\r
145 s_chan[ch].ADSR.SustainTime = lx;\r
146\r
147 lx=(lval & 0x001f);\r
148 s_chan[ch].ADSR.ReleaseVal =lx;\r
149 if(lx) // release time from 100% to 0%\r
150 { // note: the release time will be\r
151 lx = (1<<lx); // adjusted when a stop is coming,\r
152 if(lx<2147483) lx=(lx*RELEASE_MS)/10000L; // so at this time the adsr vol will \r
153 else lx=(lx/10000L)*RELEASE_MS; // run from (current volume) to 0%\r
154 if(!lx) lx=1;\r
155 }\r
156 s_chan[ch].ADSR.ReleaseTime=lx;\r
157\r
158 if(lval & 0x4000) // add/dec flag\r
159 s_chan[ch].ADSR.SustainModeDec=-1;\r
160 else s_chan[ch].ADSR.SustainModeDec=1;\r
6d866bb7 161#endif\r
ef79bbde
P
162 }\r
163 break;\r
164 //------------------------------------------------// adsr volume... mmm have to investigate this\r
165 case 12:\r
166 break;\r
167 //------------------------------------------------//\r
168 case 14: // loop?\r
169 //WaitForSingleObject(s_chan[ch].hMutex,2000); // -> no multithread fuckups\r
b00afb77 170 s_chan[ch].pLoop=spuMemC+((unsigned long)((val<<3)&~0xf));\r
5238f6f8 171 //s_chan[ch].bIgnoreLoop=1;\r
ef79bbde
P
172 //ReleaseMutex(s_chan[ch].hMutex); // -> oki, on with the thread\r
173 break;\r
174 //------------------------------------------------//\r
175 }\r
176 iSpuAsyncWait=0;\r
177 return;\r
178 }\r
179\r
180 switch(r)\r
181 {\r
182 //-------------------------------------------------//\r
183 case H_SPUaddr:\r
184 spuAddr = (unsigned long) val<<3;\r
185 break;\r
186 //-------------------------------------------------//\r
187 case H_SPUdata:\r
188 spuMem[spuAddr>>1] = val;\r
189 spuAddr+=2;\r
190 if(spuAddr>0x7ffff) spuAddr=0;\r
191 break;\r
192 //-------------------------------------------------//\r
193 case H_SPUctrl:\r
3fc2a4c2 194 if(!(spuCtrl & CTRL_IRQ))\r
195 spuStat&=~STAT_IRQ;\r
ef79bbde
P
196 spuCtrl=val;\r
197 break;\r
198 //-------------------------------------------------//\r
199 case H_SPUstat:\r
200 spuStat=val & 0xf800;\r
201 break;\r
202 //-------------------------------------------------//\r
203 case H_SPUReverbAddr:\r
204 if(val==0xFFFF || val<=0x200)\r
205 {rvb.StartAddr=rvb.CurrAddr=0;}\r
206 else\r
207 {\r
208 const long iv=(unsigned long)val<<2;\r
209 if(rvb.StartAddr!=iv)\r
210 {\r
211 rvb.StartAddr=(unsigned long)val<<2;\r
212 rvb.CurrAddr=rvb.StartAddr;\r
213 }\r
214 }\r
215 break;\r
216 //-------------------------------------------------//\r
217 case H_SPUirqAddr:\r
218 spuIrq = val;\r
3fc2a4c2 219 pSpuIrq=spuMemC+(((unsigned long) val<<3)&~0xf);\r
ef79bbde
P
220 break;\r
221 //-------------------------------------------------//\r
222 case H_SPUrvolL:\r
223 rvb.VolLeft=val;\r
224 break;\r
225 //-------------------------------------------------//\r
226 case H_SPUrvolR:\r
227 rvb.VolRight=val;\r
228 break;\r
229 //-------------------------------------------------//\r
230\r
231/*\r
232 case H_ExtLeft:\r
233 //auxprintf("EL %d\n",val);\r
234 break;\r
235 //-------------------------------------------------//\r
236 case H_ExtRight:\r
237 //auxprintf("ER %d\n",val);\r
238 break;\r
239 //-------------------------------------------------//\r
240 case H_SPUmvolL:\r
241 //auxprintf("ML %d\n",val);\r
242 break;\r
243 //-------------------------------------------------//\r
244 case H_SPUmvolR:\r
245 //auxprintf("MR %d\n",val);\r
246 break;\r
247 //-------------------------------------------------//\r
248 case H_SPUMute1:\r
249 //auxprintf("M0 %04x\n",val);\r
250 break;\r
251 //-------------------------------------------------//\r
252 case H_SPUMute2:\r
253 //auxprintf("M1 %04x\n",val);\r
254 break;\r
255*/\r
256 //-------------------------------------------------//\r
257 case H_SPUon1:\r
258 SoundOn(0,16,val);\r
259 break;\r
260 //-------------------------------------------------//\r
261 case H_SPUon2:\r
262 SoundOn(16,24,val);\r
263 break;\r
264 //-------------------------------------------------//\r
265 case H_SPUoff1:\r
266 SoundOff(0,16,val);\r
267 break;\r
268 //-------------------------------------------------//\r
269 case H_SPUoff2:\r
270 SoundOff(16,24,val);\r
271 break;\r
272 //-------------------------------------------------//\r
273 case H_CDLeft:\r
274 iLeftXAVol=val & 0x7fff;\r
275 if(cddavCallback) cddavCallback(0,val);\r
276 break;\r
277 case H_CDRight:\r
278 iRightXAVol=val & 0x7fff;\r
279 if(cddavCallback) cddavCallback(1,val);\r
280 break;\r
281 //-------------------------------------------------//\r
282 case H_FMod1:\r
283 FModOn(0,16,val);\r
284 break;\r
285 //-------------------------------------------------//\r
286 case H_FMod2:\r
287 FModOn(16,24,val);\r
288 break;\r
289 //-------------------------------------------------//\r
290 case H_Noise1:\r
291 NoiseOn(0,16,val);\r
292 break;\r
293 //-------------------------------------------------//\r
294 case H_Noise2:\r
295 NoiseOn(16,24,val);\r
296 break;\r
297 //-------------------------------------------------//\r
298 case H_RVBon1:\r
299 ReverbOn(0,16,val);\r
300 break;\r
301 //-------------------------------------------------//\r
302 case H_RVBon2:\r
303 ReverbOn(16,24,val);\r
304 break;\r
305 //-------------------------------------------------//\r
306 case H_Reverb+0:\r
307\r
308 rvb.FB_SRC_A=val;\r
309\r
310 // OK, here's the fake REVERB stuff...\r
311 // depending on effect we do more or less delay and repeats... bah\r
312 // still... better than nothing :)\r
313\r
314 SetREVERB(val);\r
315 break;\r
316\r
317\r
318 case H_Reverb+2 : rvb.FB_SRC_B=(short)val; break;\r
319 case H_Reverb+4 : rvb.IIR_ALPHA=(short)val; break;\r
320 case H_Reverb+6 : rvb.ACC_COEF_A=(short)val; break;\r
321 case H_Reverb+8 : rvb.ACC_COEF_B=(short)val; break;\r
322 case H_Reverb+10 : rvb.ACC_COEF_C=(short)val; break;\r
323 case H_Reverb+12 : rvb.ACC_COEF_D=(short)val; break;\r
324 case H_Reverb+14 : rvb.IIR_COEF=(short)val; break;\r
325 case H_Reverb+16 : rvb.FB_ALPHA=(short)val; break;\r
326 case H_Reverb+18 : rvb.FB_X=(short)val; break;\r
327 case H_Reverb+20 : rvb.IIR_DEST_A0=(short)val; break;\r
328 case H_Reverb+22 : rvb.IIR_DEST_A1=(short)val; break;\r
329 case H_Reverb+24 : rvb.ACC_SRC_A0=(short)val; break;\r
330 case H_Reverb+26 : rvb.ACC_SRC_A1=(short)val; break;\r
331 case H_Reverb+28 : rvb.ACC_SRC_B0=(short)val; break;\r
332 case H_Reverb+30 : rvb.ACC_SRC_B1=(short)val; break;\r
333 case H_Reverb+32 : rvb.IIR_SRC_A0=(short)val; break;\r
334 case H_Reverb+34 : rvb.IIR_SRC_A1=(short)val; break;\r
335 case H_Reverb+36 : rvb.IIR_DEST_B0=(short)val; break;\r
336 case H_Reverb+38 : rvb.IIR_DEST_B1=(short)val; break;\r
337 case H_Reverb+40 : rvb.ACC_SRC_C0=(short)val; break;\r
338 case H_Reverb+42 : rvb.ACC_SRC_C1=(short)val; break;\r
339 case H_Reverb+44 : rvb.ACC_SRC_D0=(short)val; break;\r
340 case H_Reverb+46 : rvb.ACC_SRC_D1=(short)val; break;\r
341 case H_Reverb+48 : rvb.IIR_SRC_B1=(short)val; break;\r
342 case H_Reverb+50 : rvb.IIR_SRC_B0=(short)val; break;\r
343 case H_Reverb+52 : rvb.MIX_DEST_A0=(short)val; break;\r
344 case H_Reverb+54 : rvb.MIX_DEST_A1=(short)val; break;\r
345 case H_Reverb+56 : rvb.MIX_DEST_B0=(short)val; break;\r
346 case H_Reverb+58 : rvb.MIX_DEST_B1=(short)val; break;\r
347 case H_Reverb+60 : rvb.IN_COEF_L=(short)val; break;\r
348 case H_Reverb+62 : rvb.IN_COEF_R=(short)val; break;\r
349 }\r
350\r
351 iSpuAsyncWait=0;\r
352}\r
353\r
354////////////////////////////////////////////////////////////////////////\r
355// READ REGISTER: called by main emu\r
356////////////////////////////////////////////////////////////////////////\r
357\r
358unsigned short CALLBACK SPUreadRegister(unsigned long reg)\r
359{\r
360 const unsigned long r=reg&0xfff;\r
361 \r
362 iSpuAsyncWait=0;\r
363\r
364 if(r>=0x0c00 && r<0x0d80)\r
365 {\r
366 switch(r&0x0f)\r
367 {\r
368 case 12: // get adsr vol\r
369 {\r
370 const int ch=(r>>4)-0xc0;\r
6d866bb7 371 if(dwNewChannel&(1<<ch)) return 1; // we are started, but not processed? return 1\r
372 if((dwChannelOn&(1<<ch)) && // same here... we haven't decoded one sample yet, so no envelope yet. return 1 as well\r
373 !s_chan[ch].ADSRX.EnvelopeVol)\r
ef79bbde
P
374 return 1;\r
375 return (unsigned short)(s_chan[ch].ADSRX.EnvelopeVol>>16);\r
376 }\r
377\r
378 case 14: // get loop address\r
379 {\r
380 const int ch=(r>>4)-0xc0;\r
ef79bbde
P
381 return (unsigned short)((s_chan[ch].pLoop-spuMemC)>>3);\r
382 }\r
383 }\r
384 }\r
385\r
386 switch(r)\r
387 {\r
388 case H_SPUctrl:\r
389 return spuCtrl;\r
390\r
391 case H_SPUstat:\r
392 return spuStat;\r
393 \r
394 case H_SPUaddr:\r
395 return (unsigned short)(spuAddr>>3);\r
396\r
397 case H_SPUdata:\r
398 {\r
399 unsigned short s=spuMem[spuAddr>>1];\r
400 spuAddr+=2;\r
401 if(spuAddr>0x7ffff) spuAddr=0;\r
402 return s;\r
403 }\r
404\r
405 case H_SPUirqAddr:\r
406 return spuIrq;\r
407\r
408 //case H_SPUIsOn1:\r
409 // return IsSoundOn(0,16);\r
410\r
411 //case H_SPUIsOn2:\r
412 // return IsSoundOn(16,24);\r
413 \r
414 }\r
415\r
416 return regArea[(r-0xc00)>>1];\r
417}\r
418 \r
419////////////////////////////////////////////////////////////////////////\r
420// SOUND ON register write\r
421////////////////////////////////////////////////////////////////////////\r
422\r
423void SoundOn(int start,int end,unsigned short val) // SOUND ON PSX COMAND\r
424{\r
425 int ch;\r
426\r
427 for(ch=start;ch<end;ch++,val>>=1) // loop channels\r
428 {\r
429 if((val&1) && s_chan[ch].pStart) // mmm... start has to be set before key on !?!\r
430 {\r
431 s_chan[ch].bIgnoreLoop=0;\r
b00afb77 432\r
433 // do this here, not in StartSound\r
434 // - fixes fussy timing issues\r
435 s_chan[ch].bStop=0;\r
b00afb77 436 s_chan[ch].pCurr=s_chan[ch].pStart;\r
437\r
ef79bbde 438 dwNewChannel|=(1<<ch); // bitfield for faster testing\r
6d866bb7 439 dwChannelOn|=1<<ch;\r
ef79bbde
P
440 }\r
441 }\r
442}\r
443\r
444////////////////////////////////////////////////////////////////////////\r
445// SOUND OFF register write\r
446////////////////////////////////////////////////////////////////////////\r
447\r
448void SoundOff(int start,int end,unsigned short val) // SOUND OFF PSX COMMAND\r
449{\r
450 int ch;\r
451 for(ch=start;ch<end;ch++,val>>=1) // loop channels\r
452 {\r
453 if(val&1) // && s_chan[i].bOn) mmm...\r
454 {\r
455 s_chan[ch].bStop=1;\r
b00afb77 456\r
457 // Jungle Book - Rhythm 'n Groove\r
458 // - turns off buzzing sound (loop hangs)\r
b00afb77 459 dwNewChannel &= ~(1<<ch);\r
ef79bbde
P
460 } \r
461 }\r
462}\r
463\r
464////////////////////////////////////////////////////////////////////////\r
465// FMOD register write\r
466////////////////////////////////////////////////////////////////////////\r
467\r
468void FModOn(int start,int end,unsigned short val) // FMOD ON PSX COMMAND\r
469{\r
470 int ch;\r
471\r
472 for(ch=start;ch<end;ch++,val>>=1) // loop channels\r
473 {\r
474 if(val&1) // -> fmod on/off\r
475 {\r
476 if(ch>0) \r
477 {\r
478 s_chan[ch].bFMod=1; // --> sound channel\r
479 s_chan[ch-1].bFMod=2; // --> freq channel\r
480 }\r
481 }\r
482 else\r
483 {\r
484 s_chan[ch].bFMod=0; // --> turn off fmod\r
07a6dd2c 485 if(ch>0&&s_chan[ch-1].bFMod==2)\r
486 s_chan[ch-1].bFMod=0;\r
ef79bbde
P
487 }\r
488 }\r
489}\r
490\r
491////////////////////////////////////////////////////////////////////////\r
492// NOISE register write\r
493////////////////////////////////////////////////////////////////////////\r
494\r
495void NoiseOn(int start,int end,unsigned short val) // NOISE ON PSX COMMAND\r
496{\r
497 int ch;\r
498\r
499 for(ch=start;ch<end;ch++,val>>=1) // loop channels\r
500 {\r
6d866bb7 501 s_chan[ch].bNoise=val&1; // -> noise on/off\r
ef79bbde
P
502 }\r
503}\r
504\r
505////////////////////////////////////////////////////////////////////////\r
506// LEFT VOLUME register write\r
507////////////////////////////////////////////////////////////////////////\r
508\r
509// please note: sweep and phase invert are wrong... but I've never seen\r
510// them used\r
511\r
512void SetVolumeL(unsigned char ch,short vol) // LEFT VOLUME\r
513{\r
ef79bbde
P
514 if(vol&0x8000) // sweep?\r
515 {\r
516 short sInc=1; // -> sweep up?\r
517 if(vol&0x2000) sInc=-1; // -> or down?\r
518 if(vol&0x1000) vol^=0xffff; // -> mmm... phase inverted? have to investigate this\r
519 vol=((vol&0x7f)+1)/2; // -> sweep: 0..127 -> 0..64\r
520 vol+=vol/(2*sInc); // -> HACK: we don't sweep right now, so we just raise/lower the volume by the half!\r
521 vol*=128;\r
522 }\r
523 else // no sweep:\r
524 {\r
525 if(vol&0x4000) // -> mmm... phase inverted? have to investigate this\r
526 //vol^=0xffff;\r
527 vol=0x3fff-(vol&0x3fff);\r
528 }\r
529\r
530 vol&=0x3fff;\r
531 s_chan[ch].iLeftVolume=vol; // store volume\r
532}\r
533\r
534////////////////////////////////////////////////////////////////////////\r
535// RIGHT VOLUME register write\r
536////////////////////////////////////////////////////////////////////////\r
537\r
538void SetVolumeR(unsigned char ch,short vol) // RIGHT VOLUME\r
539{\r
ef79bbde
P
540 if(vol&0x8000) // comments... see above :)\r
541 {\r
542 short sInc=1;\r
543 if(vol&0x2000) sInc=-1;\r
544 if(vol&0x1000) vol^=0xffff;\r
545 vol=((vol&0x7f)+1)/2; \r
546 vol+=vol/(2*sInc);\r
547 vol*=128;\r
548 }\r
549 else \r
550 {\r
551 if(vol&0x4000) //vol=vol^=0xffff;\r
552 vol=0x3fff-(vol&0x3fff);\r
553 }\r
554\r
555 vol&=0x3fff;\r
556\r
557 s_chan[ch].iRightVolume=vol;\r
558}\r
559\r
560////////////////////////////////////////////////////////////////////////\r
561// PITCH register write\r
562////////////////////////////////////////////////////////////////////////\r
563\r
564void SetPitch(int ch,unsigned short val) // SET PITCH\r
565{\r
566 int NP;\r
567 if(val>0x3fff) NP=0x3fff; // get pitch val\r
568 else NP=val;\r
569\r
570 s_chan[ch].iRawPitch=NP;\r
571\r
572 NP=(44100L*NP)/4096L; // calc frequency\r
573 if(NP<1) NP=1; // some security\r
574 s_chan[ch].iActFreq=NP; // store frequency\r
575}\r
576\r
577////////////////////////////////////////////////////////////////////////\r
578// REVERB register write\r
579////////////////////////////////////////////////////////////////////////\r
580\r
581void ReverbOn(int start,int end,unsigned short val) // REVERB ON PSX COMMAND\r
582{\r
583 int ch;\r
584\r
585 for(ch=start;ch<end;ch++,val>>=1) // loop channels\r
586 {\r
6d866bb7 587 s_chan[ch].bReverb=val&1; // -> reverb on/off\r
ef79bbde
P
588 }\r
589}\r