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