示例#1
0
文件: FiFo.cpp 项目: mfitz21/pcsx2-rr
void __fastcall WriteFIFO_page_6(u32 mem, const mem128_t *value)
{
	jASSUME( (mem >= 0x10006000) && (mem < 0x10007000) );
	GIF_LOG("WriteFIFO/GIF, addr=0x%08X\n", mem);

	//psHu64(mem  ) = value[0];
	//psHu64(mem+8) = value[1];

	psHu64(0x6000) = value[0];
	psHu64(0x6008) = value[1];

	if( mtgsThread != NULL )
	{
		const uint count = mtgsThread->PrepDataPacket( GIF_PATH_3, value, 1 );
		jASSUME( count == 1 );
		u64* data = (u64*)mtgsThread->GetDataPacketPtr();
		data[0] = value[0];
		data[1] = value[1];
		mtgsThread->SendDataPacket();
	}
	else
	{
		FreezeXMMRegs(1);
		FreezeMMXRegs(1);
		GSGIFTRANSFER3((u32*)value, 1);
		FreezeMMXRegs(0);
		FreezeXMMRegs(0);
	}
}
示例#2
0
void vuMicroMemReset()
{
	jASSUME( VU0.Mem != NULL );
	jASSUME( VU1.Mem != NULL );

	memMapVUmicro();

	// === VU0 Initialization ===
	memzero_obj(VU0.ACC);
	memzero_obj(VU0.VF);
	memzero_obj(VU0.VI);
    VU0.VF[0].f.x = 0.0f;
	VU0.VF[0].f.y = 0.0f;
	VU0.VF[0].f.z = 0.0f;
	VU0.VF[0].f.w = 1.0f;
	VU0.VI[0].UL = 0;
	memzero_ptr<4*1024>(VU0.Mem);
	memzero_ptr<4*1024>(VU0.Micro);

	/* this is kinda tricky, maxmem is set to 0x4400 here,
	   tho it's not 100% accurate, since the mem goes from
	   0x0000 - 0x1000 (Mem) and 0x4000 - 0x4400 (VU1 Regs),
	   i guess it shouldn't be a problem,
	   at least hope so :) (linuz)
	*/
	VU0.maxmem = 0x4800-4; //We are allocating 0x800 for vu1 reg's
	VU0.maxmicro = 0x1000-4;
	VU0.vuExec = vu0Exec;
	VU0.vifRegs = vif0Regs;

	// === VU1 Initialization ===
	memzero_obj(VU1.ACC);
	memzero_obj(VU1.VF);
	memzero_obj(VU1.VI);
	VU1.VF[0].f.x = 0.0f;
	VU1.VF[0].f.y = 0.0f;
	VU1.VF[0].f.z = 0.0f;
	VU1.VF[0].f.w = 1.0f;
	VU1.VI[0].UL = 0;
	memzero_ptr<16*1024>(VU1.Mem);
	memzero_ptr<16*1024>(VU1.Micro);

	VU1.maxmem   = 0x4000-4;//16*1024-4;
	VU1.maxmicro = 0x4000-4;
//	VU1.VF       = (VECTOR*)(VU0.Mem + 0x4000);
//	VU1.VI       = (REG_VI*)(VU0.Mem + 0x4200);
	VU1.vuExec   = vu1Exec;
	VU1.vifRegs  = vif1Regs;
}
示例#3
0
文件: FiFo.cpp 项目: mfitz21/pcsx2-rr
void __fastcall WriteFIFO_page_7(u32 mem, const mem128_t *value)
{
	jASSUME( (mem >= 0x10007000) && (mem < 0x10008000) );

	// All addresses in this page map to 0x7000 and 0x7010:
	mem &= 0x10;

	IPU_LOG( "WriteFIFO/IPU, addr=0x%x\n", params mem );

	if( mem == 0 )
	{
		// Should this raise a PS2 exception or just ignore silently?
		Console::Notice( "WriteFIFO/IPUout (ignored)" );
	}
	else
	{
		IPU_LOG("WriteFIFO IPU_in[%d] <- %8.8X_%8.8X_%8.8X_%8.8X\n",
			mem/16, ((u32*)value)[3], ((u32*)value)[2], ((u32*)value)[1], ((u32*)value)[0]);

		//committing every 16 bytes
		while( FIFOto_write((u32*)value, 1) == 0 )
		{
			Console::WriteLn("IPU sleeping");
			Threading::Timeslice();
		}
	}
}
示例#4
0
// Note: When using with 32 bit output buffers, the user of this function is responsible
// for shifting the values to where they need to be manually.  The fixed point depth of
// the sample output is determined by the SndOutVolumeShift, which is the number of bits
// to shift right to get a 16 bit result.
template<typename T> void SndBuffer::ReadSamples(T* bData)
{
    int nSamples = SndOutPacketSize;

    // Problem:
    //  If the SPU2 gets even the least bit out of sync with the SndOut device,
    //  the readpos of the circular buffer will overtake the writepos,
    //  leading to a prolonged period of hopscotching read/write accesses (ie,
    //  lots of staticy crap sound for several seconds).
    //
    // Fix:
    //  If the read position overtakes the write position, abort the
    //  transfer immediately and force the SndOut driver to wait until
    //  the read buffer has filled up again before proceeding.
    //  This will cause one brief hiccup that can never exceed the user's
    //  set buffer length in duration.

    int quietSamples;
    if( CheckUnderrunStatus( nSamples, quietSamples ) )
    {
        jASSUME( nSamples <= SndOutPacketSize );

        // WARNING: This code assumes there's only ONE reading process.
        int b1 = m_size - m_rpos;

        if(b1 > nSamples)
            b1 = nSamples;

        if (AdvancedVolumeControl)
        {
            // First part
            for (int i = 0; i < b1; i++)
                bData[i].AdjustFrom(m_buffer[i + m_rpos]);

            // Second part
            int b2 = nSamples - b1;
            for (int i = 0; i < b2; i++)
                bData[i + b1].AdjustFrom(m_buffer[i]);
        }
        else
        {
            // First part
            for (int i = 0; i < b1; i++)
                bData[i].ResampleFrom(m_buffer[i + m_rpos]);

            // Second part
            int b2 = nSamples - b1;
            for (int i = 0; i < b2; i++)
                bData[i + b1].ResampleFrom(m_buffer[i]);
        }

        _DropSamples_Internal(nSamples);
    }

    // If quietSamples != 0 it means we have an underrun...
    // Let's just dull out some silence, because that's usually the least
    // painful way of dealing with underruns:
    memset( bData, 0, quietSamples * sizeof(T) );
}
示例#5
0
__releaseinline u8* __fastcall iopGetPhysPtr( u32 iopaddr )
{
	const uptr masked = iopaddr & AddressMask;
	const sptr tab = tbl_Translation.Contents[masked/PageSize];

	jASSUME( tab > HandlerId_Maximum );
	return (u8*)tab + (masked & PageMask);
}
示例#6
0
__forceinline s16* GetMemPtr(u32 addr)
{
#ifndef DEBUG_FAST
	// In case you're wondering, this assert is the reason SPU2-X
	// runs so incrediously slow in Debug mode. :P
	jASSUME( addr < 0x100000 );
#endif
	return (_spu2mem+addr);
}
示例#7
0
void TranslationTable::AssignHandler( uint startaddr, HandlerIdentifier handidx, uint bytesize )
{
	const uint startpage = startaddr / PageSize;
	const uint endpage = (startaddr + bytesize + (PageSize-1)) / PageSize;	// rounded up.
	for( uint i=startpage; i<endpage; ++i )
	{
		jASSUME( i < PageCount );		// really really fallible
		Contents[i] = handidx;
	}
}
示例#8
0
	void* Thread::_internal_callback( void* itsme )
	{
		jASSUME( itsme != NULL );

		Thread& owner = *((Thread*)itsme);
		owner.m_returncode = owner.Callback();
		owner.m_terminated = true;

		return NULL;
	}
