mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-05-27 10:57:22 +08:00
Initial port to SDL3 audio subsystem
This commit is contained in:
committed by
Ryan C. Gordon
parent
05ce978e18
commit
641deb9c0e
@@ -206,7 +206,7 @@ static void DSOUND_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDe
|
|||||||
{
|
{
|
||||||
#ifdef HAVE_MMDEVICEAPI_H
|
#ifdef HAVE_MMDEVICEAPI_H
|
||||||
if (SupportsIMMDevice) {
|
if (SupportsIMMDevice) {
|
||||||
SDL_IMMDevice_EnumerateEndpoints(default_playback, default_recording, SDL_AUDIO_UNKNOWN);
|
SDL_IMMDevice_EnumerateEndpoints(default_playback, default_recording, SDL_AUDIO_UNKNOWN, false);
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ static const IID SDL_IID_IAudioClient3 = { 0x7ed4ee07, 0x8e67, 0x4cd4, { 0x8c, 0
|
|||||||
#endif //
|
#endif //
|
||||||
|
|
||||||
static bool immdevice_initialized = false;
|
static bool immdevice_initialized = false;
|
||||||
|
static bool supports_recording_on_playback_devices = false;
|
||||||
|
|
||||||
// WASAPI is _really_ particular about various things happening on the same thread, for COM and such,
|
// WASAPI is _really_ particular about various things happening on the same thread, for COM and such,
|
||||||
// so we proxy various stuff to a single background thread to manage.
|
// so we proxy various stuff to a single background thread to manage.
|
||||||
@@ -329,7 +330,7 @@ typedef struct
|
|||||||
static bool mgmtthrtask_DetectDevices(void *userdata)
|
static bool mgmtthrtask_DetectDevices(void *userdata)
|
||||||
{
|
{
|
||||||
mgmtthrtask_DetectDevicesData *data = (mgmtthrtask_DetectDevicesData *)userdata;
|
mgmtthrtask_DetectDevicesData *data = (mgmtthrtask_DetectDevicesData *)userdata;
|
||||||
SDL_IMMDevice_EnumerateEndpoints(data->default_playback, data->default_recording, SDL_AUDIO_F32);
|
SDL_IMMDevice_EnumerateEndpoints(data->default_playback, data->default_recording, SDL_AUDIO_F32, supports_recording_on_playback_devices);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -446,6 +447,8 @@ static bool mgmtthrtask_ActivateDevice(void *userdata)
|
|||||||
return false; // This is already set by SDL_IMMDevice_Get
|
return false; // This is already set by SDL_IMMDevice_Get
|
||||||
}
|
}
|
||||||
|
|
||||||
|
device->hidden->isplayback = !SDL_IMMDevice_GetIsCapture(immdevice);
|
||||||
|
|
||||||
// this is _not_ async in standard win32, yay!
|
// this is _not_ async in standard win32, yay!
|
||||||
HRESULT ret = IMMDevice_Activate(immdevice, &SDL_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&device->hidden->client);
|
HRESULT ret = IMMDevice_Activate(immdevice, &SDL_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&device->hidden->client);
|
||||||
IMMDevice_Release(immdevice);
|
IMMDevice_Release(immdevice);
|
||||||
@@ -725,6 +728,11 @@ static bool mgmtthrtask_PrepDevice(void *userdata)
|
|||||||
|
|
||||||
newspec.freq = waveformat->nSamplesPerSec;
|
newspec.freq = waveformat->nSamplesPerSec;
|
||||||
|
|
||||||
|
if (device->recording && device->hidden->isplayback)
|
||||||
|
{
|
||||||
|
streamflags |= AUDCLNT_STREAMFLAGS_LOOPBACK;
|
||||||
|
}
|
||||||
|
|
||||||
streamflags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
|
streamflags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
|
||||||
|
|
||||||
int new_sample_frames = 0;
|
int new_sample_frames = 0;
|
||||||
@@ -978,6 +986,7 @@ static bool WASAPI_Init(SDL_AudioDriverImpl *impl)
|
|||||||
impl->FreeDeviceHandle = WASAPI_FreeDeviceHandle;
|
impl->FreeDeviceHandle = WASAPI_FreeDeviceHandle;
|
||||||
|
|
||||||
impl->HasRecordingSupport = true;
|
impl->HasRecordingSupport = true;
|
||||||
|
supports_recording_on_playback_devices = SDL_GetHintBoolean(SDL_HINT_AUDIO_INCLUDE_MONITORS, false);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ struct SDL_PrivateAudioData
|
|||||||
SDL_AtomicInt device_disconnecting;
|
SDL_AtomicInt device_disconnecting;
|
||||||
bool device_lost;
|
bool device_lost;
|
||||||
bool device_dead;
|
bool device_dead;
|
||||||
|
bool isplayback;
|
||||||
};
|
};
|
||||||
|
|
||||||
// win32 implementation calls into these.
|
// win32 implementation calls into these.
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ void SDL_IMMDevice_FreeDeviceHandle(SDL_AudioDevice *device)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static SDL_AudioDevice *SDL_IMMDevice_Add(const bool recording, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid, GUID *dsoundguid, SDL_AudioFormat force_format)
|
static SDL_AudioDevice *SDL_IMMDevice_Add(const bool recording, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid, GUID *dsoundguid, SDL_AudioFormat force_format, bool supports_recording_playback_devices)
|
||||||
{
|
{
|
||||||
/* You can have multiple endpoints on a device that are mutually exclusive ("Speakers" vs "Line Out" or whatever).
|
/* You can have multiple endpoints on a device that are mutually exclusive ("Speakers" vs "Line Out" or whatever).
|
||||||
In a perfect world, things that are unplugged won't be in this collection. The only gotcha is probably for
|
In a perfect world, things that are unplugged won't be in this collection. The only gotcha is probably for
|
||||||
@@ -165,6 +165,22 @@ static SDL_AudioDevice *SDL_IMMDevice_Add(const bool recording, const char *devn
|
|||||||
spec.format = (force_format != SDL_AUDIO_UNKNOWN) ? force_format : SDL_WaveFormatExToSDLFormat((WAVEFORMATEX *)fmt);
|
spec.format = (force_format != SDL_AUDIO_UNKNOWN) ? force_format : SDL_WaveFormatExToSDLFormat((WAVEFORMATEX *)fmt);
|
||||||
|
|
||||||
device = SDL_AddAudioDevice(recording, devname, &spec, handle);
|
device = SDL_AddAudioDevice(recording, devname, &spec, handle);
|
||||||
|
|
||||||
|
if (!recording && supports_recording_playback_devices) {
|
||||||
|
// handle is freed by SDL_IMMDevice_FreeDeviceHandle!
|
||||||
|
SDL_IMMDevice_HandleData *recording_handle = (SDL_IMMDevice_HandleData *)SDL_malloc(sizeof(SDL_IMMDevice_HandleData));
|
||||||
|
if (!recording_handle) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_memcpy(&recording_handle->directsound_guid, dsoundguid, sizeof(GUID));
|
||||||
|
recording_handle->immdevice_id = SDL_wcsdup(devid);
|
||||||
|
|
||||||
|
if (!recording_handle->immdevice_id || !SDL_AddAudioDevice(true, devname, &spec, recording_handle)) {
|
||||||
|
SDL_free(recording_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!device) {
|
if (!device) {
|
||||||
SDL_free(handle->immdevice_id);
|
SDL_free(handle->immdevice_id);
|
||||||
SDL_free(handle);
|
SDL_free(handle);
|
||||||
@@ -184,6 +200,7 @@ typedef struct SDLMMNotificationClient
|
|||||||
const IMMNotificationClientVtbl *lpVtbl;
|
const IMMNotificationClientVtbl *lpVtbl;
|
||||||
SDL_AtomicInt refcount;
|
SDL_AtomicInt refcount;
|
||||||
SDL_AudioFormat force_format;
|
SDL_AudioFormat force_format;
|
||||||
|
bool supports_recording_playback_devices;
|
||||||
} SDLMMNotificationClient;
|
} SDLMMNotificationClient;
|
||||||
|
|
||||||
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_QueryInterface(IMMNotificationClient *client, REFIID iid, void **ppv)
|
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_QueryInterface(IMMNotificationClient *client, REFIID iid, void **ppv)
|
||||||
@@ -257,7 +274,7 @@ static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceStateChanged(IM
|
|||||||
GUID dsoundguid;
|
GUID dsoundguid;
|
||||||
GetMMDeviceInfo(device, &utf8dev, &fmt, &dsoundguid);
|
GetMMDeviceInfo(device, &utf8dev, &fmt, &dsoundguid);
|
||||||
if (utf8dev) {
|
if (utf8dev) {
|
||||||
SDL_IMMDevice_Add(recording, utf8dev, &fmt, pwstrDeviceId, &dsoundguid, client->force_format);
|
SDL_IMMDevice_Add(recording, utf8dev, &fmt, pwstrDeviceId, &dsoundguid, client->force_format, client->supports_recording_playback_devices);
|
||||||
SDL_free(utf8dev);
|
SDL_free(utf8dev);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -288,7 +305,7 @@ static const IMMNotificationClientVtbl notification_client_vtbl = {
|
|||||||
SDLMMNotificationClient_OnPropertyValueChanged
|
SDLMMNotificationClient_OnPropertyValueChanged
|
||||||
};
|
};
|
||||||
|
|
||||||
static SDLMMNotificationClient notification_client = { ¬ification_client_vtbl, { 1 }, SDL_AUDIO_UNKNOWN };
|
static SDLMMNotificationClient notification_client = { ¬ification_client_vtbl, { 1 }, SDL_AUDIO_UNKNOWN, false };
|
||||||
|
|
||||||
bool SDL_IMMDevice_Init(const SDL_IMMDevice_callbacks *callbacks)
|
bool SDL_IMMDevice_Init(const SDL_IMMDevice_callbacks *callbacks)
|
||||||
{
|
{
|
||||||
@@ -365,7 +382,23 @@ bool SDL_IMMDevice_Get(SDL_AudioDevice *device, IMMDevice **immdevice, bool reco
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void EnumerateEndpointsForFlow(const bool recording, SDL_AudioDevice **default_device, SDL_AudioFormat force_format)
|
bool SDL_IMMDevice_GetIsCapture(IMMDevice *device)
|
||||||
|
{
|
||||||
|
bool iscapture = false;
|
||||||
|
IMMEndpoint *endpoint = NULL;
|
||||||
|
if (SUCCEEDED(IMMDevice_QueryInterface(device, &SDL_IID_IMMEndpoint, (void **)&endpoint))) {
|
||||||
|
EDataFlow flow;
|
||||||
|
|
||||||
|
if (SUCCEEDED(IMMEndpoint_GetDataFlow(endpoint, &flow))) {
|
||||||
|
iscapture = (flow == eCapture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IMMEndpoint_Release(endpoint);
|
||||||
|
return iscapture;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void EnumerateEndpointsForFlow(const bool recording, SDL_AudioDevice **default_device, SDL_AudioFormat force_format, bool supports_recording_playback_devices)
|
||||||
{
|
{
|
||||||
/* Note that WASAPI separates "adapter devices" from "audio endpoint devices"
|
/* Note that WASAPI separates "adapter devices" from "audio endpoint devices"
|
||||||
...one adapter device ("SoundBlaster Pro") might have multiple endpoint devices ("Speakers", "Line-Out"). */
|
...one adapter device ("SoundBlaster Pro") might have multiple endpoint devices ("Speakers", "Line-Out"). */
|
||||||
@@ -407,7 +440,7 @@ static void EnumerateEndpointsForFlow(const bool recording, SDL_AudioDevice **de
|
|||||||
SDL_zero(dsoundguid);
|
SDL_zero(dsoundguid);
|
||||||
GetMMDeviceInfo(immdevice, &devname, &fmt, &dsoundguid);
|
GetMMDeviceInfo(immdevice, &devname, &fmt, &dsoundguid);
|
||||||
if (devname) {
|
if (devname) {
|
||||||
SDL_AudioDevice *sdldevice = SDL_IMMDevice_Add(recording, devname, &fmt, devid, &dsoundguid, force_format);
|
SDL_AudioDevice *sdldevice = SDL_IMMDevice_Add(recording, devname, &fmt, devid, &dsoundguid, force_format, supports_recording_playback_devices);
|
||||||
if (default_device && default_devid && SDL_wcscmp(default_devid, devid) == 0) {
|
if (default_device && default_devid && SDL_wcscmp(default_devid, devid) == 0) {
|
||||||
*default_device = sdldevice;
|
*default_device = sdldevice;
|
||||||
}
|
}
|
||||||
@@ -424,12 +457,13 @@ static void EnumerateEndpointsForFlow(const bool recording, SDL_AudioDevice **de
|
|||||||
IMMDeviceCollection_Release(collection);
|
IMMDeviceCollection_Release(collection);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDL_IMMDevice_EnumerateEndpoints(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording, SDL_AudioFormat force_format)
|
void SDL_IMMDevice_EnumerateEndpoints(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording, SDL_AudioFormat force_format, bool supports_recording_playback_devices)
|
||||||
{
|
{
|
||||||
EnumerateEndpointsForFlow(false, default_playback, force_format);
|
EnumerateEndpointsForFlow(false, default_playback, force_format, supports_recording_playback_devices);
|
||||||
EnumerateEndpointsForFlow(true, default_recording, force_format);
|
EnumerateEndpointsForFlow(true, default_recording, force_format, supports_recording_playback_devices);
|
||||||
|
|
||||||
notification_client.force_format = force_format;
|
notification_client.force_format = force_format;
|
||||||
|
notification_client.supports_recording_playback_devices = supports_recording_playback_devices;
|
||||||
|
|
||||||
// if this fails, we just won't get hotplug events. Carry on anyhow.
|
// if this fails, we just won't get hotplug events. Carry on anyhow.
|
||||||
IMMDeviceEnumerator_RegisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)¬ification_client);
|
IMMDeviceEnumerator_RegisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)¬ification_client);
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ typedef struct SDL_IMMDevice_callbacks
|
|||||||
bool SDL_IMMDevice_Init(const SDL_IMMDevice_callbacks *callbacks);
|
bool SDL_IMMDevice_Init(const SDL_IMMDevice_callbacks *callbacks);
|
||||||
void SDL_IMMDevice_Quit(void);
|
void SDL_IMMDevice_Quit(void);
|
||||||
bool SDL_IMMDevice_Get(struct SDL_AudioDevice *device, IMMDevice **immdevice, bool recording);
|
bool SDL_IMMDevice_Get(struct SDL_AudioDevice *device, IMMDevice **immdevice, bool recording);
|
||||||
void SDL_IMMDevice_EnumerateEndpoints(struct SDL_AudioDevice **default_playback, struct SDL_AudioDevice **default_recording, SDL_AudioFormat force_format);
|
bool SDL_IMMDevice_GetIsCapture(IMMDevice* device);
|
||||||
|
void SDL_IMMDevice_EnumerateEndpoints(struct SDL_AudioDevice **default_playback, struct SDL_AudioDevice **default_recording, SDL_AudioFormat force_format, bool supports_recording_playback_devices);
|
||||||
LPGUID SDL_IMMDevice_GetDirectSoundGUID(struct SDL_AudioDevice *device);
|
LPGUID SDL_IMMDevice_GetDirectSoundGUID(struct SDL_AudioDevice *device);
|
||||||
LPCWSTR SDL_IMMDevice_GetDevID(struct SDL_AudioDevice *device);
|
LPCWSTR SDL_IMMDevice_GetDevID(struct SDL_AudioDevice *device);
|
||||||
void SDL_IMMDevice_FreeDeviceHandle(struct SDL_AudioDevice *device);
|
void SDL_IMMDevice_FreeDeviceHandle(struct SDL_AudioDevice *device);
|
||||||
|
|||||||
Reference in New Issue
Block a user