8e33d7ed43e5f11462ef335f1eb8c4283fe6a19a
[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 "spu_config.h"\r
25 #include "spu.h"\r
26 \r
27 static void SoundOn(int start,int end,unsigned short val);\r
28 static void SoundOff(int start,int end,unsigned short val);\r
29 static void FModOn(int start,int end,unsigned short val);\r
30 static void NoiseOn(int start,int end,unsigned short val);\r
31 static void SetVolumeL(unsigned char ch,short vol);\r
32 static void SetVolumeR(unsigned char ch,short vol);\r
33 static void SetPitch(int ch,unsigned short val);\r
34 static void ReverbOn(int start,int end,unsigned short val);\r
35 \r
36 ////////////////////////////////////////////////////////////////////////\r
37 // WRITE REGISTERS: called by main emu\r
38 ////////////////////////////////////////////////////////////////////////\r
39 \r
40 static const uint32_t ignore_dupe[16] = {\r
41  // ch 0-15  c40         c80         cc0\r
42  0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f,\r
43  // ch 16-24 d40         control     reverb\r
44  0x7f7f7f7f, 0x7f7f7f7f, 0xff05ff0f, 0xffffffff,\r
45  0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\r
46  0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff\r
47 };\r
48 \r
49 void CALLBACK SPUwriteRegister(unsigned long reg, unsigned short val,\r
50  unsigned int cycles)\r
51 {\r
52  int r = reg & 0xffe;\r
53  int rofs = (r - 0xc00) >> 1;\r
54  int changed = spu.regArea[rofs] != val;\r
55  spu.regArea[rofs] = val;\r
56 \r
57  if (!changed && (ignore_dupe[rofs >> 5] & (1u << (rofs & 0x1f))))\r
58   return;\r
59  // zero keyon/keyoff?\r
60  if (val == 0 && (r & 0xff8) == 0xd88)\r
61   return;\r
62 \r
63  do_samples_if_needed(cycles, 0, 16);\r
64 \r
65  if(r>=0x0c00 && r<0x0d80)                             // some channel info?\r
66   {\r
67    int ch=(r>>4)-0xc0;                                 // calc channel\r
68    switch(r&0x0f)\r
69     {\r
70      //------------------------------------------------// r volume\r
71      case 0:                                           \r
72        SetVolumeL((unsigned char)ch,val);\r
73        break;\r
74      //------------------------------------------------// l volume\r
75      case 2:                                           \r
76        SetVolumeR((unsigned char)ch,val);\r
77        break;\r
78      //------------------------------------------------// pitch\r
79      case 4:                                           \r
80        SetPitch(ch,val);\r
81        goto upd_irq;\r
82      //------------------------------------------------// start\r
83      case 6:      \r
84        // taken from regArea later\r
85        break;\r
86      //------------------------------------------------// level with pre-calcs\r
87      case 8:\r
88        {\r
89         const unsigned long lval=val;\r
90         //---------------------------------------------//\r
91         spu.s_chan[ch].ADSRX.AttackModeExp=(lval&0x8000)?1:0;\r
92         spu.s_chan[ch].ADSRX.AttackRate=(lval>>8) & 0x007f;\r
93         spu.s_chan[ch].ADSRX.DecayRate=(lval>>4) & 0x000f;\r
94         spu.s_chan[ch].ADSRX.SustainLevel=lval & 0x000f;\r
95         //---------------------------------------------//\r
96        }\r
97       break;\r
98      //------------------------------------------------// adsr times with pre-calcs\r
99      case 10:\r
100       {\r
101        const unsigned long lval=val;\r
102 \r
103        //----------------------------------------------//\r
104        spu.s_chan[ch].ADSRX.SustainModeExp = (lval&0x8000)?1:0;\r
105        spu.s_chan[ch].ADSRX.SustainIncrease= (lval&0x4000)?0:1;\r
106        spu.s_chan[ch].ADSRX.SustainRate = (lval>>6) & 0x007f;\r
107        spu.s_chan[ch].ADSRX.ReleaseModeExp = (lval&0x0020)?1:0;\r
108        spu.s_chan[ch].ADSRX.ReleaseRate = lval & 0x001f;\r
109        //----------------------------------------------//\r
110       }\r
111      break;\r
112      //------------------------------------------------// adsr volume... mmm have to investigate this\r
113      case 12:\r
114        break;\r
115      //------------------------------------------------//\r
116      case 14:                                          // loop?\r
117        spu.s_chan[ch].pLoop=spu.spuMemC+((val&~1)<<3);\r
118        spu.s_chan[ch].bIgnoreLoop = 1;\r
119        goto upd_irq;\r
120      //------------------------------------------------//\r
121     }\r
122    return;\r
123   }\r
124  else if (0x0e00 <= r && r < 0x0e60)\r
125   {\r
126    int ch = (r >> 2) & 0x1f;\r
127    log_unhandled("c%02d w %cvol %04x\n", ch, (r & 2) ? 'r' : 'l', val);\r
128    spu.s_chan[ch].iVolume[(r >> 1) & 1] = (signed short)val >> 1;\r
129   }\r
130 \r
131  switch(r)\r
132    {\r
133     //-------------------------------------------------//\r
134     case H_SPUaddr:\r
135       spu.spuAddr = (unsigned int)val << 3;\r
136       //check_irq_io(spu.spuAddr);\r
137       break;\r
138     //-------------------------------------------------//\r
139     case H_SPUdata:\r
140       *(unsigned short *)(spu.spuMemC + spu.spuAddr) = HTOLE16(val);\r
141       spu.spuAddr += 2;\r
142       spu.spuAddr &= 0x7fffe;\r
143       check_irq_io(spu.spuAddr);\r
144       break;\r
145     //-------------------------------------------------//\r
146     case H_SPUctrl:\r
147       spu.spuStat = (spu.spuStat & ~0xbf) | (val & 0x3f) | ((val << 2) & 0x80);\r
148       spu.spuStat &= ~STAT_IRQ | val;\r
149       if (!(spu.spuCtrl & CTRL_IRQ)) {\r
150         if (val & CTRL_IRQ)\r
151          schedule_next_irq();\r
152       }\r
153       spu.spuCtrl=val;\r
154       break;\r
155     //-------------------------------------------------//\r
156     case H_SPUstat:\r
157       //spu.spuStat=val&0xf800;\r
158       break;\r
159     //-------------------------------------------------//\r
160     case H_SPUReverbAddr:\r
161       goto rvbd;\r
162     //-------------------------------------------------//\r
163     case H_SPUirqAddr:\r
164       //if (val & 1)\r
165       //  log_unhandled("w irq with lsb: %08lx %04x\n", reg, val);\r
166       spu.pSpuIrq = spu.spuMemC + (((int)val << 3) & ~0xf);\r
167       //check_irq_io(spu.spuAddr);\r
168       goto upd_irq;\r
169     //-------------------------------------------------//\r
170     case H_SPUrvolL:\r
171       spu.rvb->VolLeft = (int16_t)val;\r
172       break;\r
173     //-------------------------------------------------//\r
174     case H_SPUrvolR:\r
175       spu.rvb->VolRight = (int16_t)val;\r
176       break;\r
177     //-------------------------------------------------//\r
178 \r
179     case H_SPUmvolL:\r
180     case H_SPUmvolR: {\r
181       int ofs = H_SPUcmvolL - H_SPUmvolL;\r
182       unsigned short *cur = &regAreaGet(r + ofs);\r
183       if (val & 0x8000) {\r
184         // this (for now?) lacks an update mechanism, so is instant\r
185         log_unhandled("w master sweep: %08lx %04x\n", reg, val);\r
186         int was_neg = (*cur >> 14) & 1;\r
187         int dec = (val >> 13) & 1;\r
188         int inv = (val >> 12) & 1;\r
189         *cur = (was_neg ^ dec ^ inv) ? 0x7fff : 0;\r
190       }\r
191       else\r
192         *cur = val << 1;\r
193       break;\r
194      }\r
195 \r
196     case 0x0dac:\r
197      if (val != 4)\r
198        log_unhandled("1f801dac %04x\n", val);\r
199      break;\r
200 \r
201 /*\r
202     case H_ExtLeft:\r
203      //auxprintf("EL %d\n",val);\r
204       break;\r
205     //-------------------------------------------------//\r
206     case H_ExtRight:\r
207      //auxprintf("ER %d\n",val);\r
208       break;\r
209     //-------------------------------------------------//\r
210     case H_SPUMute1:\r
211      //auxprintf("M0 %04x\n",val);\r
212       break;\r
213     //-------------------------------------------------//\r
214     case H_SPUMute2:\r
215      //auxprintf("M1 %04x\n",val);\r
216       break;\r
217 */\r
218     //-------------------------------------------------//\r
219     case H_SPUon1:\r
220       spu.last_keyon_cycles = cycles;\r
221       do_samples_if_needed(cycles, 0, 2);\r
222       SoundOn(0,16,val);\r
223       break;\r
224     //-------------------------------------------------//\r
225     case H_SPUon2:\r
226       spu.last_keyon_cycles = cycles;\r
227       do_samples_if_needed(cycles, 0, 2);\r
228       SoundOn(16,24,val);\r
229       break;\r
230     //-------------------------------------------------//\r
231     case H_SPUoff1:\r
232       if (cycles - spu.last_keyon_cycles < 786u) {\r
233        if (val & regAreaGet(H_SPUon1))\r
234         log_unhandled("koff1 %04x %d\n", val, cycles - spu.last_keyon_cycles);\r
235        val &= ~regAreaGet(H_SPUon1);\r
236       }\r
237       do_samples_if_needed(cycles, 0, 2);\r
238       SoundOff(0,16,val);\r
239       break;\r
240     //-------------------------------------------------//\r
241     case H_SPUoff2:\r
242       if (cycles - spu.last_keyon_cycles < 786u) {\r
243        if (val & regAreaGet(H_SPUon1))\r
244         log_unhandled("koff2 %04x %d\n", val, cycles - spu.last_keyon_cycles);\r
245        val &= ~regAreaGet(H_SPUon2);\r
246       }\r
247       do_samples_if_needed(cycles, 0, 2);\r
248       SoundOff(16,24,val);\r
249       break;\r
250     //-------------------------------------------------//\r
251     case H_CDLeft:\r
252       spu.iLeftXAVol=(int16_t)val;\r
253       //if(spu.cddavCallback) spu.cddavCallback(0,(int16_t)val);\r
254       break;\r
255     case H_CDRight:\r
256       spu.iRightXAVol=(int16_t)val;\r
257       //if(spu.cddavCallback) spu.cddavCallback(1,(int16_t)val);\r
258       break;\r
259     //-------------------------------------------------//\r
260     case H_FMod1:\r
261       FModOn(0,16,val);\r
262       break;\r
263     //-------------------------------------------------//\r
264     case H_FMod2:\r
265       FModOn(16,24,val);\r
266       break;\r
267     //-------------------------------------------------//\r
268     case H_Noise1:\r
269       NoiseOn(0,16,val);\r
270       break;\r
271     //-------------------------------------------------//\r
272     case H_Noise2:\r
273       NoiseOn(16,24,val);\r
274       break;\r
275     //-------------------------------------------------//\r
276     case H_RVBon1:\r
277       ReverbOn(0,16,val);\r
278       break;\r
279     //-------------------------------------------------//\r
280     case H_RVBon2:\r
281       ReverbOn(16,24,val);\r
282       break;\r
283     //-------------------------------------------------//\r
284     case H_Reverb + 0x00 : goto rvbd;\r
285     case H_Reverb + 0x02 : goto rvbd;\r
286     case H_Reverb + 0x04 : spu.rvb->vIIR   = (signed short)val; break;\r
287     case H_Reverb + 0x06 : spu.rvb->vCOMB1 = (signed short)val; break;\r
288     case H_Reverb + 0x08 : spu.rvb->vCOMB2 = (signed short)val; break;\r
289     case H_Reverb + 0x0a : spu.rvb->vCOMB3 = (signed short)val; break;\r
290     case H_Reverb + 0x0c : spu.rvb->vCOMB4 = (signed short)val; break;\r
291     case H_Reverb + 0x0e : spu.rvb->vWALL  = (signed short)val; break;\r
292     case H_Reverb + 0x10 : spu.rvb->vAPF1  = (signed short)val; break;\r
293     case H_Reverb + 0x12 : spu.rvb->vAPF2  = (signed short)val; break;\r
294     case H_Reverb + 0x14 : goto rvbd;\r
295     case H_Reverb + 0x16 : goto rvbd;\r
296     case H_Reverb + 0x18 : goto rvbd;\r
297     case H_Reverb + 0x1a : goto rvbd;\r
298     case H_Reverb + 0x1c : goto rvbd;\r
299     case H_Reverb + 0x1e : goto rvbd;\r
300     case H_Reverb + 0x20 : goto rvbd;\r
301     case H_Reverb + 0x22 : goto rvbd;\r
302     case H_Reverb + 0x24 : goto rvbd;\r
303     case H_Reverb + 0x26 : goto rvbd;\r
304     case H_Reverb + 0x28 : goto rvbd;\r
305     case H_Reverb + 0x2a : goto rvbd;\r
306     case H_Reverb + 0x2c : goto rvbd;\r
307     case H_Reverb + 0x2e : goto rvbd;\r
308     case H_Reverb + 0x30 : goto rvbd;\r
309     case H_Reverb + 0x32 : goto rvbd;\r
310     case H_Reverb + 0x34 : goto rvbd;\r
311     case H_Reverb + 0x36 : goto rvbd;\r
312     case H_Reverb + 0x38 : goto rvbd;\r
313     case H_Reverb + 0x3a : goto rvbd;\r
314     case H_Reverb + 0x3c : spu.rvb->vLIN = (signed short)val; break;\r
315     case H_Reverb + 0x3e : spu.rvb->vRIN = (signed short)val; break;\r
316    }\r
317  return;\r
318 \r
319 upd_irq:\r
320  if (spu.spuCtrl & CTRL_IRQ)\r
321   schedule_next_irq();\r
322  return;\r
323 \r
324 rvbd:\r
325  spu.rvb->dirty = 1; // recalculate on next update\r
326 }\r
327 \r
328 ////////////////////////////////////////////////////////////////////////\r
329 // READ REGISTER: called by main emu\r
330 ////////////////////////////////////////////////////////////////////////\r
331 \r
332 unsigned short CALLBACK SPUreadRegister(unsigned long reg, unsigned int cycles)\r
333 {\r
334  const unsigned long r = reg & 0xffe;\r
335         \r
336  if(r>=0x0c00 && r<0x0d80)\r
337   {\r
338    switch(r&0x0f)\r
339     {\r
340      case 12:                                          // get adsr vol\r
341       {\r
342        // this used to return 1 immediately after keyon to deal with\r
343        // some poor timing, but that causes Rayman 2 to lose track of\r
344        // its channels on busy scenes and start looping some of them forever\r
345        const int ch = (r>>4) - 0xc0;\r
346        if (spu.s_chan[ch].bStarting)\r
347         do_samples_if_needed(cycles, 0, 2);\r
348        return spu.s_chan[ch].ADSRX.EnvelopeVol;\r
349       }\r
350 \r
351      case 14:                                          // get loop address\r
352       {\r
353        const int ch=(r>>4)-0xc0;\r
354        return (unsigned short)((spu.s_chan[ch].pLoop-spu.spuMemC)>>3);\r
355       }\r
356     }\r
357   }\r
358  else if (0x0e00 <= r && r < 0x0e60)\r
359   {\r
360    int ch = (r >> 2) & 0x1f;\r
361    int v = spu.s_chan[ch].iVolume[(r >> 1) & 1] << 1;\r
362    log_unhandled("c%02d r %cvol %04x\n", ch, (r & 2) ? 'r' : 'l', v);\r
363    return v;\r
364   }\r
365 \r
366  switch(r)\r
367   {\r
368     case H_SPUctrl:\r
369      return spu.spuCtrl;\r
370 \r
371     case H_SPUstat:\r
372      return spu.spuStat;\r
373         \r
374     case H_SPUaddr:\r
375      return (unsigned short)(spu.spuAddr>>3);\r
376 \r
377     // this reportedly doesn't work on real hw\r
378     case H_SPUdata:\r
379      {\r
380       unsigned short s = LE16TOH(*(unsigned short *)(spu.spuMemC + spu.spuAddr));\r
381       spu.spuAddr += 2;\r
382       spu.spuAddr &= 0x7fffe;\r
383       //check_irq_io(spu.spuAddr);\r
384       return s;\r
385      }\r
386 \r
387     //case H_SPUIsOn1:\r
388     // return IsSoundOn(0,16);\r
389 \r
390     //case H_SPUIsOn2:\r
391     // return IsSoundOn(16,24);\r
392  \r
393     case H_SPUMute1:\r
394     case H_SPUMute2:\r
395      log_unhandled("spu r isOn: %08lx %04x\n", reg, regAreaGet(r));\r
396      break;\r
397 \r
398     case H_SPUmvolL:\r
399     case H_SPUmvolR:\r
400      log_unhandled("spu r mvol: %08lx %04x\n", reg, regAreaGet(r));\r
401      break;\r
402 \r
403     case 0x0dac:\r
404     case H_SPUirqAddr:\r
405     case H_CDLeft:\r
406     case H_CDRight:\r
407     case H_ExtLeft:\r
408     case H_ExtRight:\r
409      break;\r
410 \r
411     default:\r
412      if (r >= 0xda0)\r
413        log_unhandled("spu r %08lx %04x\n", reg, regAreaGet(r));\r
414      break;\r
415   }\r
416 \r
417  return spu.regArea[(r-0xc00)>>1];\r
418 }\r
419  \r
420 ////////////////////////////////////////////////////////////////////////\r
421 // SOUND ON register write\r
422 ////////////////////////////////////////////////////////////////////////\r
423 \r
424 static void SoundOn(int start,int end,unsigned short val)\r
425 {\r
426  int ch;\r
427 \r
428  for(ch=start;ch<end;ch++,val>>=1)                     // loop channels\r
429   {\r
430    if((val&1) && regAreaGetCh(ch, 6))                  // mmm... start has to be set before key on !?!\r
431     {\r
432      spu.s_chan[ch].bIgnoreLoop = 0;\r
433      spu.s_chan[ch].bStarting = 1;\r
434      spu.dwNewChannel|=(1<<ch);\r
435     }\r
436   }\r
437 }\r
438 \r
439 ////////////////////////////////////////////////////////////////////////\r
440 // SOUND OFF register write\r
441 ////////////////////////////////////////////////////////////////////////\r
442 \r
443 static void SoundOff(int start,int end,unsigned short val)\r
444 {\r
445  int ch;\r
446  for (ch = start; val && ch < end; ch++, val >>= 1)    // loop channels\r
447   {\r
448    if(val&1)\r
449     {\r
450      spu.s_chan[ch].ADSRX.State = ADSR_RELEASE;\r
451      spu.s_chan[ch].ADSRX.StepCounter = 0;\r
452 \r
453      // Jungle Book - Rhythm 'n Groove\r
454      // - turns off buzzing sound (loop hangs)\r
455      spu.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        spu.s_chan[ch].bFMod=1;                         // --> sound channel\r
475        spu.s_chan[ch-1].bFMod=2;                       // --> freq channel\r
476       }\r
477     }\r
478    else\r
479     {\r
480      spu.s_chan[ch].bFMod=0;                           // --> turn off fmod\r
481      if(ch>0&&spu.s_chan[ch-1].bFMod==2)\r
482       spu.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    spu.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    log_unhandled("ch%d sweepl %04x\n", ch, vol);\r
514    if(vol&0x2000) sInc=-1;                             // -> or down?\r
515    if(vol&0x1000) vol^=0xffff;                         // -> mmm... phase inverted? have to investigate this\r
516    vol=((vol&0x7f)+1)/2;                               // -> sweep: 0..127 -> 0..64\r
517    vol+=vol/(2*sInc);                                  // -> HACK: we don't sweep right now, so we just raise/lower the volume by the half!\r
518    vol*=128;\r
519   }\r
520  else                                                  // no sweep:\r
521   {\r
522    if(vol&0x4000)                                      // -> mmm... phase inverted? have to investigate this\r
523     //vol^=0xffff;\r
524     vol=0x3fff-(vol&0x3fff);\r
525   }\r
526 \r
527  vol&=0x3fff;\r
528  spu.s_chan[ch].iLeftVolume=vol;                       // store volume\r
529  //spu.regArea[(0xe00-0xc00)/2 + ch*2 + 0] = vol << 1;\r
530 }\r
531 \r
532 ////////////////////////////////////////////////////////////////////////\r
533 // RIGHT VOLUME register write\r
534 ////////////////////////////////////////////////////////////////////////\r
535 \r
536 static void SetVolumeR(unsigned char ch,short vol)     // RIGHT VOLUME\r
537 {\r
538  if(vol&0x8000)                                        // comments... see above :)\r
539   {\r
540    short sInc=1;\r
541    log_unhandled("ch%d sweepr %04x\n", ch, vol);\r
542    if(vol&0x2000) sInc=-1;\r
543    if(vol&0x1000) vol^=0xffff;\r
544    vol=((vol&0x7f)+1)/2;        \r
545    vol+=vol/(2*sInc);\r
546    vol*=128;\r
547   }\r
548  else            \r
549   {\r
550    if(vol&0x4000) //vol=vol^=0xffff;\r
551     vol=0x3fff-(vol&0x3fff);\r
552   }\r
553 \r
554  vol&=0x3fff;\r
555 \r
556  spu.s_chan[ch].iRightVolume=vol;\r
557  //spu.regArea[(0xe00-0xc00)/2 + ch*2 + 1] = vol << 1;\r
558 }\r
559 \r
560 ////////////////////////////////////////////////////////////////////////\r
561 // PITCH register write\r
562 ////////////////////////////////////////////////////////////////////////\r
563 \r
564 static void SetPitch(int ch,unsigned short val)               // SET PITCH\r
565 {\r
566  int NP;\r
567  if(val>0x3fff) NP=0x3fff;                             // get pitch val\r
568  else           NP=val;\r
569 \r
570  spu.s_chan[ch].iRawPitch = NP;\r
571  spu.s_chan[ch].sinc = NP << 4;\r
572  spu.s_chan[ch].sinc_inv = 0;\r
573 \r
574  // don't mess spu.dwChannelsAudible as adsr runs independently\r
575 }\r
576 \r
577 ////////////////////////////////////////////////////////////////////////\r
578 // REVERB register write\r
579 ////////////////////////////////////////////////////////////////////////\r
580 \r
581 static void ReverbOn(int start,int end,unsigned short val)\r
582 {\r
583  int ch;\r
584 \r
585  for(ch=start;ch<end;ch++,val>>=1)                     // loop channels\r
586   {\r
587    spu.s_chan[ch].bReverb=val&1;                       // -> reverb on/off\r
588   }\r
589 }\r
590 \r
591 // vim:shiftwidth=1:expandtab\r