Files
Konrad Beckmann 20d7fefaf9 Import mikmod
2021-08-02 02:19:41 +02:00

445 lines
12 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.
*/
/*==============================================================================
$Id$
Driver for output on win32 platforms using XAudio2
==============================================================================*/
/*
Originally written by 'honza.c' <honzac@users.sourceforge.net>
Fixes, C-only conversion and float support by O.Sezer
<sezero@users.sourceforge.net>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef DRV_XAUDIO2
#ifndef _WIN32_WINNT
/* 0x0501 for WinXP, 0x0602 for Win8 */
#define _WIN32_WINNT 0x0501
#endif
#if defined(DRV_XAUDIO28) && (_WIN32_WINNT < 0x0602)
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0602
#endif
#include "mikmod_internals.h"
#define INITGUID
#include <xaudio2.h>
#if defined(_MSC_VER)
#ifdef DRV_XAUDIO28
#pragma comment(lib,"xaudio2.lib")
#endif
#pragma comment(lib,"ole32.lib")
#endif
/* PF_XMMI64_INSTRUCTIONS_AVAILABLE not in all SDKs */
#ifndef PF_XMMI64_INSTRUCTIONS_AVAILABLE
#define PF_XMMI64_INSTRUCTIONS_AVAILABLE 10
#endif
#ifndef WAVE_FORMAT_IEEE_FLOAT
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
#endif
#define XAUDIO2_NUM_BUFFERS 4
#define XAUDIO2_BUFFER_SIZE 32768
#if defined(MIKMOD_DEBUG)
# define dbgprintf fprintf
#elif defined (__GNUC__) && !(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)
# define dbgprintf(f, fmt, args...) do {} while (0)
#else
# define dbgprintf(f, ...) do {} while (0)
#endif
#ifndef __cplusplus
/* doing things C-only .. */
static HANDLE hBufferEvent;
static void STDMETHODCALLTYPE cb_OnVoiceProcessPassStart(IXAudio2VoiceCallback* p,
UINT32 SamplesRequired) {
dbgprintf(stderr, "\n>XAudio2: OnVoiceProcessingPassStart<\n");
}
static void STDMETHODCALLTYPE cb_OnVoiceProcessPassEnd(IXAudio2VoiceCallback* p) {
dbgprintf(stderr, "\n>XAudio2: OnVoiceProcessingPassEnd<\n");
}
static void STDMETHODCALLTYPE cb_OnStreamEnd(IXAudio2VoiceCallback* p) {
dbgprintf(stderr, "\n>XAudio2: OnStreamEnd<\n");
}
static void STDMETHODCALLTYPE cb_OnBufferStart(IXAudio2VoiceCallback* p,
void* pBufferContext) {
dbgprintf(stderr, "\n>XAudio2: OnBufferStart<\n");
}
static void STDMETHODCALLTYPE cb_OnBufferEnd(IXAudio2VoiceCallback* p,
void* pBufferContext) {
SetEvent(hBufferEvent);
dbgprintf(stderr, "\n>XAudio2: OnBufferEnd<\n");
}
static void STDMETHODCALLTYPE cb_OnLoopEnd(IXAudio2VoiceCallback* p,
void* pBufferContext) {
dbgprintf(stderr, "\n>XAudio2: OnLoopEnd<\n");
}
static void STDMETHODCALLTYPE cb_OnVoiceError(IXAudio2VoiceCallback* p,
void* pBufferContext,
HRESULT Error) {
dbgprintf(stderr, "\n>XAudio2: OnVoiceError: %ld <\n", Error);
}
static IXAudio2VoiceCallbackVtbl cbVoice_vtbl = {
cb_OnVoiceProcessPassStart,
cb_OnVoiceProcessPassEnd,
cb_OnStreamEnd,
cb_OnBufferStart,
cb_OnBufferEnd,
cb_OnLoopEnd,
cb_OnVoiceError
};
static IXAudio2VoiceCallback cbVoice = {
&cbVoice_vtbl
};
#define pcbVoice &cbVoice
#else /* __cplusplus: */
class XAudio2VoiceCallback: public IXAudio2VoiceCallback {
public:
HANDLE hBufferEvent;
public:
XAudio2VoiceCallback(): hBufferEvent(CreateEvent(NULL, FALSE, FALSE, TEXT("libmikmod XAudio2 Driver buffer Event"))) { }
virtual ~XAudio2VoiceCallback() { CloseHandle(hBufferEvent); }
STDMETHOD_(void, cb_OnVoiceProcessPassStart)(UINT32 SamplesRequired) {
dbgprintf(stderr, "\n>XAudio2: OnVoiceProcessingPassStart<\n");
}
STDMETHOD_(void, cb_OnVoiceProcessPassEnd)() {
dbgprintf(stderr, "\n>XAudio2: OnVoiceProcessingPassEnd<\n");
}
STDMETHOD_(void, cb_OnStreamEnd)() {
dbgprintf(stderr, "\n>XAudio2: OnStreamEnd<\n");
}
STDMETHOD_(void, cb_OnBufferStart)(void* pBufferContext) {
dbgprintf(stderr, "\n>XAudio2: OnBufferStart<\n");
}
STDMETHOD_(void, cb_OnBufferEnd)(void* pBufferContext) {
SetEvent(hBufferEvent);
dbgprintf(stderr, "\n>XAudio2: OnBufferStart<\n");
}
STDMETHOD_(void, cb_OnLoopEnd)(void* pBufferContext) {
dbgprintf(stderr, "\n>XAudio2: OnLoopEnd<\n");
}
STDMETHOD_(void, cb_OnVoiceError)(void* pBufferContext, HRESULT Error) {
dbgprintf(stderr, "\n>XAudio2: OnVoiceError: %ld <\n", Error);
}
};
static XAudio2VoiceCallback *pcbVoice = NULL;
#define hBufferEvent pcbVoice->hBufferEvent
#define IXAudio2_Release(p) ((p)->Release())
#ifndef DRV_XAUDIO28
#define IXAudio2_CreateMasteringVoice(p,a,b,c,d,e,f) ((p)->CreateMasteringVoice(a,b,c,d,e,f))
#define IXAudio2SourceVoice_GetState(p,a) ((p)->GetState(a))
#else
#define IXAudio2_CreateMasteringVoice(p,a,b,c,d,e,f,g) ((p)->CreateMasteringVoice(a,b,c,d,e,f,g))
#define IXAudio2SourceVoice_GetState(p,a,b) ((p)->GetState(a,b))
#endif
#define IXAudio2SourceVoice_SubmitSourceBuffer(p,a,b) ((p)->SubmitSourceBuffer(a,b))
#define IXAudio2_CreateSourceVoice(p,a,b,c,d,e,f,g) ((p)->CreateSourceVoice(a,b,c,d,e,f,g))
#define IXAudio2SourceVoice_DestroyVoice(p) ((p)->DestroyVoice())
#define IXAudio2MasteringVoice_DestroyVoice(p) ((p)->DestroyVoice())
#define IXAudio2SourceVoice_Start(p,a,b) ((p)->Start(a,b))
#define IXAudio2SourceVoice_Stop( p,a,b) ((p)->Stop(a,b))
#endif /* __cplusplus */
static IXAudio2* pXAudio2 = NULL;
static IXAudio2MasteringVoice* pMasterVoice = NULL;
static IXAudio2SourceVoice* pSourceVoice = NULL;
static HANDLE UpdateBufferHandle = NULL;
static BOOL threadInUse = FALSE;
static BYTE buffers[XAUDIO2_NUM_BUFFERS][XAUDIO2_BUFFER_SIZE];
static DWORD current_buf = 0;
static DWORD WINAPI UpdateBufferProc(LPVOID lpParameter) {
while (threadInUse) {
while (1) {
XAUDIO2_VOICE_STATE state;
XAUDIO2_BUFFER audio_buf;
#ifndef DRV_XAUDIO28
IXAudio2SourceVoice_GetState(pSourceVoice, &state);
#else
IXAudio2SourceVoice_GetState(pSourceVoice, &state, XAUDIO2_VOICE_NOSAMPLESPLAYED);
#endif
if (state.BuffersQueued >= XAUDIO2_NUM_BUFFERS - 1)
break;
MUTEX_LOCK(vars);
if (Player_Paused_internal())
VC_SilenceBytes((SBYTE *) buffers[current_buf], (ULONG) XAUDIO2_BUFFER_SIZE);
else
VC_WriteBytes((SBYTE *) buffers[current_buf], (ULONG) XAUDIO2_BUFFER_SIZE);
MUTEX_UNLOCK(vars);
memset(&audio_buf, 0, sizeof(XAUDIO2_BUFFER));
audio_buf.AudioBytes = XAUDIO2_BUFFER_SIZE;
audio_buf.pAudioData = buffers[current_buf];
IXAudio2SourceVoice_SubmitSourceBuffer(pSourceVoice, &audio_buf, NULL);
current_buf++;
current_buf %= XAUDIO2_NUM_BUFFERS;
}
WaitForSingleObject(hBufferEvent, INFINITE);
}
return 0;
}
static void XAudio2_CommandLine(const CHAR *cmdline) {
/* no options */
}
static BOOL XAudio2_IsPresent(void) {
HRESULT r;
if (pXAudio2 == NULL) {
#ifndef _XBOX
CoInitializeEx(NULL, COINIT_MULTITHREADED);
#endif
r = XAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR);
if (pXAudio2) {
IXAudio2_Release(pXAudio2);
pXAudio2 = NULL;
}
#ifndef _XBOX
CoUninitialize();
#endif
if (FAILED(r))
return 0;
}
return 1;
}
static int XAudio2_Init(void) {
UINT32 flags;
DWORD thread_id;
WAVEFORMATEX wfmt;
memset(&wfmt, 0, sizeof(WAVEFORMATEX));
wfmt.wFormatTag= (md_mode & DMODE_FLOAT)? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM;
wfmt.nChannels = (md_mode & DMODE_STEREO)? 2: 1;
wfmt.nSamplesPerSec = md_mixfreq;
wfmt.wBitsPerSample = (md_mode & DMODE_FLOAT)? 32: (md_mode & DMODE_16BITS)? 16: 8;
wfmt.nBlockAlign = (wfmt.wBitsPerSample * wfmt.nChannels) / 8;
wfmt.nAvgBytesPerSec = wfmt.nSamplesPerSec * wfmt.nBlockAlign;
if (wfmt.nSamplesPerSec < XAUDIO2_MIN_SAMPLE_RATE ||
wfmt.nSamplesPerSec > XAUDIO2_MAX_SAMPLE_RATE ||
wfmt.nChannels > XAUDIO2_MAX_AUDIO_CHANNELS) {
return 1;
}
current_buf = 0;
flags = 0;
#if defined(_DEBUG) && !defined(DRV_XAUDIO28)
/* flags |= XAUDIO2_DEBUG_ENGINE;*/
#endif
#ifndef _XBOX
CoInitializeEx(NULL, COINIT_MULTITHREADED);
#endif
if (FAILED(XAudio2Create(&pXAudio2, flags, XAUDIO2_DEFAULT_PROCESSOR))) {
goto fail;
}
#ifdef DRV_XAUDIO28
if (FAILED(IXAudio2_CreateMasteringVoice(pXAudio2, &pMasterVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE,
0, NULL, NULL, AudioCategory_Other))) {
goto fail;
}
#else
if (FAILED(IXAudio2_CreateMasteringVoice(pXAudio2, &pMasterVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, 0, NULL))) {
goto fail;
}
#endif
if (FAILED(IXAudio2_CreateSourceVoice(pXAudio2, &pSourceVoice, &wfmt, 0, 1.0f, pcbVoice, NULL, NULL))) {
goto fail;
}
#ifndef __cplusplus
if ((hBufferEvent = CreateEvent(NULL, FALSE, FALSE, TEXT("libmikmod XAudio2 Driver buffer Event"))) == NULL) {
goto fail;
}
#endif
if ((UpdateBufferHandle = CreateThread(NULL, 0, UpdateBufferProc, NULL, CREATE_SUSPENDED, &thread_id)) == NULL) {
goto fail;
}
#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();
fail:
if (pSourceVoice) {
IXAudio2SourceVoice_DestroyVoice(pSourceVoice);
pSourceVoice = NULL;
}
if (pMasterVoice) {
IXAudio2MasteringVoice_DestroyVoice(pMasterVoice);
pMasterVoice = NULL;
}
if (pXAudio2) {
IXAudio2_Release(pXAudio2);
pXAudio2 = NULL;
}
#ifndef _XBOX
CoUninitialize();
#endif
return 1;
}
static void XAudio2_Exit(void) {
if (UpdateBufferHandle != NULL) {
/* signal thread to exit and wait for the exit */
if (threadInUse) {
threadInUse = 0;
MUTEX_UNLOCK(vars);
SetEvent(hBufferEvent);
WaitForSingleObject(UpdateBufferHandle, INFINITE);
MUTEX_LOCK(vars);
}
CloseHandle(UpdateBufferHandle);
UpdateBufferHandle = NULL;
}
IXAudio2SourceVoice_Stop(pSourceVoice, 0, 0);
if (pSourceVoice) {
IXAudio2SourceVoice_DestroyVoice(pSourceVoice);
pSourceVoice = NULL;
}
if (pMasterVoice) {
IXAudio2MasteringVoice_DestroyVoice(pMasterVoice);
pMasterVoice = NULL;
}
if (pXAudio2) {
IXAudio2_Release(pXAudio2);
pXAudio2 = NULL;
}
#ifndef __cplusplus
if (hBufferEvent != NULL) {
CloseHandle(hBufferEvent);
hBufferEvent = NULL;
}
#endif
#ifndef _XBOX
CoUninitialize();
#endif
VC_Exit();
}
static BOOL do_update = 0;
static void XAudio2_Update(void) {
if (do_update && pSourceVoice) {
do_update = 0;
while (1) {
XAUDIO2_VOICE_STATE state;
XAUDIO2_BUFFER audio_buf;
#ifndef DRV_XAUDIO28
IXAudio2SourceVoice_GetState(pSourceVoice, &state);
#else
IXAudio2SourceVoice_GetState(pSourceVoice, &state, XAUDIO2_VOICE_NOSAMPLESPLAYED);
#endif
if (state.BuffersQueued > 0)
break;
current_buf %= XAUDIO2_NUM_BUFFERS;
if (Player_Paused_internal())
VC_SilenceBytes((SBYTE *) buffers[current_buf], (ULONG) XAUDIO2_BUFFER_SIZE);
else
VC_WriteBytes((SBYTE *) buffers[current_buf], (ULONG) XAUDIO2_BUFFER_SIZE);
memset(&audio_buf, 0, sizeof(XAUDIO2_BUFFER));
audio_buf.AudioBytes = XAUDIO2_BUFFER_SIZE;
audio_buf.pAudioData = buffers[current_buf];
IXAudio2SourceVoice_SubmitSourceBuffer(pSourceVoice, &audio_buf, NULL);
current_buf++;
current_buf %= XAUDIO2_NUM_BUFFERS;
}
IXAudio2SourceVoice_Start(pSourceVoice, 0, 0);
threadInUse = 1;
ResumeThread(UpdateBufferHandle);
}
}
static void XAudio2_PlayStop(void) {
do_update = 0;
if (pSourceVoice)
IXAudio2SourceVoice_Stop(pSourceVoice, 0, 0);
VC_PlayStop();
}
static int XAudio2_PlayStart(void) {
do_update = 1;
return VC_PlayStart();
}
MIKMODAPI MDRIVER drv_xaudio2 = {
NULL,
"XAudio2",
"DirectX XAudio2 Driver v0.3",
0,255,
"xaudio2",
"",
XAudio2_CommandLine,
XAudio2_IsPresent,
VC_SampleLoad,
VC_SampleUnload,
VC_SampleSpace,
VC_SampleLength,
XAudio2_Init,
XAudio2_Exit,
NULL,
VC_SetNumVoices,
XAudio2_PlayStart,
XAudio2_PlayStop,
XAudio2_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
#include "mikmod_internals.h"
MISSING(drv_xaudio2);
#endif
/* ex:set ts=8: */