spu: get rid of iSPUIRQWait
[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 \r
25 /*\r
26 // adsr time values (in ms) by James Higgs ... see the end of\r
27 // the adsr.c source for details\r
28 \r
29 #define ATTACK_MS     514L\r
30 #define DECAYHALF_MS  292L\r
31 #define DECAY_MS      584L\r
32 #define SUSTAIN_MS    450L\r
33 #define RELEASE_MS    446L\r
34 */\r
35 \r
36 // we have a timebase of 1.020408f ms, not 1 ms... so adjust adsr defines\r
37 #define ATTACK_MS      494L\r
38 #define DECAYHALF_MS   286L\r
39 #define DECAY_MS       572L\r
40 #define SUSTAIN_MS     441L\r
41 #define RELEASE_MS     437L\r
42 \r
43 static void SoundOn(int start,int end,unsigned short val);\r
44 static void SoundOff(int start,int end,unsigned short val);\r
45 static void FModOn(int start,int end,unsigned short val);\r
46 static void NoiseOn(int start,int end,unsigned short val);\r
47 static void SetVolumeL(unsigned char ch,short vol);\r
48 static void SetVolumeR(unsigned char ch,short vol);\r
49 static void SetPitch(int ch,unsigned short val);\r
50 static void ReverbOn(int start,int end,unsigned short val);\r
51 \r
52 ////////////////////////////////////////////////////////////////////////\r
53 // WRITE REGISTERS: called by main emu\r
54 ////////////////////////////////////////////////////////////////////////\r
55 \r
56 void CALLBACK SPUwriteRegister(unsigned long reg, unsigned short val)\r
57 {\r
58  const unsigned long r=reg&0xfff;\r
59  regArea[(r-0xc00)>>1] = val;\r
60 \r
61  if(r>=0x0c00 && r<0x0d80)                             // some channel info?\r
62   {\r
63    int ch=(r>>4)-0xc0;                                 // calc channel\r
64    switch(r&0x0f)\r
65     {\r
66      //------------------------------------------------// r volume\r
67      case 0:                                           \r
68        SetVolumeL((unsigned char)ch,val);\r
69        break;\r
70      //------------------------------------------------// l volume\r
71      case 2:                                           \r
72        SetVolumeR((unsigned char)ch,val);\r
73        break;\r
74      //------------------------------------------------// pitch\r
75      case 4:                                           \r
76        SetPitch(ch,val);\r
77        break;\r
78      //------------------------------------------------// start\r
79      case 6:      \r
80        // taken from regArea later\r
81        break;\r
82      //------------------------------------------------// level with pre-calcs\r
83      case 8:\r
84        {\r
85         const unsigned long lval=val;\r
86         //---------------------------------------------//\r
87         s_chan[ch].ADSRX.AttackModeExp=(lval&0x8000)?1:0; \r
88         s_chan[ch].ADSRX.AttackRate=(lval>>8) & 0x007f;\r
89         s_chan[ch].ADSRX.DecayRate=(lval>>4) & 0x000f;\r
90         s_chan[ch].ADSRX.SustainLevel=lval & 0x000f;\r
91         //---------------------------------------------//\r
92 #if 0\r
93         if(!iDebugMode) break;\r
94         //---------------------------------------------// stuff below is only for debug mode\r
95 \r
96         s_chan[ch].ADSR.AttackModeExp=(lval&0x8000)?1:0;        //0x007f\r
97 \r
98         lx=(((lval>>8) & 0x007f)>>2);                  // attack time to run from 0 to 100% volume\r
99         lx=min(31,lx);                                 // no overflow on shift!\r
100         if(lx) \r
101          { \r
102           lx = (1<<lx);\r
103           if(lx<2147483) lx=(lx*ATTACK_MS)/10000L;     // another overflow check\r
104           else           lx=(lx/10000L)*ATTACK_MS;\r
105           if(!lx) lx=1;\r
106          }\r
107         s_chan[ch].ADSR.AttackTime=lx;                \r
108 \r
109         s_chan[ch].ADSR.SustainLevel=                 // our adsr vol runs from 0 to 1024, so scale the sustain level\r
110          (1024*((lval) & 0x000f))/15;\r
111 \r
112         lx=(lval>>4) & 0x000f;                         // decay:\r
113         if(lx)                                         // our const decay value is time it takes from 100% to 0% of volume\r
114          {\r
115           lx = ((1<<(lx))*DECAY_MS)/10000L;\r
116           if(!lx) lx=1;\r
117          }\r
118         s_chan[ch].ADSR.DecayTime =                   // so calc how long does it take to run from 100% to the wanted sus level\r
119          (lx*(1024-s_chan[ch].ADSR.SustainLevel))/1024;\r
120 #endif\r
121        }\r
122       break;\r
123      //------------------------------------------------// adsr times with pre-calcs\r
124      case 10:\r
125       {\r
126        const unsigned long lval=val;\r
127 \r
128        //----------------------------------------------//\r
129        s_chan[ch].ADSRX.SustainModeExp = (lval&0x8000)?1:0;\r
130        s_chan[ch].ADSRX.SustainIncrease= (lval&0x4000)?0:1;\r
131        s_chan[ch].ADSRX.SustainRate = (lval>>6) & 0x007f;\r
132        s_chan[ch].ADSRX.ReleaseModeExp = (lval&0x0020)?1:0;\r
133        s_chan[ch].ADSRX.ReleaseRate = lval & 0x001f;\r
134        //----------------------------------------------//\r
135 #if 0\r
136        if(!iDebugMode) break;\r
137        //----------------------------------------------// stuff below is only for debug mode\r
138 \r
139        s_chan[ch].ADSR.SustainModeExp = (lval&0x8000)?1:0;\r
140        s_chan[ch].ADSR.ReleaseModeExp = (lval&0x0020)?1:0;\r
141                    \r
142        lx=((((lval>>6) & 0x007f)>>2));                 // sustain time... often very high\r
143        lx=min(31,lx);                                  // values are used to hold the volume\r
144        if(lx)                                          // until a sound stop occurs\r
145         {                                              // the highest value we reach (due to \r
146          lx = (1<<lx);                                 // overflow checking) is: \r
147          if(lx<2147483) lx=(lx*SUSTAIN_MS)/10000L;     // 94704 seconds = 1578 minutes = 26 hours... \r
148          else           lx=(lx/10000L)*SUSTAIN_MS;     // should be enuff... if the stop doesn't \r
149          if(!lx) lx=1;                                 // come in this time span, I don't care :)\r
150         }\r
151        s_chan[ch].ADSR.SustainTime = lx;\r
152 \r
153        lx=(lval & 0x001f);\r
154        s_chan[ch].ADSR.ReleaseVal     =lx;\r
155        if(lx)                                          // release time from 100% to 0%\r
156         {                                              // note: the release time will be\r
157          lx = (1<<lx);                                 // adjusted when a stop is coming,\r
158          if(lx<2147483) lx=(lx*RELEASE_MS)/10000L;     // so at this time the adsr vol will \r
159          else           lx=(lx/10000L)*RELEASE_MS;     // run from (current volume) to 0%\r
160          if(!lx) lx=1;\r
161         }\r
162        s_chan[ch].ADSR.ReleaseTime=lx;\r
163 \r
164        if(lval & 0x4000)                               // add/dec flag\r
165             s_chan[ch].ADSR.SustainModeDec=-1;\r
166        else s_chan[ch].ADSR.SustainModeDec=1;\r
167 #endif\r
168       }\r
169      break;\r
170      //------------------------------------------------// adsr volume... mmm have to investigate this\r
171      case 12:\r
172        break;\r
173      //------------------------------------------------//\r
174      case 14:                                          // loop?\r
175        s_chan[ch].pLoop=spuMemC+((val&~1)<<3);\r
176        if(s_chan[ch].bJump)\r
177         // real machine would be most likely still doing the last block and use new value for the jump;\r
178         // but we decode ahead a bit and already did the jump part, so compensate for that now.\r
179         s_chan[ch].pCurr=s_chan[ch].pLoop;\r
180        break;\r
181      //------------------------------------------------//\r
182     }\r
183    return;\r
184   }\r
185 \r
186  switch(r)\r
187    {\r
188     //-------------------------------------------------//\r
189     case H_SPUaddr:\r
190       spuAddr = (unsigned long) val<<3;\r
191       break;\r
192     //-------------------------------------------------//\r
193     case H_SPUdata:\r
194       spuMem[spuAddr>>1] = val;\r
195       spuAddr+=2;\r
196       if(spuAddr>0x7ffff) spuAddr=0;\r
197       break;\r
198     //-------------------------------------------------//\r
199     case H_SPUctrl:\r
200       if(!(spuCtrl & CTRL_IRQ))\r
201         spuStat&=~STAT_IRQ;\r
202       spuCtrl=val;\r
203       break;\r
204     //-------------------------------------------------//\r
205     case H_SPUstat:\r
206       spuStat=val & 0xf800;\r
207       break;\r
208     //-------------------------------------------------//\r
209     case H_SPUReverbAddr:\r
210       if(val==0xFFFF || val<=0x200)\r
211        {rvb.StartAddr=rvb.CurrAddr=0;}\r
212       else\r
213        {\r
214         const long iv=(unsigned long)val<<2;\r
215         if(rvb.StartAddr!=iv)\r
216          {\r
217           rvb.StartAddr=(unsigned long)val<<2;\r
218           rvb.CurrAddr=rvb.StartAddr;\r
219          }\r
220        }\r
221       rvb.dirty = 1;\r
222       break;\r
223     //-------------------------------------------------//\r
224     case H_SPUirqAddr:\r
225       spuIrq = val;\r
226       pSpuIrq=spuMemC+(((unsigned long) val<<3)&~0xf);\r
227       break;\r
228     //-------------------------------------------------//\r
229     case H_SPUrvolL:\r
230       rvb.VolLeft=val;\r
231       break;\r
232     //-------------------------------------------------//\r
233     case H_SPUrvolR:\r
234       rvb.VolRight=val;\r
235       break;\r
236     //-------------------------------------------------//\r
237 \r
238 /*\r
239     case H_ExtLeft:\r
240      //auxprintf("EL %d\n",val);\r
241       break;\r
242     //-------------------------------------------------//\r
243     case H_ExtRight:\r
244      //auxprintf("ER %d\n",val);\r
245       break;\r
246     //-------------------------------------------------//\r
247     case H_SPUmvolL:\r
248      //auxprintf("ML %d\n",val);\r
249       break;\r
250     //-------------------------------------------------//\r
251     case H_SPUmvolR:\r
252      //auxprintf("MR %d\n",val);\r
253       break;\r
254     //-------------------------------------------------//\r
255     case H_SPUMute1:\r
256      //auxprintf("M0 %04x\n",val);\r
257       break;\r
258     //-------------------------------------------------//\r
259     case H_SPUMute2:\r
260      //auxprintf("M1 %04x\n",val);\r
261       break;\r
262 */\r
263     //-------------------------------------------------//\r
264     case H_SPUon1:\r
265       SoundOn(0,16,val);\r
266       break;\r
267     //-------------------------------------------------//\r
268      case H_SPUon2:\r
269       SoundOn(16,24,val);\r
270       break;\r
271     //-------------------------------------------------//\r
272     case H_SPUoff1:\r
273       SoundOff(0,16,val);\r
274       break;\r
275     //-------------------------------------------------//\r
276     case H_SPUoff2:\r
277       SoundOff(16,24,val);\r
278       break;\r
279     //-------------------------------------------------//\r
280     case H_CDLeft:\r
281       iLeftXAVol=val  & 0x7fff;\r
282       if(cddavCallback) cddavCallback(0,val);\r
283       break;\r
284     case H_CDRight:\r
285       iRightXAVol=val & 0x7fff;\r
286       if(cddavCallback) cddavCallback(1,val);\r
287       break;\r
288     //-------------------------------------------------//\r
289     case H_FMod1:\r
290       FModOn(0,16,val);\r
291       break;\r
292     //-------------------------------------------------//\r
293     case H_FMod2:\r
294       FModOn(16,24,val);\r
295       break;\r
296     //-------------------------------------------------//\r
297     case H_Noise1:\r
298       NoiseOn(0,16,val);\r
299       break;\r
300     //-------------------------------------------------//\r
301     case H_Noise2:\r
302       NoiseOn(16,24,val);\r
303       break;\r
304     //-------------------------------------------------//\r
305     case H_RVBon1:\r
306       ReverbOn(0,16,val);\r
307       break;\r
308     //-------------------------------------------------//\r
309     case H_RVBon2:\r
310       ReverbOn(16,24,val);\r
311       break;\r
312     //-------------------------------------------------//\r
313     case H_Reverb+0   : rvb.FB_SRC_A=val*4;            break;\r
314     case H_Reverb+2   : rvb.FB_SRC_B=val*4;            break;\r
315     case H_Reverb+4   : rvb.IIR_ALPHA=(short)val;      break;\r
316     case H_Reverb+6   : rvb.ACC_COEF_A=(short)val;     break;\r
317     case H_Reverb+8   : rvb.ACC_COEF_B=(short)val;     break;\r
318     case H_Reverb+10  : rvb.ACC_COEF_C=(short)val;     break;\r
319     case H_Reverb+12  : rvb.ACC_COEF_D=(short)val;     break;\r
320     case H_Reverb+14  : rvb.IIR_COEF=(short)val;       break;\r
321     case H_Reverb+16  : rvb.FB_ALPHA=(short)val;       break;\r
322     case H_Reverb+18  : rvb.FB_X=(short)val;           break;\r
323     case H_Reverb+20  : rvb.IIR_DEST_A0=val*4;         break;\r
324     case H_Reverb+22  : rvb.IIR_DEST_A1=val*4;         break;\r
325     case H_Reverb+24  : rvb.ACC_SRC_A0=val*4;          break;\r
326     case H_Reverb+26  : rvb.ACC_SRC_A1=val*4;          break;\r
327     case H_Reverb+28  : rvb.ACC_SRC_B0=val*4;          break;\r
328     case H_Reverb+30  : rvb.ACC_SRC_B1=val*4;          break;\r
329     case H_Reverb+32  : rvb.IIR_SRC_A0=val*4;          break;\r
330     case H_Reverb+34  : rvb.IIR_SRC_A1=val*4;          break;\r
331     case H_Reverb+36  : rvb.IIR_DEST_B0=val*4;         break;\r
332     case H_Reverb+38  : rvb.IIR_DEST_B1=val*4;         break;\r
333     case H_Reverb+40  : rvb.ACC_SRC_C0=val*4;          break;\r
334     case H_Reverb+42  : rvb.ACC_SRC_C1=val*4;          break;\r
335     case H_Reverb+44  : rvb.ACC_SRC_D0=val*4;          break;\r
336     case H_Reverb+46  : rvb.ACC_SRC_D1=val*4;          break;\r
337     case H_Reverb+48  : rvb.IIR_SRC_B1=val*4;          break;\r
338     case H_Reverb+50  : rvb.IIR_SRC_B0=val*4;          break;\r
339     case H_Reverb+52  : rvb.MIX_DEST_A0=val*4;         break;\r
340     case H_Reverb+54  : rvb.MIX_DEST_A1=val*4;         break;\r
341     case H_Reverb+56  : rvb.MIX_DEST_B0=val*4;         break;\r
342     case H_Reverb+58  : rvb.MIX_DEST_B1=val*4;         break;\r
343     case H_Reverb+60  : rvb.IN_COEF_L=(short)val;      break;\r
344     case H_Reverb+62  : rvb.IN_COEF_R=(short)val;      break;\r
345    }\r
346 \r
347  if ((r & ~0x3f) == H_Reverb)\r
348   rvb.dirty = 1; // recalculate on next update\r
349 }\r
350 \r
351 ////////////////////////////////////////////////////////////////////////\r
352 // READ REGISTER: called by main emu\r
353 ////////////////////////////////////////////////////////////////////////\r
354 \r
355 unsigned short CALLBACK SPUreadRegister(unsigned long reg)\r
356 {\r
357  const unsigned long r=reg&0xfff;\r
358         \r
359  if(r>=0x0c00 && r<0x0d80)\r
360   {\r
361    switch(r&0x0f)\r
362     {\r
363      case 12:                                          // get adsr vol\r
364       {\r
365        const int ch=(r>>4)-0xc0;\r
366        if(dwNewChannel&(1<<ch)) return 1;              // we are started, but not processed? return 1\r
367        if((dwChannelOn&(1<<ch)) &&                     // same here... we haven't decoded one sample yet, so no envelope yet. return 1 as well\r
368           !s_chan[ch].ADSRX.EnvelopeVol)\r
369         return 1;\r
370        return (unsigned short)(s_chan[ch].ADSRX.EnvelopeVol>>16);\r
371       }\r
372 \r
373      case 14:                                          // get loop address\r
374       {\r
375        const int ch=(r>>4)-0xc0;\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 static void SoundOn(int start,int end,unsigned short val)\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) && regAreaGet(ch,6))                     // mmm... start has to be set before key on !?!\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=spuMemC+((regAreaGet(ch,6)&~1)<<3); // must be block aligned\r
430      s_chan[ch].pLoop=spuMemC+((regAreaGet(ch,14)&~1)<<3);\r
431      s_chan[ch].bJump=0;\r
432 \r
433      dwNewChannel|=(1<<ch);                            // bitfield for faster testing\r
434      dwChannelOn|=1<<ch;\r
435      dwChannelDead&=~(1<<ch);\r
436     }\r
437   }\r
438 }\r
439 \r
440 ////////////////////////////////////////////////////////////////////////\r
441 // SOUND OFF register write\r
442 ////////////////////////////////////////////////////////////////////////\r
443 \r
444 static void SoundOff(int start,int end,unsigned short val)\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      dwNewChannel &= ~(1<<ch);\r
456     }                                                  \r
457   }\r
458 }\r
459 \r
460 ////////////////////////////////////////////////////////////////////////\r
461 // FMOD register write\r
462 ////////////////////////////////////////////////////////////////////////\r
463 \r
464 static void FModOn(int start,int end,unsigned short val)\r
465 {\r
466  int ch;\r
467 \r
468  for(ch=start;ch<end;ch++,val>>=1)                     // loop channels\r
469   {\r
470    if(val&1)                                           // -> fmod on/off\r
471     {\r
472      if(ch>0) \r
473       {\r
474        s_chan[ch].bFMod=1;                             // --> sound channel\r
475        s_chan[ch-1].bFMod=2;                           // --> freq channel\r
476       }\r
477     }\r
478    else\r
479     {\r
480      s_chan[ch].bFMod=0;                               // --> turn off fmod\r
481      if(ch>0&&s_chan[ch-1].bFMod==2)\r
482       s_chan[ch-1].bFMod=0;\r
483     }\r
484   }\r
485 }\r
486 \r
487 ////////////////////////////////////////////////////////////////////////\r
488 // NOISE register write\r
489 ////////////////////////////////////////////////////////////////////////\r
490 \r
491 static void NoiseOn(int start,int end,unsigned short val)\r
492 {\r
493  int ch;\r
494 \r
495  for(ch=start;ch<end;ch++,val>>=1)                     // loop channels\r
496   {\r
497    s_chan[ch].bNoise=val&1;                            // -> noise on/off\r
498   }\r
499 }\r
500 \r
501 ////////////////////////////////////////////////////////////////////////\r
502 // LEFT VOLUME register write\r
503 ////////////////////////////////////////////////////////////////////////\r
504 \r
505 // please note: sweep and phase invert are wrong... but I've never seen\r
506 // them used\r
507 \r
508 static void SetVolumeL(unsigned char ch,short vol)     // LEFT VOLUME\r
509 {\r
510  if(vol&0x8000)                                        // sweep?\r
511   {\r
512    short sInc=1;                                       // -> sweep up?\r
513    if(vol&0x2000) sInc=-1;                             // -> or down?\r
514    if(vol&0x1000) vol^=0xffff;                         // -> mmm... phase inverted? have to investigate this\r
515    vol=((vol&0x7f)+1)/2;                               // -> sweep: 0..127 -> 0..64\r
516    vol+=vol/(2*sInc);                                  // -> HACK: we don't sweep right now, so we just raise/lower the volume by the half!\r
517    vol*=128;\r
518   }\r
519  else                                                  // no sweep:\r
520   {\r
521    if(vol&0x4000)                                      // -> mmm... phase inverted? have to investigate this\r
522     //vol^=0xffff;\r
523     vol=0x3fff-(vol&0x3fff);\r
524   }\r
525 \r
526  vol&=0x3fff;\r
527  s_chan[ch].iLeftVolume=vol;                           // store volume\r
528 }\r
529 \r
530 ////////////////////////////////////////////////////////////////////////\r
531 // RIGHT VOLUME register write\r
532 ////////////////////////////////////////////////////////////////////////\r
533 \r
534 static void SetVolumeR(unsigned char ch,short vol)     // RIGHT VOLUME\r
535 {\r
536  if(vol&0x8000)                                        // comments... see above :)\r
537   {\r
538    short sInc=1;\r
539    if(vol&0x2000) sInc=-1;\r
540    if(vol&0x1000) vol^=0xffff;\r
541    vol=((vol&0x7f)+1)/2;        \r
542    vol+=vol/(2*sInc);\r
543    vol*=128;\r
544   }\r
545  else            \r
546   {\r
547    if(vol&0x4000) //vol=vol^=0xffff;\r
548     vol=0x3fff-(vol&0x3fff);\r
549   }\r
550 \r
551  vol&=0x3fff;\r
552 \r
553  s_chan[ch].iRightVolume=vol;\r
554 }\r
555 \r
556 ////////////////////////////////////////////////////////////////////////\r
557 // PITCH register write\r
558 ////////////////////////////////////////////////////////////////////////\r
559 \r
560 static void SetPitch(int ch,unsigned short val)               // SET PITCH\r
561 {\r
562  int NP;\r
563  if(val>0x3fff) NP=0x3fff;                             // get pitch val\r
564  else           NP=val;\r
565 \r
566  s_chan[ch].iRawPitch=NP;\r
567  s_chan[ch].sinc=(NP<<4)|8;\r
568  if(iUseInterpolation==1) s_chan[ch].SB[32]=1;         // -> freq change in simple interpolation mode: set flag\r
569 }\r
570 \r
571 ////////////////////////////////////////////////////////////////////////\r
572 // REVERB register write\r
573 ////////////////////////////////////////////////////////////////////////\r
574 \r
575 static void ReverbOn(int start,int end,unsigned short val)\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