mirror of
https://github.com/libretro/gambatte-libretro.git
synced 2024-11-23 07:49:48 +00:00
- WasapiEngine: Add device selection.
- WasapiEngine: Add static isUsable() method. Only listed if isUsable(). Default engine if isUsable(). git-svn-id: https://gambatte.svn.sourceforge.net/svnroot/gambatte@218 9dfb2916-2d38-0410-aef4-c5fe6c9ffc24
This commit is contained in:
parent
7fcab361f0
commit
d7b4708ef3
@ -22,6 +22,8 @@
|
||||
#include "audioengines/wasapiengine.h"
|
||||
|
||||
void addAudioEngines(std::vector<AudioEngine*> &audioEngines, WId winId) {
|
||||
if (WasapiEngine::isUsable())
|
||||
audioEngines.push_back(new WasapiEngine);
|
||||
|
||||
audioEngines.push_back(new DirectSoundEngine(winId));
|
||||
audioEngines.push_back(new WasapiEngine);
|
||||
}
|
||||
|
@ -20,8 +20,14 @@
|
||||
#include "wasapiinc.h"
|
||||
#include <QWidget>
|
||||
#include <QCheckBox>
|
||||
#include <QLabel>
|
||||
#include <QComboBox>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QSettings>
|
||||
#include <QString>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
static const CLSID CLSID_MMDeviceEnumerator = {
|
||||
@ -49,6 +55,12 @@ static const IID IID_IAudioClock = {
|
||||
Data4: {0x81, 0x2C, 0xEF, 0x96, 0x35, 0x87, 0x28, 0xE7}
|
||||
};
|
||||
|
||||
static const PROPERTYKEY PKEY_Device_FriendlyName = {
|
||||
fmtid: { Data1: 0xa45c254e, Data2: 0xdf1c, Data3: 0x4efd,
|
||||
Data4: {0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0} },
|
||||
pid: 14
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
static void safeRelease(T *&t) {
|
||||
if (t) {
|
||||
@ -57,13 +69,84 @@ static void safeRelease(T *&t) {
|
||||
}
|
||||
}
|
||||
|
||||
WasapiEngine::WasapiEngine() : AudioEngine("WASAPI"), confWidget(new QWidget),
|
||||
Q_DECLARE_METATYPE(std::wstring);
|
||||
|
||||
static void addDeviceSelectorItem(QComboBox *const deviceSelector, IMMDevice *const pEndpoint) {
|
||||
LPWSTR pwszID = NULL;
|
||||
IPropertyStore *pProps = NULL;
|
||||
|
||||
pEndpoint->GetId(&pwszID);
|
||||
pEndpoint->OpenPropertyStore(STGM_READ, &pProps);
|
||||
|
||||
if (pwszID && pProps) {
|
||||
PROPVARIANT varName;
|
||||
std::memset(&varName, 0, sizeof(PROPVARIANT));
|
||||
|
||||
if (SUCCEEDED(pProps->GetValue(PKEY_Device_FriendlyName, &varName))) {
|
||||
deviceSelector->addItem(QString::fromWCharArray(varName.pwszVal), QVariant::fromValue(std::wstring(pwszID)));
|
||||
CoTaskMemFree(varName.pwszVal);
|
||||
//PropVariantClear(&varName);
|
||||
}
|
||||
|
||||
CoTaskMemFree(pwszID);
|
||||
pProps->Release();
|
||||
}
|
||||
}
|
||||
|
||||
static void fillDeviceSelector(QComboBox *const deviceSelector) {
|
||||
IMMDeviceEnumerator *pEnumerator = NULL;
|
||||
IMMDeviceCollection *pCollection = NULL;
|
||||
UINT count = 0;
|
||||
|
||||
CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator);
|
||||
|
||||
if (pEnumerator)
|
||||
pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE | DEVICE_STATE_UNPLUGGED, &pCollection);
|
||||
|
||||
if (pCollection && FAILED(pCollection->GetCount(&count)))
|
||||
count = 0;
|
||||
|
||||
for (ULONG i = 0; i < count; ++i) {
|
||||
IMMDevice *pEndpoint = NULL;
|
||||
pCollection->Item(i, &pEndpoint);
|
||||
|
||||
if (pEndpoint) {
|
||||
addDeviceSelectorItem(deviceSelector, pEndpoint);
|
||||
pEndpoint->Release();
|
||||
}
|
||||
}
|
||||
|
||||
safeRelease(pCollection);
|
||||
safeRelease(pEnumerator);
|
||||
}
|
||||
|
||||
bool WasapiEngine::isUsable() {
|
||||
IMMDeviceEnumerator *pEnumerator = NULL;
|
||||
const HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator);
|
||||
|
||||
if (pEnumerator)
|
||||
pEnumerator->Release();
|
||||
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
WasapiEngine::WasapiEngine() : AudioEngine("WASAPI"), confWidget(new QWidget), deviceSelector(new QComboBox),
|
||||
exclusiveBox(new QCheckBox("Exclusive mode")), pAudioClient(NULL),
|
||||
pRenderClient(NULL), pAudioClock(NULL), pos_(0), posFrames(0), bufferFrameCount(0), started(false),
|
||||
pRenderClient(NULL), pAudioClock(NULL), pos_(0), posFrames(0), deviceIndex(0), bufferFrameCount(0), started(false),
|
||||
exclusive(false) {
|
||||
fillDeviceSelector(deviceSelector);
|
||||
|
||||
{
|
||||
QVBoxLayout *const mainLayout = new QVBoxLayout;
|
||||
mainLayout->setMargin(0);
|
||||
|
||||
if (deviceSelector->count() > 1) {
|
||||
QHBoxLayout *const hlayout = new QHBoxLayout;
|
||||
hlayout->addWidget(new QLabel(QString("WASAPI device:")));
|
||||
hlayout->addWidget(deviceSelector);
|
||||
mainLayout->addLayout(hlayout);
|
||||
}
|
||||
|
||||
mainLayout->addWidget(exclusiveBox);
|
||||
confWidget->setLayout(mainLayout);
|
||||
}
|
||||
@ -72,29 +155,36 @@ exclusive(false) {
|
||||
QSettings settings;
|
||||
settings.beginGroup("wasapiengine");
|
||||
exclusive = settings.value("exclusive", exclusive).toBool();
|
||||
|
||||
if ((deviceIndex = settings.value("deviceIndex", deviceIndex).toUInt()) >= static_cast<unsigned>(deviceSelector->count()))
|
||||
deviceIndex = 0;
|
||||
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
rejectSettings();
|
||||
}
|
||||
|
||||
void WasapiEngine::acceptSettings() {
|
||||
exclusive = exclusiveBox->isChecked();
|
||||
}
|
||||
|
||||
void WasapiEngine::rejectSettings() {
|
||||
exclusiveBox->setChecked(exclusive);
|
||||
}
|
||||
|
||||
WasapiEngine::~WasapiEngine() {
|
||||
uninit();
|
||||
|
||||
QSettings settings;
|
||||
settings.beginGroup("wasapiengine");
|
||||
settings.setValue("exclusive", exclusive);
|
||||
settings.setValue("deviceIndex", deviceIndex);
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
void WasapiEngine::acceptSettings() {
|
||||
exclusive = exclusiveBox->isChecked();
|
||||
deviceIndex = deviceSelector->currentIndex();
|
||||
}
|
||||
|
||||
void WasapiEngine::rejectSettings() {
|
||||
exclusiveBox->setChecked(exclusive);
|
||||
deviceSelector->setCurrentIndex(deviceIndex);
|
||||
}
|
||||
|
||||
int WasapiEngine::doInit(const int rate, const unsigned latency) {
|
||||
{
|
||||
IMMDevice *pDevice = NULL;
|
||||
@ -106,7 +196,11 @@ int WasapiEngine::doInit(const int rate, const unsigned latency) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice);
|
||||
if (deviceSelector->count() > 0)
|
||||
hr = pEnumerator->GetDevice(deviceSelector->itemData(deviceIndex).value<std::wstring>().c_str(), &pDevice);
|
||||
else
|
||||
hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice);
|
||||
|
||||
pEnumerator->Release();
|
||||
|
||||
if (FAILED(hr)) {
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <BaseTsd.h>
|
||||
#include <memory>
|
||||
|
||||
class QComboBox;
|
||||
class QCheckBox;
|
||||
class IAudioClient;
|
||||
class IAudioRenderClient;
|
||||
@ -30,12 +31,14 @@ class IAudioClock;
|
||||
|
||||
class WasapiEngine: public AudioEngine {
|
||||
const std::auto_ptr<QWidget> confWidget;
|
||||
QComboBox *const deviceSelector;
|
||||
QCheckBox *const exclusiveBox;
|
||||
IAudioClient *pAudioClient;
|
||||
IAudioRenderClient *pRenderClient;
|
||||
IAudioClock *pAudioClock;
|
||||
unsigned pos_;
|
||||
unsigned posFrames;
|
||||
unsigned deviceIndex;
|
||||
UINT32 bufferFrameCount;
|
||||
RateEst est;
|
||||
bool started;
|
||||
@ -46,6 +49,7 @@ class WasapiEngine: public AudioEngine {
|
||||
int write(void *buffer, unsigned frames, UINT32 numFramesPadding);
|
||||
|
||||
public:
|
||||
static bool isUsable();
|
||||
WasapiEngine();
|
||||
~WasapiEngine();
|
||||
void uninit();
|
||||
|
@ -24,6 +24,12 @@ struct PROPERTYKEY {
|
||||
#define AUDCLNT_STREAMFLAGS_EVENTCALLBACK 0x00040000
|
||||
#define AUDCLNT_STREAMFLAGS_NOPERSIST 0x00080000
|
||||
|
||||
#define DEVICE_STATE_ACTIVE 0x00000001
|
||||
#define DEVICE_STATE_DISABLED 0x00000002
|
||||
#define DEVICE_STATE_NOTPRESENT 0x00000004
|
||||
#define DEVICE_STATE_UNPLUGGED 0x00000008
|
||||
#define DEVICE_STATEMASK_ALL 0x0000000F
|
||||
|
||||
class IMMNotificationClient;
|
||||
|
||||
class IAudioClient : public IUnknown {
|
||||
|
Loading…
Reference in New Issue
Block a user