示例#9
0
// Parameter note: Size should always be a multiple of 128, thanks!
static void CvtPacketToInt( StereoOut32* srcdest, uint size )
{
	jASSUME( (size & 127) == 0 );
	
	const StereoOutFloat* src = (StereoOutFloat*)srcdest;
	StereoOut32* dest = srcdest;

	for( uint i=0; i<size; ++i, ++dest, ++src )
		*dest = (StereoOut32)*src;
}
示例#10
0
void TranslationTable::AssignLookup( uint startaddr, u8* dest, uint bytesize )
{
	const uint startpage = startaddr / PageSize;
	const uint endpage = (startaddr + bytesize + (PageSize-1)) / PageSize;	// rounded up.

	for( uint i=startpage; i<endpage; ++i, dest+=PageSize )
	{
		jASSUME( i < PageCount );		// because I'm fallible.
		Contents[i] = (sptr)dest;
	}
}
示例#11
0
文件: FiFo.cpp 项目: mfitz21/pcsx2-rr
void __fastcall ReadFIFO_page_4(u32 mem, u64 *out)
{
	jASSUME( (mem >= 0x10004000) && (mem < 0x10005000) );
	
	VIF_LOG("ReadFIFO/VIF0 0x%08X\n", mem);
	//out[0] = psHu64(mem  );
	//out[1] = psHu64(mem+8);

	out[0] = psHu64(0x4000);
	out[1] = psHu64(0x4008);
}
示例#12
0
文件: FiFo.cpp 项目: mfitz21/pcsx2-rr
void __fastcall ReadFIFO_page_6(u32 mem, u64 *out)
{
	jASSUME( (mem >= 0x10006000) && (mem < 0x10007000) );

	DevCon::Notice( "ReadFIFO/GIF, addr=0x%x", params mem );

	//out[0] = psHu64(mem  );
	//out[1] = psHu64(mem+8);

	out[0] = psHu64(0x6000);
	out[1] = psHu64(0x6008);	
}
示例#13
0
static void recRecompile()
{
	// Look up the block...
	// (Mask the IOP address accordingly to account for the many various segments and
	//  mirrors).

	u32 masked_pc = iopRegs.pc & IopMemory::AddressMask;
	if( masked_pc < 0x800000 )
		masked_pc &= Ps2MemSize::IopRam-1;
	
	xBlocksMap::Blockmap_iterator blowme( g_PersState.xBlockMap.Map.find( masked_pc ) );

	memzero_obj( g_BlockState );
	//g_BlockState.pc = iopRegs.pc;

	if( blowme == g_PersState.xBlockMap.Map.end() )
	{
		//Console::WriteLn( "IOP First-pass block at PC: 0x%08x  (total blocks=%d)", params masked_pc, g_PersState.xBlockMap.Blocks.GetLength() );
		recIR_FirstPassInterpreter();

		jASSUME( iopRegs.evtCycleCountdown <= iopRegs.evtCycleDuration );
		if( iopRegs.evtCycleCountdown <= 0 )
			iopRegs.ExecutePendingEvents();

		//if( !IsIopRamPage( masked_pc ) )	// disable block checking for non-ram (rom, rom1, etc)
		//	m_blockspace.ramlen = 0;
		
		g_PersState.xBlockMap.Map[masked_pc] = g_PersState.xBlockMap.Blocks.GetLength();
		g_PersState.xBlockMap.Blocks.New().Assign( m_blockspace );
	}
	else
	{
		recBlockItem& mess( g_PersState.xBlockMap.Blocks[blowme->second] );

		if( !mess.InstOptInfo.IsDisposed() )
		{
			//Console::WriteLn( "IOP Second-pass block at PC: 0x%08x  (total blocks=%d)", params masked_pc, g_PersState.xBlockMap.Blocks.GetLength() );

			m_tempIR.GenerateIR( mess );
			mess.InstOptInfo.Dispose();

			m_x86blockstate.AssignBlock( m_tempIR );
			m_x86blockstate.RegisterMapper();

			g_BlockState.xBlockPtr = m_xBlock_CurPtr;
			m_x86blockstate.EmitSomeExecutableGoodness();
			m_xBlock_CurPtr = xGetPtr();
			
			uptr temp = m_tbl_TranslatePC[masked_pc>>XlatePC_PageBitShift];
			uptr* dispatch_ptr = (uptr*)(temp + (masked_pc & XlatePC_PageMask));
			*dispatch_ptr = (uptr)g_BlockState.xBlockPtr;
		}
	}
示例#14
0
// ------------------------------------------------------------------------
//
void xBlocksMap::AddLink( u32 pc, JccComparisonType cctype )
{
	recBlockItem& destItem( _getItem( pc ) );

	const xJumpLink& newlink( destItem.DependentLinks.AddNew( xJumpLink( xGetPtr(), cctype ) ) );
	newlink.SetTarget( m_xBlockLut[pc / 4] );

	if( destItem.x86len != 0 )
	{
		// Sanity Check: if block is already compiled, then the BlockPtr should *not* be dispatcher.
		jASSUME( m_xBlockLut[pc / 4] != DynFunc::Dispatcher );
	}
}
示例#15
0
void SaveState::vuMicroFreeze()
{
	jASSUME( VU0.Mem != NULL );
	jASSUME( VU1.Mem != NULL );

	Freeze(VU0.ACC);
	Freeze(VU0.code);
	FreezeMem(VU0.Mem,   4*1024);
	FreezeMem(VU0.Micro, 4*1024);

	Freeze(VU0.VF);
	if( GetVersion() >= 0x0012 )
		Freeze(VU0.VI);
	else
	{
		// Old versions stored the VIregs as 32 bit values...
		memzero_obj( VU0.VI );
		for(int i=0; i<32; i++ )
			Freeze( VU0.VI[i].UL );
	}

	Freeze(VU1.ACC);
	Freeze(VU1.code);
	FreezeMem(VU1.Mem,   16*1024);
	FreezeMem(VU1.Micro, 16*1024);

	Freeze(VU1.VF);
	if( GetVersion() >= 0x0012 )
		Freeze(VU1.VI);
	else
	{
		// Old versions stored the VIregs as 32 bit values...
		memzero_obj( VU1.VI );
		for(int i=0; i<32; i++ )
			Freeze( VU1.VI[i].UL );
	}

}
示例#16
0
	void* Thread::_internal_callback( void* itsme )
	{
		jASSUME( itsme != NULL );

		pthread_win32_thread_attach_np();

		Thread& owner = *((Thread*)itsme);
		owner.m_returncode = owner.Callback();
		owner.m_terminated = true;

		pthread_win32_thread_detach_np();

		return NULL;
	}
示例#17
0
文件: FiFo.cpp 项目: mfitz21/pcsx2-rr
void __fastcall WriteFIFO_page_4(u32 mem, const mem128_t *value)
{
	jASSUME( (mem >= 0x10004000) && (mem < 0x10005000) );

	VIF_LOG("WriteFIFO/VIF0, addr=0x%08X\n", mem);
	
	//psHu64(mem  ) = value[0];
	//psHu64(mem+8) = value[1];

	psHu64(0x4000) = value[0];
	psHu64(0x4008) = value[1];
	
	vif0ch->qwc += 1;
	int ret = VIF0transfer((u32*)value, 4, 0);
	assert( ret == 0 ); // vif stall code not implemented
}
示例#18
0
void V_Core::WriteRegPS1( u32 mem, u16 value )
{
	jASSUME( Index == 0 );		// Valid on Core 0 only!

	bool show	= true;
	u32 reg		= mem & 0xffff;

	if((reg>=0x1c00)&&(reg<0x1d80))
	{
		//voice values
		u8 voice = ((reg-0x1c00)>>4);
		u8 vval = reg&0xf;
		switch(vval)
		{
			case 0: //VOLL (Volume L)
				Voices[voice].Volume.Left.Mode = 0;
				Voices[voice].Volume.Left.RegSet( value << 1 );
				Voices[voice].Volume.Left.Reg_VOL = value;
			break;

			case 1: //VOLR (Volume R)
				Voices[voice].Volume.Right.Mode = 0;
				Voices[voice].Volume.Right.RegSet( value << 1 );
				Voices[voice].Volume.Right.Reg_VOL = value;
			break;

			case 2:	Voices[voice].Pitch = value; break;
			case 3:	Voices[voice].StartA = (u32)value<<8; break;

			case 4: // ADSR1 (Envelope)
				Voices[voice].ADSR.regADSR1 = value;
			break;

			case 5: // ADSR2 (Envelope)
				Voices[voice].ADSR.regADSR2 = value;
			break;

			case 6:
				Voices[voice].ADSR.Value = ((s32)value<<16) | value;
				ConLog( "* SPU2: Mysterious ADSR Volume Set to 0x%x", value );
			break;

			case 7:	Voices[voice].LoopStartA = (u32)value <<8;	break;

			jNO_DEFAULT;
		}
	}
示例#19
0
void vuMicroMemAlloc()
{
	if( m_vuAllMem == NULL )
		m_vuAllMem = vtlb_malloc( m_vuMemSize, 16, 0x28000000 );

	if( m_vuAllMem == NULL )
		throw Exception::OutOfMemory( "vuMicroMemInit > Failed to allocate VUmicro memory." );

	jASSUME( sizeof( VURegs ) <= 0x800 );

	u8* curpos = m_vuAllMem;
	VU0.Micro	= curpos; curpos += 0x1000;
	VU0.Mem		= curpos; curpos += 0x4000;
	g_pVU1		= (VURegs*)curpos; curpos += 0x800;	
	VU1.Micro	= curpos; curpos += 0x4000;
	VU1.Mem		= curpos; 
	 //curpos += 0x4000;
}
示例#20
0
static __forceinline T _HwRead_16or32_Page1( u32 addr )
{
	HwAddrPrep( 1 );

	// all addresses should be aligned to the data operand size:
	jASSUME(
		( sizeof(T) == 2 && (addr & 1) == 0 ) ||
		( sizeof(T) == 4 && (addr & 3) == 0 )
	);

	u32 masked_addr = pgmsk( addr );
	T ret;

	// ------------------------------------------------------------------------
	// Counters, 16-bit varieties!
	//
	// Note: word reads/writes to the uppoer halfword of the 16 bit registers should 
	// just map to the HW memory map (tested on real IOP) -- ie, a write to the upper
	// halfword of 0xcccc will have those upper values return 0xcccc always.
	//
	if( masked_addr >= 0x100 && masked_addr < 0x130 )
	{
		int cntidx = ( masked_addr >> 4 ) & 0xf;
		switch( masked_addr & 0xf )
		{
			case 0x0:
				ret = (T)IopCounters::ReadCount16( cntidx );
			break;

			case 0x4:
				ret = IopCounters::ReadMode( cntidx );
			break;

			case 0x8:
				ret = (T)IopCounters::ReadTarget16( cntidx );
			break;
			
			default:
				ret = psxHu32(addr);
			break;
		}
	}
示例#21
0
文件: FiFo.cpp 项目: mfitz21/pcsx2-rr
void __fastcall ReadFIFO_page_7(u32 mem, u64 *out)
{
	jASSUME( (mem >= 0x10007000) && (mem < 0x10008000) );

	// All addresses in this page map to 0x7000 and 0x7010:
	mem &= 0x10;

	if( mem == 0 )
	{
		if( g_nIPU0Data > 0 )
		{
			out[0] = *(u64*)(g_pIPU0Pointer);
			out[1] = *(u64*)(g_pIPU0Pointer+8);
			FOreadpos = (FOreadpos + 4) & 31;
			g_nIPU0Data--;
			g_pIPU0Pointer += 16;
		}
	}
	else
		FIFOfrom_readsingle((void*)out);
}
示例#22
0
文件: FiFo.cpp 项目: mfitz21/pcsx2-rr
void __fastcall WriteFIFO_page_5(u32 mem, const mem128_t *value)
{
	jASSUME( (mem >= 0x10005000) && (mem < 0x10006000) );

	VIF_LOG("WriteFIFO/VIF1, addr=0x%08X\n", mem);
	
	//psHu64(mem  ) = value[0];
	//psHu64(mem+8) = value[1];

	psHu64(0x5000) = value[0];
	psHu64(0x5008) = value[1];

	if(vif1Regs->stat & VIF1_STAT_FDR)
		DevCon::Notice("writing to fifo when fdr is set!");
	if( vif1Regs->stat & (VIF1_STAT_INT|VIF1_STAT_VSS|VIF1_STAT_VIS|VIF1_STAT_VFS) )
		DevCon::Notice("writing to vif1 fifo when stalled");

	vif1ch->qwc += 1;
	int ret = VIF1transfer((u32*)value, 4, 0);
	assert( ret == 0 ); // vif stall code not implemented
}
示例#23
0
文件: FiFo.cpp 项目: mfitz21/pcsx2-rr
void __fastcall ReadFIFO_page_5(u32 mem, u64 *out)
{
	jASSUME( (mem >= 0x10005000) && (mem < 0x10006000) );

	VIF_LOG("ReadFIFO/VIF1, addr=0x%08X\n", mem);

	if( vif1Regs->stat & (VIF1_STAT_INT|VIF1_STAT_VSS|VIF1_STAT_VIS|VIF1_STAT_VFS) )
		DevCon::Notice( "Reading from vif1 fifo when stalled" );

	if (vif1Regs->stat & 0x800000)
	{
		if (--psHu32(D1_QWC) == 0)
			vif1Regs->stat&= ~0x1f000000;
	}

	//out[0] = psHu64(mem  );
	//out[1] = psHu64(mem+8);

	out[0] = psHu64(0x5000);
	out[1] = psHu64(0x5008);
}
示例#24
0
	s32 Init()
	{
		numBuffers = Config_WaveOut.NumBuffers;

		MMRESULT woores;

		if (Test()) return -1;

		// TODO : Use dsound to determine the speaker configuration, and expand audio from there.

		#if 0
		int speakerConfig;

		//if( StereoExpansionEnabled )
			speakerConfig = 2;  // better not mess with this in wavout :p (rama)

		// Any windows driver should support stereo at the software level, I should think!
		jASSUME( speakerConfig > 1 );
		LPTHREAD_START_ROUTINE threadproc;

		switch( speakerConfig )
		{
		case 2:
			ConLog( "* SPU2 > Using normal 2 speaker stereo output.\n" );
			threadproc = (LPTHREAD_START_ROUTINE)&RThread<StereoOut16>;
			speakerConfig = 2;
		break;

		case 4:
			ConLog( "* SPU2 > 4 speaker expansion enabled [quadraphenia]\n" );
			threadproc = (LPTHREAD_START_ROUTINE)&RThread<StereoQuadOut16>;
			speakerConfig = 4;
		break;

		case 6:
		case 7:
			ConLog( "* SPU2 > 5.1 speaker expansion enabled.\n" );
			threadproc = (LPTHREAD_START_ROUTINE)&RThread<Stereo51Out16>;
			speakerConfig = 6;
		break;

		default:
			ConLog( "* SPU2 > 7.1 speaker expansion enabled.\n" );
			threadproc = (LPTHREAD_START_ROUTINE)&RThread<Stereo51Out16>;
			speakerConfig = 8;
		break;
		}
		#endif

		wformat.wFormatTag		= WAVE_FORMAT_PCM;
		wformat.nSamplesPerSec	= SampleRate;
		wformat.wBitsPerSample	= 16;
		wformat.nChannels		= 2;
		wformat.nBlockAlign		= ((wformat.wBitsPerSample * wformat.nChannels) / 8);
		wformat.nAvgBytesPerSec	= (wformat.nSamplesPerSec * wformat.nBlockAlign);
		wformat.cbSize			= 0;

		qbuffer = new StereoOut16[BufferSize*numBuffers];

		woores = waveOutOpen(&hwodevice,WAVE_MAPPER,&wformat,0,0,0);
		if (woores != MMSYSERR_NOERROR)
		{
			waveOutGetErrorText(woores,(wchar_t *)&ErrText,255);
			SysMessage("WaveOut Error: %s",ErrText);
			return -1;
		}

		const int BufferSizeBytes = wformat.nBlockAlign * BufferSize;

		for(u32 i=0;i<numBuffers;i++)
		{
			whbuffer[i].dwBufferLength=BufferSizeBytes;
			whbuffer[i].dwBytesRecorded=BufferSizeBytes;
			whbuffer[i].dwFlags=0;
			whbuffer[i].dwLoops=0;
			whbuffer[i].dwUser=0;
			whbuffer[i].lpData=(LPSTR)QBUFFER(i);
			whbuffer[i].lpNext=0;
			whbuffer[i].reserved=0;
			waveOutPrepareHeader(hwodevice,whbuffer+i,sizeof(WAVEHDR));
			whbuffer[i].dwFlags|=WHDR_DONE; //avoid deadlock
		}

		// Start Thread
		// [Air]: The waveout code does not use wait objects, so setting a time critical
		// priority level is a bad idea.  Standard priority will do fine.  The buffer will get the
		// love it needs and won't suck resources idling pointlessly.  Just don't try to
		// run it in uber-low-latency mode.
		waveout_running = true;
		thread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)RThread<StereoOut16>,this,0,&tid);

		return 0;
	}
示例#25
0
BOOL CALLBACK DSound::DSEnumCallback( LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext )
{
	jASSUME( DSoundOut != NULL );
	return DS._DSEnumCallback( lpGuid, lpcstrDescription, lpcstrModule, lpContext );
}