1 /* Mednafen - Multi-system Emulator
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17 
18 #include "pcfx.h"
19 #include "soundbox.h"
20 #include "king.h"
21 #include "../cdrom/scsicd.h"
22 #include "pce_psg/pce_psg.h"
23 #include "../clamp.h"
24 #include "../sound/OwlResampler.h"
25 
26 #include <math.h>
27 #include <string.h>
28 
29 static const int StepSizes[49] =
30 {
31  16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50,
32  55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157,
33  173, 190,  209, 230, 253, 279, 307, 337, 371, 408, 449,
34  494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552
35 };
36 
37 static const int StepIndexDeltas[16] =
38 {
39  -1, -1, -1, -1, 2, 4, 6, 8,
40  -1, -1, -1, -1, 2, 4, 6, 8
41 };
42 
43 static OwlResampler* FXres = NULL;
44 static OwlBuffer* FXsbuf[2] = { NULL, NULL };
45 RavenBuffer* FXCDDABufs[2] = { NULL, NULL };	// Used in the CDROM code
46 
47 static PCE_PSG *pce_psg = NULL;
48 
49 static bool SoundEnabled;
50 static uint32 adpcm_lastts;
51 
52 struct SoundBox
53 {
54    uint16 ADPCMControl;
55    uint8 ADPCMVolume[2][2]; // ADPCMVolume[channel(0 or 1)][left(0) or right(1)]
56    uint8 CDDAVolume[2];
57    int32 bigdiv;
58    int32 smalldiv;
59 
60    int64 ResetAntiClick[2];
61    double VolumeFiltered[2][2];
62    double vf_xv[2][2][1+1], vf_yv[2][2][1+1];
63 
64    int32 ADPCMDelta[2];
65    int32 ADPCMHaveDelta[2];
66 
67    int32 ADPCMPredictor[2];
68    int32 StepSizeIndex[2];
69 
70    uint32 ADPCMWhichNibble[2];
71    uint16 ADPCMHalfWord[2];
72    bool ADPCMHaveHalfWord[2];
73 
74    int32 ADPCM_last[2][2];
75 };
76 
77 static SoundBox sbox;
78 static double ADPCMVolTable[0x40];
79 
80 static bool EmulateBuggyCodec;		// If true, emulate the buggy codec/algorithm used by an official PC-FX ADPCM encoder, rather than how the
81 					// hardware actually works.
82 static bool ResetAntiClickEnabled;	// = true;
83 
84 
RedoVolume(void)85 static void RedoVolume(void)
86 {
87  pce_psg->SetVolume(0.681);	//0.227 * 0.50);
88  //ADPCMSynth.volume(0.50);
89 }
90 
SoundBox_SetSoundRate(uint32 rate)91 bool SoundBox_SetSoundRate(uint32 rate)
92 {
93    SoundEnabled = (bool)rate;
94 
95    if(FXres)
96    {
97       delete FXres;
98       FXres = NULL;
99    }
100 
101    if(rate > 0)
102    {
103       FXres = new OwlResampler(PCFX_MASTER_CLOCK / 12, rate, MDFN_GetSettingF("pcfx.resamp_rate_error"), 20, MDFN_GetSettingUI("pcfx.resamp_quality"));
104 
105       for(unsigned i = 0; i < 2; i++)
106          FXres->ResetBufResampState(FXsbuf[i]);
107    }
108 
109    RedoVolume();
110 
111    return(TRUE);
112 }
113 
SoundBox_Init(bool arg_EmulateBuggyCodec,bool arg_ResetAntiClickEnabled)114 int SoundBox_Init(bool arg_EmulateBuggyCodec, bool arg_ResetAntiClickEnabled)
115 {
116    adpcm_lastts = 0;
117    SoundEnabled = false;
118 
119    EmulateBuggyCodec = arg_EmulateBuggyCodec;
120    ResetAntiClickEnabled = arg_ResetAntiClickEnabled;
121 
122    for(unsigned i = 0; i < 2; i++)
123    {
124       FXsbuf[i] = new OwlBuffer();
125       FXCDDABufs[i] = new RavenBuffer();
126    }
127 
128    pce_psg = new PCE_PSG(FXsbuf[0]->Buf(), FXsbuf[1]->Buf(), PCE_PSG::REVISION_HUC6280A);
129 
130    memset(&sbox, 0, sizeof(sbox));
131 
132    // Build ADPCM volume table, 1.5dB per step, ADPCM volume settings of 0x0 through 0x1B result in silence.
133    for(int x = 0; x < 0x40; x++)
134    {
135       double flub = 1;
136       int vti = 0x3F - x;
137 
138       if(x)
139          flub /= powf(2, (double)1 / 4 * x);
140 
141       if(vti <= 0x1B)
142          ADPCMVolTable[vti] = 0;
143       else
144          ADPCMVolTable[vti] = flub;
145    }
146 
147    return (1);
148 }
149 
SoundBox_Kill(void)150 void SoundBox_Kill(void)
151 {
152    if(pce_psg)
153    {
154       delete pce_psg;
155       pce_psg = NULL;
156    }
157 
158    for(unsigned i = 0; i < 2; i++)
159    {
160       if(FXsbuf[i])
161       {
162          delete FXsbuf[i];
163          FXsbuf[i] = NULL;
164       }
165       if(FXCDDABufs[i])
166       {
167          delete FXCDDABufs[i];
168          FXCDDABufs[i] = NULL;
169       }
170    }
171 
172    if(FXres)
173    {
174       delete FXres;
175       FXres = NULL;
176    }
177 }
178 
179 /* Macro to access currently selected PSG channel */
SoundBox_Write(uint32 A,uint16 V,const v810_timestamp_t timestamp)180 void SoundBox_Write(uint32 A, uint16 V, const v810_timestamp_t timestamp)
181 {
182    A &= 0x3F;
183 
184    if(A < 0x20)
185       pce_psg->Write(timestamp / 3, A >> 1, V);
186    else
187    {
188       //printf("%04x %04x %d\n", A, V, timestamp);
189       switch(A & 0x3F)
190       {
191          case 0x20:
192             SoundBox_ADPCMUpdate(timestamp);
193             for(int ch = 0; ch < 2; ch++)
194             {
195                if(!(sbox.ADPCMControl & (0x10 << ch)) && (V & (0x10 << ch)))
196                {
197                   //printf("Reset: %d\n", ch);
198 
199                   if(ResetAntiClickEnabled)
200                   {
201                      sbox.ResetAntiClick[ch] += (int64)sbox.ADPCMPredictor[ch] << 32;
202                      if(sbox.ResetAntiClick[ch] > ((int64)0x3FFF << 32))
203                         sbox.ResetAntiClick[ch] = (int64)0x3FFF << 32;
204                      if(sbox.ResetAntiClick[ch] < ((int64)-0x4000 << 32))
205                         sbox.ResetAntiClick[ch] = (int64)-0x4000 << 32;
206                   }
207 
208                   sbox.ADPCMPredictor[ch] = 0;
209                   sbox.StepSizeIndex[ch] = 0;
210                }
211             }
212             sbox.ADPCMControl = V;
213             break;
214 
215          case 0x22:
216             SoundBox_ADPCMUpdate(timestamp);
217             sbox.ADPCMVolume[0][0] = V & 0x3F;
218             break;
219 
220          case 0x24:
221             SoundBox_ADPCMUpdate(timestamp);
222             sbox.ADPCMVolume[0][1] = V & 0x3F;
223             break;
224 
225          case 0x26:
226             SoundBox_ADPCMUpdate(timestamp);
227             sbox.ADPCMVolume[1][0] = V & 0x3F;
228             break;
229 
230          case 0x28:
231             SoundBox_ADPCMUpdate(timestamp);
232             sbox.ADPCMVolume[1][1] = V & 0x3F;
233             break;
234 
235          case 0x2A:
236             sbox.CDDAVolume[0] = V & 0x3F;
237             SCSICD_SetCDDAVolume(0.50f * sbox.CDDAVolume[0] / 63, 0.50f * sbox.CDDAVolume[1] / 63);
238             break;
239 
240          case 0x2C:
241             sbox.CDDAVolume[1] = V & 0x3F;
242             SCSICD_SetCDDAVolume(0.50f * sbox.CDDAVolume[0] / 63, 0.50f * sbox.CDDAVolume[1] / 63);
243             break;
244       }
245    }
246 }
247 
248 static uint32 KINGADPCMControl;
249 
SoundBox_SetKINGADPCMControl(uint32 value)250 void SoundBox_SetKINGADPCMControl(uint32 value)
251 {
252    KINGADPCMControl = value;
253 }
254 
255 /* Digital filter designed by mkfilter/mkshape/gencode   A.J. Fisher
256    Command line: /www/usr/fisher/helpers/mkfilter -Bu -Lp -o 1 -a 1.5888889125e-04 0.0000000000e+00 -l */
DoVolumeFilter(int ch,int lr)257 static void DoVolumeFilter(int ch, int lr)
258 {
259    sbox.vf_xv[ch][lr][0] = sbox.vf_xv[ch][lr][1];
260    sbox.vf_xv[ch][lr][1] = (double)ADPCMVolTable[sbox.ADPCMVolume[ch][lr]] / 2.004348738e+03;
261 
262    sbox.vf_yv[ch][lr][0] = sbox.vf_yv[ch][lr][1];
263    sbox.vf_yv[ch][lr][1] = (sbox.vf_xv[ch][lr][0] + sbox.vf_xv[ch][lr][1]) + (  0.9990021696 * sbox.vf_yv[ch][lr][0]);
264    sbox.VolumeFiltered[ch][lr] = sbox.vf_yv[ch][lr][1];
265 }
266 
267 static const int16 ADPCM_PhaseFilter[8][7] =
268 {
269  /*   0 */ {    40,   283,   654,   683,   331,    56,     1 }, //  2048
270  /*   1 */ {    28,   238,   618,   706,   381,    75,     2 }, //  2048
271  /*   2 */ {    19,   197,   577,   720,   432,    99,     4 }, //  2048
272  /*   3 */ {    12,   160,   532,   726,   483,   128,     7 }, //  2048
273  /*   4 */ {     7,   128,   483,   726,   532,   160,    12 }, //  2048
274  /*   5 */ {     4,    99,   432,   720,   577,   197,    19 }, //  2048
275  /*   6 */ {     2,    75,   381,   706,   618,   238,    28 }, //  2048
276  /*   7 */ {     1,    56,   331,   683,   654,   283,    40 }, //  2048
277 };
278 
SoundBox_ADPCMUpdate(const v810_timestamp_t timestamp)279 v810_timestamp_t SoundBox_ADPCMUpdate(const v810_timestamp_t timestamp)
280 {
281    int32 run_time = timestamp - adpcm_lastts;
282 
283    adpcm_lastts = timestamp;
284 
285    sbox.bigdiv -= run_time * 2;
286 
287    while(sbox.bigdiv <= 0)
288    {
289       sbox.smalldiv--;
290       while(sbox.smalldiv <= 0)
291       {
292          sbox.smalldiv += 1 << ((KINGADPCMControl >> 2) & 0x3);
293          for(int ch = 0; ch < 2; ch++)
294          {
295             // Keep playing our last halfword fetched even if KING ADPCM is disabled
296             if(sbox.ADPCMHaveHalfWord[ch] || KINGADPCMControl & (1 << ch))
297             {
298                if(!sbox.ADPCMWhichNibble[ch])
299                {
300                   sbox.ADPCMHalfWord[ch] = KING_GetADPCMHalfWord(ch);
301                   sbox.ADPCMHaveHalfWord[ch] = TRUE;
302                }
303 
304                // If the channel's reset bit is set, don't update its ADPCM state.
305                if(sbox.ADPCMControl & (0x10 << ch))
306                {
307                   sbox.ADPCMDelta[ch] = 0;
308                }
309                else
310                {
311                   uint8 nibble = (sbox.ADPCMHalfWord[ch] >> (sbox.ADPCMWhichNibble[ch])) & 0xF;
312                   int32 BaseStepSize = StepSizes[sbox.StepSizeIndex[ch]];
313 
314                   //if(!ch)
315                   //printf("Nibble: %02x\n", nibble);
316 
317                   if(EmulateBuggyCodec)
318                   {
319                      if(BaseStepSize == 1552)
320                         BaseStepSize = 1522;
321 
322                      sbox.ADPCMDelta[ch] = BaseStepSize * ((nibble & 0x7) + 1) * 2;
323                   }
324                   else
325                      sbox.ADPCMDelta[ch] = BaseStepSize * ((nibble & 0x7) + 1);
326 
327                   // Linear interpolation turned on?
328                   if(sbox.ADPCMControl & (0x4 << ch))
329                      sbox.ADPCMDelta[ch] >>= (KINGADPCMControl >> 2) & 0x3;
330 
331                   if(nibble & 0x8)
332                      sbox.ADPCMDelta[ch] = -sbox.ADPCMDelta[ch];
333 
334                   sbox.StepSizeIndex[ch] += StepIndexDeltas[nibble];
335 
336                   if(sbox.StepSizeIndex[ch] < 0)
337                      sbox.StepSizeIndex[ch] = 0;
338 
339                   if(sbox.StepSizeIndex[ch] > 48)
340                      sbox.StepSizeIndex[ch] = 48;
341                }
342                sbox.ADPCMHaveDelta[ch] = 1;
343 
344                // Linear interpolation turned on?
345                if(sbox.ADPCMControl & (0x4 << ch))
346                   sbox.ADPCMHaveDelta[ch] = 1 << ((KINGADPCMControl >> 2) & 0x3);
347 
348                sbox.ADPCMWhichNibble[ch] = (sbox.ADPCMWhichNibble[ch] + 4) & 0xF;
349 
350                if(!sbox.ADPCMWhichNibble[ch])
351                   sbox.ADPCMHaveHalfWord[ch] = FALSE;
352             }
353          } // for(int ch...)
354       } // while(sbox.smalldiv <= 0)
355 
356       const uint32 synthtime42 = (timestamp << 1) + sbox.bigdiv;
357       const uint32 synthtime14 = synthtime42 / 3;
358       const uint32 synthtime = synthtime14 >> 3;
359       const unsigned synthtime_phase = synthtime14 & 7;
360 
361       //printf("Phase: %d, %d\n", synthtime42 % 24, (synthtime42 / 3) & 7);
362 
363       for(int ch = 0; ch < 2; ch++)
364       {
365          //if(!ch)
366          //{
367          // printf("%d\n", synthtime - last_synthtime);
368          // last_synthtime = synthtime;
369          //}
370 
371          if(sbox.ADPCMHaveDelta[ch])
372          {
373             sbox.ADPCMPredictor[ch] += sbox.ADPCMDelta[ch];
374 
375             sbox.ADPCMHaveDelta[ch]--;
376 
377             if(sbox.ADPCMPredictor[ch] > 0x3FFF) { sbox.ADPCMPredictor[ch] = 0x3FFF; /*printf("Overflow: %d\n", ch);*/ }
378             if(sbox.ADPCMPredictor[ch] < -0x4000) { sbox.ADPCMPredictor[ch] = -0x4000; /*printf("Underflow: %d\n", ch);*/ }
379          }
380          else
381          {
382 
383          }
384 
385          if(SoundEnabled)
386          {
387             int32 samp[2];
388 
389             if(EmulateBuggyCodec)
390             {
391                samp[0] = (int32)(((sbox.ADPCMPredictor[ch] >> 1) + (sbox.ResetAntiClick[ch] >> 33)) * sbox.VolumeFiltered[ch][0]);
392                samp[1] = (int32)(((sbox.ADPCMPredictor[ch] >> 1) + (sbox.ResetAntiClick[ch] >> 33)) * sbox.VolumeFiltered[ch][1]);
393             }
394             else
395             {
396                samp[0] = (int32)((sbox.ADPCMPredictor[ch] + (sbox.ResetAntiClick[ch] >> 32)) * sbox.VolumeFiltered[ch][0]);
397                samp[1] = (int32)((sbox.ADPCMPredictor[ch] + (sbox.ResetAntiClick[ch] >> 32)) * sbox.VolumeFiltered[ch][1]);
398             }
399 #if 0
400             printf("%d, %f %f\n", ch, sbox.VolumeFiltered[ch][0], sbox.VolumeFiltered[ch][1]);
401 
402             {
403                static int inv = 0x1FFF;
404 
405                samp[0] = samp[1] = inv;
406 
407                if(ch == 1)
408                   inv = -inv;
409             }
410 #endif
411             for(unsigned y = 0; y < 2; y++)
412             {
413                const int32 delta = samp[y] - sbox.ADPCM_last[ch][y];
414                int32* tb = FXsbuf[y]->Buf() + (synthtime & 0xFFFF);
415                const int16* coeffs = ADPCM_PhaseFilter[synthtime_phase];
416 
417                for(unsigned c = 0; c < 7; c++)
418                {
419                   int32 tmp = delta * coeffs[c];
420 
421                   tb[c] += tmp;
422                }
423             }
424 
425             sbox.ADPCM_last[ch][0] = samp[0];
426             sbox.ADPCM_last[ch][1] = samp[1];
427          }
428       }
429 
430       for(int ch = 0; ch < 2; ch++)
431       {
432          sbox.ResetAntiClick[ch] -= sbox.ResetAntiClick[ch] >> 8;
433          //if(ch)
434          // MDFN_DispMessage("%d", (int)(sbox.ResetAntiClick[ch] >> 32));
435       }
436 
437       for(int ch = 0; ch < 2; ch++)
438          for(int lr = 0; lr < 2; lr++)
439          {
440             DoVolumeFilter(ch, lr);
441          }
442       sbox.bigdiv += 1365 * 2 / 2;
443    }
444 
445    return(timestamp + (sbox.bigdiv + 1) / 2);
446 }
447 
SoundBox_Flush(const v810_timestamp_t end_timestamp,v810_timestamp_t * new_base_timestamp,int16 * SoundBuf,const int32 MaxSoundFrames)448 int32 SoundBox_Flush(const v810_timestamp_t end_timestamp, v810_timestamp_t* new_base_timestamp, int16 *SoundBuf, const int32 MaxSoundFrames)
449 {
450    const uint32 end_timestamp_div3 = end_timestamp / 3;
451    const uint32 end_timestamp_div12 = end_timestamp / 12;
452    const uint32 end_timestamp_mod12 = end_timestamp % 12;
453    const unsigned rsc = std::min<unsigned>(65536, end_timestamp_div12);
454    int32 FrameCount = 0;
455 
456    *new_base_timestamp = end_timestamp_mod12;
457 
458    pce_psg->Update(end_timestamp_div3);
459 
460    for(unsigned y = 0; y < 2; y++)
461    {
462       if(SoundEnabled && FXres)
463       {
464          FXsbuf[y]->Integrate(rsc, 0, 0, FXCDDABufs[y]);
465          FrameCount = FXres->Resample(FXsbuf[y], rsc, SoundBuf + y, MaxSoundFrames);
466       }
467       else
468          FXsbuf[y]->ResampleSkipped(rsc);
469 
470       FXCDDABufs[y]->Finish(rsc);
471    }
472 
473    return(FrameCount);
474 }
475 
SoundBox_ResetTS(const v810_timestamp_t ts_base)476 void SoundBox_ResetTS(const v810_timestamp_t ts_base)
477 {
478    pce_psg->ResetTS(ts_base / 3);
479    adpcm_lastts = ts_base;
480 }
481 
SoundBox_Reset(const v810_timestamp_t timestamp)482 void SoundBox_Reset(const v810_timestamp_t timestamp)
483 {
484    SoundBox_ADPCMUpdate(timestamp);
485    pce_psg->Power(timestamp / 3);
486 
487    sbox.ADPCMControl = 0;
488 
489    memset(&sbox.vf_xv, 0, sizeof(sbox.vf_xv));
490    memset(&sbox.vf_yv, 0, sizeof(sbox.vf_yv));
491 
492    for(int lr = 0; lr < 2; lr++)
493    {
494       for(int ch = 0; ch < 2; ch++)
495       {
496          sbox.ADPCMVolume[ch][lr] = 0;
497          sbox.VolumeFiltered[ch][lr] = 0;
498       }
499 
500       sbox.CDDAVolume[lr] = 0;
501    }
502 
503    for(int ch = 0; ch < 2; ch++)
504    {
505       sbox.ADPCMPredictor[ch] = 0;
506       sbox.StepSizeIndex[ch] = 0;
507    }
508 
509    memset(sbox.ADPCMWhichNibble, 0, sizeof(sbox.ADPCMWhichNibble));
510    memset(sbox.ADPCMHalfWord, 0, sizeof(sbox.ADPCMHalfWord));
511    memset(sbox.ADPCMHaveHalfWord, 0, sizeof(sbox.ADPCMHaveHalfWord));
512 
513    SCSICD_SetCDDAVolume(0.50f * sbox.CDDAVolume[0] / 63, 0.50f * sbox.CDDAVolume[1] / 63);
514 
515    sbox.bigdiv = 2;	// TODO: KING->SBOX ADPCM Synch //(1365 - 85 * 4) * 2; //1365 * 2 / 2;
516    sbox.smalldiv = 0;
517 }
518 
SoundBox_StateAction(StateMem * sm,int load,int data_only)519 int SoundBox_StateAction(StateMem *sm, int load, int data_only)
520 {
521    int ret = 1;
522 
523    ret &= pce_psg->StateAction(sm, load, data_only);
524 
525    SFORMAT SoundBox_StateRegs[] =
526    {
527       SFVARN(sbox.ADPCMControl, "ADPCMControl"),
528       SFARRAYN(&sbox.ADPCMVolume[0][0], 2 * 2, "ADPCMVolume"),
529       SFARRAYN(sbox.CDDAVolume, 2, "CDDAVolume"),
530       SFVARN(sbox.bigdiv, "bigdiv"),
531       SFVARN(sbox.smalldiv, "smalldiv"),
532 
533       SFARRAY64N(&sbox.ResetAntiClick[0], 2, "ResetAntiClick"),
534       SFARRAYDN(&sbox.VolumeFiltered[0][0], 2 * 2, "VolumeFiltered"),
535       SFARRAYDN(&sbox.vf_xv[0][0][0], 2 * 2 * (1 + 1), "vf_xv"),
536       SFARRAYDN(&sbox.vf_yv[0][0][0], 2 * 2 * (1 + 1), "vf_yv"),
537 
538       SFARRAY32N(sbox.ADPCMDelta, 2, "ADPCMDelta"),
539       SFARRAY32N(sbox.ADPCMHaveDelta, 2, "ADPCMHaveDelta"),
540 
541       SFARRAY32N(&sbox.ADPCMPredictor[0], 2, "ADPCMPredictor"),
542       SFARRAY32N(&sbox.StepSizeIndex[0], 2, "ADPCMStepSizeIndex"),
543 
544       SFARRAY32N(sbox.ADPCMWhichNibble, 2, "ADPCMWNibble"),
545       SFARRAY16N(sbox.ADPCMHalfWord, 2, "ADPCMHalfWord"),
546       SFARRAYBN(sbox.ADPCMHaveHalfWord, 2, "ADPCMHHW"),
547 
548       SFEND
549    };
550 
551    ret &= MDFNSS_StateAction(sm, load, data_only, SoundBox_StateRegs, "SBOX", false);
552 
553    if(load)
554    {
555       for(int ch = 0; ch < 2; ch++)
556       {
557          clamp(&sbox.ADPCMPredictor[ch], -0x4000, 0x3FFF);
558          clamp(&sbox.ResetAntiClick[ch], (int64)-0x4000 << 32, (int64)0x3FFF << 32);
559 
560          if(!ResetAntiClickEnabled)
561             sbox.ResetAntiClick[ch] = 0;
562 
563          clamp(&sbox.StepSizeIndex[ch], 0, 48);
564 
565          clamp(&sbox.bigdiv, 1, 1365);
566          clamp(&sbox.smalldiv, 1, 8);
567 
568          for(int lr = 0; lr < 2; lr++)
569          {
570 
571          }
572       }
573       SCSICD_SetCDDAVolume(0.50f * sbox.CDDAVolume[0] / 63, 0.50f * sbox.CDDAVolume[1] / 63);
574    }
575    return(ret);
576 }
577