spu: get rid of pCurr -1, pass right chans to silent handler
[pcsx_rearmed.git] / plugins / dfsound / registers.c
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 \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
48 void 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
72        // Brain Dead 13 - align to 16 boundary\r
73        s_chan[ch].pStart= spuMemC+(unsigned long)((val<<3)&~0xf);\r
74        break;\r
75      //------------------------------------------------// level with pre-calcs\r
76      case 8:\r
77        {\r
78         const unsigned long lval=val;\r
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
85 #if 0\r
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
113 #endif\r
114        }\r
115       break;\r
116      //------------------------------------------------// adsr times with pre-calcs\r
117      case 10:\r
118       {\r
119        const unsigned long lval=val;\r
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
128 #if 0\r
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
160 #endif\r
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
169        s_chan[ch].pLoop=spuMemC+((unsigned long)((val<<3)&~0xf));\r
170        //s_chan[ch].bIgnoreLoop=1;\r
171        //ReleaseMutex(s_chan[ch].hMutex);                    // -> oki, on with the thread\r
172        dwChannelDead&=~(1<<ch);\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
194       if(!(spuCtrl & CTRL_IRQ))\r
195         spuStat&=~STAT_IRQ;\r
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       rvb.dirty = 1;\r
216       break;\r
217     //-------------------------------------------------//\r
218     case H_SPUirqAddr:\r
219       spuIrq = val;\r
220       pSpuIrq=spuMemC+(((unsigned long) val<<3)&~0xf);\r
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
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
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
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
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
341  if ((r & ~0x3f) == H_Reverb)\r
342   rvb.dirty = 1; // recalculate on next update\r
343 \r
344  iSpuAsyncWait=0;\r
345 }\r
346 \r
347 ////////////////////////////////////////////////////////////////////////\r
348 // READ REGISTER: called by main emu\r
349 ////////////////////////////////////////////////////////////////////////\r
350 \r
351 unsigned 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
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
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
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
416 void 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
425 \r
426      // do this here, not in StartSound\r
427      // - fixes fussy timing issues\r
428      s_chan[ch].bStop=0;\r
429      s_chan[ch].pCurr=s_chan[ch].pStart;\r
430 \r
431      dwNewChannel|=(1<<ch);                            // bitfield for faster testing\r
432      dwChannelOn|=1<<ch;\r
433      dwChannelDead&=~(1<<ch);\r
434     }\r
435   }\r
436 }\r
437 \r
438 ////////////////////////////////////////////////////////////////////////\r
439 // SOUND OFF register write\r
440 ////////////////////////////////////////////////////////////////////////\r
441 \r
442 void 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
450 \r
451      // Jungle Book - Rhythm 'n Groove\r
452      // - turns off buzzing sound (loop hangs)\r
453      dwNewChannel &= ~(1<<ch);\r
454     }                                                  \r
455   }\r
456 }\r
457 \r
458 ////////////////////////////////////////////////////////////////////////\r
459 // FMOD register write\r
460 ////////////////////////////////////////////////////////////////////////\r
461 \r
462 void 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
479      if(ch>0&&s_chan[ch-1].bFMod==2)\r
480       s_chan[ch-1].bFMod=0;\r
481     }\r
482   }\r
483 }\r
484 \r
485 ////////////////////////////////////////////////////////////////////////\r
486 // NOISE register write\r
487 ////////////////////////////////////////////////////////////////////////\r
488 \r
489 void 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
495    s_chan[ch].bNoise=val&1;                            // -> noise on/off\r
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
506 void SetVolumeL(unsigned char ch,short vol)            // LEFT VOLUME\r
507 {\r
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
532 void SetVolumeR(unsigned char ch,short vol)            // RIGHT VOLUME\r
533 {\r
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
558 void 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
575 void 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
581    s_chan[ch].bReverb=val&1;                           // -> reverb on/off\r
582   }\r
583 }\r