Commit 8c2ec131 authored by Robert Reif's avatar Robert Reif Committed by Alexandre Julliard

Added a configuration option for setting the hardware acceleration

level just like the direct sound control panel app. More work on the full duplex interface so the compile warning is no longer generated. Full duplex interface is still stubbed out. Moved full duplex mixer reset fix into wineoss driver where it belongs. Added some more locking fixes. Capture and full duplex are now stable.
parent 998f56e0
...@@ -64,6 +64,16 @@ static HRESULT DSOUND_CreateDirectSoundCaptureBuffer( ...@@ -64,6 +64,16 @@ static HRESULT DSOUND_CreateDirectSoundCaptureBuffer(
IDirectSoundCaptureImpl *ipDSC, IDirectSoundCaptureImpl *ipDSC,
LPCDSCBUFFERDESC lpcDSCBufferDesc, LPCDSCBUFFERDESC lpcDSCBufferDesc,
LPVOID* ppobj ); LPVOID* ppobj );
static HRESULT WINAPI IDirectSoundFullDuplexImpl_Initialize(
LPDIRECTSOUNDFULLDUPLEX iface,
LPCGUID pCaptureGuid,
LPCGUID pRendererGuid,
LPCDSCBUFFERDESC lpDscBufferDesc,
LPCDSBUFFERDESC lpDsBufferDesc,
HWND hWnd,
DWORD dwLevel,
LPLPDIRECTSOUNDCAPTUREBUFFER8 lplpDirectSoundCaptureBuffer8,
LPLPDIRECTSOUNDBUFFER8 lplpDirectSoundBuffer8 );
static ICOM_VTABLE(IDirectSoundCapture) dscvt; static ICOM_VTABLE(IDirectSoundCapture) dscvt;
static ICOM_VTABLE(IDirectSoundCaptureBuffer8) dscbvt; static ICOM_VTABLE(IDirectSoundCaptureBuffer8) dscbvt;
...@@ -427,9 +437,13 @@ IDirectSoundCaptureImpl_Initialize( ...@@ -427,9 +437,13 @@ IDirectSoundCaptureImpl_Initialize(
} }
err = DS_OK; err = DS_OK;
/* Disable the direct sound driver to force emulation if requested. */
if (ds_hw_accel == DS_HW_ACCEL_EMULATION)
This->driver = NULL;
/* Get driver description */ /* Get driver description */
if (This->driver) { if (This->driver) {
ERR("You have a sound card that is Direct Sound Capture capable but the driver is not finished\n"); ERR("You have a sound card that is Direct Sound Capture capable but the driver is not finished. You can add a line to the wine config file in [dsound]: \"HardwareAcceleration\" = \"Emulation\" to force emulation mode.\n");
/* FIXME: remove this return to test driver */ /* FIXME: remove this return to test driver */
return DSERR_NODRIVER; return DSERR_NODRIVER;
TRACE("using DirectSound driver\n"); TRACE("using DirectSound driver\n");
...@@ -455,6 +469,7 @@ IDirectSoundCaptureImpl_Initialize( ...@@ -455,6 +469,7 @@ IDirectSoundCaptureImpl_Initialize(
/* the driver is now open, so it's now allowed to call GetCaps */ /* the driver is now open, so it's now allowed to call GetCaps */
if (This->driver) { if (This->driver) {
This->drvcaps.dwSize = sizeof(This->drvcaps);
err = IDsCaptureDriver_GetCaps(This->driver,&(This->drvcaps)); err = IDsCaptureDriver_GetCaps(This->driver,&(This->drvcaps));
if (err != DS_OK) { if (err != DS_OK) {
WARN("IDsCaptureDriver_GetCaps failed\n"); WARN("IDsCaptureDriver_GetCaps failed\n");
...@@ -580,10 +595,12 @@ DSOUND_CreateDirectSoundCaptureBuffer( ...@@ -580,10 +595,12 @@ DSOUND_CreateDirectSoundCaptureBuffer(
} else { } else {
LPBYTE newbuf; LPBYTE newbuf;
DWORD buflen; DWORD buflen;
DWORD flags = CALLBACK_FUNCTION;
if (ds_hw_accel != DS_HW_ACCEL_EMULATION)
flags |= WAVE_DIRECTSOUND;
err = mmErr(waveInOpen(&(ipDSC->hwi), err = mmErr(waveInOpen(&(ipDSC->hwi),
ipDSC->drvdesc.dnDevNode, &(ipDSC->wfx), ipDSC->drvdesc.dnDevNode, &(ipDSC->wfx),
(DWORD)DSOUND_capture_callback, (DWORD)ipDSC, (DWORD)DSOUND_capture_callback, (DWORD)ipDSC, flags));
CALLBACK_FUNCTION | WAVE_DIRECTSOUND));
if (err != DS_OK) { if (err != DS_OK) {
WARN("waveInOpen failed\n"); WARN("waveInOpen failed\n");
ipDSC->hwi = 0; ipDSC->hwi = 0;
...@@ -757,6 +774,7 @@ IDirectSoundCaptureBufferImpl_GetCurrentPosition( ...@@ -757,6 +774,7 @@ IDirectSoundCaptureBufferImpl_GetCurrentPosition(
if (This->dsound->driver) { if (This->dsound->driver) {
return IDsCaptureDriverBuffer_GetPosition(This->dsound->hwbuf, lpdwCapturePosition, lpdwReadPosition ); return IDsCaptureDriverBuffer_GetPosition(This->dsound->hwbuf, lpdwCapturePosition, lpdwReadPosition );
} else if (This->dsound->hwi) { } else if (This->dsound->hwi) {
EnterCriticalSection(&(This->dsound->lock));
TRACE("old This->dsound->state=%ld\n",This->dsound->state); TRACE("old This->dsound->state=%ld\n",This->dsound->state);
if (lpdwCapturePosition) { if (lpdwCapturePosition) {
MMTIME mtime; MMTIME mtime;
...@@ -777,6 +795,7 @@ IDirectSoundCaptureBufferImpl_GetCurrentPosition( ...@@ -777,6 +795,7 @@ IDirectSoundCaptureBufferImpl_GetCurrentPosition(
*lpdwReadPosition = This->dsound->read_position; *lpdwReadPosition = This->dsound->read_position;
} }
TRACE("new This->dsound->state=%ld\n",This->dsound->state); TRACE("new This->dsound->state=%ld\n",This->dsound->state);
LeaveCriticalSection(&(This->dsound->lock));
if (lpdwCapturePosition) TRACE("*lpdwCapturePosition=%ld\n",*lpdwCapturePosition); if (lpdwCapturePosition) TRACE("*lpdwCapturePosition=%ld\n",*lpdwCapturePosition);
if (lpdwReadPosition) TRACE("*lpdwReadPosition=%ld\n",*lpdwReadPosition); if (lpdwReadPosition) TRACE("*lpdwReadPosition=%ld\n",*lpdwReadPosition);
} else { } else {
...@@ -838,6 +857,8 @@ IDirectSoundCaptureBufferImpl_GetStatus( ...@@ -838,6 +857,8 @@ IDirectSoundCaptureBufferImpl_GetStatus(
} }
*lpdwStatus = 0; *lpdwStatus = 0;
EnterCriticalSection(&(This->dsound->lock));
TRACE("old This->dsound->state=%ld, old lpdwStatus=%08lx\n",This->dsound->state,*lpdwStatus); TRACE("old This->dsound->state=%ld, old lpdwStatus=%08lx\n",This->dsound->state,*lpdwStatus);
if ((This->dsound->state == STATE_STARTING) || if ((This->dsound->state == STATE_STARTING) ||
(This->dsound->state == STATE_CAPTURING)) { (This->dsound->state == STATE_CAPTURING)) {
...@@ -846,6 +867,7 @@ IDirectSoundCaptureBufferImpl_GetStatus( ...@@ -846,6 +867,7 @@ IDirectSoundCaptureBufferImpl_GetStatus(
*lpdwStatus |= DSCBSTATUS_LOOPING; *lpdwStatus |= DSCBSTATUS_LOOPING;
} }
TRACE("new This->dsound->state=%ld, new lpdwStatus=%08lx\n",This->dsound->state,*lpdwStatus); TRACE("new This->dsound->state=%ld, new lpdwStatus=%08lx\n",This->dsound->state,*lpdwStatus);
LeaveCriticalSection(&(This->dsound->lock));
TRACE("status=%lx\n", *lpdwStatus); TRACE("status=%lx\n", *lpdwStatus);
TRACE("returning DS_OK\n"); TRACE("returning DS_OK\n");
...@@ -1170,6 +1192,63 @@ static ICOM_VTABLE(IDirectSoundCaptureBuffer8) dscbvt = ...@@ -1170,6 +1192,63 @@ static ICOM_VTABLE(IDirectSoundCaptureBuffer8) dscbvt =
IDirectSoundCaptureBufferImpl_GetFXStatus IDirectSoundCaptureBufferImpl_GetFXStatus
}; };
/***************************************************************************
* DirectSoundFullDuplexCreate8 [DSOUND.8]
*
* Create and initialize a DirectSoundFullDuplex interface
*
* RETURNS
* Success: DS_OK
* Failure: DSERR_NOAGGREGATION, DSERR_ALLOCATED, DSERR_INVALIDPARAM,
* DSERR_OUTOFMEMORY DSERR_INVALIDCALL DSERR_NODRIVER
*/
HRESULT WINAPI
DirectSoundFullDuplexCreate8(
LPCGUID pcGuidCaptureDevice,
LPCGUID pcGuidRenderDevice,
LPCDSCBUFFERDESC pcDSCBufferDesc,
LPCDSBUFFERDESC pcDSBufferDesc,
HWND hWnd,
DWORD dwLevel,
LPDIRECTSOUNDFULLDUPLEX *ppDSFD,
LPDIRECTSOUNDCAPTUREBUFFER8 *ppDSCBuffer8,
LPDIRECTSOUNDBUFFER8 *ppDSBuffer8,
LPUNKNOWN pUnkOuter)
{
IDirectSoundFullDuplexImpl** ippDSFD=(IDirectSoundFullDuplexImpl**)ppDSFD;
TRACE("(%s,%s,%p,%p,%lx,%lx,%p,%p,%p,%p)\n", debugstr_guid(pcGuidCaptureDevice),
debugstr_guid(pcGuidRenderDevice), pcDSCBufferDesc, pcDSBufferDesc,
(DWORD)hWnd, dwLevel, ppDSFD, ppDSCBuffer8, ppDSBuffer8, pUnkOuter);
if ( pUnkOuter ) {
WARN("pUnkOuter != 0\n");
return DSERR_NOAGGREGATION;
}
*ippDSFD = (IDirectSoundFullDuplexImpl*)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, sizeof(IDirectSoundFullDuplexImpl));
if (*ippDSFD == NULL) {
TRACE("couldn't allocate memory\n");
return DSERR_OUTOFMEMORY;
}
else
{
ICOM_THIS(IDirectSoundFullDuplexImpl, *ippDSFD);
This->ref = 1;
InitializeCriticalSection( &(This->lock) );
ICOM_VTBL(This) = &dsfdvt;
return IDirectSoundFullDuplexImpl_Initialize( (LPDIRECTSOUNDFULLDUPLEX)This,
pcGuidCaptureDevice, pcGuidRenderDevice,
pcDSCBufferDesc, pcDSBufferDesc,
hWnd, dwLevel, ppDSCBuffer8, ppDSBuffer8);
}
}
static HRESULT WINAPI static HRESULT WINAPI
IDirectSoundFullDuplexImpl_QueryInterface( IDirectSoundFullDuplexImpl_QueryInterface(
LPDIRECTSOUNDFULLDUPLEX iface, LPDIRECTSOUNDFULLDUPLEX iface,
...@@ -1192,6 +1271,7 @@ IDirectSoundFullDuplexImpl_AddRef( LPDIRECTSOUNDFULLDUPLEX iface ) ...@@ -1192,6 +1271,7 @@ IDirectSoundFullDuplexImpl_AddRef( LPDIRECTSOUNDFULLDUPLEX iface )
TRACE( "(%p) was 0x%08lx\n", This, This->ref ); TRACE( "(%p) was 0x%08lx\n", This, This->ref );
uRef = ++(This->ref); uRef = ++(This->ref);
LeaveCriticalSection( &(This->lock) ); LeaveCriticalSection( &(This->lock) );
return uRef; return uRef;
......
...@@ -115,6 +115,7 @@ int ds_hel_margin = DS_HEL_MARGIN; ...@@ -115,6 +115,7 @@ int ds_hel_margin = DS_HEL_MARGIN;
int ds_hel_queue = DS_HEL_QUEUE; int ds_hel_queue = DS_HEL_QUEUE;
int ds_snd_queue_max = DS_SND_QUEUE_MAX; int ds_snd_queue_max = DS_SND_QUEUE_MAX;
int ds_snd_queue_min = DS_SND_QUEUE_MIN; int ds_snd_queue_min = DS_SND_QUEUE_MIN;
int ds_hw_accel = DS_HW_ACCEL_FULL;
/* /*
* Call the callback provided to DirectSoundEnumerateA. * Call the callback provided to DirectSoundEnumerateA.
...@@ -199,6 +200,17 @@ void setup_dsound_options(void) ...@@ -199,6 +200,17 @@ void setup_dsound_options(void)
if (!get_config_key( hkey, appkey, "SndQueueMin", buffer, MAX_PATH )) if (!get_config_key( hkey, appkey, "SndQueueMin", buffer, MAX_PATH ))
ds_snd_queue_min = atoi(buffer); ds_snd_queue_min = atoi(buffer);
if (!get_config_key( hkey, appkey, "HardwareAcceleration", buffer, MAX_PATH )) {
if (strcmp(buffer, "Full") == 0)
ds_hw_accel = DS_HW_ACCEL_FULL;
else if (strcmp(buffer, "Standard") == 0)
ds_hw_accel = DS_HW_ACCEL_STANDARD;
else if (strcmp(buffer, "Basic") == 0)
ds_hw_accel = DS_HW_ACCEL_BASIC;
else if (strcmp(buffer, "Emulation") == 0)
ds_hw_accel = DS_HW_ACCEL_EMULATION;
}
if (appkey) RegCloseKey( appkey ); if (appkey) RegCloseKey( appkey );
RegCloseKey( hkey ); RegCloseKey( hkey );
...@@ -212,7 +224,13 @@ void setup_dsound_options(void) ...@@ -212,7 +224,13 @@ void setup_dsound_options(void)
WARN("ds_snd_queue_max = %d (default=%d)\n",ds_snd_queue_max ,DS_SND_QUEUE_MAX); WARN("ds_snd_queue_max = %d (default=%d)\n",ds_snd_queue_max ,DS_SND_QUEUE_MAX);
if (ds_snd_queue_min != DS_SND_QUEUE_MIN) if (ds_snd_queue_min != DS_SND_QUEUE_MIN)
WARN("ds_snd_queue_min = %d (default=%d)\n",ds_snd_queue_min ,DS_SND_QUEUE_MIN); WARN("ds_snd_queue_min = %d (default=%d)\n",ds_snd_queue_min ,DS_SND_QUEUE_MIN);
if (ds_hw_accel != DS_HW_ACCEL_FULL)
WARN("ds_hw_accel = %s (default=Full)\n",
ds_hw_accel==DS_HW_ACCEL_FULL ? "Full" :
ds_hw_accel==DS_HW_ACCEL_STANDARD ? "Standard" :
ds_hw_accel==DS_HW_ACCEL_BASIC ? "Basic" :
ds_hw_accel==DS_HW_ACCEL_EMULATION ? "Emulation" :
"Unknown");
} }
...@@ -604,6 +622,10 @@ HRESULT WINAPI DirectSoundCreate8(REFGUID lpGUID,LPDIRECTSOUND8 *ppDS,IUnknown * ...@@ -604,6 +622,10 @@ HRESULT WINAPI DirectSoundCreate8(REFGUID lpGUID,LPDIRECTSOUND8 *ppDS,IUnknown *
/* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */ /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
waveOutMessage((HWAVEOUT)wod, DRV_QUERYDSOUNDIFACE, (DWORD)&drv, 0); waveOutMessage((HWAVEOUT)wod, DRV_QUERYDSOUNDIFACE, (DWORD)&drv, 0);
/* Disable the direct sound driver to force emulation if requested. */
if (ds_hw_accel == DS_HW_ACCEL_EMULATION)
drv = NULL;
/* Allocate memory */ /* Allocate memory */
*ippDS = (IDirectSoundImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundImpl)); *ippDS = (IDirectSoundImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundImpl));
if (*ippDS == NULL) if (*ippDS == NULL)
...@@ -654,6 +676,12 @@ HRESULT WINAPI DirectSoundCreate8(REFGUID lpGUID,LPDIRECTSOUND8 *ppDS,IUnknown * ...@@ -654,6 +676,12 @@ HRESULT WINAPI DirectSoundCreate8(REFGUID lpGUID,LPDIRECTSOUND8 *ppDS,IUnknown *
* before the DirectSound interface is opened */ * before the DirectSound interface is opened */
if ((*ippDS)->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN) if ((*ippDS)->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
{ {
DWORD flags = CALLBACK_FUNCTION;
/* disable direct sound if requested */
if (ds_hw_accel != DS_HW_ACCEL_EMULATION)
flags |= WAVE_DIRECTSOUND;
/* FIXME: is this right? */ /* FIXME: is this right? */
(*ippDS)->drvdesc.dnDevNode = 0; (*ippDS)->drvdesc.dnDevNode = 0;
err = DSERR_ALLOCATED; err = DSERR_ALLOCATED;
...@@ -665,7 +693,7 @@ HRESULT WINAPI DirectSoundCreate8(REFGUID lpGUID,LPDIRECTSOUND8 *ppDS,IUnknown * ...@@ -665,7 +693,7 @@ HRESULT WINAPI DirectSoundCreate8(REFGUID lpGUID,LPDIRECTSOUND8 *ppDS,IUnknown *
err = mmErr(waveOutOpen(&((*ippDS)->hwo), err = mmErr(waveOutOpen(&((*ippDS)->hwo),
(*ippDS)->drvdesc.dnDevNode, &((*ippDS)->wfx), (*ippDS)->drvdesc.dnDevNode, &((*ippDS)->wfx),
(DWORD)DSOUND_callback, (DWORD)(*ippDS), (DWORD)DSOUND_callback, (DWORD)(*ippDS),
CALLBACK_FUNCTION | WAVE_DIRECTSOUND)); flags));
(*ippDS)->drvdesc.dnDevNode++; /* next wave device */ (*ippDS)->drvdesc.dnDevNode++; /* next wave device */
} }
......
...@@ -26,11 +26,18 @@ ...@@ -26,11 +26,18 @@
#define DS_HEL_FRAGS 48 /* HEL only: number of waveOut fragments in primary buffer #define DS_HEL_FRAGS 48 /* HEL only: number of waveOut fragments in primary buffer
* (changing this won't help you) */ * (changing this won't help you) */
/* direct sound hardware acceleration levels */
#define DS_HW_ACCEL_FULL 0 /* default on Windows 98 */
#define DS_HW_ACCEL_STANDARD 1 /* default on Windows 2000 */
#define DS_HW_ACCEL_BASIC 2
#define DS_HW_ACCEL_EMULATION 3
extern int ds_emuldriver; extern int ds_emuldriver;
extern int ds_hel_margin; extern int ds_hel_margin;
extern int ds_hel_queue; extern int ds_hel_queue;
extern int ds_snd_queue_max; extern int ds_snd_queue_max;
extern int ds_snd_queue_min; extern int ds_snd_queue_min;
extern int ds_hw_accel;
/***************************************************************************** /*****************************************************************************
* Predeclare the interface implementation structures * Predeclare the interface implementation structures
......
...@@ -901,11 +901,7 @@ void DSOUND_PerformMix(void) ...@@ -901,11 +901,7 @@ void DSOUND_PerformMix(void)
/* DSOUND_callback may need this lock */ /* DSOUND_callback may need this lock */
LeaveCriticalSection(&(dsound->mixlock)); LeaveCriticalSection(&(dsound->mixlock));
#endif #endif
/* FIXME: OSS doesn't allow independent stopping of input and output streams */ DSOUND_PrimaryStop(dsound);
/* in full duplex mode so don't stop when capturing. This should be moved into */
/* the OSS driver someday. */
if ( (dsound_capture == NULL) || (dsound_capture->state != STATE_CAPTURING) )
DSOUND_PrimaryStop(dsound);
#ifdef SYNC_CALLBACK #ifdef SYNC_CALLBACK
EnterCriticalSection(&(dsound->mixlock)); EnterCriticalSection(&(dsound->mixlock));
#endif #endif
......
...@@ -213,6 +213,9 @@ HRESULT DSOUND_PrimaryStop(IDirectSoundImpl *This) ...@@ -213,6 +213,9 @@ HRESULT DSOUND_PrimaryStop(IDirectSoundImpl *This)
if (This->hwbuf) { if (This->hwbuf) {
err = IDsDriverBuffer_Stop(This->hwbuf); err = IDsDriverBuffer_Stop(This->hwbuf);
if (err == DSERR_BUFFERLOST) { if (err == DSERR_BUFFERLOST) {
DWORD flags = CALLBACK_FUNCTION;
if (ds_hw_accel != DS_HW_ACCEL_EMULATION)
flags |= WAVE_DIRECTSOUND;
/* Wine-only: the driver wants us to reopen the device */ /* Wine-only: the driver wants us to reopen the device */
/* FIXME: check for errors */ /* FIXME: check for errors */
IDsDriverBuffer_Release(This->hwbuf); IDsDriverBuffer_Release(This->hwbuf);
...@@ -220,7 +223,7 @@ HRESULT DSOUND_PrimaryStop(IDirectSoundImpl *This) ...@@ -220,7 +223,7 @@ HRESULT DSOUND_PrimaryStop(IDirectSoundImpl *This)
This->hwo = 0; This->hwo = 0;
err = mmErr(waveOutOpen(&(This->hwo), This->drvdesc.dnDevNode, err = mmErr(waveOutOpen(&(This->hwo), This->drvdesc.dnDevNode,
&(This->wfx), (DWORD)DSOUND_callback, (DWORD)This, &(This->wfx), (DWORD)DSOUND_callback, (DWORD)This,
CALLBACK_FUNCTION | WAVE_DIRECTSOUND)); flags));
if (err == DS_OK) if (err == DS_OK)
err = IDsDriver_CreateSoundBuffer(This->driver,&(This->wfx), err = IDsDriver_CreateSoundBuffer(This->driver,&(This->wfx),
DSBCAPS_PRIMARYBUFFER,0, DSBCAPS_PRIMARYBUFFER,0,
...@@ -323,13 +326,16 @@ static HRESULT WINAPI PrimaryBufferImpl_SetFormat( ...@@ -323,13 +326,16 @@ static HRESULT WINAPI PrimaryBufferImpl_SetFormat(
dsound->wfx.nSamplesPerSec * dsound->wfx.nBlockAlign; dsound->wfx.nSamplesPerSec * dsound->wfx.nBlockAlign;
if (dsound->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) { if (dsound->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) {
DWORD flags = CALLBACK_FUNCTION;
if (ds_hw_accel != DS_HW_ACCEL_EMULATION)
flags |= WAVE_DIRECTSOUND;
/* FIXME: check for errors */ /* FIXME: check for errors */
DSOUND_PrimaryClose(dsound); DSOUND_PrimaryClose(dsound);
waveOutClose(dsound->hwo); waveOutClose(dsound->hwo);
dsound->hwo = 0; dsound->hwo = 0;
err = mmErr(waveOutOpen(&(dsound->hwo), dsound->drvdesc.dnDevNode, err = mmErr(waveOutOpen(&(dsound->hwo), dsound->drvdesc.dnDevNode,
&(dsound->wfx), (DWORD)DSOUND_callback, (DWORD)dsound, &(dsound->wfx), (DWORD)DSOUND_callback, (DWORD)dsound,
CALLBACK_FUNCTION | WAVE_DIRECTSOUND)); flags));
if (err == DS_OK) if (err == DS_OK)
DSOUND_PrimaryOpen(dsound); DSOUND_PrimaryOpen(dsound);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment