mirror of
https://github.com/kbeckmann/game-and-watch-retro-go.git
synced 2025-12-16 13:15:55 +01:00
426 lines
11 KiB
C
426 lines
11 KiB
C
/* MikMod sound library
|
|
(c) 1998-2005 Miodrag Vallat and others - see file AUTHORS for
|
|
complete list.
|
|
|
|
This library is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Library 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 Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
02111-1307, USA.
|
|
*/
|
|
|
|
/*==============================================================================
|
|
|
|
Driver for output on win32 platforms using DirectSound
|
|
|
|
==============================================================================*/
|
|
|
|
/*
|
|
|
|
Written by Brian McKinney <Brian.McKinney@colorado.edu>
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "mikmod_internals.h"
|
|
|
|
#ifdef DRV_DS
|
|
|
|
#include <memory.h>
|
|
#include <string.h>
|
|
|
|
/* Including dsound.h breaks in MSYS2 unless windows.h is
|
|
* explicitly included first (without WIN32_LEAN_AND_MEAN).
|
|
*/
|
|
#include <windows.h>
|
|
|
|
#define INITGUID
|
|
#if defined(__WATCOMC__)
|
|
#include <guiddef.h>
|
|
#else
|
|
#include <basetyps.h> /* guiddef.h not in all SDKs, e.g. mingw.org */
|
|
#endif
|
|
|
|
#if !defined(__cplusplus) && !defined(CINTERFACE)
|
|
#define CINTERFACE
|
|
#endif
|
|
#include <dsound.h>
|
|
|
|
#ifdef __WATCOMC__
|
|
/* If you encounter build failures from Open Watcom's dsound.h,
|
|
* see: https://github.com/open-watcom/open-watcom-v2/pull/313
|
|
*/
|
|
/* Open Watcom has broken __cdecl (leading underscore) name mangling for Windows
|
|
* internal var names. It is fixed in Open Watcom V2 fork as of May/2014:
|
|
* https://github.com/open-watcom/open-watcom-v2/commit/961ef1ff756f3ec5a7248cefcae00a6ecaa97ff4
|
|
* Therefore, we define and use a local copy of IID_IDirectSoundNotify here.
|
|
*/
|
|
DEFINE_GUID(IID_IDirectSoundNotify,0xB0210783,0x89cd,0x11d0,0xAF,0x08,0x00,0xA0,0xC9,0x25,0xCD,0x16);
|
|
#endif
|
|
|
|
/* PF_XMMI64_INSTRUCTIONS_AVAILABLE not in all SDKs */
|
|
#ifndef PF_XMMI64_INSTRUCTIONS_AVAILABLE
|
|
#define PF_XMMI64_INSTRUCTIONS_AVAILABLE 10
|
|
#endif
|
|
|
|
/* DSBCAPS_CTRLALL is not defined anymore with DirectX 7. Of course DirectSound
|
|
is a coherent, backwards compatible API... */
|
|
#ifndef DSBCAPS_CTRLALL
|
|
#define DSBCAPS_CTRLALL ( DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_CTRLVOLUME | \
|
|
DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY | \
|
|
DSBCAPS_CTRL3D )
|
|
#endif
|
|
|
|
#ifndef WAVE_FORMAT_IEEE_FLOAT
|
|
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
|
|
#endif
|
|
|
|
/* size of each buffer */
|
|
#define FRAGSIZE 16
|
|
/* buffer count */
|
|
#define UPDATES 2
|
|
|
|
static LPDIRECTSOUND pSoundCard = NULL;
|
|
static LPDIRECTSOUNDBUFFER pPrimarySoundBuffer = NULL, pSoundBuffer = NULL;
|
|
static LPDIRECTSOUNDNOTIFY pSoundBufferNotify = NULL;
|
|
|
|
static HANDLE notifyUpdateHandle = NULL, updateBufferHandle = NULL;
|
|
static BOOL threadInUse = FALSE;
|
|
static int fragsize=1<<FRAGSIZE;
|
|
static DWORD controlflags = DSBCAPS_CTRLALL & ~DSBCAPS_GLOBALFOCUS;
|
|
|
|
static DWORD WINAPI updateBufferProc(LPVOID lpParameter)
|
|
{
|
|
LPVOID pBlock1 = NULL, pBlock2 = NULL;
|
|
DWORD soundBufferCurrentPosition, blockBytes1, blockBytes2;
|
|
DWORD start;
|
|
|
|
while (threadInUse) {
|
|
if (WaitForSingleObject(notifyUpdateHandle,INFINITE)==WAIT_OBJECT_0) {
|
|
|
|
if (!threadInUse) break;
|
|
|
|
IDirectSoundBuffer_GetCurrentPosition
|
|
(pSoundBuffer,&soundBufferCurrentPosition,NULL);
|
|
|
|
if (soundBufferCurrentPosition < fragsize)
|
|
start = fragsize;
|
|
else
|
|
start = 0;
|
|
|
|
if (IDirectSoundBuffer_Lock
|
|
(pSoundBuffer,start,fragsize,&pBlock1,&blockBytes1,
|
|
&pBlock2,&blockBytes2,0)==DSERR_BUFFERLOST) {
|
|
IDirectSoundBuffer_Restore(pSoundBuffer);
|
|
IDirectSoundBuffer_Lock
|
|
(pSoundBuffer,start,fragsize,&pBlock1,&blockBytes1,
|
|
&pBlock2,&blockBytes2,0);
|
|
}
|
|
|
|
MUTEX_LOCK(vars);
|
|
if (Player_Paused_internal()) {
|
|
VC_SilenceBytes((SBYTE*)pBlock1,(ULONG)blockBytes1);
|
|
if (pBlock2)
|
|
VC_SilenceBytes((SBYTE*)pBlock2,(ULONG)blockBytes2);
|
|
} else {
|
|
VC_WriteBytes((SBYTE*)pBlock1,(ULONG)blockBytes1);
|
|
if (pBlock2)
|
|
VC_WriteBytes((SBYTE*)pBlock2,(ULONG)blockBytes2);
|
|
}
|
|
MUTEX_UNLOCK(vars);
|
|
|
|
IDirectSoundBuffer_Unlock
|
|
(pSoundBuffer,pBlock1,blockBytes1,pBlock2,blockBytes2);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void DS_CommandLine(const CHAR *cmdline)
|
|
{
|
|
CHAR *ptr=MD_GetAtom("buffer",cmdline,0);
|
|
|
|
if (ptr) {
|
|
int buf=atoi(ptr);
|
|
|
|
if ((buf<12)||(buf>19)) buf=FRAGSIZE;
|
|
fragsize=1<<buf;
|
|
|
|
MikMod_free(ptr);
|
|
}
|
|
|
|
ptr=MD_GetAtom("globalfocus",cmdline,1);
|
|
if (ptr) {
|
|
controlflags |= DSBCAPS_GLOBALFOCUS;
|
|
MikMod_free(ptr);
|
|
} else
|
|
controlflags &= ~DSBCAPS_GLOBALFOCUS;
|
|
}
|
|
|
|
static BOOL DS_IsPresent(void)
|
|
{
|
|
if(DirectSoundCreate(NULL,&pSoundCard,NULL)!=DS_OK)
|
|
return 0;
|
|
if (pSoundCard) {
|
|
IDirectSound_Release(pSoundCard);
|
|
pSoundCard = NULL;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int DS_Init(void)
|
|
{
|
|
DSBUFFERDESC soundBufferFormat;
|
|
WAVEFORMATEX pcmwf;
|
|
DSBPOSITIONNOTIFY positionNotifications[2];
|
|
DWORD updateBufferThreadID;
|
|
LPVOID p = NULL;
|
|
|
|
if (DirectSoundCreate(NULL,&pSoundCard,NULL)!=DS_OK) {
|
|
_mm_errno=MMERR_OPENING_AUDIO;
|
|
return 1;
|
|
}
|
|
|
|
if (IDirectSound_SetCooperativeLevel
|
|
(pSoundCard,GetForegroundWindow(),DSSCL_PRIORITY)!=DS_OK) {
|
|
_mm_errno=MMERR_DS_PRIORITY;
|
|
return 1;
|
|
}
|
|
|
|
memset(&soundBufferFormat,0,sizeof(DSBUFFERDESC));
|
|
soundBufferFormat.dwSize = sizeof(DSBUFFERDESC);
|
|
soundBufferFormat.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
|
soundBufferFormat.dwBufferBytes = 0;
|
|
soundBufferFormat.lpwfxFormat = NULL;
|
|
|
|
if (IDirectSound_CreateSoundBuffer
|
|
(pSoundCard,&soundBufferFormat,&pPrimarySoundBuffer,NULL)!=DS_OK) {
|
|
_mm_errno=MMERR_DS_BUFFER;
|
|
return 1;
|
|
}
|
|
|
|
memset(&pcmwf,0,sizeof(WAVEFORMATEX));
|
|
pcmwf.wFormatTag =(md_mode&DMODE_FLOAT)? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM;
|
|
pcmwf.nChannels =(md_mode&DMODE_STEREO)?2:1;
|
|
pcmwf.nSamplesPerSec =md_mixfreq;
|
|
pcmwf.wBitsPerSample =(md_mode&DMODE_FLOAT)?32:(md_mode&DMODE_16BITS)?16:8;
|
|
pcmwf.nBlockAlign =(pcmwf.wBitsPerSample * pcmwf.nChannels) / 8;
|
|
pcmwf.nAvgBytesPerSec=pcmwf.nSamplesPerSec*pcmwf.nBlockAlign;
|
|
|
|
if (IDirectSoundBuffer_SetFormat(pPrimarySoundBuffer,&pcmwf)!=DS_OK) {
|
|
_mm_errno=MMERR_DS_FORMAT;
|
|
return 1;
|
|
}
|
|
IDirectSoundBuffer_Play(pPrimarySoundBuffer,0,0,DSBPLAY_LOOPING);
|
|
|
|
memset(&soundBufferFormat,0,sizeof(DSBUFFERDESC));
|
|
soundBufferFormat.dwSize =sizeof(DSBUFFERDESC);
|
|
soundBufferFormat.dwFlags =controlflags|DSBCAPS_GETCURRENTPOSITION2 ;
|
|
soundBufferFormat.dwBufferBytes =fragsize*UPDATES;
|
|
soundBufferFormat.lpwfxFormat =&pcmwf;
|
|
|
|
if (IDirectSound_CreateSoundBuffer
|
|
(pSoundCard,&soundBufferFormat,&pSoundBuffer,NULL)!=DS_OK) {
|
|
_mm_errno=MMERR_DS_BUFFER;
|
|
return 1;
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
IDirectSoundBuffer_QueryInterface(pSoundBuffer, IID_IDirectSoundNotify,&p);
|
|
#else
|
|
IDirectSoundBuffer_QueryInterface(pSoundBuffer,&IID_IDirectSoundNotify,&p);
|
|
#endif
|
|
if (!p) {
|
|
_mm_errno=MMERR_DS_NOTIFY;
|
|
return 1;
|
|
}
|
|
pSoundBufferNotify = (LPDIRECTSOUNDNOTIFY) p;
|
|
|
|
notifyUpdateHandle=CreateEvent
|
|
(NULL,FALSE,FALSE,TEXT("libmikmod DirectSound Driver positionNotify Event"));
|
|
if (!notifyUpdateHandle) {
|
|
_mm_errno=MMERR_DS_EVENT;
|
|
return 1;
|
|
}
|
|
|
|
updateBufferHandle=CreateThread
|
|
(NULL,0,updateBufferProc,NULL,CREATE_SUSPENDED,&updateBufferThreadID);
|
|
if (!updateBufferHandle) {
|
|
_mm_errno=MMERR_DS_THREAD;
|
|
return 1;
|
|
}
|
|
|
|
memset(positionNotifications,0,2*sizeof(DSBPOSITIONNOTIFY));
|
|
positionNotifications[0].dwOffset =0;
|
|
positionNotifications[0].hEventNotify=notifyUpdateHandle;
|
|
positionNotifications[1].dwOffset =fragsize;
|
|
positionNotifications[1].hEventNotify=notifyUpdateHandle;
|
|
if (IDirectSoundNotify_SetNotificationPositions
|
|
(pSoundBufferNotify,2,positionNotifications) != DS_OK) {
|
|
_mm_errno=MMERR_DS_UPDATE;
|
|
return 1;
|
|
}
|
|
|
|
#if defined HAVE_SSE2
|
|
/* this test only works on Windows XP or later */
|
|
if (IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE)) {
|
|
md_mode|=DMODE_SIMDMIXER;
|
|
}
|
|
#endif
|
|
return VC_Init();
|
|
}
|
|
|
|
static void DS_Exit(void)
|
|
{
|
|
DWORD statusInfo;
|
|
|
|
if(updateBufferHandle) {
|
|
/* Signal thread to exit and wait for the exit */
|
|
if (threadInUse) {
|
|
threadInUse = 0;
|
|
MUTEX_UNLOCK(vars);
|
|
SetEvent (notifyUpdateHandle);
|
|
WaitForSingleObject (updateBufferHandle, INFINITE);
|
|
MUTEX_LOCK(vars);
|
|
}
|
|
|
|
CloseHandle(updateBufferHandle),
|
|
updateBufferHandle = NULL;
|
|
}
|
|
if (notifyUpdateHandle) {
|
|
CloseHandle(notifyUpdateHandle),
|
|
notifyUpdateHandle = NULL;
|
|
}
|
|
|
|
if (pSoundBufferNotify) {
|
|
IDirectSoundNotify_Release(pSoundBufferNotify);
|
|
pSoundBufferNotify = NULL;
|
|
}
|
|
if(pSoundBuffer) {
|
|
if(IDirectSoundBuffer_GetStatus(pSoundBuffer,&statusInfo)==DS_OK)
|
|
if(statusInfo&DSBSTATUS_PLAYING)
|
|
IDirectSoundBuffer_Stop(pSoundBuffer);
|
|
IDirectSoundBuffer_Release(pSoundBuffer);
|
|
pSoundBuffer = NULL;
|
|
}
|
|
|
|
if(pPrimarySoundBuffer) {
|
|
if(IDirectSoundBuffer_GetStatus(pPrimarySoundBuffer,&statusInfo)==DS_OK)
|
|
if(statusInfo&DSBSTATUS_PLAYING)
|
|
IDirectSoundBuffer_Stop(pPrimarySoundBuffer);
|
|
IDirectSoundBuffer_Release(pPrimarySoundBuffer);
|
|
pPrimarySoundBuffer = NULL;
|
|
}
|
|
|
|
if (pSoundCard) {
|
|
IDirectSound_Release(pSoundCard);
|
|
pSoundCard = NULL;
|
|
}
|
|
|
|
VC_Exit();
|
|
}
|
|
|
|
static BOOL do_update = 0;
|
|
|
|
static void DS_Update(void)
|
|
{
|
|
LPVOID block;
|
|
DWORD bBytes;
|
|
|
|
/* Do first update in DS_Update() to be consistent with other
|
|
non threaded drivers. */
|
|
if (do_update && pSoundBuffer) {
|
|
do_update = 0;
|
|
|
|
if (IDirectSoundBuffer_Lock(pSoundBuffer, 0, fragsize, &block, &bBytes, NULL, NULL, 0)
|
|
== DSERR_BUFFERLOST) {
|
|
IDirectSoundBuffer_Restore (pSoundBuffer);
|
|
IDirectSoundBuffer_Lock (pSoundBuffer, 0, fragsize, &block, &bBytes, NULL, NULL, 0);
|
|
}
|
|
|
|
if (Player_Paused_internal()) {
|
|
VC_SilenceBytes ((SBYTE *)block, (ULONG)bBytes);
|
|
} else {
|
|
VC_WriteBytes ((SBYTE *)block, (ULONG)bBytes);
|
|
}
|
|
|
|
IDirectSoundBuffer_Unlock (pSoundBuffer, block, bBytes, NULL, 0);
|
|
|
|
IDirectSoundBuffer_SetCurrentPosition(pSoundBuffer, 0);
|
|
IDirectSoundBuffer_Play(pSoundBuffer, 0, 0, DSBPLAY_LOOPING);
|
|
|
|
threadInUse=1;
|
|
ResumeThread (updateBufferHandle);
|
|
}
|
|
}
|
|
|
|
static void DS_PlayStop(void)
|
|
{
|
|
do_update = 0;
|
|
if (pSoundBuffer)
|
|
IDirectSoundBuffer_Stop(pSoundBuffer);
|
|
VC_PlayStop();
|
|
}
|
|
|
|
static int DS_PlayStart(void)
|
|
{
|
|
do_update = 1;
|
|
return VC_PlayStart();
|
|
}
|
|
|
|
MIKMODAPI MDRIVER drv_ds=
|
|
{
|
|
NULL,
|
|
"DirectSound",
|
|
"DirectSound Driver (DX6+) v0.6",
|
|
0,255,
|
|
"ds",
|
|
"buffer:r:12,19,16:Audio buffer log2 size\n"
|
|
"globalfocus:b:0:Play if window does not have the focus\n",
|
|
DS_CommandLine,
|
|
DS_IsPresent,
|
|
VC_SampleLoad,
|
|
VC_SampleUnload,
|
|
VC_SampleSpace,
|
|
VC_SampleLength,
|
|
DS_Init,
|
|
DS_Exit,
|
|
NULL,
|
|
VC_SetNumVoices,
|
|
DS_PlayStart,
|
|
DS_PlayStop,
|
|
DS_Update,
|
|
NULL,
|
|
VC_VoiceSetVolume,
|
|
VC_VoiceGetVolume,
|
|
VC_VoiceSetFrequency,
|
|
VC_VoiceGetFrequency,
|
|
VC_VoiceSetPanning,
|
|
VC_VoiceGetPanning,
|
|
VC_VoicePlay,
|
|
VC_VoiceStop,
|
|
VC_VoiceStopped,
|
|
VC_VoiceGetPosition,
|
|
VC_VoiceRealVolume
|
|
};
|
|
|
|
#else
|
|
|
|
MISSING(drv_ds);
|
|
|
|
#endif
|
|
|
|
/* ex:set ts=4: */
|