mirror of
https://github.com/holub/mame
synced 2025-06-06 21:03:47 +03:00
sound/xaudio2_sound.cpp: Initial support for new sound system features.
This commit is contained in:
parent
571bc03fcc
commit
aa1a83e481
@ -132,6 +132,8 @@ function osdmodulesbuild()
|
||||
MAME_DIR .. "src/osd/modules/sound/coreaudio_sound.cpp",
|
||||
MAME_DIR .. "src/osd/modules/sound/direct_sound.cpp",
|
||||
MAME_DIR .. "src/osd/modules/sound/js_sound.cpp",
|
||||
MAME_DIR .. "src/osd/modules/sound/mmdevice_helpers.cpp",
|
||||
MAME_DIR .. "src/osd/modules/sound/mmdevice_helpers.h",
|
||||
MAME_DIR .. "src/osd/modules/sound/none.cpp",
|
||||
MAME_DIR .. "src/osd/modules/sound/pa_sound.cpp",
|
||||
MAME_DIR .. "src/osd/modules/sound/pulse_sound.cpp",
|
||||
|
@ -5,20 +5,28 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "modules/osdmodule.h"
|
||||
#include "monitor_module.h"
|
||||
|
||||
#include "modules/osdmodule.h"
|
||||
|
||||
#if defined(OSD_WINDOWS)
|
||||
|
||||
// local headers
|
||||
#include "monitor_common.h"
|
||||
|
||||
// OSD headers
|
||||
#include "osdcore.h"
|
||||
#include "strconv.h"
|
||||
#include "window.h"
|
||||
#include "windows/video.h"
|
||||
|
||||
// standard windows headers
|
||||
#include <windows.h>
|
||||
#undef interface
|
||||
|
||||
#include "osdcore.h"
|
||||
#include "strconv.h"
|
||||
#include "windows/video.h"
|
||||
#include "window.h"
|
||||
#include "monitor_common.h"
|
||||
namespace osd {
|
||||
|
||||
namespace {
|
||||
|
||||
class win32_monitor_module;
|
||||
|
||||
@ -28,10 +36,10 @@ private:
|
||||
MONITORINFOEX m_info;
|
||||
|
||||
public:
|
||||
win32_monitor_info(monitor_module& module, const HMONITOR handle, const char* monitor_device, float aspect)
|
||||
: osd_monitor_info(module, std::uintptr_t(handle), monitor_device, aspect)
|
||||
win32_monitor_info(monitor_module& module, const HMONITOR handle, std::string &&monitor_device, float aspect)
|
||||
: osd_monitor_info(module, std::uintptr_t(handle), std::move(monitor_device), aspect)
|
||||
{
|
||||
win32_monitor_info::refresh();
|
||||
refresh();
|
||||
}
|
||||
|
||||
void refresh() override
|
||||
@ -93,12 +101,10 @@ protected:
|
||||
EnumDisplayMonitors(nullptr, nullptr, monitor_enum_callback, reinterpret_cast<std::intptr_t>(this));
|
||||
|
||||
// if we're verbose, print the list of monitors
|
||||
{
|
||||
for (const auto &monitor : list())
|
||||
{
|
||||
osd_printf_verbose("Video: Monitor %u = \"%s\" %s\n", monitor->oshandle(), monitor->devicename(), monitor->is_primary() ? "(primary)" : "");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -106,7 +112,7 @@ protected:
|
||||
private:
|
||||
static BOOL CALLBACK monitor_enum_callback(HMONITOR handle, HDC dc, LPRECT rect, LPARAM data)
|
||||
{
|
||||
auto* self = reinterpret_cast<win32_monitor_module*>(data);
|
||||
auto *const self = reinterpret_cast<win32_monitor_module *>(data);
|
||||
MONITORINFOEX info;
|
||||
BOOL result;
|
||||
|
||||
@ -117,13 +123,13 @@ private:
|
||||
(void)result; // to silence gcc 4.6
|
||||
|
||||
// guess the aspect ratio assuming square pixels
|
||||
float aspect = static_cast<float>(info.rcMonitor.right - info.rcMonitor.left) / static_cast<float>(info.rcMonitor.bottom - info.rcMonitor.top);
|
||||
float aspect = float(info.rcMonitor.right - info.rcMonitor.left) / float(info.rcMonitor.bottom - info.rcMonitor.top);
|
||||
|
||||
// allocate a new monitor info
|
||||
auto temp = osd::text::from_tstring(info.szDevice);
|
||||
|
||||
// copy in the data
|
||||
auto monitor = std::make_shared<win32_monitor_info>(*self, handle, temp.c_str(), aspect);
|
||||
auto monitor = std::make_shared<win32_monitor_info>(*self, handle, std::move(temp), aspect);
|
||||
|
||||
// hook us into the list
|
||||
self->add_monitor(monitor);
|
||||
@ -133,8 +139,14 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
} // namespace osd
|
||||
|
||||
#else
|
||||
MODULE_NOT_SUPPORTED(win32_monitor_module, OSD_MONITOR_PROVIDER, "win32")
|
||||
|
||||
namespace osd { namespace { MODULE_NOT_SUPPORTED(win32_monitor_module, OSD_MONITOR_PROVIDER, "win32") } }
|
||||
|
||||
#endif
|
||||
|
||||
MODULE_DEFINITION(MONITOR_WIN32, win32_monitor_module)
|
||||
MODULE_DEFINITION(MONITOR_WIN32, osd::win32_monitor_module)
|
||||
|
337
src/osd/modules/sound/mmdevice_helpers.cpp
Normal file
337
src/osd/modules/sound/mmdevice_helpers.cpp
Normal file
@ -0,0 +1,337 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Vas Crabb
|
||||
#include "mmdevice_helpers.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include "osdcore.h"
|
||||
#include "strconv.h"
|
||||
|
||||
#include "util/coretmpl.h"
|
||||
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <combaseapi.h>
|
||||
#include <oleauto.h>
|
||||
|
||||
#include <mmreg.h>
|
||||
|
||||
#include <functiondiscoverykeys_devpkey.h>
|
||||
|
||||
|
||||
namespace osd {
|
||||
|
||||
namespace {
|
||||
|
||||
using mm_endpoint_ptr = Microsoft::WRL::ComPtr<IMMEndpoint>;
|
||||
|
||||
char const *const f_speaker_names[] ={
|
||||
"FL", // SPEAKER_FRONT_LEFT
|
||||
"FR", // SPEAKER_FRONT_RIGHT
|
||||
"FC", // SPEAKER_FRONT_CENTER
|
||||
"LFE", // SPEAKER_LOW_FREQUENCY
|
||||
"BL", // SPEAKER_BACK_LEFT
|
||||
"BR", // SPEAKER_BACK_RIGHT
|
||||
"FCL", // SPEAKER_FRONT_LEFT_OF_CENTER
|
||||
"FCR", // SPEAKER_FRONT_RIGHT_OF_CENTER
|
||||
"BC", // SPEAKER_BACK_CENTER
|
||||
"SL", // SPEAKER_SIDE_LEFT
|
||||
"SR", // SPEAKER_SIDE_RIGHT
|
||||
"TC", // SPEAKER_TOP_CENTER
|
||||
"TFL", // SPEAKER_TOP_FRONT_LEFT
|
||||
"TFC", // SPEAKER_TOP_FRONT_CENTER
|
||||
"TFR", // SPEAKER_TOP_FRONT_RIGHT
|
||||
"TBL", // SPEAKER_TOP_BACK_LEFT
|
||||
"TBC", // SPEAKER_TOP_BACK_CENTER
|
||||
"TBR" }; // SPEAKER_TOP_BACK_RIGHT
|
||||
|
||||
std::array<double, 3> const f_speaker_positions[] = {
|
||||
{ -0.2, 0.0, 1.0 }, // SPEAKER_FRONT_LEFT
|
||||
{ 0.2, 0.0, 1.0 }, // SPEAKER_FRONT_RIGHT
|
||||
{ 0.0, 0.0, 1.0 }, // SPEAKER_FRONT_CENTER
|
||||
{ 0.0, -0.5, 1.0 }, // SPEAKER_LOW_FREQUENCY
|
||||
{ -0.2, 0.0, -0.5 }, // SPEAKER_BACK_LEFT
|
||||
{ 0.2, 0.0, -0.5 }, // SPEAKER_BACK_RIGHT
|
||||
{ -0.1, 0.0, 1.0 }, // SPEAKER_FRONT_LEFT_OF_CENTER
|
||||
{ 0.1, 0.0, 1.0 }, // SPEAKER_FRONT_RIGHT_OF_CENTER
|
||||
{ 0.0, 0.0, -0.5 }, // SPEAKER_BACK_CENTER
|
||||
{ -0.2, 0.0, 0.0 }, // SPEAKER_SIDE_LEFT
|
||||
{ 0.2, 0.0, 0.0 }, // SPEAKER_SIDE_RIGHT
|
||||
{ 0.0, 0.5, 0.0 }, // SPEAKER_TOP_CENTER
|
||||
{ -0.2, 0.5, 1.0 }, // SPEAKER_TOP_FRONT_LEFT
|
||||
{ 0.0, 0.5, 1.0 }, // SPEAKER_TOP_FRONT_CENTER
|
||||
{ 0.2, 0.5, 1.0 }, // SPEAKER_TOP_FRONT_RIGHT
|
||||
{ -0.2, 0.5, -0.5 }, // SPEAKER_TOP_BACK_LEFT
|
||||
{ 0.0, 0.5, -0.5 }, // SPEAKER_TOP_BACK_CENTER
|
||||
{ 0.2, 0.5, -0.5 } }; // SPEAKER_TOP_BACK_RIGHT
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
HRESULT populate_audio_node_info(
|
||||
IMMDevice &device,
|
||||
std::wstring &device_id,
|
||||
audio_info::node_info &info)
|
||||
{
|
||||
HRESULT result;
|
||||
|
||||
// get the device ID
|
||||
co_task_wstr_ptr device_id_w;
|
||||
std::string id_string;
|
||||
{
|
||||
LPWSTR id_raw = nullptr;
|
||||
result = device.GetId(&id_raw);
|
||||
if (FAILED(result) || !id_raw)
|
||||
{
|
||||
osd_printf_error(
|
||||
"Error getting ID for audio device. Error: 0x%X\n",
|
||||
static_cast<unsigned int>(result));
|
||||
return FAILED(result) ? result : E_POINTER;
|
||||
}
|
||||
device_id_w.reset(std::exchange(id_raw, nullptr));
|
||||
try
|
||||
{
|
||||
osd::text::from_wstring(id_string, device_id_w.get());
|
||||
}
|
||||
catch (std::bad_alloc const &)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
// get the property store (needed for various important things)
|
||||
property_store_ptr properties;
|
||||
result = device.OpenPropertyStore(STGM_READ, properties.GetAddressOf());
|
||||
if (FAILED(result) || !properties)
|
||||
{
|
||||
osd_printf_error(
|
||||
"Error opening property store for audio device %s. Error: 0x%X\n",
|
||||
id_string,
|
||||
static_cast<unsigned int>(result));
|
||||
return FAILED(result) ? result : E_POINTER;
|
||||
}
|
||||
|
||||
// get the display name
|
||||
std::string device_name;
|
||||
{
|
||||
std::optional<std::string> name_string;
|
||||
result = get_string_property_value(*properties.Get(), PKEY_Device_FriendlyName, name_string);
|
||||
if (FAILED(result) || !name_string)
|
||||
{
|
||||
// fall back to using device ID
|
||||
osd_printf_error(
|
||||
"Error getting display name for audio device %s. Error: 0x%X\n",
|
||||
id_string,
|
||||
static_cast<unsigned int>(result));
|
||||
try
|
||||
{
|
||||
device_name = id_string;
|
||||
}
|
||||
catch (std::bad_alloc const &)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
device_name = std::move(*name_string);
|
||||
}
|
||||
}
|
||||
|
||||
// see whether it's an input or output
|
||||
EDataFlow data_flow;
|
||||
{
|
||||
mm_endpoint_ptr endpoint;
|
||||
result = device.QueryInterface(endpoint.GetAddressOf());
|
||||
if (FAILED(result) || !endpoint)
|
||||
{
|
||||
osd_printf_error(
|
||||
"Error getting endpoint information for audio device %s. Error: 0x%X\n",
|
||||
device_name,
|
||||
static_cast<unsigned int>(result));
|
||||
return FAILED(result) ? result : E_POINTER;
|
||||
}
|
||||
|
||||
result = endpoint->GetDataFlow(&data_flow);
|
||||
if (FAILED(result))
|
||||
{
|
||||
osd_printf_error(
|
||||
"Error getting data flow direction for audio device %s. Error: 0x%X\n",
|
||||
device_name,
|
||||
static_cast<unsigned int>(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((eRender != data_flow) && (eCapture != data_flow))
|
||||
{
|
||||
osd_printf_error(
|
||||
"Invalid data flow direction for audio device %s. Value: %u\n",
|
||||
device_name,
|
||||
std::underlying_type_t<EDataFlow>(data_flow));
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
}
|
||||
|
||||
// get format information
|
||||
prop_variant_helper format_property;
|
||||
result = properties->GetValue(PKEY_AudioEngine_DeviceFormat, &format_property.value);
|
||||
if (FAILED(result))
|
||||
{
|
||||
osd_printf_error(
|
||||
"Error getting stream format for audio device %s. Error: 0x%X\n",
|
||||
device_name,
|
||||
static_cast<unsigned int>(result));
|
||||
return result;
|
||||
}
|
||||
else if (VT_BLOB != format_property.value.vt)
|
||||
{
|
||||
// you can get VT_EMPTY when a device is initially added - don't warn about it
|
||||
if (VT_EMPTY != format_property.value.vt)
|
||||
{
|
||||
osd_printf_error(
|
||||
"Stream format has invalid data type for audio device %s. Type: %u\n",
|
||||
device_name,
|
||||
std::underlying_type_t<VARENUM>(format_property.value.vt));
|
||||
}
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
auto const format = reinterpret_cast<WAVEFORMATEX const *>(format_property.value.blob.pBlobData);
|
||||
|
||||
// get the channel mask for speaker positions
|
||||
std::optional<std::uint32_t> channel_mask;
|
||||
{
|
||||
prop_variant_helper speakers_property;
|
||||
result = properties->GetValue(PKEY_AudioEndpoint_PhysicalSpeakers, &speakers_property.value);
|
||||
if (FAILED(result))
|
||||
{
|
||||
osd_printf_error(
|
||||
"Error getting speaker arrangement for audio device %s. Error: 0x%X\n",
|
||||
device_name,
|
||||
static_cast<unsigned int>(result));
|
||||
}
|
||||
else switch (speakers_property.value.vt)
|
||||
{
|
||||
case VT_EMPTY:
|
||||
break;
|
||||
case VT_UI4:
|
||||
channel_mask = speakers_property.value.ulVal;
|
||||
break;
|
||||
case VT_UINT:
|
||||
channel_mask = speakers_property.value.uintVal;
|
||||
break;
|
||||
default:
|
||||
osd_printf_error(
|
||||
"Speaker arrangement has invalid data type for audio device %s. Type: %u\n",
|
||||
device_name,
|
||||
std::underlying_type_t<VARENUM>(speakers_property.value.vt));
|
||||
}
|
||||
}
|
||||
if (!channel_mask && (WAVE_FORMAT_EXTENSIBLE == format->wFormatTag))
|
||||
{
|
||||
auto const extensible_format = reinterpret_cast<WAVEFORMATEXTENSIBLE const *>(format);
|
||||
channel_mask = extensible_format->dwChannelMask;
|
||||
}
|
||||
|
||||
// set up channel info
|
||||
std::vector<std::string> channel_names;
|
||||
std::vector<std::array<double, 3> > channel_positions;
|
||||
try
|
||||
{
|
||||
channel_names.reserve(format->nChannels);
|
||||
channel_positions.reserve(format->nChannels);
|
||||
DWORD i = 0;
|
||||
if ((eRender == data_flow) && channel_mask)
|
||||
{
|
||||
static_assert(std::size(f_speaker_names) == std::size(f_speaker_positions));
|
||||
DWORD b = 0;
|
||||
while ((format->nChannels > i) && (std::size(f_speaker_names) > b))
|
||||
{
|
||||
if (util::BIT(*channel_mask, b))
|
||||
{
|
||||
channel_names.emplace_back(f_speaker_names[b]);
|
||||
channel_positions.emplace_back(f_speaker_positions[b]);
|
||||
++i;
|
||||
}
|
||||
++b;
|
||||
}
|
||||
}
|
||||
while (format->nChannels > i)
|
||||
{
|
||||
channel_names.emplace_back(util::string_format("Channel %u", i + 1));
|
||||
++i;
|
||||
}
|
||||
channel_positions.resize(format->nChannels, std::array<double, 3>{ 0.0, 0.0, 0.0 });
|
||||
}
|
||||
catch (std::bad_alloc const &)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
// set results
|
||||
try
|
||||
{
|
||||
device_id = device_id_w.get();
|
||||
info.m_name = std::move(device_name);
|
||||
info.m_rate.m_default_rate = format->nSamplesPerSec;
|
||||
info.m_rate.m_min_rate = format->nSamplesPerSec;
|
||||
info.m_rate.m_max_rate = format->nSamplesPerSec;
|
||||
info.m_port_names = std::move(channel_names);
|
||||
info.m_port_positions = std::move(channel_positions);
|
||||
info.m_sinks = (eRender == data_flow) ? format->nChannels : 0;
|
||||
info.m_sources = (eCapture == data_flow) ? format->nChannels : 0;
|
||||
}
|
||||
catch (std::bad_alloc const &)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT get_string_property_value(
|
||||
IPropertyStore &properties,
|
||||
REFPROPERTYKEY key,
|
||||
std::optional<std::string> &value)
|
||||
{
|
||||
prop_variant_helper property_value;
|
||||
HRESULT const result = properties.GetValue(key, &property_value.value);
|
||||
if (FAILED(result))
|
||||
return result;
|
||||
|
||||
try
|
||||
{
|
||||
switch (property_value.value.vt)
|
||||
{
|
||||
case VT_EMPTY:
|
||||
value = std::nullopt;
|
||||
return result;
|
||||
|
||||
case VT_BSTR:
|
||||
value = osd::text::from_wstring(std::wstring_view(property_value.value.bstrVal, SysStringLen(property_value.value.bstrVal)));
|
||||
return result;
|
||||
|
||||
case VT_LPSTR:
|
||||
value = osd::text::from_astring(property_value.value.pszVal);
|
||||
return result;
|
||||
|
||||
case VT_LPWSTR:
|
||||
value = osd::text::from_wstring(property_value.value.pwszVal);
|
||||
return result;
|
||||
|
||||
default:
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
}
|
||||
catch (std::bad_alloc const &)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace osd
|
||||
|
||||
#endif // defined(_WIN32)
|
105
src/osd/modules/sound/mmdevice_helpers.h
Normal file
105
src/osd/modules/sound/mmdevice_helpers.h
Normal file
@ -0,0 +1,105 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Vas Crabb
|
||||
#ifndef MAME_OSD_SOUND_MMDEVICE_HELPERS_H
|
||||
#define MAME_OSD_SOUND_MMDEVICE_HELPERS_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include "interface/audio.h"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <objbase.h>
|
||||
#include <propidl.h>
|
||||
#include <propsys.h>
|
||||
|
||||
#include <mmdeviceapi.h>
|
||||
|
||||
#include <wrl/client.h>
|
||||
|
||||
|
||||
namespace osd {
|
||||
|
||||
struct prop_variant_helper
|
||||
{
|
||||
PROPVARIANT value;
|
||||
|
||||
prop_variant_helper() { PropVariantInit(&value); }
|
||||
~prop_variant_helper() { PropVariantClear(&value); }
|
||||
prop_variant_helper(prop_variant_helper const &) = delete;
|
||||
prop_variant_helper &operator=(prop_variant_helper const &) = delete;
|
||||
};
|
||||
|
||||
struct co_task_mem_deleter
|
||||
{
|
||||
template <typename T>
|
||||
void operator()(T *obj) const
|
||||
{
|
||||
if (obj)
|
||||
CoTaskMemFree(obj);
|
||||
}
|
||||
};
|
||||
|
||||
using co_task_wstr_ptr = std::unique_ptr<wchar_t, co_task_mem_deleter>;
|
||||
|
||||
using mm_device_ptr = Microsoft::WRL::ComPtr<IMMDevice>;
|
||||
using mm_device_collection_ptr = Microsoft::WRL::ComPtr<IMMDeviceCollection>;
|
||||
using mm_device_enumerator_ptr = Microsoft::WRL::ComPtr<IMMDeviceEnumerator>;
|
||||
using property_store_ptr = Microsoft::WRL::ComPtr<IPropertyStore>;
|
||||
|
||||
|
||||
template <typename T>
|
||||
HRESULT enumerate_audio_endpoints(
|
||||
IMMDeviceEnumerator &enumerator,
|
||||
EDataFlow data_flow,
|
||||
DWORD state_mask,
|
||||
T &&action)
|
||||
{
|
||||
HRESULT result;
|
||||
|
||||
// get devices
|
||||
mm_device_collection_ptr devices;
|
||||
result = enumerator.EnumAudioEndpoints(data_flow, state_mask, devices.GetAddressOf());
|
||||
if (FAILED(result))
|
||||
return result;
|
||||
|
||||
// count devices
|
||||
UINT count;
|
||||
result = devices->GetCount(&count);
|
||||
if (FAILED(result))
|
||||
return result;
|
||||
|
||||
// enumerate devices
|
||||
for (UINT i = 0; count > i; ++i)
|
||||
{
|
||||
mm_device_ptr device;
|
||||
result = devices->Item(i, device.GetAddressOf());
|
||||
if (!action(result, device))
|
||||
break;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT populate_audio_node_info(
|
||||
IMMDevice &device,
|
||||
std::wstring &device_id,
|
||||
audio_info::node_info &info);
|
||||
|
||||
HRESULT get_string_property_value(
|
||||
IPropertyStore &properties,
|
||||
REFPROPERTYKEY key,
|
||||
std::optional<std::string> &value);
|
||||
|
||||
|
||||
} // namespace osd
|
||||
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
#endif // MAME_OSD_SOUND_MMDEVICE_HELPERS_H
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user