mirror of
https://github.com/ZDoom/fluidsynth.git
synced 2025-04-07 08:14:11 +00:00
WASAPI driver reorganization (#839)
Avoid initializing COM in the caller's thread context. See also: #833
This commit is contained in:
parent
149e08f181
commit
03fb32c979
2 changed files with 265 additions and 215 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -38,3 +38,4 @@ install_manifest.txt
|
|||
|
||||
# ProjectFiles
|
||||
*.pro.user*
|
||||
*.user
|
||||
|
|
|
@ -135,6 +135,7 @@ typedef struct
|
|||
int channels_count;
|
||||
int float_samples;
|
||||
|
||||
HANDLE start_ev;
|
||||
HANDLE thread;
|
||||
DWORD thread_id;
|
||||
HANDLE quit_ev;
|
||||
|
@ -142,6 +143,17 @@ typedef struct
|
|||
IAudioClient *aucl;
|
||||
IAudioRenderClient *arcl;
|
||||
|
||||
double sample_rate;
|
||||
int periods, period_size;
|
||||
fluid_long_long_t buffer_duration_reftime;
|
||||
fluid_long_long_t periods_reftime;
|
||||
fluid_long_long_t latency_reftime;
|
||||
int audio_channels;
|
||||
int sample_size;
|
||||
char *dname;
|
||||
int exclusive;
|
||||
unsigned short sample_format;
|
||||
|
||||
} fluid_wasapi_audio_driver_t;
|
||||
|
||||
fluid_audio_driver_t *new_fluid_wasapi_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
|
||||
|
@ -151,24 +163,9 @@ fluid_audio_driver_t *new_fluid_wasapi_audio_driver(fluid_settings_t *settings,
|
|||
|
||||
fluid_audio_driver_t *new_fluid_wasapi_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func, void *data)
|
||||
{
|
||||
DWORD ret;
|
||||
HANDLE wait_handles[2];
|
||||
fluid_wasapi_audio_driver_t *dev = NULL;
|
||||
double sample_rate;
|
||||
int periods, period_size;
|
||||
fluid_long_long_t buffer_duration_reftime;
|
||||
fluid_long_long_t periods_reftime;
|
||||
fluid_long_long_t latency_reftime;
|
||||
int audio_channels;
|
||||
int sample_size;
|
||||
int i;
|
||||
char *dname = NULL;
|
||||
DWORD flags = 0;
|
||||
//GUID guid_float = {DEFINE_WAVEFORMATEX_GUID(WAVE_FORMAT_IEEE_FLOAT)};
|
||||
WAVEFORMATEXTENSIBLE wfx;
|
||||
WAVEFORMATEXTENSIBLE *rwfx = NULL;
|
||||
AUDCLNT_SHAREMODE share_mode;
|
||||
IMMDeviceEnumerator *denum = NULL;
|
||||
IMMDevice *mmdev = NULL;
|
||||
HRESULT ret;
|
||||
OSVERSIONINFOEXW vi = {sizeof(vi), 6, 0, 0, 0, {0}, 0, 0, 0, 0, 0};
|
||||
|
||||
if(!VerifyVersionInfoW(&vi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR,
|
||||
|
@ -181,43 +178,6 @@ fluid_audio_driver_t *new_fluid_wasapi_audio_driver2(fluid_settings_t *settings,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* Retrieve the settings */
|
||||
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);
|
||||
fluid_settings_getint(settings, "synth.audio-channels", &audio_channels);
|
||||
|
||||
if(audio_channels > FLUID_WASAPI_MAX_OUTPUTS)
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "wasapi: channel configuration with more than one stereo pair is not supported.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Clear format structure */
|
||||
ZeroMemory(&wfx, sizeof(WAVEFORMATEXTENSIBLE));
|
||||
|
||||
if(fluid_settings_str_equal(settings, "audio.sample-format", "16bits"))
|
||||
{
|
||||
sample_size = sizeof(int16_t);
|
||||
wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
}
|
||||
else
|
||||
{
|
||||
sample_size = sizeof(float);
|
||||
wfx.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
||||
}
|
||||
|
||||
wfx.Format.nChannels = 2;
|
||||
wfx.Format.wBitsPerSample = sample_size * 8;
|
||||
wfx.Format.nBlockAlign = sample_size * wfx.Format.nChannels;
|
||||
wfx.Format.nSamplesPerSec = (DWORD)sample_rate;
|
||||
wfx.Format.nAvgBytesPerSec = (DWORD)sample_rate * wfx.Format.nBlockAlign;
|
||||
//wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
//wfx.Format.cbSize = 22;
|
||||
//wfx.SubFormat = guid_float;
|
||||
//wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
|
||||
//wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;
|
||||
|
||||
dev = FLUID_NEW(fluid_wasapi_audio_driver_t);
|
||||
|
||||
if(dev == NULL)
|
||||
|
@ -228,14 +188,188 @@ fluid_audio_driver_t *new_fluid_wasapi_audio_driver2(fluid_settings_t *settings,
|
|||
|
||||
FLUID_MEMSET(dev, 0, sizeof(fluid_wasapi_audio_driver_t));
|
||||
|
||||
/* Retrieve the settings */
|
||||
|
||||
fluid_settings_getnum(settings, "synth.sample-rate", &dev->sample_rate);
|
||||
fluid_settings_getint(settings, "audio.periods", &dev->periods);
|
||||
fluid_settings_getint(settings, "audio.period-size", &dev->period_size);
|
||||
fluid_settings_getint(settings, "synth.audio-channels", &dev->audio_channels);
|
||||
fluid_settings_getint(settings, "audio.wasapi.exclusive-mode", &dev->exclusive);
|
||||
|
||||
if(dev->audio_channels > FLUID_WASAPI_MAX_OUTPUTS)
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "wasapi: channel configuration with more than one stereo pair is not supported.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if(fluid_settings_str_equal(settings, "audio.sample-format", "16bits"))
|
||||
{
|
||||
dev->sample_size = sizeof(int16_t);
|
||||
dev->sample_format = WAVE_FORMAT_PCM;
|
||||
}
|
||||
else
|
||||
{
|
||||
dev->sample_size = sizeof(float);
|
||||
dev->sample_format = WAVE_FORMAT_IEEE_FLOAT;
|
||||
}
|
||||
|
||||
if(fluid_settings_dupstr(settings, "audio.wasapi.device", &dev->dname) != FLUID_OK)
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "wasapi: out of memory.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
dev->func = func;
|
||||
dev->user_pointer = data;
|
||||
dev->buffer_duration = periods * period_size / sample_rate;
|
||||
dev->channels_count = audio_channels * 2;
|
||||
dev->float_samples = (wfx.Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT);
|
||||
buffer_duration_reftime = (fluid_long_long_t)(dev->buffer_duration * 1e7 + .5);
|
||||
periods_reftime = (fluid_long_long_t)(period_size / sample_rate * 1e7 + .5);
|
||||
dev->buffer_duration = dev->periods * dev->period_size / dev->sample_rate;
|
||||
dev->channels_count = dev->audio_channels * 2;
|
||||
dev->float_samples = (dev->sample_format == WAVE_FORMAT_IEEE_FLOAT);
|
||||
dev->buffer_duration_reftime = (fluid_long_long_t)(dev->buffer_duration * 1e7 + .5);
|
||||
dev->periods_reftime = (fluid_long_long_t)(dev->period_size / dev->sample_rate * 1e7 + .5);
|
||||
|
||||
dev->quit_ev = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
|
||||
if(dev->quit_ev == NULL)
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "wasapi: failed to create quit event: '%s'", fluid_get_windows_error());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
dev->start_ev = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
|
||||
if(dev->start_ev == NULL)
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "wasapi: failed to create start event: '%s'", fluid_get_windows_error());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
dev->thread = CreateThread(NULL, 0, fluid_wasapi_audio_run, dev, 0, &dev->thread_id);
|
||||
|
||||
if(dev->thread == NULL)
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "wasapi: failed to create audio thread: '%s'", fluid_get_windows_error());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* start event must be first */
|
||||
wait_handles[0] = dev->start_ev;
|
||||
wait_handles[1] = dev->thread;
|
||||
ret = WaitForMultipleObjects(FLUID_N_ELEMENTS(wait_handles), wait_handles, FALSE, 1000);
|
||||
|
||||
switch(ret)
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
return &dev->driver;
|
||||
|
||||
case WAIT_TIMEOUT:
|
||||
FLUID_LOG(FLUID_WARN, "wasapi: initialization timeout!");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
|
||||
delete_fluid_wasapi_audio_driver(&dev->driver);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void delete_fluid_wasapi_audio_driver(fluid_audio_driver_t *p)
|
||||
{
|
||||
fluid_wasapi_audio_driver_t *dev = (fluid_wasapi_audio_driver_t *) p;
|
||||
int i;
|
||||
|
||||
fluid_return_if_fail(dev != NULL);
|
||||
|
||||
if(dev->thread != NULL)
|
||||
{
|
||||
SetEvent(dev->quit_ev);
|
||||
|
||||
if(WaitForSingleObject(dev->thread, 2000) != WAIT_OBJECT_0)
|
||||
{
|
||||
FLUID_LOG(FLUID_WARN, "wasapi: couldn't join the audio thread. killing it.");
|
||||
TerminateThread(dev->thread, 0);
|
||||
}
|
||||
|
||||
CloseHandle(dev->thread);
|
||||
}
|
||||
|
||||
if(dev->quit_ev != NULL)
|
||||
{
|
||||
CloseHandle(dev->quit_ev);
|
||||
}
|
||||
|
||||
if(dev->start_ev != NULL)
|
||||
{
|
||||
CloseHandle(dev->start_ev);
|
||||
}
|
||||
|
||||
if(dev->drybuf)
|
||||
{
|
||||
for(i = 0; i < dev->channels_count; ++i)
|
||||
{
|
||||
FLUID_FREE(dev->drybuf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
FLUID_FREE(dev->dname);
|
||||
|
||||
FLUID_FREE(dev->drybuf);
|
||||
|
||||
FLUID_FREE(dev);
|
||||
}
|
||||
|
||||
void fluid_wasapi_audio_driver_settings(fluid_settings_t *settings)
|
||||
{
|
||||
fluid_settings_register_int(settings, "audio.wasapi.exclusive-mode", 0, 0, 1, FLUID_HINT_TOGGLED);
|
||||
fluid_settings_register_str(settings, "audio.wasapi.device", "default", 0);
|
||||
fluid_settings_add_option(settings, "audio.wasapi.device", "default");
|
||||
fluid_wasapi_foreach_device(fluid_wasapi_register_callback, settings);
|
||||
}
|
||||
|
||||
static DWORD WINAPI fluid_wasapi_audio_run(void *p)
|
||||
{
|
||||
fluid_wasapi_audio_driver_t *dev = (fluid_wasapi_audio_driver_t *)p;
|
||||
DWORD time_to_sleep = dev->buffer_duration * 1000 / 2;
|
||||
UINT32 pos;
|
||||
DWORD len;
|
||||
void *channels_out[2];
|
||||
int channels_off[2] = {0, 1};
|
||||
int channels_incr[2] = {2, 2};
|
||||
BYTE *pbuf;
|
||||
HRESULT ret;
|
||||
IMMDeviceEnumerator *denum = NULL;
|
||||
IMMDevice *mmdev = NULL;
|
||||
DWORD flags = 0;
|
||||
WAVEFORMATEXTENSIBLE wfx;
|
||||
WAVEFORMATEXTENSIBLE *rwfx = NULL;
|
||||
AUDCLNT_SHAREMODE share_mode;
|
||||
OSVERSIONINFOEXW vi = {sizeof(vi), 6, 0, 0, 0, {0}, 0, 0, 0, 0, 0};
|
||||
int needs_com_uninit = FALSE;
|
||||
int i;
|
||||
|
||||
if(time_to_sleep < 1)
|
||||
{
|
||||
time_to_sleep = 1;
|
||||
}
|
||||
|
||||
/* Clear format structure */
|
||||
ZeroMemory(&wfx, sizeof(WAVEFORMATEXTENSIBLE));
|
||||
|
||||
wfx.Format.nChannels = 2;
|
||||
wfx.Format.wBitsPerSample = dev->sample_size * 8;
|
||||
wfx.Format.nBlockAlign = dev->sample_size * wfx.Format.nChannels;
|
||||
wfx.Format.nSamplesPerSec = (DWORD) dev->sample_rate;
|
||||
wfx.Format.nAvgBytesPerSec = (DWORD) dev->sample_rate * wfx.Format.nBlockAlign;
|
||||
wfx.Format.wFormatTag = dev->sample_format;
|
||||
//wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
//wfx.Format.cbSize = 22;
|
||||
//wfx.SubFormat = guid_float;
|
||||
//wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
|
||||
//wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;
|
||||
|
||||
/* initialize COM in a worker thread to avoid a potential double initialization in the callers thread */
|
||||
ret = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
|
||||
if(FAILED(ret))
|
||||
|
@ -244,6 +378,8 @@ fluid_audio_driver_t *new_fluid_wasapi_audio_driver2(fluid_settings_t *settings,
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
needs_com_uninit = TRUE;
|
||||
|
||||
ret = CoCreateInstance(
|
||||
&_CLSID_MMDeviceEnumerator, NULL,
|
||||
CLSCTX_ALL, &_IID_IMMDeviceEnumerator,
|
||||
|
@ -255,24 +391,13 @@ fluid_audio_driver_t *new_fluid_wasapi_audio_driver2(fluid_settings_t *settings,
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
if(fluid_settings_dupstr(settings, "audio.wasapi.device", &dname) == FLUID_OK)
|
||||
{
|
||||
mmdev = fluid_wasapi_find_device(denum, dname);
|
||||
mmdev = fluid_wasapi_find_device(denum, dev->dname);
|
||||
|
||||
if(mmdev == NULL)
|
||||
{
|
||||
FLUID_FREE(dname);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
else
|
||||
if(mmdev == NULL)
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "wasapi: out of memory.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
FLUID_FREE(dname);
|
||||
|
||||
ret = IMMDevice_Activate(mmdev,
|
||||
&_IID_IAudioClient,
|
||||
CLSCTX_ALL, NULL,
|
||||
|
@ -284,24 +409,17 @@ fluid_audio_driver_t *new_fluid_wasapi_audio_driver2(fluid_settings_t *settings,
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
share_mode = AUDCLNT_SHAREMODE_SHARED;
|
||||
|
||||
if(fluid_settings_getint(settings, "audio.wasapi.exclusive-mode", &i) == FLUID_OK)
|
||||
if(dev->exclusive)
|
||||
{
|
||||
if(i)
|
||||
{
|
||||
share_mode = AUDCLNT_SHAREMODE_EXCLUSIVE;
|
||||
}
|
||||
}
|
||||
|
||||
if(share_mode == AUDCLNT_SHAREMODE_SHARED)
|
||||
{
|
||||
FLUID_LOG(FLUID_DBG, "wasapi: using shared mode.");
|
||||
periods_reftime = 0;
|
||||
share_mode = AUDCLNT_SHAREMODE_EXCLUSIVE;
|
||||
FLUID_LOG(FLUID_DBG, "wasapi: using exclusive mode.");
|
||||
}
|
||||
else
|
||||
{
|
||||
FLUID_LOG(FLUID_DBG, "wasapi: using exclusive mode.");
|
||||
share_mode = AUDCLNT_SHAREMODE_SHARED;
|
||||
FLUID_LOG(FLUID_DBG, "wasapi: using shared mode.");
|
||||
dev->periods_reftime = 0;
|
||||
}
|
||||
|
||||
ret = IAudioClient_IsFormatSupported(dev->aucl, share_mode, (const WAVEFORMATEX *)&wfx, (WAVEFORMATEX **)&rwfx);
|
||||
|
@ -336,7 +454,7 @@ fluid_audio_driver_t *new_fluid_wasapi_audio_driver2(fluid_settings_t *settings,
|
|||
}
|
||||
|
||||
ret = IAudioClient_Initialize(dev->aucl, share_mode, flags,
|
||||
buffer_duration_reftime, periods_reftime, (WAVEFORMATEX *)&wfx, &GUID_NULL);
|
||||
dev->buffer_duration_reftime, dev->periods_reftime, (WAVEFORMATEX *)&wfx, &GUID_NULL);
|
||||
|
||||
if(FAILED(ret))
|
||||
{
|
||||
|
@ -349,9 +467,9 @@ fluid_audio_driver_t *new_fluid_wasapi_audio_driver2(fluid_settings_t *settings,
|
|||
|
||||
if(SUCCEEDED(IAudioClient_GetDevicePeriod(dev->aucl, &defp, &minp)))
|
||||
{
|
||||
int defpf = (int)(defp / 1e7 * sample_rate);
|
||||
int minpf = (int)(minp / 1e7 * sample_rate);
|
||||
FLUID_LOG(FLUID_ERR, "wasapi: minimum period is %d, default period is %d. selected %d.", minpf, defpf, period_size);
|
||||
int defpf = (int)(defp / 1e7 * dev->sample_rate);
|
||||
int minpf = (int)(minp / 1e7 * dev->sample_rate);
|
||||
FLUID_LOG(FLUID_ERR, "wasapi: minimum period is %d, default period is %d. selected %d.", minpf, defpf, dev->period_size);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -366,10 +484,10 @@ fluid_audio_driver_t *new_fluid_wasapi_audio_driver2(fluid_settings_t *settings,
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
FLUID_LOG(FLUID_DBG, "wasapi: requested %d frames of buffers, got %u.", periods * period_size, dev->nframes);
|
||||
dev->buffer_duration = dev->nframes / sample_rate;
|
||||
FLUID_LOG(FLUID_DBG, "wasapi: requested %d frames of buffers, got %u.", dev->periods * dev->period_size, dev->nframes);
|
||||
dev->buffer_duration = dev->nframes / dev->sample_rate;
|
||||
|
||||
dev->drybuf = FLUID_ARRAY(float *, audio_channels * 2);
|
||||
dev->drybuf = FLUID_ARRAY(float *, dev->audio_channels * 2);
|
||||
|
||||
if(dev->drybuf == NULL)
|
||||
{
|
||||
|
@ -377,9 +495,9 @@ fluid_audio_driver_t *new_fluid_wasapi_audio_driver2(fluid_settings_t *settings,
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
FLUID_MEMSET(dev->drybuf, 0, sizeof(float *) * audio_channels * 2);
|
||||
FLUID_MEMSET(dev->drybuf, 0, sizeof(float *) * dev->audio_channels * 2);
|
||||
|
||||
for(i = 0; i < audio_channels * 2; ++i)
|
||||
for(i = 0; i < dev->audio_channels * 2; ++i)
|
||||
{
|
||||
dev->drybuf[i] = FLUID_ARRAY(float, dev->nframes);
|
||||
|
||||
|
@ -398,121 +516,9 @@ fluid_audio_driver_t *new_fluid_wasapi_audio_driver2(fluid_settings_t *settings,
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
if(SUCCEEDED(IAudioClient_GetStreamLatency(dev->aucl, &latency_reftime)))
|
||||
if(SUCCEEDED(IAudioClient_GetStreamLatency(dev->aucl, &dev->latency_reftime)))
|
||||
{
|
||||
FLUID_LOG(FLUID_DBG, "wasapi: latency: %fms.", latency_reftime / 1e4);
|
||||
}
|
||||
|
||||
dev->quit_ev = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
|
||||
if(dev->quit_ev == NULL)
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "wasapi: failed to create quit event.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
dev->thread = CreateThread(NULL, 0, fluid_wasapi_audio_run, (void *)dev, 0, &dev->thread_id);
|
||||
|
||||
if(dev->thread == NULL)
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "wasapi: failed to create audio thread.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
IMMDevice_Release(mmdev);
|
||||
IMMDeviceEnumerator_Release(denum);
|
||||
return &dev->driver;
|
||||
|
||||
cleanup:
|
||||
|
||||
if(mmdev != NULL)
|
||||
{
|
||||
IMMDevice_Release(mmdev);
|
||||
}
|
||||
|
||||
if(denum != NULL)
|
||||
{
|
||||
IMMDeviceEnumerator_Release(denum);
|
||||
}
|
||||
|
||||
delete_fluid_wasapi_audio_driver(&dev->driver);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void delete_fluid_wasapi_audio_driver(fluid_audio_driver_t *p)
|
||||
{
|
||||
fluid_wasapi_audio_driver_t *dev = (fluid_wasapi_audio_driver_t *) p;
|
||||
int i;
|
||||
|
||||
fluid_return_if_fail(dev != NULL);
|
||||
|
||||
if(dev->thread != NULL)
|
||||
{
|
||||
SetEvent(dev->quit_ev);
|
||||
|
||||
if(WaitForSingleObject(dev->thread, 2000) != WAIT_OBJECT_0)
|
||||
{
|
||||
FLUID_LOG(FLUID_WARN, "wasapi: couldn't join the audio thread. killing it.");
|
||||
TerminateThread(dev->thread, 0);
|
||||
}
|
||||
|
||||
CloseHandle(dev->thread);
|
||||
}
|
||||
|
||||
if(dev->quit_ev != NULL)
|
||||
{
|
||||
CloseHandle(dev->quit_ev);
|
||||
}
|
||||
|
||||
if(dev->aucl != NULL)
|
||||
{
|
||||
IAudioClient_Stop(dev->aucl);
|
||||
IAudioClient_Release(dev->aucl);
|
||||
}
|
||||
|
||||
if(dev->arcl != NULL)
|
||||
{
|
||||
IAudioRenderClient_Release(dev->arcl);
|
||||
}
|
||||
|
||||
CoUninitialize();
|
||||
|
||||
if(dev->drybuf)
|
||||
{
|
||||
for(i = 0; i < dev->channels_count; ++i)
|
||||
{
|
||||
FLUID_FREE(dev->drybuf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
FLUID_FREE(dev->drybuf);
|
||||
|
||||
FLUID_FREE(dev);
|
||||
}
|
||||
|
||||
void fluid_wasapi_audio_driver_settings(fluid_settings_t *settings)
|
||||
{
|
||||
fluid_settings_register_int(settings, "audio.wasapi.exclusive-mode", 0, 0, 1, FLUID_HINT_TOGGLED);
|
||||
fluid_settings_register_str(settings, "audio.wasapi.device", "default", 0);
|
||||
fluid_settings_add_option(settings, "audio.wasapi.device", "default");
|
||||
fluid_wasapi_foreach_device(fluid_wasapi_register_callback, settings);
|
||||
}
|
||||
|
||||
static DWORD WINAPI fluid_wasapi_audio_run(void *p)
|
||||
{
|
||||
fluid_wasapi_audio_driver_t *dev = (fluid_wasapi_audio_driver_t *)p;
|
||||
DWORD time_to_sleep = dev->buffer_duration * 1000 / 2;
|
||||
UINT32 pos;
|
||||
DWORD len;
|
||||
void *channels_out[2];
|
||||
int channels_off[2] = {0, 1};
|
||||
int channels_incr[2] = {2, 2};
|
||||
BYTE *pbuf;
|
||||
HRESULT ret;
|
||||
|
||||
if(time_to_sleep < 1)
|
||||
{
|
||||
time_to_sleep = 1;
|
||||
FLUID_LOG(FLUID_DBG, "wasapi: latency: %fms.", dev->latency_reftime / 1e4);
|
||||
}
|
||||
|
||||
ret = IAudioClient_Start(dev->aucl);
|
||||
|
@ -520,9 +526,12 @@ static DWORD WINAPI fluid_wasapi_audio_run(void *p)
|
|||
if(FAILED(ret))
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "wasapi: failed to start audio client. 0x%x", (unsigned)ret);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Signal the success of the driver initialization */
|
||||
SetEvent(dev->start_ev);
|
||||
|
||||
for(;;)
|
||||
{
|
||||
ret = IAudioClient_GetCurrentPadding(dev->aucl, &pos);
|
||||
|
@ -530,7 +539,7 @@ static DWORD WINAPI fluid_wasapi_audio_run(void *p)
|
|||
if(FAILED(ret))
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "wasapi: cannot get buffer padding. 0x%x", (unsigned)ret);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
len = dev->nframes - pos;
|
||||
|
@ -546,7 +555,7 @@ static DWORD WINAPI fluid_wasapi_audio_run(void *p)
|
|||
if(FAILED(ret))
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "wasapi: cannot get buffer. 0x%x", (unsigned)ret);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
channels_out[0] = channels_out[1] = (void *)pbuf;
|
||||
|
@ -559,7 +568,7 @@ static DWORD WINAPI fluid_wasapi_audio_run(void *p)
|
|||
if(FAILED(ret))
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "wasapi: failed to release buffer. 0x%x", (unsigned)ret);
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if(WaitForSingleObject(dev->quit_ev, time_to_sleep) == WAIT_OBJECT_0)
|
||||
|
@ -568,6 +577,34 @@ static DWORD WINAPI fluid_wasapi_audio_run(void *p)
|
|||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
|
||||
if(dev->aucl != NULL)
|
||||
{
|
||||
IAudioClient_Stop(dev->aucl);
|
||||
IAudioClient_Release(dev->aucl);
|
||||
}
|
||||
|
||||
if(dev->arcl != NULL)
|
||||
{
|
||||
IAudioRenderClient_Release(dev->arcl);
|
||||
}
|
||||
|
||||
if(mmdev != NULL)
|
||||
{
|
||||
IMMDevice_Release(mmdev);
|
||||
}
|
||||
|
||||
if(denum != NULL)
|
||||
{
|
||||
IMMDeviceEnumerator_Release(denum);
|
||||
}
|
||||
|
||||
if(needs_com_uninit)
|
||||
{
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -617,13 +654,22 @@ static void fluid_wasapi_foreach_device(fluid_wasapi_devenum_callback_t callback
|
|||
IMMDeviceCollection *dcoll = NULL;
|
||||
UINT cnt, i;
|
||||
HRESULT ret;
|
||||
int com_was_initialized = FALSE;
|
||||
|
||||
ret = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
|
||||
if(FAILED(ret))
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "wasapi: cannot initialize COM. 0x%x", (unsigned)ret);
|
||||
goto cleanup;
|
||||
if(ret == RPC_E_CHANGED_MODE)
|
||||
{
|
||||
com_was_initialized = TRUE;
|
||||
FLUID_LOG(FLUID_DBG, "wasapi: COM was already initialized");
|
||||
}
|
||||
else
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "wasapi: cannot initialize COM. 0x%x", (unsigned)ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ret = CoCreateInstance(
|
||||
|
@ -684,7 +730,10 @@ cleanup:
|
|||
IMMDeviceEnumerator_Release(denum);
|
||||
}
|
||||
|
||||
CoUninitialize();
|
||||
if(!com_was_initialized)
|
||||
{
|
||||
CoUninitialize();
|
||||
}
|
||||
}
|
||||
|
||||
static void fluid_wasapi_register_callback(IMMDevice *dev, void *data)
|
||||
|
|
Loading…
Reference in a new issue