mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-08-11 17:53:58 +02:00
Update to 20180808 release.
byuu says: This release fixes the XAudio 2.1 and WASAPI drivers on Windows, and extends XAudio to support device selection (eg headphones, speakers, monitor, etc.) It also adds DRC to XAudio, however it's not currently working. The code is courtesy of Talarubi, I just botched it somewhere upon porting it to the newer version of ruby.
This commit is contained in:
@@ -3,21 +3,31 @@
|
||||
#undef interface
|
||||
|
||||
struct AudioXAudio2 : AudioDriver, public IXAudio2VoiceCallback {
|
||||
enum : uint { Buffers = 32 };
|
||||
|
||||
AudioXAudio2& self = *this;
|
||||
AudioXAudio2(Audio& super) : AudioDriver(super) {}
|
||||
~AudioXAudio2() { terminate(); }
|
||||
AudioXAudio2(Audio& super) : AudioDriver(super) { construct(); }
|
||||
~AudioXAudio2() { destruct(); }
|
||||
|
||||
auto create() -> bool override {
|
||||
super.setDevice(hasDevices().first());
|
||||
super.setChannels(2);
|
||||
super.setFrequency(48000);
|
||||
super.setLatency(40);
|
||||
return initialize();
|
||||
}
|
||||
|
||||
auto driver() -> string override { return "XAudio2"; }
|
||||
auto driver() -> string override { return "XAudio 2.1"; }
|
||||
auto ready() -> bool override { return self.isReady; }
|
||||
|
||||
auto hasBlocking() -> bool override { return true; }
|
||||
auto hasDynamic() -> bool override { return true; }
|
||||
|
||||
auto hasDevices() -> vector<string> override {
|
||||
vector<string> devices;
|
||||
for(auto& device : self.devices) devices.append(device.name);
|
||||
return devices;
|
||||
}
|
||||
|
||||
auto hasFrequencies() -> vector<uint> override {
|
||||
return {44100, 48000, 96000};
|
||||
@@ -27,69 +37,120 @@ struct AudioXAudio2 : AudioDriver, public IXAudio2VoiceCallback {
|
||||
return {20, 40, 60, 80, 100};
|
||||
}
|
||||
|
||||
auto setDevice(string device) -> bool override { return initialize(); }
|
||||
auto setBlocking(bool blocking) -> bool override { return true; }
|
||||
auto setFrequency(uint frequency) -> bool override { return initialize(); }
|
||||
auto setLatency(uint latency) -> bool override { return initialize(); }
|
||||
|
||||
auto clear() -> void override {
|
||||
if(!self.sourceVoice) return;
|
||||
self.sourceVoice->Stop(0);
|
||||
self.sourceVoice->FlushSourceBuffers(); //calls OnBufferEnd for all currently submitted buffers
|
||||
|
||||
self.bufferIndex = 0;
|
||||
|
||||
self.bufferOffset = 0;
|
||||
if(self.buffer) memory::fill<uint32_t>(self.buffer, self.period * self.bufferCount);
|
||||
self.index = 0;
|
||||
self.queue = 0;
|
||||
for(uint n : range(Buffers)) self.buffers[n].fill();
|
||||
|
||||
self.sourceVoice->Start(0);
|
||||
}
|
||||
|
||||
auto output(const double samples[]) -> void override {
|
||||
self.buffer[self.bufferIndex * self.period + self.bufferOffset] = (uint16_t)sclamp<16>(samples[0] * 32767.0) << 0;
|
||||
self.buffer[self.bufferIndex * self.period + self.bufferOffset] |= (uint16_t)sclamp<16>(samples[1] * 32767.0) << 16;
|
||||
if(++self.bufferOffset < self.period) return;
|
||||
self.bufferOffset = 0;
|
||||
auto level() -> double override {
|
||||
XAUDIO2_VOICE_STATE state{};
|
||||
self.sourceVoice->GetState(&state);
|
||||
uint level = state.BuffersQueued * self.period - state.SamplesPlayed % self.period;
|
||||
uint limit = Buffers * self.period;
|
||||
return (double)(limit - level) / limit;
|
||||
}
|
||||
|
||||
if(self.bufferQueue == self.bufferCount - 1) {
|
||||
auto output(const double samples[]) -> void override {
|
||||
uint32_t frame = 0;
|
||||
frame |= (uint16_t)sclamp<16>(samples[0] * 32767.0) << 0;
|
||||
frame |= (uint16_t)sclamp<16>(samples[1] * 32767.0) << 16;
|
||||
|
||||
auto& buffer = self.buffers[self.index];
|
||||
buffer.write(frame);
|
||||
if(!buffer.full()) return;
|
||||
|
||||
buffer.flush();
|
||||
if(self.queue == Buffers - 1) {
|
||||
if(self.blocking) {
|
||||
//wait until there is at least one other free buffer for the next sample
|
||||
while(self.bufferQueue == self.bufferCount - 1);
|
||||
} else { //we need one free buffer for the next sample, so ignore the current contents
|
||||
while(self.queue == Buffers - 1);
|
||||
} else {
|
||||
//there is no free buffer for the next block, so ignore the current contents
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pushBuffer(self.period * 4, self.buffer + self.bufferIndex * self.period);
|
||||
self.bufferIndex = (self.bufferIndex + 1) % self.bufferCount;
|
||||
write(buffer.data(), buffer.capacity<uint8_t>());
|
||||
self.index = (self.index + 1) % Buffers;
|
||||
}
|
||||
|
||||
private:
|
||||
auto initialize() -> bool {
|
||||
terminate();
|
||||
struct Device {
|
||||
uint id = 0;
|
||||
uint channels = 0;
|
||||
uint frequency = 0;
|
||||
Format format = Format::none;
|
||||
string name;
|
||||
};
|
||||
vector<Device> devices;
|
||||
|
||||
self.bufferCount = 8;
|
||||
self.period = self.frequency * self.latency / self.bufferCount / 1000.0 + 0.5;
|
||||
self.buffer = new uint32_t[self.period * self.bufferCount];
|
||||
self.bufferOffset = 0;
|
||||
self.bufferIndex = 0;
|
||||
self.bufferQueue = 0;
|
||||
|
||||
if(FAILED(XAudio2Create(&self.interface, 0 , XAUDIO2_DEFAULT_PROCESSOR))) return false;
|
||||
auto construct() -> void {
|
||||
XAudio2Create(&self.interface, 0 , XAUDIO2_DEFAULT_PROCESSOR);
|
||||
|
||||
uint deviceCount = 0;
|
||||
self.interface->GetDeviceCount(&deviceCount);
|
||||
if(deviceCount == 0) return terminate(), false;
|
||||
|
||||
uint deviceID = 0;
|
||||
for(uint deviceIndex : range(deviceCount)) {
|
||||
XAUDIO2_DEVICE_DETAILS deviceDetails = {};
|
||||
XAUDIO2_DEVICE_DETAILS deviceDetails{};
|
||||
self.interface->GetDeviceDetails(deviceIndex, &deviceDetails);
|
||||
if(deviceDetails.Role & DefaultGameDevice) deviceID = deviceIndex;
|
||||
auto format = deviceDetails.OutputFormat.Format.wFormatTag;
|
||||
auto bits = deviceDetails.OutputFormat.Format.wBitsPerSample;
|
||||
|
||||
Device device;
|
||||
device.id = deviceIndex;
|
||||
device.name = (const char*)utf8_t(deviceDetails.DisplayName);
|
||||
device.channels = deviceDetails.OutputFormat.Format.nChannels;
|
||||
device.frequency = deviceDetails.OutputFormat.Format.nSamplesPerSec;
|
||||
if(format == WAVE_FORMAT_PCM) {
|
||||
if(bits == 16) device.format = Format::int16;
|
||||
if(bits == 32) device.format = Format::int32;
|
||||
} else if(format == WAVE_FORMAT_IEEE_FLOAT) {
|
||||
if(bits == 32) device.format = Format::float32;
|
||||
}
|
||||
|
||||
//ensure devices.first() is the default device
|
||||
if(deviceDetails.Role & DefaultGameDevice) {
|
||||
devices.prepend(device);
|
||||
} else {
|
||||
devices.append(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto destruct() -> void {
|
||||
terminate();
|
||||
|
||||
if(self.interface) {
|
||||
self.interface->Release();
|
||||
self.interface = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
auto initialize() -> bool {
|
||||
terminate();
|
||||
if(!self.interface) return false;
|
||||
|
||||
self.period = self.frequency * self.latency / Buffers / 1000.0 + 0.5;
|
||||
for(uint n : range(Buffers)) buffers[n].resize(self.period);
|
||||
self.index = 0;
|
||||
self.queue = 0;
|
||||
|
||||
if(!hasDevices().find(self.device)) self.device = hasDevices().first();
|
||||
uint deviceID = devices[hasDevices().find(self.device)()].id;
|
||||
|
||||
if(FAILED(self.interface->CreateMasteringVoice(&self.masterVoice, self.channels, self.frequency, 0, deviceID, nullptr))) return terminate(), false;
|
||||
|
||||
WAVEFORMATEX waveFormat;
|
||||
WAVEFORMATEX waveFormat{};
|
||||
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
|
||||
waveFormat.nChannels = self.channels;
|
||||
waveFormat.nSamplesPerSec = self.frequency;
|
||||
@@ -117,33 +178,23 @@ private:
|
||||
self.masterVoice->DestroyVoice();
|
||||
self.masterVoice = nullptr;
|
||||
}
|
||||
|
||||
if(self.interface) {
|
||||
self.interface->Release();
|
||||
self.interface = nullptr;
|
||||
}
|
||||
|
||||
delete[] self.buffer;
|
||||
self.buffer = nullptr;
|
||||
}
|
||||
|
||||
auto pushBuffer(uint bytes, uint32_t* audioData) -> void {
|
||||
XAUDIO2_BUFFER buffer = {};
|
||||
auto write(const uint32_t* audioData, uint bytes) -> void {
|
||||
XAUDIO2_BUFFER buffer{};
|
||||
buffer.AudioBytes = bytes;
|
||||
buffer.pAudioData = (BYTE*)audioData;
|
||||
buffer.pContext = 0;
|
||||
InterlockedIncrement(&self.bufferQueue);
|
||||
buffer.pAudioData = (const BYTE*)audioData;
|
||||
buffer.pContext = nullptr;
|
||||
InterlockedIncrement(&self.queue);
|
||||
self.sourceVoice->SubmitSourceBuffer(&buffer);
|
||||
}
|
||||
|
||||
bool isReady = false;
|
||||
|
||||
uint32_t* buffer = nullptr;
|
||||
uint period = 0;
|
||||
uint bufferCount = 0;
|
||||
uint bufferOffset = 0;
|
||||
uint bufferIndex = 0;
|
||||
volatile long bufferQueue = 0; //how many buffers are queued and ready for playback
|
||||
queue<uint32_t> buffers[Buffers];
|
||||
uint period = 0; //amount (in 32-bit frames) of samples per buffer
|
||||
uint index = 0; //current buffer for writing samples to
|
||||
volatile long queue = 0; //how many buffers are queued and ready for playback
|
||||
|
||||
IXAudio2* interface = nullptr;
|
||||
IXAudio2MasteringVoice* masterVoice = nullptr;
|
||||
@@ -158,6 +209,6 @@ private:
|
||||
STDMETHODIMP_(void) OnVoiceProcessingPassStart(UINT32 BytesRequired) {}
|
||||
|
||||
STDMETHODIMP_(void) OnBufferEnd(void* pBufferContext) {
|
||||
InterlockedDecrement(&self.bufferQueue);
|
||||
InterlockedDecrement(&self.queue);
|
||||
}
|
||||
};
|
||||
|
Reference in New Issue
Block a user