gte_neon: hook into recompiler
[pcsx_rearmed.git] / plugins / dfsound / registers.c
CommitLineData
ef79bbde
P
1/***************************************************************************\r
2 registers.c - description\r
3 -------------------\r
4 begin : Wed May 15 2002\r
5 copyright : (C) 2002 by Pete Bernert\r
6 email : BlackDove@addcom.de\r
7 ***************************************************************************/\r
8/***************************************************************************\r
9 * *\r
10 * This program is free software; you can redistribute it and/or modify *\r
11 * it under the terms of the GNU General Public License as published by *\r
12 * the Free Software Foundation; either version 2 of the License, or *\r
13 * (at your option) any later version. See also the license.txt file for *\r
14 * additional informations. *\r
15 * *\r
16 ***************************************************************************/\r
17\r
18#include "stdafx.h"\r
19\r
20#define _IN_REGISTERS\r
21\r
22#include "externals.h"\r
23#include "registers.h"\r
24#include "regs.h"\r
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
49void 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
b00afb77 73 // Brain Dead 13 - align to 16 boundary\r
74 s_chan[ch].pStart= spuMemC+(unsigned long)((val<<3)&~0xf);\r
ef79bbde
P
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
b00afb77 166 s_chan[ch].pLoop=spuMemC+((unsigned long)((val<<3)&~0xf));\r
ef79bbde
P
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
352unsigned 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
418void 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
b00afb77 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
ef79bbde
P
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
444void 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
b00afb77 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
ef79bbde
P
457 } \r
458 }\r
459}\r
460\r
461////////////////////////////////////////////////////////////////////////\r
462// FMOD register write\r
463////////////////////////////////////////////////////////////////////////\r
464\r
465void 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
490void 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
514void 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
542void 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
570void 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
587void 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