SPU2-X: Some bugfixes to reverb that were introduced in my last commit. Still some minor problems in this build. I'll fix them soon.

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@527 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2009-02-18 14:20:06 +00:00
parent 058ec7db30
commit d97cde6583
4 changed files with 107 additions and 95 deletions

View File

@ -113,13 +113,18 @@ struct StereoOut32
StereoOut16 DownSample() const; StereoOut16 DownSample() const;
StereoOut32 operator+( const StereoOut32& right ) StereoOut32 operator+( const StereoOut32& right ) const
{ {
return StereoOut32( return StereoOut32(
Left + right.Left, Left + right.Left,
Right + right.Right Right + right.Right
); );
} }
StereoOut32 operator/( int src ) const
{
return StereoOut32( Left / src, Right / src );
}
}; };
#endif #endif

View File

@ -50,7 +50,11 @@ void Reverb_AdvanceBuffer( V_Core& thiscore )
{ {
if( (Cycles & 1) && (thiscore.EffectsBufferSize > 0) ) if( (Cycles & 1) && (thiscore.EffectsBufferSize > 0) )
{ {
thiscore.ReverbX = RevbGetIndexer( thiscore, 1 ); //thiscore.ReverbX = RevbGetIndexer( thiscore, 1 );
thiscore.ReverbX += 1;
if( thiscore.ReverbX >= thiscore.EffectsBufferSize ) thiscore.ReverbX = 0;
//thiscore.ReverbX += 1; //thiscore.ReverbX += 1;
//if(thiscore.ReverbX >= (u32)thiscore.EffectsBufferSize ) //if(thiscore.ReverbX >= (u32)thiscore.EffectsBufferSize )
// thiscore.ReverbX %= (u32)thiscore.EffectsBufferSize; // thiscore.ReverbX %= (u32)thiscore.EffectsBufferSize;
@ -64,17 +68,10 @@ StereoOut32 DoReverb( V_Core& thiscore, const StereoOut32& Input )
// Reverb processing occurs at 24khz, so we skip processing every other sample, // Reverb processing occurs at 24khz, so we skip processing every other sample,
// and use the previous calculation for this core instead. // and use the previous calculation for this core instead.
if( thiscore.EffectsBufferSize <= 0 )
{
// StartA is past EndA, so effects are disabled.
//ConLog( " * SPU2: Effects disabled due to leapfrogged EffectsStart." );
return Input;
}
if( (Cycles&1)==0 ) if( (Cycles&1)==0 )
{ {
StereoOut32 retval( thiscore.LastEffect ); StereoOut32 retval( thiscore.LastEffect );
thiscore.LastEffect = Input; thiscore.LastEffect = Input/2;
return retval; return retval;
} }
else else
@ -82,6 +79,13 @@ StereoOut32 DoReverb( V_Core& thiscore, const StereoOut32& Input )
if( thiscore.RevBuffers.NeedsUpdated ) if( thiscore.RevBuffers.NeedsUpdated )
thiscore.UpdateEffectsBufferSize(); thiscore.UpdateEffectsBufferSize();
if( thiscore.EffectsBufferSize <= 0 )
{
// StartA is past EndA, so effects are disabled.
//ConLog( " * SPU2: Effects disabled due to leapfrogged EffectsStart." );
return Input;
}
// Advance the current reverb buffer pointer, and cache the read/write addresses we'll be // Advance the current reverb buffer pointer, and cache the read/write addresses we'll be
// needing for this session of reverb. // needing for this session of reverb.
@ -131,21 +135,20 @@ StereoOut32 DoReverb( V_Core& thiscore, const StereoOut32& Input )
const s32 IIR_INPUT_B0 = ((_spu2mem[src_b0] * thiscore.Revb.IIR_COEF) + (INPUT_SAMPLE.Left * thiscore.Revb.IN_COEF_L))>>16; const s32 IIR_INPUT_B0 = ((_spu2mem[src_b0] * thiscore.Revb.IIR_COEF) + (INPUT_SAMPLE.Left * thiscore.Revb.IN_COEF_L))>>16;
const s32 IIR_INPUT_B1 = ((_spu2mem[src_b1] * thiscore.Revb.IIR_COEF) + (INPUT_SAMPLE.Right * thiscore.Revb.IN_COEF_R))>>16; const s32 IIR_INPUT_B1 = ((_spu2mem[src_b1] * thiscore.Revb.IIR_COEF) + (INPUT_SAMPLE.Right * thiscore.Revb.IN_COEF_R))>>16;
//const s32 IIR_A0 = (IIR_INPUT_A0 * thiscore.Revb.IIR_ALPHA) + (_spu2mem[dest_a0] * (0x7fff - thiscore.Revb.IIR_ALPHA)); /*const s32 IIR_A0 = (IIR_INPUT_A0 * thiscore.Revb.IIR_ALPHA) + (_spu2mem[dest_a0] * (0x7fff - thiscore.Revb.IIR_ALPHA));
//const s32 IIR_A1 = (IIR_INPUT_A1 * thiscore.Revb.IIR_ALPHA) + (_spu2mem[dest_a1] * (0x7fff - thiscore.Revb.IIR_ALPHA)); const s32 IIR_A1 = (IIR_INPUT_A1 * thiscore.Revb.IIR_ALPHA) + (_spu2mem[dest_a1] * (0x7fff - thiscore.Revb.IIR_ALPHA));
//const s32 IIR_B0 = (IIR_INPUT_B0 * thiscore.Revb.IIR_ALPHA) + (_spu2mem[dest_b0] * (0x7fff - thiscore.Revb.IIR_ALPHA)); const s32 IIR_B0 = (IIR_INPUT_B0 * thiscore.Revb.IIR_ALPHA) + (_spu2mem[dest_b0] * (0x7fff - thiscore.Revb.IIR_ALPHA));
//const s32 IIR_B1 = (IIR_INPUT_B1 * thiscore.Revb.IIR_ALPHA) + (_spu2mem[dest_b1] * (0x7fff - thiscore.Revb.IIR_ALPHA)); const s32 IIR_B1 = (IIR_INPUT_B1 * thiscore.Revb.IIR_ALPHA) + (_spu2mem[dest_b1] * (0x7fff - thiscore.Revb.IIR_ALPHA));
_spu2mem[dest2_a0] = clamp_mix( IIR_A0 >> 16 );
//_spu2mem[dest2_a0] = clamp_mix( IIR_A0 >> 16 ); _spu2mem[dest2_a1] = clamp_mix( IIR_A1 >> 16 );
//_spu2mem[dest2_a1] = clamp_mix( IIR_A1 >> 16 ); _spu2mem[dest2_b0] = clamp_mix( IIR_B0 >> 16 );
//_spu2mem[dest2_b0] = clamp_mix( IIR_B0 >> 16 ); _spu2mem[dest2_b1] = clamp_mix( IIR_B1 >> 16 );*/
//_spu2mem[dest2_b1] = clamp_mix( IIR_B1 >> 16 );
// Faster single-mul approach to interpolation: // Faster single-mul approach to interpolation:
const s32 IIR_A0 = IIR_INPUT_A0 + ((_spu2mem[dest_a0]-IIR_INPUT_A0) * thiscore.Revb.IIR_ALPHA)>>16; const s32 IIR_A0 = IIR_INPUT_A0 + (((_spu2mem[dest_a0]-IIR_INPUT_A0) * thiscore.Revb.IIR_ALPHA)>>16);
const s32 IIR_A1 = IIR_INPUT_A1 + ((_spu2mem[dest_a1]-IIR_INPUT_A1) * thiscore.Revb.IIR_ALPHA)>>16; const s32 IIR_A1 = IIR_INPUT_A1 + (((_spu2mem[dest_a1]-IIR_INPUT_A1) * thiscore.Revb.IIR_ALPHA)>>16);
const s32 IIR_B0 = IIR_INPUT_B0 + ((_spu2mem[dest_b0]-IIR_INPUT_B0) * thiscore.Revb.IIR_ALPHA)>>16; const s32 IIR_B0 = IIR_INPUT_B0 + (((_spu2mem[dest_b0]-IIR_INPUT_B0) * thiscore.Revb.IIR_ALPHA)>>16);
const s32 IIR_B1 = IIR_INPUT_B1 + ((_spu2mem[dest_b1]-IIR_INPUT_B1) * thiscore.Revb.IIR_ALPHA)>>16; const s32 IIR_B1 = IIR_INPUT_B1 + (((_spu2mem[dest_b1]-IIR_INPUT_B1) * thiscore.Revb.IIR_ALPHA)>>16);
_spu2mem[dest2_a0] = clamp_mix( IIR_A0 ); _spu2mem[dest2_a0] = clamp_mix( IIR_A0 );
_spu2mem[dest2_a1] = clamp_mix( IIR_A1 ); _spu2mem[dest2_a1] = clamp_mix( IIR_A1 );

View File

@ -200,18 +200,18 @@ void V_Core::Reset()
s32 V_Core::EffectsBufferIndexer( s32 offset ) const s32 V_Core::EffectsBufferIndexer( s32 offset ) const
{ {
u32 pos = EffectsStartA + ReverbX + offset; u32 pos = EffectsStartA + offset;
// Need to use modulus here, because games can and will drop the buffer size // Need to use modulus here, because games can and will drop the buffer size
// without notice, and it leads to offsets several times past the end of the buffer. // without notice, and it leads to offsets several times past the end of the buffer.
if( pos > EffectsEndA ) if( pos > EffectsEndA )
{ {
pos = EffectsStartA + ((ReverbX + offset) % (u32)EffectsBufferSize); pos = EffectsStartA + (offset % EffectsBufferSize);
} }
else if( pos < EffectsStartA ) else if( pos < EffectsStartA )
{ {
pos = EffectsEndA+1 - ((ReverbX + offset) % (u32)EffectsBufferSize ); pos = EffectsEndA+1 - (offset % EffectsBufferSize );
} }
return pos; return pos;
} }
@ -230,12 +230,11 @@ void V_Core::UpdateFeedbackBuffersB()
void V_Core::UpdateEffectsBufferSize() void V_Core::UpdateEffectsBufferSize()
{ {
ReverbX = 0;
const s32 newbufsize = EffectsEndA - EffectsStartA + 1; const s32 newbufsize = EffectsEndA - EffectsStartA + 1;
if( !RevBuffers.NeedsUpdated && newbufsize == EffectsBufferSize ) return; if( !RevBuffers.NeedsUpdated && newbufsize == EffectsBufferSize ) return;
RevBuffers.NeedsUpdated = false; RevBuffers.NeedsUpdated = false;
EffectsBufferSize = newbufsize;
if( EffectsBufferSize == 0 ) return; if( EffectsBufferSize == 0 ) return;
@ -675,6 +674,7 @@ u16 SPU_ps1_read(u32 mem)
{ {
value = Cores[0].EffectsStartA>>3; value = Cores[0].EffectsStartA>>3;
Cores[0].UpdateEffectsBufferSize(); Cores[0].UpdateEffectsBufferSize();
Cores[0].ReverbX = 0;
} }
break; break;
case 0x1da4: value = Cores[0].IRQA>>3; break; case 0x1da4: value = Cores[0].IRQA>>3; break;
@ -862,52 +862,53 @@ __forceinline void SPU2_FastWrite( u32 rmem, u16 value )
} }
else else
{ {
V_Core& thiscore = Cores[core];
switch(omem) switch(omem)
{ {
case REG_C_ATTR: case REG_C_ATTR:
{ {
int irqe = Cores[core].IRQEnable; int irqe = thiscore.IRQEnable;
int bit0 = Cores[core].AttrBit0; int bit0 = thiscore.AttrBit0;
int bit4 = Cores[core].AttrBit4; int bit4 = thiscore.AttrBit4;
if( ((value>>15)&1) && (!Cores[core].CoreEnabled) && (Cores[core].InitDelay==0) ) // on init/reset if( ((value>>15)&1) && (!thiscore.CoreEnabled) && (thiscore.InitDelay==0) ) // on init/reset
{ {
if(hasPtr) if(hasPtr)
{ {
Cores[core].InitDelay=1; thiscore.InitDelay=1;
Cores[core].Regs.STATX=0; thiscore.Regs.STATX=0;
} }
else else
{ {
Cores[core].Reset(); thiscore.Reset();
} }
} }
Cores[core].AttrBit0 =(value>> 0) & 0x01; //1 bit thiscore.AttrBit0 =(value>> 0) & 0x01; //1 bit
Cores[core].DMABits =(value>> 1) & 0x07; //3 bits thiscore.DMABits =(value>> 1) & 0x07; //3 bits
Cores[core].AttrBit4 =(value>> 4) & 0x01; //1 bit thiscore.AttrBit4 =(value>> 4) & 0x01; //1 bit
Cores[core].AttrBit5 =(value>> 5) & 0x01; //1 bit thiscore.AttrBit5 =(value>> 5) & 0x01; //1 bit
Cores[core].IRQEnable =(value>> 6) & 0x01; //1 bit thiscore.IRQEnable =(value>> 6) & 0x01; //1 bit
Cores[core].FxEnable =(value>> 7) & 0x01; //1 bit thiscore.FxEnable =(value>> 7) & 0x01; //1 bit
Cores[core].NoiseClk =(value>> 8) & 0x3f; //6 bits thiscore.NoiseClk =(value>> 8) & 0x3f; //6 bits
//Cores[core].Mute =(value>>14) & 0x01; //1 bit //thiscore.Mute =(value>>14) & 0x01; //1 bit
Cores[core].Mute=0; thiscore.Mute=0;
Cores[core].CoreEnabled=(value>>15) & 0x01; //1 bit thiscore.CoreEnabled=(value>>15) & 0x01; //1 bit
Cores[core].Regs.ATTR =value&0x7fff; thiscore.Regs.ATTR =value&0x7fff;
if(value&0x000E) if(value&0x000E)
{ {
ConLog(" * SPU2: Core %d ATTR unknown bits SET! value=%04x\n",core,value); ConLog(" * SPU2: Core %d ATTR unknown bits SET! value=%04x\n",core,value);
} }
if(Cores[core].AttrBit0!=bit0) if(thiscore.AttrBit0!=bit0)
{ {
ConLog(" * SPU2: ATTR bit 0 set to %d\n",Cores[core].AttrBit0); ConLog(" * SPU2: ATTR bit 0 set to %d\n",thiscore.AttrBit0);
} }
if(Cores[core].IRQEnable!=irqe) if(thiscore.IRQEnable!=irqe)
{ {
ConLog(" * SPU2: IRQ %s\n",((Cores[core].IRQEnable==0)?"disabled":"enabled")); ConLog(" * SPU2: IRQ %s\n",((thiscore.IRQEnable==0)?"disabled":"enabled"));
if(!Cores[core].IRQEnable) if(!thiscore.IRQEnable)
Spdif.Info=0; Spdif.Info=0;
} }
@ -915,23 +916,23 @@ __forceinline void SPU2_FastWrite( u32 rmem, u16 value )
break; break;
case REG_S_PMON: case REG_S_PMON:
vx=2; for (vc=1;vc<16;vc++) { Cores[core].Voices[vc].Modulated=(s8)((value & vx)/vx); vx<<=1; } vx=2; for (vc=1;vc<16;vc++) { thiscore.Voices[vc].Modulated=(s8)((value & vx)/vx); vx<<=1; }
SetLoWord( Cores[core].Regs.PMON, value ); SetLoWord( thiscore.Regs.PMON, value );
break; break;
case (REG_S_PMON + 2): case (REG_S_PMON + 2):
vx=1; for (vc=16;vc<24;vc++) { Cores[core].Voices[vc].Modulated=(s8)((value & vx)/vx); vx<<=1; } vx=1; for (vc=16;vc<24;vc++) { thiscore.Voices[vc].Modulated=(s8)((value & vx)/vx); vx<<=1; }
SetHiWord( Cores[core].Regs.PMON, value ); SetHiWord( thiscore.Regs.PMON, value );
break; break;
case REG_S_NON: case REG_S_NON:
vx=1; for (vc=0;vc<16;vc++) { Cores[core].Voices[vc].Noise=(s8)((value & vx)/vx); vx<<=1; } vx=1; for (vc=0;vc<16;vc++) { thiscore.Voices[vc].Noise=(s8)((value & vx)/vx); vx<<=1; }
SetLoWord( Cores[core].Regs.NON, value ); SetLoWord( thiscore.Regs.NON, value );
break; break;
case (REG_S_NON + 2): case (REG_S_NON + 2):
vx=1; for (vc=16;vc<24;vc++) { Cores[core].Voices[vc].Noise=(s8)((value & vx)/vx); vx<<=1; } vx=1; for (vc=16;vc<24;vc++) { thiscore.Voices[vc].Noise=(s8)((value & vx)/vx); vx<<=1; }
SetHiWord( Cores[core].Regs.NON, value ); SetHiWord( thiscore.Regs.NON, value );
break; break;
// Games like to repeatedly write these regs over and over with the same value, hence // Games like to repeatedly write these regs over and over with the same value, hence
@ -940,12 +941,12 @@ __forceinline void SPU2_FastWrite( u32 rmem, u16 value )
{ \ { \
const uint start_bit = hiword ? 16 : 0; \ const uint start_bit = hiword ? 16 : 0; \
const uint end_bit = hiword ? 24 : 16; \ const uint end_bit = hiword ? 24 : 16; \
const u32 result = hiword ? SetHiWord( Cores[core].Regs.reg_out, value ) : SetLoWord( Cores[core].Regs.reg_out, value ); \ const u32 result = hiword ? SetHiWord( thiscore.Regs.reg_out, value ) : SetLoWord( thiscore.Regs.reg_out, value ); \
if( result == Cores[core].Regs.reg_out ) return; \ if( result == thiscore.Regs.reg_out ) return; \
\ \
Cores[core].Regs.reg_out = result; \ thiscore.Regs.reg_out = result; \
for (uint vc=start_bit, vx=1; vc<end_bit; vc++, vx<<=1) \ for (uint vc=start_bit, vx=1; vc<end_bit; vc++, vx<<=1) \
Cores[core].Voices[vc].mask_out = (value & vx) ? -1 : 0; \ thiscore.Voices[vc].mask_out = (value & vx) ? -1 : 0; \
} }
case REG_S_VMIXL: case REG_S_VMIXL:
@ -987,19 +988,19 @@ __forceinline void SPU2_FastWrite( u32 rmem, u16 value )
vx = value; vx = value;
if (core == 0) vx&=0xFF0; if (core == 0) vx&=0xFF0;
Cores[core].ExtWetR = (vx & 0x001) ? -1 : 0; thiscore.ExtWetR = (vx & 0x001) ? -1 : 0;
Cores[core].ExtWetL = (vx & 0x002) ? -1 : 0; thiscore.ExtWetL = (vx & 0x002) ? -1 : 0;
Cores[core].ExtDryR = (vx & 0x004) ? -1 : 0; thiscore.ExtDryR = (vx & 0x004) ? -1 : 0;
Cores[core].ExtDryL = (vx & 0x008) ? -1 : 0; thiscore.ExtDryL = (vx & 0x008) ? -1 : 0;
Cores[core].InpWetR = (vx & 0x010) ? -1 : 0; thiscore.InpWetR = (vx & 0x010) ? -1 : 0;
Cores[core].InpWetL = (vx & 0x020) ? -1 : 0; thiscore.InpWetL = (vx & 0x020) ? -1 : 0;
Cores[core].InpDryR = (vx & 0x040) ? -1 : 0; thiscore.InpDryR = (vx & 0x040) ? -1 : 0;
Cores[core].InpDryL = (vx & 0x080) ? -1 : 0; thiscore.InpDryL = (vx & 0x080) ? -1 : 0;
Cores[core].SndWetR = (vx & 0x100) ? -1 : 0; thiscore.SndWetR = (vx & 0x100) ? -1 : 0;
Cores[core].SndWetL = (vx & 0x200) ? -1 : 0; thiscore.SndWetL = (vx & 0x200) ? -1 : 0;
Cores[core].SndDryR = (vx & 0x400) ? -1 : 0; thiscore.SndDryR = (vx & 0x400) ? -1 : 0;
Cores[core].SndDryL = (vx & 0x800) ? -1 : 0; thiscore.SndDryL = (vx & 0x800) ? -1 : 0;
Cores[core].Regs.MMIX = value; thiscore.Regs.MMIX = value;
break; break;
case (REG_S_KON + 2): case (REG_S_KON + 2):
@ -1019,11 +1020,11 @@ __forceinline void SPU2_FastWrite( u32 rmem, u16 value )
break; break;
case REG_S_ENDX: case REG_S_ENDX:
Cores[core].Regs.ENDX&=0x00FF0000; thiscore.Regs.ENDX&=0x00FF0000;
break; break;
case (REG_S_ENDX + 2): case (REG_S_ENDX + 2):
Cores[core].Regs.ENDX&=0xFFFF; thiscore.Regs.ENDX&=0xFFFF;
break; break;
// Reverb Start and End Address Writes! // Reverb Start and End Address Writes!
@ -1034,18 +1035,21 @@ __forceinline void SPU2_FastWrite( u32 rmem, u16 value )
// change the end address anyway. // change the end address anyway.
case REG_A_ESA: case REG_A_ESA:
SetHiWord( Cores[core].EffectsStartA, value ); SetHiWord( thiscore.EffectsStartA, value );
Cores[core].UpdateEffectsBufferSize(); thiscore.UpdateEffectsBufferSize();
thiscore.ReverbX = 0;
break; break;
case (REG_A_ESA + 2): case (REG_A_ESA + 2):
SetLoWord( Cores[core].EffectsStartA, value ); SetLoWord( thiscore.EffectsStartA, value );
Cores[core].UpdateEffectsBufferSize(); thiscore.UpdateEffectsBufferSize();
thiscore.ReverbX = 0;
break; break;
case REG_A_EEA: case REG_A_EEA:
Cores[core].EffectsEndA = ((u32)value<<16) | 0xFFFF; thiscore.EffectsEndA = ((u32)value<<16) | 0xFFFF;
Cores[core].UpdateEffectsBufferSize(); thiscore.UpdateEffectsBufferSize();
thiscore.ReverbX = 0;
break; break;
// Master Volume Address Write! // Master Volume Address Write!
@ -1053,7 +1057,7 @@ __forceinline void SPU2_FastWrite( u32 rmem, u16 value )
case REG_P_MVOLL: case REG_P_MVOLL:
case REG_P_MVOLR: case REG_P_MVOLR:
{ {
V_VolumeSlide& thisvol = (omem==REG_P_MVOLL) ? Cores[core].MasterVol.Left : Cores[core].MasterVol.Right; V_VolumeSlide& thisvol = (omem==REG_P_MVOLL) ? thiscore.MasterVol.Left : thiscore.MasterVol.Right;
if( value & 0x8000 ) // +Lin/-Lin/+Exp/-Exp if( value & 0x8000 ) // +Lin/-Lin/+Exp/-Exp
{ {
@ -1075,36 +1079,36 @@ __forceinline void SPU2_FastWrite( u32 rmem, u16 value )
break; break;
case REG_P_EVOLL: case REG_P_EVOLL:
Cores[core].FxVol.Left = GetVol32( value ); thiscore.FxVol.Left = GetVol32( value );
break; break;
case REG_P_EVOLR: case REG_P_EVOLR:
Cores[core].FxVol.Right = GetVol32( value ); thiscore.FxVol.Right = GetVol32( value );
break; break;
case REG_P_AVOLL: case REG_P_AVOLL:
Cores[core].ExtVol.Left = GetVol32( value ); thiscore.ExtVol.Left = GetVol32( value );
break; break;
case REG_P_AVOLR: case REG_P_AVOLR:
Cores[core].ExtVol.Right = GetVol32( value ); thiscore.ExtVol.Right = GetVol32( value );
break; break;
case REG_P_BVOLL: case REG_P_BVOLL:
Cores[core].InpVol.Left = GetVol32( value ); thiscore.InpVol.Left = GetVol32( value );
break; break;
case REG_P_BVOLR: case REG_P_BVOLR:
Cores[core].InpVol.Right = GetVol32( value ); thiscore.InpVol.Right = GetVol32( value );
break; break;
case REG_S_ADMAS: case REG_S_ADMAS:
//ConLog(" * SPU2: Core %d AutoDMAControl set to %d (%d)\n",core,value, Cycles); //ConLog(" * SPU2: Core %d AutoDMAControl set to %d (%d)\n",core,value, Cycles);
Cores[core].AutoDMACtrl=value; thiscore.AutoDMACtrl=value;
if(value==0) if(value==0)
{ {
Cores[core].AdmaInProgress=0; thiscore.AdmaInProgress=0;
} }
break; break;

View File

@ -144,9 +144,9 @@ public:
memset(&wfx, 0, sizeof(WAVEFORMATEX)); memset(&wfx, 0, sizeof(WAVEFORMATEX));
wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nSamplesPerSec = SampleRate; wfx.nSamplesPerSec = SampleRate;
wfx.nChannels = speakerConfig; wfx.nChannels = (WORD)speakerConfig;
wfx.wBitsPerSample = 16; wfx.wBitsPerSample = 16;
wfx.nBlockAlign = 2*speakerConfig; wfx.nBlockAlign = 2*(WORD)speakerConfig;
wfx.nAvgBytesPerSec = SampleRate * wfx.nBlockAlign; wfx.nAvgBytesPerSec = SampleRate * wfx.nBlockAlign;
wfx.cbSize = 0; wfx.cbSize = 0;