questzdoom/Projects/Android/jni/SupportLibs/fluidsynth/fluid_dsound.c

399 lines
12 KiB
C
Raw Normal View History

2021-04-20 20:09:02 +00:00
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* 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 library 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
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#define INITGUID
#include "fluidsynth_priv.h"
#include "fluid_synth.h"
#include "fluid_sys.h"
#include "fluid_adriver.h"
#include "fluid_settings.h"
#include <windows.h>
#include <mmsystem.h>
#include <dsound.h>
fluid_audio_driver_t*
new_fluid_dsound_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth);
int delete_fluid_dsound_audio_driver(fluid_audio_driver_t* data);
DWORD WINAPI fluid_dsound_audio_run(LPVOID lpParameter);
HWND fluid_win32_get_window(void);
char* fluid_win32_error(HRESULT hr);
#define FLUID_HINSTANCE ((HINSTANCE)fluid_get_hinstance())
typedef struct {
fluid_audio_driver_t driver;
LPDIRECTSOUND direct_sound;
LPDIRECTSOUNDBUFFER prim_buffer;
LPDIRECTSOUNDBUFFER sec_buffer;
WAVEFORMATEX* format;
HANDLE thread;
DWORD threadID;
fluid_synth_t* synth;
fluid_audio_callback_t write;
int cont;
DWORD buffer_byte_size;
DWORD queue_byte_size;
DWORD frame_size;
} fluid_dsound_audio_driver_t;
typedef struct {
LPGUID devGUID;
char* devname;
} fluid_dsound_devsel_t;
BOOL CALLBACK
fluid_dsound_enum_callback(LPGUID guid, LPCTSTR description, LPCTSTR module, LPVOID context)
{
fluid_settings_t* settings = (fluid_settings_t*) context;
fluid_settings_add_option(settings, "audio.dsound.device", (char *)description);
return TRUE;
}
BOOL CALLBACK
fluid_dsound_enum_callback2(LPGUID guid, LPCTSTR description, LPCTSTR module, LPVOID context)
{
fluid_dsound_devsel_t* devsel = (fluid_dsound_devsel_t*) context;
FLUID_LOG(FLUID_DBG, "Testing audio device: %s", description);
if (strcasecmp(devsel->devname, description) == 0) {
devsel->devGUID = FLUID_NEW(GUID);
if(devsel->devGUID) {
memcpy(devsel->devGUID, guid, sizeof(GUID));
FLUID_LOG(FLUID_DBG, "Selected audio device GUID: %p", devsel->devGUID);
}
}
return TRUE;
}
void fluid_dsound_audio_driver_settings(fluid_settings_t* settings)
{
fluid_settings_register_str(settings, "audio.dsound.device", "default", 0, NULL, NULL);
fluid_settings_add_option(settings, "audio.dsound.device", "default");
DirectSoundEnumerate((LPDSENUMCALLBACK) fluid_dsound_enum_callback, settings);
}
/*
* new_fluid_dsound_audio_driver
*/
fluid_audio_driver_t*
new_fluid_dsound_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth)
{
HRESULT hr;
DSBUFFERDESC desc;
fluid_dsound_audio_driver_t* dev = NULL;
DSCAPS caps;
char *buf1;
DWORD bytes1;
double sample_rate;
int periods, period_size;
fluid_dsound_devsel_t devsel;
/* check if the globals are initialized */
if (FLUID_HINSTANCE == NULL) {
FLUID_LOG(FLUID_ERR, "FluidSynth hinstance not set, which is needed for DirectSound");
return NULL;
}
/*
if (fluid_wnd == NULL) {
if (fluid_win32_create_window() != 0) {
FLUID_LOG(FLUID_ERR, "Couldn't create window needed for DirectSound");
return NULL;
}
}
*/
/* create and clear the driver data */
dev = FLUID_NEW(fluid_dsound_audio_driver_t);
if (dev == NULL) {
FLUID_LOG(FLUID_ERR, "Out of memory");
return NULL;
}
FLUID_MEMSET(dev, 0, sizeof(fluid_dsound_audio_driver_t));
dev->synth = synth;
dev->cont = 1;
fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate);
fluid_settings_getint(settings, "audio.periods", &periods);
fluid_settings_getint(settings, "audio.period-size", &period_size);
/* check the format */
if (!fluid_settings_str_equal(settings, "audio.sample-format", "16bits")) {
FLUID_LOG(FLUID_ERR, "Unhandled sample format");
goto error_recovery;
}
dev->frame_size = 2 * sizeof(short);
dev->buffer_byte_size = period_size * dev->frame_size;
dev->queue_byte_size = periods * dev->buffer_byte_size;
dev->write = fluid_synth_write_s16;
/* create and initialize the buffer format */
dev->format = (WAVEFORMATEX*) FLUID_MALLOC(sizeof(WAVEFORMATEX));
if (dev->format == NULL) {
FLUID_LOG(FLUID_ERR, "Out of memory");
goto error_recovery;
}
ZeroMemory(dev->format, sizeof(WAVEFORMATEX));
dev->format->wFormatTag = WAVE_FORMAT_PCM;
dev->format->nChannels = 2;
dev->format->wBitsPerSample = 16;
dev->format->nSamplesPerSec = (DWORD) sample_rate;
dev->format->nBlockAlign = (WORD) dev->frame_size;
dev->format->nAvgBytesPerSec = dev->format->nSamplesPerSec * dev->frame_size;
dev->format->cbSize = 0;
devsel.devGUID = NULL;
/* get the selected device name. if none is specified, use NULL for the default device. */
if(fluid_settings_getstr(settings, "audio.dsound.device", &devsel.devname)) {
/* look for the GUID of the selected device */
DirectSoundEnumerate((LPDSENUMCALLBACK) fluid_dsound_enum_callback2, (void *)&devsel);
}
/* open DirectSound */
hr = DirectSoundCreate(devsel.devGUID, &dev->direct_sound, NULL);
if (hr != DS_OK) {
FLUID_LOG(FLUID_ERR, "Failed to create the DirectSound object");
goto error_recovery;
}
hr = IDirectSound_SetCooperativeLevel(dev->direct_sound, fluid_win32_get_window(), DSSCL_PRIORITY);
if (hr != DS_OK) {
FLUID_LOG(FLUID_ERR, "Failed to set the cooperative level");
goto error_recovery;
}
caps.dwSize = sizeof(caps);
hr = IDirectSound_GetCaps(dev->direct_sound, &caps);
if (hr != DS_OK) {
FLUID_LOG(FLUID_ERR, "Failed to query the device capacities");
goto error_recovery;
}
/* create primary buffer */
ZeroMemory(&desc, sizeof(DSBUFFERDESC));
desc.dwSize = sizeof(DSBUFFERDESC);
desc.dwFlags = DSBCAPS_PRIMARYBUFFER;
if (caps.dwFreeHwMixingStreamingBuffers > 0) {
desc.dwFlags |= DSBCAPS_LOCHARDWARE;
}
hr = IDirectSound_CreateSoundBuffer(dev->direct_sound, &desc, &dev->prim_buffer, NULL);
if (hr != DS_OK) {
FLUID_LOG(FLUID_ERR, "Failed to allocate the primary buffer");
goto error_recovery;
}
/* set the primary sound buffer to this format. if it fails, just
print a warning. */
hr = IDirectSoundBuffer_SetFormat(dev->prim_buffer, dev->format);
if (hr != DS_OK) {
FLUID_LOG(FLUID_WARN, "Can't set format of primary sound buffer", fluid_win32_error(hr));
}
/* initialize the buffer description */
ZeroMemory(&desc, sizeof(DSBUFFERDESC));
desc.dwSize = sizeof(DSBUFFERDESC);
desc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
desc.lpwfxFormat = dev->format;
desc.dwBufferBytes = dev->queue_byte_size;
desc.dwReserved = 0;
if (caps.dwFreeHwMixingStreamingBuffers > 0) {
desc.dwFlags |= DSBCAPS_LOCHARDWARE;
}
/* create the secondary sound buffer */
hr = IDirectSound_CreateSoundBuffer(dev->direct_sound, &desc, &dev->sec_buffer, NULL);
if (hr != DS_OK) {
FLUID_LOG(FLUID_ERR, "dsound: Can't create sound buffer: %s", fluid_win32_error(hr));
goto error_recovery;
}
/* Lock */
hr = IDirectSoundBuffer_Lock(dev->sec_buffer, 0, 0, (void*) &buf1, &bytes1, 0, 0, DSBLOCK_ENTIREBUFFER);
if ((hr != DS_OK) || (buf1 == NULL)) {
FLUID_LOG(FLUID_PANIC, "Failed to lock the audio buffer. Exiting.");
goto error_recovery;
}
/* fill the buffer with silence */
memset(buf1, 0, bytes1);
/* Unlock */
IDirectSoundBuffer_Unlock(dev->sec_buffer, buf1, bytes1, 0, 0);
/* start the audio thread */
dev->thread = CreateThread(NULL, 0, &fluid_dsound_audio_run, (LPVOID) dev, 0, &dev->threadID);
if (dev->thread == NULL) {
goto error_recovery;
}
return (fluid_audio_driver_t*) dev;
error_recovery:
delete_fluid_dsound_audio_driver((fluid_audio_driver_t*) dev);
return NULL;
}
int delete_fluid_dsound_audio_driver(fluid_audio_driver_t* d)
{
fluid_dsound_audio_driver_t* dev = (fluid_dsound_audio_driver_t*) d;
if (dev == NULL) {
return FLUID_OK;
}
/* tell the audio thread to stop its loop */
dev->cont = 0;
/* wait till the audio thread exits */
if (dev->thread != 0) {
if (WaitForSingleObject(dev->thread, 2000) != WAIT_OBJECT_0) {
/* on error kill the thread mercilessly */
FLUID_LOG(FLUID_DBG, "Couldn't join the audio thread. killing it.");
TerminateThread(dev->thread, 0);
}
}
/* release all the allocated ressources */
if (dev->format != NULL) {
FLUID_FREE(dev->format);
}
if (dev->sec_buffer != NULL) {
IDirectSoundBuffer_Stop(dev->sec_buffer);
IDirectSoundBuffer_Release(dev->sec_buffer);
}
if (dev->prim_buffer != NULL) {
IDirectSoundBuffer_Release(dev->prim_buffer);
}
if (dev->direct_sound != NULL) {
IDirectSound_Release(dev->direct_sound);
}
FLUID_FREE(dev);
// fluid_win32_destroy_window();
return 0;
}
DWORD WINAPI fluid_dsound_audio_run(LPVOID lpParameter)
{
fluid_dsound_audio_driver_t* dev = (fluid_dsound_audio_driver_t*) lpParameter;
short *buf1, *buf2;
DWORD bytes1, bytes2;
DWORD offset = 0;
DWORD cur_position, frames, play_position, write_position, bytes;
HRESULT res;
cur_position = 0;
/* boost the priority of the audio thread */
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
IDirectSoundBuffer_Play(dev->sec_buffer, 0, 0, DSBPLAY_LOOPING);
while (dev->cont) {
IDirectSoundBuffer_GetCurrentPosition(dev->sec_buffer, &play_position, &write_position);
if (cur_position <= play_position) {
bytes = play_position - cur_position;
} else if ((play_position < cur_position) && (write_position <= cur_position)) {
bytes = dev->queue_byte_size + play_position - cur_position;
} else {
bytes = 0;
}
if (bytes >= dev->buffer_byte_size) {
/* Lock */
res = IDirectSoundBuffer_Lock(dev->sec_buffer, cur_position, bytes, (void*) &buf1, &bytes1, (void*) &buf2, &bytes2, 0);
if ((res != DS_OK) || (buf1 == NULL)) {
FLUID_LOG(FLUID_PANIC, "Failed to lock the audio buffer. System lockup might follow. Exiting.");
ExitProcess(0);
}
/* fill the first part of the buffer */
if (bytes1 > 0) {
frames = bytes1 / dev->frame_size;
dev->write(dev->synth, frames, buf1, 0, 2, buf1, 1, 2);
cur_position += frames * dev->frame_size;
}
/* fill the second part of the buffer */
if ((buf2 != NULL) && (bytes2 > 0)) {
frames = bytes2 / dev->frame_size;
dev->write(dev->synth, frames, buf2, 0, 2, buf2, 1, 2);
cur_position += frames * dev->frame_size;
}
/* Unlock */
IDirectSoundBuffer_Unlock(dev->sec_buffer, buf1, bytes1, buf2, bytes2);
if (cur_position >= dev->queue_byte_size) {
cur_position -= dev->queue_byte_size;
}
} else {
Sleep(1);
}
}
ExitThread(0);
return 0; /* never reached */
}
char* fluid_win32_error(HRESULT hr) {
char *s = "Don't know why";
switch (hr) {
case E_NOINTERFACE: s = "No such interface"; break;
case DSERR_GENERIC: s = "Generic error"; break;
case DSERR_ALLOCATED: s = "Required resources already allocated"; break;
case DSERR_BADFORMAT: s = "The format is not supported"; break;
case DSERR_INVALIDPARAM: s = "Invalid parameter"; break;
case DSERR_NOAGGREGATION: s = "No aggregation"; break;
case DSERR_OUTOFMEMORY: s = "Out of memory"; break;
case DSERR_UNINITIALIZED: s = "Uninitialized"; break;
case DSERR_UNSUPPORTED: s = "Function not supported"; break;
}
return s;
}