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