mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-08-13 16:54:03 +02:00
Update to v103r18 release.
byuu says: Changelog: - tomoko: improved handling of changing audio devices on the audio settings panel - ruby/audio/wasapi: added device enumeration and selection support¹ - ruby/audio/wasapi: release property store handle from audio device - ruby/audio/wasapi: fix exclusive mode buffer filling - ruby/video/glx2: ported to new API -- tested and confirmed working great² - ruby/video/sdl: fixed initialization -- tested and confirmed working on FreeBSD now³ - ruby/video/xv: ported to new API -- tested and mostly working great, sans fullscreen mode⁴ Errata: - accidentally changed "Driver Settings" label to "Driver" on the audio settings tab because I deleted the line and forgot the "Settings" part - need to use "return initialize();" from setDevice() in the WASAPI driver, instead of "return true;", so device selection is currently not functioning in this WIP for said driver ¹: for now, this will likely end up selecting the first available endpoint device, which is probably wrong. I need to come up with a system to expose good 'default values' when selecting new audio drivers, or changing audio device settings. ²: glx2 is a fallback driver for system with only OpenGL 2.0 and no OpenGL 3.2 drivers, such as FreeBSD 10.1 with AMD graphics cards. ³: although I really should track down why InputManager::poll() is crashing the emulator when Video::ready() returns false ... ⁴: really bizarrely, when entering fullscreen mode, it looks like the image was a triangle strip, and the bottom right triange is missing, and the top left triangle skews the entire image into it. I'm suspecting this is a Radeon driver bug when trying to create a 2560x1600 X-Video surface. The glitch persists when exiting fullscreen, too. If anyone can test the X-Video driver on their Linux/BSD system, it'd be appreciated. If it's just my video card, I'll ignore it. If not, hopefully someone can find the cause of the issue :|
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
#include <audiopolicy.h>
|
||||
#include <devicetopology.h>
|
||||
#include <endpointvolume.h>
|
||||
#include <functiondiscoverykeys_devpkey.h>
|
||||
|
||||
struct AudioWASAPI : Audio {
|
||||
AudioWASAPI() { initialize(); }
|
||||
@@ -13,14 +14,15 @@ struct AudioWASAPI : Audio {
|
||||
|
||||
auto information() -> Information {
|
||||
Information information;
|
||||
information.devices = {"Default"};
|
||||
for(auto& device : _devices) information.devices.append(device);
|
||||
information.channels = {2};
|
||||
information.frequencies = {(double)_frequency};
|
||||
information.latencies = {20, 40, 60, 80, 100};
|
||||
information.latencies = {0, 20, 40, 60, 80, 100};
|
||||
return information;
|
||||
}
|
||||
|
||||
auto exclusive() -> bool { return _exclusive; }
|
||||
auto device() -> string { return _device; }
|
||||
auto blocking() -> bool { return _blocking; }
|
||||
auto channels() -> uint { return _channels; }
|
||||
auto frequency() -> double { return (double)_frequency; }
|
||||
@@ -32,6 +34,12 @@ struct AudioWASAPI : Audio {
|
||||
return initialize();
|
||||
}
|
||||
|
||||
auto setDevice(string device) -> bool {
|
||||
if(_device == device) return true;
|
||||
_device = device;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto setBlocking(bool blocking) -> bool {
|
||||
if(_blocking == blocking) return true;
|
||||
_blocking = blocking;
|
||||
@@ -77,31 +85,65 @@ private:
|
||||
terminate();
|
||||
|
||||
if(CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&_enumerator) != S_OK) return false;
|
||||
if(_enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &_audioDevice) != S_OK) return false;
|
||||
|
||||
//enumerate all audio endpoint devices, and select the first to match the device() name
|
||||
IMMDeviceCollection* deviceCollection = nullptr;
|
||||
if(_enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &deviceCollection) != S_OK) return false;
|
||||
uint deviceCount = 0;
|
||||
if(deviceCollection->GetCount(&deviceCount) != S_OK) return false;
|
||||
for(uint deviceIndex : range(deviceCount)) {
|
||||
IMMDevice* device = nullptr;
|
||||
if(deviceCollection->Item(deviceIndex, &device) != S_OK) return false;
|
||||
IPropertyStore* propertyStore = nullptr;
|
||||
device->OpenPropertyStore(STGM_READ, &propertyStore);
|
||||
PROPVARIANT propVariant;
|
||||
propertyStore->GetValue(PKEY_Device_FriendlyName, &propVariant);
|
||||
_devices.append((const char*)utf8_t(propVariant.pwszVal));
|
||||
propertyStore->Release();
|
||||
if(!_audioDevice && _devices.right() == _device) {
|
||||
_audioDevice = device;
|
||||
} else {
|
||||
device->Release();
|
||||
}
|
||||
}
|
||||
deviceCollection->Release();
|
||||
|
||||
//if no match is found, choose the default audio endpoint for the device()
|
||||
if(!_audioDevice) {
|
||||
if(_enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &_audioDevice) != S_OK) return false;
|
||||
}
|
||||
|
||||
if(_audioDevice->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void**)&_audioClient) != S_OK) return false;
|
||||
|
||||
WAVEFORMATEXTENSIBLE waveFormat = {};
|
||||
if(_exclusive) {
|
||||
if(_audioDevice->OpenPropertyStore(STGM_READ, &_propertyStore) != S_OK) return false;
|
||||
if(_propertyStore->GetValue(PKEY_AudioEngine_DeviceFormat, &_propVariant) != S_OK) return false;
|
||||
_waveFormat = (WAVEFORMATEX*)_propVariant.blob.pBlobData;
|
||||
IPropertyStore* propertyStore = nullptr;
|
||||
if(_audioDevice->OpenPropertyStore(STGM_READ, &propertyStore) != S_OK) return false;
|
||||
PROPVARIANT propVariant;
|
||||
if(propertyStore->GetValue(PKEY_AudioEngine_DeviceFormat, &propVariant) != S_OK) return false;
|
||||
waveFormat = *(WAVEFORMATEXTENSIBLE*)propVariant.blob.pBlobData;
|
||||
propertyStore->Release();
|
||||
if(_audioClient->GetDevicePeriod(nullptr, &_devicePeriod) != S_OK) return false;
|
||||
auto latency = max(_devicePeriod, (REFERENCE_TIME)_latency * 10'000); //1ms to 100ns units
|
||||
auto result = _audioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, latency, latency, _waveFormat, nullptr);
|
||||
auto result = _audioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, latency, latency, &waveFormat.Format, nullptr);
|
||||
if(result == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
|
||||
if(_audioClient->GetBufferSize(&_bufferSize) != S_OK) return false;
|
||||
_audioClient->Release();
|
||||
latency = (REFERENCE_TIME)(10'000 * 1'000 * _bufferSize / _waveFormat->nSamplesPerSec);
|
||||
latency = (REFERENCE_TIME)(10'000 * 1'000 * _bufferSize / waveFormat.Format.nSamplesPerSec);
|
||||
if(_audioDevice->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void**)&_audioClient) != S_OK) return false;
|
||||
result = _audioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, latency, latency, _waveFormat, nullptr);
|
||||
result = _audioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, latency, latency, &waveFormat.Format, nullptr);
|
||||
}
|
||||
if(result != S_OK) return false;
|
||||
DWORD taskIndex = 0;
|
||||
_taskHandle = AvSetMmThreadCharacteristics(L"Pro Audio", &taskIndex);
|
||||
} else {
|
||||
if(_audioClient->GetMixFormat(&_waveFormat) != S_OK) return false;
|
||||
WAVEFORMATEX* waveFormatEx = nullptr;
|
||||
if(_audioClient->GetMixFormat(&waveFormatEx) != S_OK) return false;
|
||||
waveFormat = *(WAVEFORMATEXTENSIBLE*)waveFormatEx;
|
||||
CoTaskMemFree(waveFormatEx);
|
||||
if(_audioClient->GetDevicePeriod(&_devicePeriod, nullptr)) return false;
|
||||
auto latency = max(_devicePeriod, (REFERENCE_TIME)_latency * 10'000); //1ms to 100ns units
|
||||
if(_audioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, latency, 0, _waveFormat, nullptr) != S_OK) return false;
|
||||
if(_audioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, latency, 0, &waveFormat.Format, nullptr) != S_OK) return false;
|
||||
}
|
||||
|
||||
_eventHandle = CreateEvent(nullptr, false, false, nullptr);
|
||||
@@ -109,19 +151,19 @@ private:
|
||||
if(_audioClient->GetService(IID_IAudioRenderClient, (void**)&_renderClient) != S_OK) return false;
|
||||
if(_audioClient->GetBufferSize(&_bufferSize) != S_OK) return false;
|
||||
|
||||
_channels = _waveFormat->nChannels;
|
||||
_frequency = _waveFormat->nSamplesPerSec;
|
||||
_mode = ((WAVEFORMATEXTENSIBLE*)_waveFormat)->SubFormat.Data1;
|
||||
_precision = _waveFormat->wBitsPerSample;
|
||||
_channels = waveFormat.Format.nChannels;
|
||||
_frequency = waveFormat.Format.nSamplesPerSec;
|
||||
_mode = waveFormat.SubFormat.Data1;
|
||||
_precision = waveFormat.Format.wBitsPerSample;
|
||||
|
||||
clear();
|
||||
return _ready = true;
|
||||
}
|
||||
|
||||
auto terminate() -> void {
|
||||
_devices.reset();
|
||||
if(_audioClient) _audioClient->Stop();
|
||||
if(_renderClient) _renderClient->Release(), _renderClient = nullptr;
|
||||
if(_waveFormat) CoTaskMemFree(_waveFormat), _waveFormat = nullptr;
|
||||
if(_audioClient) _audioClient->Release(), _audioClient = nullptr;
|
||||
if(_audioDevice) _audioDevice->Release(), _audioDevice = nullptr;
|
||||
if(_eventHandle) CloseHandle(_eventHandle), _eventHandle = nullptr;
|
||||
@@ -129,9 +171,12 @@ private:
|
||||
}
|
||||
|
||||
auto write() -> void {
|
||||
uint32_t padding = 0;
|
||||
_audioClient->GetCurrentPadding(&padding);
|
||||
uint32_t available = _bufferSize - padding;
|
||||
uint32_t available = _bufferSize;
|
||||
if(!_exclusive) {
|
||||
uint32_t padding = 0;
|
||||
_audioClient->GetCurrentPadding(&padding);
|
||||
available = _bufferSize - padding;
|
||||
}
|
||||
|
||||
uint8_t* buffer = nullptr;
|
||||
if(_renderClient->GetBuffer(available, &buffer) == S_OK) {
|
||||
@@ -171,6 +216,7 @@ private:
|
||||
|
||||
bool _ready = false;
|
||||
bool _exclusive = false;
|
||||
string _device;
|
||||
bool _blocking = true;
|
||||
uint _channels = 2;
|
||||
uint _frequency = 48000;
|
||||
@@ -187,12 +233,10 @@ private:
|
||||
} _queue;
|
||||
|
||||
IMMDeviceEnumerator* _enumerator = nullptr;
|
||||
vector<string> _devices;
|
||||
IMMDevice* _audioDevice = nullptr;
|
||||
IPropertyStore* _propertyStore = nullptr;
|
||||
IAudioClient* _audioClient = nullptr;
|
||||
IAudioRenderClient* _renderClient = nullptr;
|
||||
WAVEFORMATEX* _waveFormat = nullptr;
|
||||
PROPVARIANT _propVariant;
|
||||
HANDLE _eventHandle = nullptr;
|
||||
HANDLE _taskHandle = nullptr;
|
||||
REFERENCE_TIME _devicePeriod = 0;
|
||||
|
Reference in New Issue
Block a user