Various fixes to dsound driver (#435)

- fluid_dsound_audio_run() and fluid_win32_error() are now static functions.

- WAVEFORMATEX is not required by DirectSound during the playback. We do not need it anymore after calling ::SetFormat(), so allocating this structure dynamically inside driver structure is useless.

- Implemented support for float sample type.

- Uses an event object for handling the end of thread, it allows to combine the quit event with the later wait in milliseconds in a single block.

- Calculates the amount of milliseconds to sleep rather than sleeping always for one millisecond.

- Fix an error into a FLUID_LOG() call.

- Fix handle leak of the thread, now it is correctly closed with CloseHandle().

- ExitThread() is a nonsense in that position, since the thread is already exiting.

- Fix error when compiling with WSDK 8.1, by defining NOBITMAP, in case NOGDI macro added somewhere
This commit is contained in:
carlo-bramini 2018-09-29 10:08:34 +02:00 committed by Tom M
parent 923f5f3544
commit 6f8a574e36

View file

@ -30,9 +30,12 @@
#include <mmsystem.h> #include <mmsystem.h>
#include <dsound.h> #include <dsound.h>
DWORD WINAPI fluid_dsound_audio_run(LPVOID lpParameter); #define NOBITMAP
#include <mmreg.h>
char *fluid_win32_error(HRESULT hr); static DWORD WINAPI fluid_dsound_audio_run(LPVOID lpParameter);
static char *fluid_win32_error(HRESULT hr);
typedef struct typedef struct
{ {
@ -40,12 +43,12 @@ typedef struct
LPDIRECTSOUND direct_sound; LPDIRECTSOUND direct_sound;
LPDIRECTSOUNDBUFFER prim_buffer; LPDIRECTSOUNDBUFFER prim_buffer;
LPDIRECTSOUNDBUFFER sec_buffer; LPDIRECTSOUNDBUFFER sec_buffer;
WAVEFORMATEX *format;
HANDLE thread; HANDLE thread;
DWORD threadID; DWORD threadID;
fluid_synth_t *synth; fluid_synth_t *synth;
fluid_audio_callback_t write; fluid_audio_callback_t write;
int cont; HANDLE quit_ev;
int bytes_per_second;
DWORD buffer_byte_size; DWORD buffer_byte_size;
DWORD queue_byte_size; DWORD queue_byte_size;
DWORD frame_size; DWORD frame_size;
@ -109,6 +112,7 @@ new_fluid_dsound_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
double sample_rate; double sample_rate;
int periods, period_size; int periods, period_size;
fluid_dsound_devsel_t devsel; fluid_dsound_devsel_t devsel;
WAVEFORMATEX format;
/* create and clear the driver data */ /* create and clear the driver data */
dev = FLUID_NEW(fluid_dsound_audio_driver_t); dev = FLUID_NEW(fluid_dsound_audio_driver_t);
@ -121,42 +125,49 @@ new_fluid_dsound_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
FLUID_MEMSET(dev, 0, sizeof(fluid_dsound_audio_driver_t)); FLUID_MEMSET(dev, 0, sizeof(fluid_dsound_audio_driver_t));
dev->synth = synth; dev->synth = synth;
dev->cont = 1;
fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate); fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate);
fluid_settings_getint(settings, "audio.periods", &periods); fluid_settings_getint(settings, "audio.periods", &periods);
fluid_settings_getint(settings, "audio.period-size", &period_size); fluid_settings_getint(settings, "audio.period-size", &period_size);
/* Clear the buffer format */
ZeroMemory(&format, sizeof(WAVEFORMATEX));
/* check the format */ /* check the format */
if(!fluid_settings_str_equal(settings, "audio.sample-format", "16bits")) if(fluid_settings_str_equal(settings, "audio.sample-format", "float"))
{
FLUID_LOG(FLUID_DBG, "Selected 32 bit sample format");
dev->frame_size = 2 * sizeof(float);
dev->write = fluid_synth_write_float;
format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
}
else if(fluid_settings_str_equal(settings, "audio.sample-format", "16bits"))
{
FLUID_LOG(FLUID_DBG, "Selected 16 bit sample format");
dev->frame_size = 2 * sizeof(short);
dev->write = fluid_synth_write_s16;
format.wFormatTag = WAVE_FORMAT_PCM;
}
else
{ {
FLUID_LOG(FLUID_ERR, "Unhandled sample format"); FLUID_LOG(FLUID_ERR, "Unhandled sample format");
goto error_recovery; goto error_recovery;
} }
dev->frame_size = 2 * sizeof(short);
dev->buffer_byte_size = period_size * dev->frame_size; dev->buffer_byte_size = period_size * dev->frame_size;
dev->queue_byte_size = periods * dev->buffer_byte_size; dev->queue_byte_size = periods * dev->buffer_byte_size;
dev->write = fluid_synth_write_s16; dev->bytes_per_second = sample_rate * dev->frame_size;
/* create and initialize the buffer format */ /* Finish to initialize the buffer format */
dev->format = (WAVEFORMATEX *) FLUID_MALLOC(sizeof(WAVEFORMATEX)); format.nChannels = 2;
format.wBitsPerSample = dev->frame_size * 4;
if(dev->format == NULL) format.nSamplesPerSec = (DWORD) sample_rate;
{ format.nBlockAlign = (WORD) dev->frame_size;
FLUID_LOG(FLUID_ERR, "Out of memory"); format.nAvgBytesPerSec = dev->bytes_per_second;
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; devsel.devGUID = NULL;
@ -220,11 +231,11 @@ new_fluid_dsound_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
/* set the primary sound buffer to this format. if it fails, just /* set the primary sound buffer to this format. if it fails, just
print a warning. */ print a warning. */
hr = IDirectSoundBuffer_SetFormat(dev->prim_buffer, dev->format); hr = IDirectSoundBuffer_SetFormat(dev->prim_buffer, &format);
if(hr != DS_OK) if(hr != DS_OK)
{ {
FLUID_LOG(FLUID_WARN, "Can't set format of primary sound buffer", fluid_win32_error(hr)); FLUID_LOG(FLUID_WARN, "Can't set format of primary sound buffer: %s", fluid_win32_error(hr));
} }
/* initialize the buffer description */ /* initialize the buffer description */
@ -232,9 +243,8 @@ new_fluid_dsound_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
ZeroMemory(&desc, sizeof(DSBUFFERDESC)); ZeroMemory(&desc, sizeof(DSBUFFERDESC));
desc.dwSize = sizeof(DSBUFFERDESC); desc.dwSize = sizeof(DSBUFFERDESC);
desc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2; desc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
desc.lpwfxFormat = dev->format; desc.lpwfxFormat = &format;
desc.dwBufferBytes = dev->queue_byte_size; desc.dwBufferBytes = dev->queue_byte_size;
desc.dwReserved = 0;
if(caps.dwFreeHwMixingStreamingBuffers > 0) if(caps.dwFreeHwMixingStreamingBuffers > 0)
{ {
@ -267,9 +277,16 @@ new_fluid_dsound_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
/* Unlock */ /* Unlock */
IDirectSoundBuffer_Unlock(dev->sec_buffer, buf1, bytes1, 0, 0); IDirectSoundBuffer_Unlock(dev->sec_buffer, buf1, bytes1, 0, 0);
/* Create object to signal thread exit */
dev->quit_ev = CreateEvent(NULL, FALSE, FALSE, NULL);
if(dev->quit_ev == NULL)
{
goto error_recovery;
}
/* start the audio thread */ /* start the audio thread */
dev->thread = CreateThread(NULL, 0, &fluid_dsound_audio_run, (LPVOID) dev, 0, &dev->threadID); dev->thread = CreateThread(NULL, 0, fluid_dsound_audio_run, (LPVOID) dev, 0, &dev->threadID);
if(dev->thread == NULL) if(dev->thread == NULL)
{ {
@ -289,23 +306,30 @@ void delete_fluid_dsound_audio_driver(fluid_audio_driver_t *d)
fluid_dsound_audio_driver_t *dev = (fluid_dsound_audio_driver_t *) d; fluid_dsound_audio_driver_t *dev = (fluid_dsound_audio_driver_t *) d;
fluid_return_if_fail(dev != NULL); fluid_return_if_fail(dev != NULL);
/* tell the audio thread to stop its loop */
dev->cont = 0;
/* wait till the audio thread exits */ /* wait till the audio thread exits */
if(dev->thread != 0) if(dev->thread != NULL)
{ {
/* tell the audio thread to stop its loop */
SetEvent(dev->quit_ev);
if(WaitForSingleObject(dev->thread, 2000) != WAIT_OBJECT_0) if(WaitForSingleObject(dev->thread, 2000) != WAIT_OBJECT_0)
{ {
/* on error kill the thread mercilessly */ /* on error kill the thread mercilessly */
FLUID_LOG(FLUID_DBG, "Couldn't join the audio thread. killing it."); FLUID_LOG(FLUID_DBG, "Couldn't join the audio thread. killing it.");
TerminateThread(dev->thread, 0); TerminateThread(dev->thread, 0);
} }
/* Release the thread object */
CloseHandle(dev->thread);
} }
/* release all the allocated ressources */ /* Release the event object */
if(dev->quit_ev != NULL)
{
CloseHandle(dev->quit_ev);
}
FLUID_FREE(dev->format); /* release all the allocated resources */
if(dev->sec_buffer != NULL) if(dev->sec_buffer != NULL)
{ {
@ -326,13 +350,14 @@ void delete_fluid_dsound_audio_driver(fluid_audio_driver_t *d)
FLUID_FREE(dev); FLUID_FREE(dev);
} }
DWORD WINAPI fluid_dsound_audio_run(LPVOID lpParameter) static DWORD WINAPI fluid_dsound_audio_run(LPVOID lpParameter)
{ {
fluid_dsound_audio_driver_t *dev = (fluid_dsound_audio_driver_t *) lpParameter; fluid_dsound_audio_driver_t *dev = (fluid_dsound_audio_driver_t *) lpParameter;
short *buf1, *buf2; short *buf1, *buf2;
DWORD bytes1, bytes2; DWORD bytes1, bytes2;
DWORD cur_position, frames, play_position, write_position, bytes; DWORD cur_position, frames, play_position, write_position, bytes;
HRESULT res; HRESULT res;
int ms;
cur_position = 0; cur_position = 0;
@ -341,9 +366,8 @@ DWORD WINAPI fluid_dsound_audio_run(LPVOID lpParameter)
IDirectSoundBuffer_Play(dev->sec_buffer, 0, 0, DSBPLAY_LOOPING); IDirectSoundBuffer_Play(dev->sec_buffer, 0, 0, DSBPLAY_LOOPING);
while(dev->cont) for(;;)
{ {
IDirectSoundBuffer_GetCurrentPosition(dev->sec_buffer, &play_position, &write_position); IDirectSoundBuffer_GetCurrentPosition(dev->sec_buffer, &play_position, &write_position);
if(cur_position <= play_position) if(cur_position <= play_position)
@ -395,19 +419,32 @@ DWORD WINAPI fluid_dsound_audio_run(LPVOID lpParameter)
cur_position -= dev->queue_byte_size; cur_position -= dev->queue_byte_size;
} }
/* 1 ms of wait */
ms = 1;
} }
else else
{ {
Sleep(1); /* Calculate how many milliseconds to sleep (minus 1 for safety) */
ms = (dev->buffer_byte_size - bytes) * 1000 / dev->bytes_per_second - 1;
if(ms < 1)
{
ms = 1;
}
}
/* Wait quit event or timeout */
if(WaitForSingleObject(dev->quit_ev, ms) == WAIT_OBJECT_0)
{
break;
} }
} }
ExitThread(0); return 0;
return 0; /* never reached */
} }
char *fluid_win32_error(HRESULT hr) static char *fluid_win32_error(HRESULT hr)
{ {
char *s = "Don't know why"; char *s = "Don't know why";