mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-26 03:30:46 +00:00
ee4573779c
git-svn-id: https://svn.eduke32.com/eduke32@483 1a8010ca-5511-0410-912e-c29ae57300e0
626 lines
16 KiB
C
626 lines
16 KiB
C
/*
|
|
* DirectSound output code for MultiVoc
|
|
* by Jonathon Fowler (jonof@edgenetwk.com)
|
|
*/
|
|
//-------------------------------------------------------------------------
|
|
/*
|
|
Duke Nukem Copyright (C) 1996, 2003 3D Realms Entertainment
|
|
|
|
This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
|
|
|
|
Duke Nukem 3D is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
//-------------------------------------------------------------------------
|
|
|
|
#include "dsoundout.h"
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
#include <mmsystem.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "dsound.h"
|
|
|
|
#include "compat.h"
|
|
#include "winlayer.h"
|
|
|
|
#if defined(__WATCOMC__) || defined(_MSC_VER)
|
|
#include <malloc.h>
|
|
#endif
|
|
|
|
|
|
#ifndef RENDERTYPEWIN
|
|
#error The DirectSound output module for AudioLib only works with the Windows interface.
|
|
#endif
|
|
|
|
static CRITICAL_SECTION mutex;
|
|
static int _DSOUND_CriticalSectionAlloced = FALSE;
|
|
static HANDLE isrthread = NULL;
|
|
static HANDLE isrfinish = NULL;
|
|
|
|
static HMODULE hDSoundDLL = NULL;
|
|
static LPDIRECTSOUND lpDS = NULL;
|
|
static LPDIRECTSOUNDBUFFER lpDSBPrimary = NULL;
|
|
static LPDIRECTSOUNDBUFFER lpDSBSecondary = NULL;
|
|
static LPDIRECTSOUNDNOTIFY lpDSNotify = NULL;
|
|
static HANDLE *hPosNotify = NULL;
|
|
|
|
static int(*_DSOUND_CallBack)(int) = NULL;
|
|
static int _DSOUND_BufferLength = 0;
|
|
static int _DSOUND_NumBuffers = 0;
|
|
static char *_DSOUND_MixBuffer = NULL;
|
|
|
|
static int DSOUND_Installed = FALSE;
|
|
|
|
int DSOUND_ErrorCode = DSOUND_Ok;
|
|
|
|
#define DSOUND_SetErrorCode( status ) \
|
|
DSOUND_ErrorCode = ( status );
|
|
|
|
|
|
|
|
/*
|
|
* DisableInterrupts
|
|
* Enter the critical section.
|
|
*/
|
|
int DisableInterrupts(void)
|
|
{
|
|
if (!_DSOUND_CriticalSectionAlloced) return -1;
|
|
|
|
EnterCriticalSection(&mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* RestoreInterrupts
|
|
* Leave the critical section.
|
|
*/
|
|
int RestoreInterrupts(int a)
|
|
{
|
|
if (!_DSOUND_CriticalSectionAlloced) return -1;
|
|
|
|
LeaveCriticalSection(&mutex);
|
|
|
|
return 0;
|
|
a=a;
|
|
}
|
|
|
|
|
|
/*
|
|
* DSOUND_ErrorString
|
|
* Returns a description of an error code.
|
|
*/
|
|
char *DSOUND_ErrorString(int errorcode)
|
|
{
|
|
switch (errorcode)
|
|
{
|
|
case DSOUND_Warning:
|
|
case DSOUND_Error:
|
|
return DSOUND_ErrorString(DSOUND_ErrorCode);
|
|
|
|
case DSOUND_Ok:
|
|
return "DirectSound ok.";
|
|
|
|
case DSOUND_NoDLL:
|
|
return "Failed loading DSOUND.DLL.";
|
|
|
|
case DSOUND_NoDirectSoundCreate:
|
|
return "Failed getting DirectSoundCreate entry point.";
|
|
|
|
case DSOUND_FailedDSC:
|
|
return "DirectSoundCreate failed.";
|
|
|
|
case DSOUND_FailedSetCoopLevel:
|
|
return "Failed setting cooperative level.";
|
|
|
|
case DSOUND_FailedCreatePrimary:
|
|
return "Failed creating primary buffer.";
|
|
|
|
case DSOUND_FailedSetFormat:
|
|
return "Failed setting primary buffer format.";
|
|
|
|
case DSOUND_FailedCreateSecondary:
|
|
return "Failed creating secondary buffer.";
|
|
|
|
case DSOUND_FailedCreateNotifyEvent:
|
|
return "Failed creating notification event object.";
|
|
|
|
case DSOUND_FailedQueryNotify:
|
|
return "Failed querying notification interface.";
|
|
|
|
case DSOUND_FailedSetNotify:
|
|
return "Failed setting notification positions.";
|
|
|
|
case DSOUND_FailedCreateFinishEvent:
|
|
return "Failed creating finish event object.";
|
|
|
|
case DSOUND_FailedCreateThread:
|
|
return "Failed creating playback thread.";
|
|
|
|
case DSOUND_FailedPlaySecondary:
|
|
return "Failed playing secondary buffer.";
|
|
|
|
case DSOUND_FailedGetExitCode:
|
|
return "GetExitCodeThread failed.";
|
|
|
|
default:
|
|
return "Unknown DirectSound error code.";
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* DSOUND_Init
|
|
* Initializes the DirectSound objects.
|
|
*/
|
|
int DSOUND_Init(int soundcard, int mixrate, int numchannels, int samplebits, int buffersize)
|
|
{
|
|
HRESULT(WINAPI *aDirectSoundCreate)(LPGUID,LPDIRECTSOUND*,LPUNKNOWN);
|
|
HRESULT hr;
|
|
DSBUFFERDESC dsbuf;
|
|
WAVEFORMATEX wfex;
|
|
DSBPOSITIONNOTIFY posn;
|
|
|
|
if (DSOUND_Installed)
|
|
{
|
|
DSOUND_Shutdown();
|
|
}
|
|
|
|
initprintf("Initializing DirectSound...\n");
|
|
|
|
if (!_DSOUND_CriticalSectionAlloced)
|
|
{
|
|
// initialize the critical section object we'll use to
|
|
// simulate (dis|en)abling interrupts
|
|
InitializeCriticalSection(&mutex);
|
|
_DSOUND_CriticalSectionAlloced = TRUE;
|
|
}
|
|
|
|
initprintf(" - Loading DSOUND.DLL\n");
|
|
hDSoundDLL = LoadLibrary("DSOUND.DLL");
|
|
if (!hDSoundDLL)
|
|
{
|
|
DSOUND_Shutdown();
|
|
DSOUND_SetErrorCode(DSOUND_NoDLL);
|
|
return DSOUND_Error;
|
|
}
|
|
|
|
aDirectSoundCreate = (void *)GetProcAddress(hDSoundDLL, "DirectSoundCreate");
|
|
if (!aDirectSoundCreate)
|
|
{
|
|
DSOUND_Shutdown();
|
|
DSOUND_SetErrorCode(DSOUND_NoDirectSoundCreate);
|
|
return DSOUND_Error;
|
|
}
|
|
|
|
initprintf(" - Creating DirectSound object\n");
|
|
hr = aDirectSoundCreate(NULL, &lpDS, NULL);
|
|
if (hr != DS_OK)
|
|
{
|
|
DSOUND_Shutdown();
|
|
DSOUND_SetErrorCode(DSOUND_FailedDSC);
|
|
return DSOUND_Error;
|
|
}
|
|
|
|
hr = IDirectSound_SetCooperativeLevel(lpDS, (HWND)win_gethwnd(), DSSCL_EXCLUSIVE);
|
|
if (hr != DS_OK)
|
|
{
|
|
DSOUND_Shutdown();
|
|
DSOUND_SetErrorCode(DSOUND_FailedSetCoopLevel);
|
|
return DSOUND_Error;
|
|
}
|
|
|
|
initprintf(" - Creating primary buffer\n");
|
|
ZeroMemory(&dsbuf, sizeof(dsbuf));
|
|
dsbuf.dwSize = sizeof(DSBUFFERDESC);
|
|
dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
|
hr = IDirectSound_CreateSoundBuffer(lpDS, &dsbuf, &lpDSBPrimary, NULL);
|
|
if (hr != DS_OK)
|
|
{
|
|
DSOUND_Shutdown();
|
|
DSOUND_SetErrorCode(DSOUND_FailedCreatePrimary);
|
|
return DSOUND_Error;
|
|
}
|
|
|
|
initprintf(" - Setting primary buffer format\n"
|
|
" Channels: %d\n"
|
|
" Sample rate: %dHz\n"
|
|
" Sample size: %d bits\n",
|
|
numchannels, mixrate, samplebits);
|
|
ZeroMemory(&wfex, sizeof(wfex));
|
|
wfex.wFormatTag = WAVE_FORMAT_PCM;
|
|
wfex.nChannels = numchannels;
|
|
wfex.nSamplesPerSec = mixrate;
|
|
wfex.wBitsPerSample = samplebits;
|
|
wfex.nBlockAlign = (wfex.wBitsPerSample / 8) * wfex.nChannels;
|
|
wfex.nAvgBytesPerSec = wfex.nBlockAlign * wfex.nSamplesPerSec;
|
|
hr = IDirectSoundBuffer_SetFormat(lpDSBPrimary, &wfex);
|
|
if (hr != DS_OK)
|
|
{
|
|
DSOUND_Shutdown();
|
|
DSOUND_SetErrorCode(DSOUND_FailedSetFormat);
|
|
return DSOUND_Error;
|
|
}
|
|
|
|
initprintf(" - Creating secondary buffer\n");
|
|
ZeroMemory(&dsbuf, sizeof(dsbuf));
|
|
dsbuf.dwSize = sizeof(DSBUFFERDESC);
|
|
dsbuf.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_LOCSOFTWARE;
|
|
dsbuf.dwBufferBytes = buffersize;
|
|
dsbuf.lpwfxFormat = &wfex;
|
|
hr = IDirectSound_CreateSoundBuffer(lpDS, &dsbuf, &lpDSBSecondary, NULL);
|
|
if (hr != DS_OK)
|
|
{
|
|
DSOUND_Shutdown();
|
|
DSOUND_SetErrorCode(DSOUND_FailedCreateSecondary);
|
|
return DSOUND_Error;
|
|
}
|
|
|
|
hr = IDirectSoundBuffer_QueryInterface(lpDSBSecondary, &IID_IDirectSoundNotify, &lpDSNotify);
|
|
if (hr != DS_OK)
|
|
{
|
|
DSOUND_Shutdown();
|
|
DSOUND_SetErrorCode(DSOUND_FailedQueryNotify);
|
|
return DSOUND_Error;
|
|
}
|
|
|
|
hPosNotify = (HANDLE *)malloc(sizeof(HANDLE));
|
|
if (!hPosNotify)
|
|
{
|
|
DSOUND_Shutdown();
|
|
DSOUND_SetErrorCode(DSOUND_FailedSetNotify);
|
|
return DSOUND_Error;
|
|
}
|
|
|
|
hPosNotify[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (!hPosNotify)
|
|
{
|
|
DSOUND_Shutdown();
|
|
DSOUND_SetErrorCode(DSOUND_FailedCreateNotifyEvent);
|
|
return DSOUND_Error;
|
|
}
|
|
|
|
_DSOUND_BufferLength = 0;
|
|
_DSOUND_NumBuffers = 1;
|
|
posn.dwOffset = 0;
|
|
posn.hEventNotify = hPosNotify[0];
|
|
|
|
hr = IDirectSoundNotify_SetNotificationPositions(lpDSNotify, 1, &posn);
|
|
if (hr != DS_OK)
|
|
{
|
|
DSOUND_Shutdown();
|
|
DSOUND_SetErrorCode(DSOUND_FailedSetNotify);
|
|
return DSOUND_Error;
|
|
}
|
|
|
|
DSOUND_Installed = TRUE;
|
|
|
|
DSOUND_SetErrorCode(DSOUND_Ok);
|
|
return DSOUND_Ok;
|
|
}
|
|
|
|
|
|
/*
|
|
* DSOUND_Shutdown
|
|
* Shuts down DirectSound and it's associates.
|
|
*/
|
|
int DSOUND_Shutdown(void)
|
|
{
|
|
int i;
|
|
|
|
if (DSOUND_Installed) initprintf("Uninitializing DirectSound...\n");
|
|
|
|
DSOUND_Installed = FALSE;
|
|
|
|
DSOUND_StopPlayback();
|
|
|
|
if (lpDSNotify)
|
|
{
|
|
IDirectSoundNotify_Release(lpDSNotify);
|
|
lpDSNotify = NULL;
|
|
}
|
|
|
|
if (hPosNotify)
|
|
{
|
|
for (i=0; i<_DSOUND_NumBuffers; i++)
|
|
{
|
|
if (hPosNotify[i]) CloseHandle(hPosNotify[i]);
|
|
}
|
|
free(hPosNotify);
|
|
hPosNotify = NULL;
|
|
}
|
|
|
|
if (lpDSBSecondary)
|
|
{
|
|
initprintf(" - Releasing secondary buffer\n");
|
|
IDirectSoundBuffer_Stop(lpDSBSecondary);
|
|
IDirectSoundBuffer_Release(lpDSBSecondary);
|
|
lpDSBSecondary = NULL;
|
|
}
|
|
|
|
if (lpDSBPrimary)
|
|
{
|
|
initprintf(" - Releasing primary buffer\n");
|
|
IDirectSoundBuffer_Release(lpDSBPrimary);
|
|
lpDSBPrimary = NULL;
|
|
}
|
|
|
|
if (lpDS)
|
|
{
|
|
initprintf(" - Releasing DirectSound object\n");
|
|
IDirectSound_Release(lpDS);
|
|
lpDS = NULL;
|
|
}
|
|
|
|
if (hDSoundDLL)
|
|
{
|
|
initprintf(" - Unloading DSOUND.DLL\n");
|
|
FreeLibrary(hDSoundDLL);
|
|
hDSoundDLL = NULL;
|
|
}
|
|
|
|
DSOUND_SetErrorCode(DSOUND_Ok);
|
|
return DSOUND_Ok;
|
|
}
|
|
|
|
|
|
/*
|
|
* DSOUND_SetMixMode
|
|
* Bit of filler for the future.
|
|
*/
|
|
int DSOUND_SetMixMode(int mode)
|
|
{
|
|
return mode;
|
|
}
|
|
|
|
|
|
//#define DEBUGAUDIO
|
|
|
|
#ifdef DEBUGAUDIO
|
|
#include <sys/stat.h>
|
|
#endif
|
|
static DWORD WINAPI isr(LPVOID parm)
|
|
{
|
|
HANDLE *handles;
|
|
HRESULT hr;
|
|
int rv;
|
|
#ifdef DEBUGAUDIO
|
|
int h;
|
|
#endif
|
|
int p;
|
|
LPVOID lockptr; DWORD lockbytes;
|
|
LPVOID lockptr2; DWORD lockbytes2;
|
|
|
|
handles = (HANDLE *)malloc(sizeof(HANDLE)*(1+_DSOUND_NumBuffers));
|
|
if (!handles) return 1;
|
|
|
|
handles[0] = isrfinish;
|
|
for (p=0; p<_DSOUND_NumBuffers; p++)
|
|
handles[p+1] = hPosNotify[p];
|
|
|
|
#ifdef DEBUGAUDIO
|
|
h = creat("audio.raw",S_IREAD|S_IWRITE);
|
|
#endif
|
|
|
|
while (1)
|
|
{
|
|
rv = WaitForMultipleObjects(1+_DSOUND_NumBuffers, handles, FALSE, INFINITE);
|
|
|
|
if (!(rv >= WAIT_OBJECT_0 && rv <= WAIT_OBJECT_0+1+_DSOUND_NumBuffers)) return -1;
|
|
|
|
if (rv == WAIT_OBJECT_0)
|
|
{
|
|
// we've been asked to finish up
|
|
break;
|
|
}
|
|
|
|
// otherwise we just service the interrupt
|
|
if (_DSOUND_CallBack)
|
|
{
|
|
DisableInterrupts();
|
|
|
|
p = _DSOUND_CallBack(rv-WAIT_OBJECT_0-1);
|
|
#ifdef DEBUGAUDIO
|
|
write(h, _DSOUND_MixBuffer + p*_DSOUND_BufferLength, _DSOUND_BufferLength);
|
|
#endif
|
|
|
|
hr = IDirectSoundBuffer_Lock(lpDSBSecondary, p*_DSOUND_BufferLength, _DSOUND_BufferLength,
|
|
&lockptr, &lockbytes, &lockptr2, &lockbytes2, 0);
|
|
if (hr == DSERR_BUFFERLOST)
|
|
{
|
|
hr = IDirectSoundBuffer_Restore(lpDSBSecondary);
|
|
}
|
|
if (hr == DS_OK)
|
|
{
|
|
/*
|
|
#define copybuf(S,D,c) \
|
|
({ void *__S=(S), *__D=(D); long __c=(c); \
|
|
__asm__ __volatile__ ("rep; movsl" \
|
|
: "+S" (__S), "+D" (__D), "+c" (__c) : : "memory", "cc"); \
|
|
0; })
|
|
*/
|
|
//copybuf(_DSOUND_MixBuffer + p * _DSOUND_BufferLength, lockptr, _DSOUND_BufferLength >> 2);
|
|
memcpy(lockptr, _DSOUND_MixBuffer + p * _DSOUND_BufferLength, _DSOUND_BufferLength);
|
|
IDirectSoundBuffer_Unlock(lpDSBSecondary, lockptr, lockbytes, lockptr2, lockbytes2);
|
|
}
|
|
|
|
RestoreInterrupts(0);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUGAUDIO
|
|
close(h);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* DSOUND_BeginBufferedPlayback
|
|
* Spins off a thread that behaves somewhat like the SoundBlaster DMA ISR did.
|
|
*/
|
|
int DSOUND_BeginBufferedPlayback(char *BufferStart, int(*CallBackFunc)(int), int buffersize, int numdivisions)
|
|
{
|
|
DWORD threadid;
|
|
HRESULT hr;
|
|
DSBPOSITIONNOTIFY *posns;
|
|
int i;
|
|
|
|
_DSOUND_CallBack = CallBackFunc;
|
|
_DSOUND_MixBuffer = BufferStart;
|
|
|
|
if (!lpDSBSecondary) return DSOUND_Error;
|
|
|
|
if (isrthread)
|
|
{
|
|
DSOUND_StopPlayback();
|
|
}
|
|
|
|
isrfinish = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (!isrfinish)
|
|
{
|
|
DSOUND_SetErrorCode(DSOUND_FailedCreateFinishEvent);
|
|
return DSOUND_Error;
|
|
}
|
|
|
|
isrthread = CreateThread(NULL, 0, isr, NULL, CREATE_SUSPENDED, &threadid);
|
|
if (!isrthread)
|
|
{
|
|
DSOUND_SetErrorCode(DSOUND_FailedCreateThread);
|
|
return DSOUND_Error;
|
|
}
|
|
|
|
hPosNotify = (HANDLE *)malloc(sizeof(HANDLE)*numdivisions);
|
|
if (!hPosNotify)
|
|
{
|
|
DSOUND_Shutdown();
|
|
DSOUND_SetErrorCode(DSOUND_FailedSetNotify);
|
|
return DSOUND_Error;
|
|
}
|
|
|
|
memset(hPosNotify, 0, sizeof(HANDLE)*numdivisions);
|
|
for (i=0; i<numdivisions; i++)
|
|
{
|
|
hPosNotify[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (!hPosNotify[i])
|
|
{
|
|
DSOUND_Shutdown();
|
|
DSOUND_SetErrorCode(DSOUND_FailedSetNotify);
|
|
return DSOUND_Error;
|
|
}
|
|
}
|
|
|
|
posns = (LPDSBPOSITIONNOTIFY)malloc(sizeof(DSBPOSITIONNOTIFY)*numdivisions);
|
|
if (!posns)
|
|
{
|
|
DSOUND_Shutdown();
|
|
DSOUND_SetErrorCode(DSOUND_FailedSetNotify);
|
|
return DSOUND_Error;
|
|
}
|
|
|
|
_DSOUND_BufferLength = buffersize/numdivisions;
|
|
_DSOUND_NumBuffers = numdivisions;
|
|
for (i=0; i<numdivisions; i++)
|
|
{
|
|
posns[i].dwOffset = i*_DSOUND_BufferLength;
|
|
posns[i].hEventNotify = hPosNotify[i];
|
|
}
|
|
|
|
hr = IDirectSoundNotify_SetNotificationPositions(lpDSNotify, numdivisions, posns);
|
|
if (hr != DS_OK)
|
|
{
|
|
free(posns);
|
|
DSOUND_Shutdown();
|
|
DSOUND_SetErrorCode(DSOUND_FailedSetNotify);
|
|
return DSOUND_Error;
|
|
}
|
|
|
|
free(posns);
|
|
|
|
SetThreadPriority(isrthread, THREAD_PRIORITY_ABOVE_NORMAL);
|
|
ResumeThread(isrthread);
|
|
|
|
hr = IDirectSoundBuffer_Play(lpDSBSecondary, 0, 0, DSBPLAY_LOOPING);
|
|
if (hr != DS_OK)
|
|
{
|
|
DSOUND_SetErrorCode(DSOUND_FailedPlaySecondary);
|
|
return DSOUND_Error;
|
|
}
|
|
|
|
return DSOUND_Ok;
|
|
}
|
|
|
|
|
|
/*
|
|
* DSOUND_StopPlayback
|
|
* Halts the playback thread.
|
|
*/
|
|
int DSOUND_StopPlayback(void)
|
|
{
|
|
// DWORD exitcode;
|
|
BOOL t;
|
|
int i;
|
|
|
|
if (isrthread)
|
|
{
|
|
SetEvent(isrfinish);
|
|
|
|
initprintf("DirectSound: Waiting for sound thread to exit\n");
|
|
if (WaitForSingleObject(isrthread, 300) == WAIT_OBJECT_0)
|
|
initprintf("DirectSound: Sound thread has exited\n");
|
|
else
|
|
initprintf("DirectSound: Sound thread failed to exit!\n");
|
|
/*
|
|
while (1) {
|
|
if (!GetExitCodeThread(isrthread, &exitcode)) {
|
|
DSOUND_SetErrorCode(DSOUND_FailedGetExitCode);
|
|
return DSOUND_Warning;
|
|
}
|
|
if (exitcode != STILL_ACTIVE) break;
|
|
}*/
|
|
|
|
CloseHandle(isrthread);
|
|
isrthread = NULL;
|
|
}
|
|
|
|
if (isrfinish)
|
|
{
|
|
CloseHandle(isrfinish);
|
|
isrfinish = NULL;
|
|
}
|
|
|
|
if (lpDSBSecondary)
|
|
{
|
|
IDirectSoundBuffer_Stop(lpDSBSecondary);
|
|
}
|
|
|
|
if (hPosNotify)
|
|
{
|
|
for (i=0; i<_DSOUND_NumBuffers; i++)
|
|
{
|
|
if (hPosNotify[i]) CloseHandle(hPosNotify[i]);
|
|
}
|
|
free(hPosNotify);
|
|
hPosNotify = NULL;
|
|
}
|
|
|
|
return DSOUND_Ok;
|
|
}
|
|
|
|
|