raze-gles/polymer/eduke32/source/jaudiolib/src/driver_directsound.c
hendricks266 fcf9beae6a Work-in-progress adjustment to the C code to compile under C++. It builds for me without errors using Win32 MinGW-GCC, but it still generates warning soup. No guarantees about MSVC or anything using SDL. Enable C++ by building with CPLUSPLUS=1. C remains the default and should compile with no change in setup.
Credit to Plagman for the idea and doing the work on the game side, which is included in this commit.

(Building as C++ will give us features with which we can make improvements and optimizations on the multiplayer code and Polymer.)

git-svn-id: https://svn.eduke32.com/eduke32@3116 1a8010ca-5511-0410-912e-c29ae57300e0
2012-11-05 02:49:08 +00:00

498 lines
14 KiB
C

/*
Copyright (C) 2009 Jonathon Fowler <jf@jonof.id.au>
This program 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.
*/
/**
* DirectSound output driver for MultiVoc
*/
#define WIN32_LEAN_AND_MEAN
#define DIRECTSOUND_VERSION 0x0700
#define CINTERFACE
#include <windows.h>
#include <mmsystem.h>
#include <dsound.h>
#include <stdlib.h>
#include <stdio.h>
#include "driver_directsound.h"
#include "multivoc.h"
enum {
DSErr_Warning = -2,
DSErr_Error = -1,
DSErr_Ok = 0,
DSErr_Uninitialised,
DSErr_DirectSoundCreate,
DSErr_SetCooperativeLevel,
DSErr_CreateSoundBuffer,
DSErr_CreateSoundBufferSecondary,
DSErr_SetFormat,
DSErr_SetFormatSecondary,
DSErr_Notify,
DSErr_NotifyEvents,
DSErr_SetNotificationPositions,
DSErr_Play,
DSErr_PlaySecondary,
DSErr_CreateThread,
DSErr_CreateMutex
};
static int32_t ErrorCode = DSErr_Ok;
static int32_t Initialised = 0;
static int32_t Playing = 0;
static char *MixBuffer = 0;
static int32_t MixBufferSize = 0;
static int32_t MixBufferCount = 0;
static int32_t MixBufferCurrent = 0;
static int32_t MixBufferUsed = 0;
static void ( *MixCallBack )( void ) = 0;
static LPDIRECTSOUND lpds = 0;
static LPDIRECTSOUNDBUFFER lpdsbprimary = 0, lpdsbsec = 0;
static LPDIRECTSOUNDNOTIFY lpdsnotify = 0;
static DSBPOSITIONNOTIFY notifyPositions[3] = { { 0,0 }, { 0,0 }, { 0,0 } };
static HANDLE mixThread = 0;
static HANDLE mutex = 0;
static void FillBufferPortion(char * ptr, int32_t remaining)
{
int32_t len = 0;
char *sptr;
while (remaining >= len) {
if (MixBufferUsed == MixBufferSize) {
MixCallBack();
MixBufferUsed = 0;
MixBufferCurrent++;
if (MixBufferCurrent >= MixBufferCount) {
MixBufferCurrent -= MixBufferCount;
}
}
while (remaining >= len && MixBufferUsed < MixBufferSize) {
sptr = MixBuffer + (MixBufferCurrent * MixBufferSize) + MixBufferUsed;
len = MixBufferSize - MixBufferUsed;
if (remaining < len) {
len = remaining;
}
memcpy(ptr, sptr, len);
ptr += len;
MixBufferUsed += len;
remaining -= len;
}
}
}
static void FillBuffer(int32_t bufnum)
{
HRESULT err;
LPVOID ptr, ptr2;
DWORD remaining, remaining2;
int32_t retries = 1;
//initprintf( "DirectSound FillBuffer: filling %d\n", bufnum);
do {
err = IDirectSoundBuffer_Lock(lpdsbsec,
notifyPositions[bufnum].dwOffset,
notifyPositions[1].dwOffset,
&ptr, &remaining,
&ptr2, &remaining2,
0);
if (FAILED(err)) {
if (err == DSERR_BUFFERLOST) {
err = IDirectSoundBuffer_Restore(lpdsbsec);
if (FAILED(err)) {
return;
}
if (retries-- > 0) {
continue;
}
}
if (MV_Printf)
MV_Printf("DirectSound FillBuffer: err %x\n", (uint32_t) err);
return;
}
break;
} while (1);
if (ptr) {
FillBufferPortion((char *) ptr, remaining);
}
if (ptr2) {
FillBufferPortion((char *) ptr2, remaining2);
}
IDirectSoundBuffer_Unlock(lpdsbsec, ptr, remaining, ptr2, remaining2);
}
static DWORD WINAPI fillDataThread(LPVOID lpParameter)
{
DWORD waitret, waitret2;
HANDLE handles[] = { handles[0] = notifyPositions[0].hEventNotify,
handles[1] = notifyPositions[1].hEventNotify,
handles[2] = notifyPositions[2].hEventNotify };
UNREFERENCED_PARAMETER(lpParameter);
do {
waitret = WaitForMultipleObjects(3, handles, FALSE, INFINITE);
switch (waitret) {
case WAIT_OBJECT_0:
case WAIT_OBJECT_0+1:
waitret2 = WaitForSingleObject(mutex, INFINITE);
if (waitret2 == WAIT_OBJECT_0) {
FillBuffer(WAIT_OBJECT_0 + 1 - waitret);
ReleaseMutex(mutex);
} else {
if (MV_Printf)
MV_Printf( "DirectSound fillDataThread: wfso err %d\n", (int32_t) waitret2);
}
break;
case WAIT_OBJECT_0+2:
// initprintf( "DirectSound fillDataThread: exiting\n");
ExitThread(0);
break;
default:
if (MV_Printf)
MV_Printf( "DirectSound fillDataThread: wfmo err %d\n", (int32_t) waitret);
break;
}
} while (1);
return 0;
}
int32_t DirectSoundDrv_GetError(void)
{
return ErrorCode;
}
const char *DirectSoundDrv_ErrorString( int32_t ErrorNumber )
{
const char *ErrorString;
switch( ErrorNumber )
{
case DSErr_Warning :
case DSErr_Error :
ErrorString = DirectSoundDrv_ErrorString( ErrorCode );
break;
case DSErr_Ok :
ErrorString = "DirectSound ok.";
break;
case DSErr_Uninitialised:
ErrorString = "DirectSound uninitialised.";
break;
case DSErr_DirectSoundCreate:
ErrorString = "DirectSound error: DirectSoundCreate failed.";
break;
case DSErr_SetCooperativeLevel:
ErrorString = "DirectSound error: SetCooperativeLevel failed.";
break;
case DSErr_CreateSoundBuffer:
ErrorString = "DirectSound error: primary CreateSoundBuffer failed.";
break;
case DSErr_CreateSoundBufferSecondary:
ErrorString = "DirectSound error: secondary CreateSoundBuffer failed.";
break;
case DSErr_SetFormat:
ErrorString = "DirectSound error: primary buffer SetFormat failed.";
break;
case DSErr_SetFormatSecondary:
ErrorString = "DirectSound error: secondary buffer SetFormat failed.";
break;
case DSErr_Notify:
ErrorString = "DirectSound error: failed querying secondary buffer for notify interface.";
break;
case DSErr_NotifyEvents:
ErrorString = "DirectSound error: failed creating notify events.";
break;
case DSErr_SetNotificationPositions:
ErrorString = "DirectSound error: failed setting notification positions.";
break;
case DSErr_Play:
ErrorString = "DirectSound error: primary buffer Play failed.";
break;
case DSErr_PlaySecondary:
ErrorString = "DirectSound error: secondary buffer Play failed.";
break;
case DSErr_CreateThread:
ErrorString = "DirectSound error: failed creating mix thread.";
break;
case DSErr_CreateMutex:
ErrorString = "DirectSound error: failed creating mix mutex.";
break;
default:
ErrorString = "Unknown DirectSound error code.";
break;
}
return ErrorString;
}
static void TeardownDSound(HRESULT err)
{
if (FAILED(err)) {
if (MV_Printf)
MV_Printf( "Dying error: %x\n", (uint32_t) err);
}
if (lpdsnotify) IDirectSoundNotify_Release(lpdsnotify);
if (notifyPositions[0].hEventNotify) CloseHandle(notifyPositions[0].hEventNotify);
if (notifyPositions[1].hEventNotify) CloseHandle(notifyPositions[1].hEventNotify);
if (notifyPositions[2].hEventNotify) CloseHandle(notifyPositions[2].hEventNotify);
if (mutex) CloseHandle(mutex);
if (lpdsbsec) IDirectSoundBuffer_Release(lpdsbsec);
if (lpdsbprimary) IDirectSoundBuffer_Release(lpdsbprimary);
if (lpds) IDirectSound_Release(lpds);
notifyPositions[0].hEventNotify =
notifyPositions[1].hEventNotify =
notifyPositions[2].hEventNotify = 0;
mutex = 0;
lpdsnotify = 0;
lpdsbsec = 0;
lpdsbprimary = 0;
lpds = 0;
}
int32_t DirectSoundDrv_PCM_Init(int32_t *mixrate, int32_t *numchannels, int32_t *samplebits, void * initdata)
{
HRESULT err;
DSBUFFERDESC bufdesc;
WAVEFORMATEX wfex;
if (Initialised) {
DirectSoundDrv_PCM_Shutdown();
}
err = DirectSoundCreate(0, &lpds, 0);
if (FAILED( err )) {
ErrorCode = DSErr_DirectSoundCreate;
return DSErr_Error;
}
err = IDirectSound_SetCooperativeLevel(lpds, (HWND) initdata, DSSCL_PRIORITY);
if (FAILED( err )) {
TeardownDSound(err);
ErrorCode = DSErr_SetCooperativeLevel;
return DSErr_Error;
}
memset(&bufdesc, 0, sizeof(DSBUFFERDESC));
bufdesc.dwSize = sizeof(DSBUFFERDESC);
bufdesc.dwFlags = DSBCAPS_LOCSOFTWARE |
DSBCAPS_PRIMARYBUFFER |
DSBCAPS_GETCURRENTPOSITION2 |
DSBCAPS_STICKYFOCUS ;
err = IDirectSound_CreateSoundBuffer(lpds, &bufdesc, &lpdsbprimary, 0);
if (FAILED( err )) {
TeardownDSound(err);
ErrorCode = DSErr_CreateSoundBuffer;
return DSErr_Error;
}
memset(&wfex, 0, sizeof(WAVEFORMATEX));
wfex.wFormatTag = WAVE_FORMAT_PCM;
wfex.nChannels = *numchannels;
wfex.nSamplesPerSec = *mixrate;
wfex.wBitsPerSample = *samplebits;
wfex.nBlockAlign = wfex.nChannels * wfex.wBitsPerSample / 8;
wfex.nAvgBytesPerSec = wfex.nSamplesPerSec * wfex.nBlockAlign;
err = IDirectSoundBuffer_SetFormat(lpdsbprimary, &wfex);
if (FAILED( err )) {
TeardownDSound(err);
ErrorCode = DSErr_SetFormat;
return DSErr_Error;
}
bufdesc.dwFlags = DSBCAPS_LOCSOFTWARE |
DSBCAPS_CTRLPOSITIONNOTIFY |
DSBCAPS_GETCURRENTPOSITION2 |
DSBCAPS_STICKYFOCUS ;
bufdesc.dwBufferBytes = wfex.nBlockAlign * 2560 * 2;
bufdesc.lpwfxFormat = &wfex;
err = IDirectSound_CreateSoundBuffer(lpds, &bufdesc, &lpdsbsec, 0);
if (FAILED( err )) {
TeardownDSound(err);
ErrorCode = DSErr_SetFormatSecondary;
return DSErr_Error;
}
err = IDirectSoundBuffer_QueryInterface(lpdsbsec, &IID_IDirectSoundNotify,
(LPVOID *) &lpdsnotify);
if (FAILED( err )) {
TeardownDSound(err);
ErrorCode = DSErr_Notify;
return DSErr_Error;
}
notifyPositions[0].dwOffset = 0;
notifyPositions[0].hEventNotify = CreateEvent(NULL, FALSE, FALSE, NULL);
notifyPositions[1].dwOffset = bufdesc.dwBufferBytes / 2;
notifyPositions[1].hEventNotify = CreateEvent(NULL, FALSE, FALSE, NULL);
notifyPositions[2].dwOffset = DSBPN_OFFSETSTOP;
notifyPositions[2].hEventNotify = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!notifyPositions[0].hEventNotify ||
!notifyPositions[1].hEventNotify ||
!notifyPositions[2].hEventNotify) {
TeardownDSound(DS_OK);
ErrorCode = DSErr_NotifyEvents;
return DSErr_Error;
}
err = IDirectSoundNotify_SetNotificationPositions(lpdsnotify, 3, notifyPositions);
if (FAILED( err )) {
TeardownDSound(err);
ErrorCode = DSErr_SetNotificationPositions;
return DSErr_Error;
}
err = IDirectSoundBuffer_Play(lpdsbprimary, 0, 0, DSBPLAY_LOOPING);
if (FAILED( err )) {
TeardownDSound(err);
ErrorCode = DSErr_Play;
return DSErr_Error;
}
mutex = CreateMutex(0, FALSE, 0);
if (!mutex) {
TeardownDSound(DS_OK);
ErrorCode = DSErr_CreateMutex;
return DSErr_Error;
}
Initialised = 1;
// initprintf("DirectSound Init: yay\n");
return DSErr_Ok;
}
void DirectSoundDrv_PCM_Shutdown(void)
{
if (!Initialised) {
return;
}
DirectSoundDrv_PCM_StopPlayback();
TeardownDSound(DS_OK);
Initialised = 0;
}
int32_t DirectSoundDrv_PCM_BeginPlayback(char *BufferStart, int32_t BufferSize,
int32_t NumDivisions, void ( *CallBackFunc )( void ) )
{
HRESULT err;
if (!Initialised) {
ErrorCode = DSErr_Uninitialised;
return DSErr_Error;
}
DirectSoundDrv_PCM_StopPlayback();
MixBuffer = BufferStart;
MixBufferSize = BufferSize;
MixBufferCount = NumDivisions;
MixBufferCurrent = 0;
MixBufferUsed = 0;
MixCallBack = CallBackFunc;
// prime the buffer
FillBuffer(0);
mixThread = CreateThread(NULL, 0, fillDataThread, 0, 0, 0);
if (!mixThread) {
ErrorCode = DSErr_CreateThread;
return DSErr_Error;
}
SetThreadPriority(mixThread, THREAD_PRIORITY_ABOVE_NORMAL);
err = IDirectSoundBuffer_Play(lpdsbsec, 0, 0, DSBPLAY_LOOPING);
if (FAILED( err )) {
ErrorCode = DSErr_PlaySecondary;
return DSErr_Error;
}
Playing = 1;
return DSErr_Ok;
}
void DirectSoundDrv_PCM_StopPlayback(void)
{
if (!Playing) {
return;
}
IDirectSoundBuffer_Stop(lpdsbsec);
IDirectSoundBuffer_SetCurrentPosition(lpdsbsec, 0);
Playing = 0;
}
void DirectSoundDrv_PCM_Lock(void)
{
DWORD err;
err = WaitForSingleObject(mutex, INFINITE);
if (err != WAIT_OBJECT_0) {
if (MV_Printf)
MV_Printf( "DirectSound lock: wfso %d\n", (int32_t) err);
}
}
void DirectSoundDrv_PCM_Unlock(void)
{
ReleaseMutex(mutex);
}