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:
Tim Allen
2017-07-20 21:52:47 +10:00
parent 0b4e7fb5a5
commit 284e4c043e
10 changed files with 526 additions and 481 deletions

View File

@@ -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;