From 55e507d5dfa43031b4388e1eeaa9643fc995b5f2 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Mon, 18 Apr 2016 20:49:45 +1000 Subject: [PATCH] Update to v098r05 release. byuu says: Changelog: - WS/WSC: re-added support for screen rotation (code is inside WS core) - ruby: changed sample(uint16_t left, uint16_t right) to sample(int16_t left, int16_t right); - requires casting to uint prior to shifting in each driver, but I felt it was misleading to use uint16_t just to avoid that - ruby: WASAPI is now built in by default; has wareya's improvements, and now supports latency adjust - tomoko: audio settings panel has new "Exclusive Mode" checkbox for WASAPI driver only - note: although the setting *does* take effect in real-time, I'd suggest restarting the emulator after changing it - tomoko: audio latency can now be set to 0ms (which in reality means "the minimum supported by the driver") - all: increased cothread size from 512KiB to 2MiB to see if it fixes bullshit AMD driver crashes - this appears to cause a slight speed penalty due to cache locality going down between threads, though --- higan/emulator/emulator.hpp | 2 +- higan/fc/fc.hpp | 2 +- higan/gb/gb.hpp | 2 +- higan/gba/gba.hpp | 2 +- higan/sfc/sfc.hpp | 2 +- higan/target-tomoko/GNUmakefile | 2 +- .../configuration/configuration.cpp | 1 + higan/target-tomoko/program/program.cpp | 4 +- higan/target-tomoko/program/program.hpp | 1 + higan/target-tomoko/program/utility.cpp | 5 + higan/target-tomoko/settings/audio.cpp | 33 ++-- higan/target-tomoko/settings/settings.hpp | 2 + higan/ws/interface/interface.cpp | 2 +- higan/ws/ppu/ppu.cpp | 11 +- higan/ws/ppu/ppu.hpp | 5 +- higan/ws/ppu/serialization.cpp | 1 + higan/ws/ws.hpp | 2 +- ruby/audio/alsa.cpp | 4 +- ruby/audio/ao.cpp | 4 +- ruby/audio/directsound.cpp | 4 +- ruby/audio/openal.cpp | 4 +- ruby/audio/oss.cpp | 4 +- ruby/audio/pulseaudio.cpp | 4 +- ruby/audio/pulseaudiosimple.cpp | 4 +- ruby/audio/wasapi.cpp | 151 +++++++++--------- ruby/audio/xaudio2.cpp | 4 +- ruby/audio/xaudio2.hpp | 25 +-- ruby/ruby.hpp | 8 +- 28 files changed, 149 insertions(+), 146 deletions(-) diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 199be88d..35517608 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -8,7 +8,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "098.04"; + static const string Version = "098.05"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/fc/fc.hpp b/higan/fc/fc.hpp index b178c4bb..77cdf803 100644 --- a/higan/fc/fc.hpp +++ b/higan/fc/fc.hpp @@ -22,7 +22,7 @@ namespace Famicom { auto create(auto (*entrypoint)() -> void, uint frequency) -> void { if(thread) co_delete(thread); - thread = co_create(65536 * sizeof(void*), entrypoint); + thread = co_create(262'144 * sizeof(void*), entrypoint); this->frequency = frequency; clock = 0; } diff --git a/higan/gb/gb.hpp b/higan/gb/gb.hpp index a4218891..8b8e5bf9 100644 --- a/higan/gb/gb.hpp +++ b/higan/gb/gb.hpp @@ -22,7 +22,7 @@ namespace GameBoy { auto create(auto (*entrypoint)() -> void, uint frequency) -> void { if(thread) co_delete(thread); - thread = co_create(65536 * sizeof(void*), entrypoint); + thread = co_create(262'144 * sizeof(void*), entrypoint); this->frequency = frequency; clock = 0; } diff --git a/higan/gba/gba.hpp b/higan/gba/gba.hpp index 673c7911..18a2055b 100644 --- a/higan/gba/gba.hpp +++ b/higan/gba/gba.hpp @@ -34,7 +34,7 @@ namespace GameBoyAdvance { auto create(auto (*entrypoint)() -> void, uint frequency) -> void { if(thread) co_delete(thread); - thread = co_create(65536 * sizeof(void*), entrypoint); + thread = co_create(262'144 * sizeof(void*), entrypoint); this->frequency = frequency; clock = 0; } diff --git a/higan/sfc/sfc.hpp b/higan/sfc/sfc.hpp index 05fc0ceb..87aecc83 100644 --- a/higan/sfc/sfc.hpp +++ b/higan/sfc/sfc.hpp @@ -31,7 +31,7 @@ namespace SuperFamicom { auto create(auto (*entrypoint)() -> void, uint frequency) -> void { if(thread) co_delete(thread); - thread = co_create(65536 * sizeof(void*), entrypoint); + thread = co_create(262'144 * sizeof(void*), entrypoint); this->frequency = frequency; clock = 0; } diff --git a/higan/target-tomoko/GNUmakefile b/higan/target-tomoko/GNUmakefile index 3b1df5e8..df8d742c 100644 --- a/higan/target-tomoko/GNUmakefile +++ b/higan/target-tomoko/GNUmakefile @@ -16,7 +16,7 @@ ui_objects += $(if $(call streq,$(platform),windows),ui-resource) # platform ifeq ($(platform),windows) ruby += video.direct3d video.wgl video.directdraw video.gdi - ruby += audio.xaudio2 audio.directsound + ruby += audio.wasapi audio.xaudio2 audio.directsound ruby += input.windows else ifeq ($(platform),macosx) ruby += video.cgl diff --git a/higan/target-tomoko/configuration/configuration.cpp b/higan/target-tomoko/configuration/configuration.cpp index 3eec3de5..078067cc 100644 --- a/higan/target-tomoko/configuration/configuration.cpp +++ b/higan/target-tomoko/configuration/configuration.cpp @@ -29,6 +29,7 @@ Settings::Settings() { set("Audio/Driver", ruby::Audio::optimalDriver()); set("Audio/Device", ""); + set("Audio/Exclusive", false); set("Audio/Synchronize", true); set("Audio/Mute", false); set("Audio/Volume", 100); diff --git a/higan/target-tomoko/program/program.cpp b/higan/target-tomoko/program/program.cpp index 3d8f76b0..2318ce86 100644 --- a/higan/target-tomoko/program/program.cpp +++ b/higan/target-tomoko/program/program.cpp @@ -43,7 +43,7 @@ Program::Program(lstring args) { dsp.setPrecision(16); dsp.setBalance(0.0); - dsp.setFrequency(32040); + dsp.setFrequency(44100); dsp.setResampler(DSP::ResampleEngine::Sinc); dsp.setResamplerFrequency(audio->get(Audio::Frequency).get()); @@ -55,7 +55,7 @@ Program::Program(lstring args) { new ToolsManager; updateVideoShader(); - updateAudioVolume(); + updateAudio(); args.takeFirst(); //ignore program location in argument parsing for(auto& argument : args) { diff --git a/higan/target-tomoko/program/program.hpp b/higan/target-tomoko/program/program.hpp index 6dc3e04e..f7e2d5d8 100644 --- a/higan/target-tomoko/program/program.hpp +++ b/higan/target-tomoko/program/program.hpp @@ -35,6 +35,7 @@ struct Program : Emulator::Interface::Bind { auto updateStatusText() -> void; auto updateVideoShader() -> void; auto updateAudio() -> void; + auto updateAudioMode() -> void; auto updateAudioVolume() -> void; auto updateDSP() -> void; diff --git a/higan/target-tomoko/program/utility.cpp b/higan/target-tomoko/program/utility.cpp index da4e9584..e32fdef3 100644 --- a/higan/target-tomoko/program/utility.cpp +++ b/higan/target-tomoko/program/utility.cpp @@ -71,10 +71,15 @@ auto Program::updateAudio() -> void { if(settings["Audio/Resampler"].text() == "Hermite") dsp.setResampler(DSP::ResampleEngine::Hermite); if(settings["Audio/Resampler"].text() == "Sinc" ) dsp.setResampler(DSP::ResampleEngine::Sinc); dsp.setResamplerFrequency(audio->get(Audio::Frequency).get()); + updateAudioMode(); updateAudioVolume(); updateDSP(); } +auto Program::updateAudioMode() -> void { + audio->set(Audio::Exclusive, settings["Audio/Exclusive"].boolean()); +} + auto Program::updateAudioVolume() -> void { dsp.setVolume(settings["Audio/Mute"].boolean() ? 0.0 : settings["Audio/Volume"].natural() * 0.01); } diff --git a/higan/target-tomoko/settings/audio.cpp b/higan/target-tomoko/settings/audio.cpp index 18d96f55..c2c0ea26 100644 --- a/higan/target-tomoko/settings/audio.cpp +++ b/higan/target-tomoko/settings/audio.cpp @@ -10,17 +10,19 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) { frequencyCombo.setEnabled(false); //not user configurable latencyLabel.setText("Latency:"); + latencyCombo.append(ComboButtonItem().setText("0ms")); latencyCombo.append(ComboButtonItem().setText("20ms")); latencyCombo.append(ComboButtonItem().setText("40ms")); latencyCombo.append(ComboButtonItem().setText("60ms")); latencyCombo.append(ComboButtonItem().setText("80ms")); latencyCombo.append(ComboButtonItem().setText("100ms")); switch(settings["Audio/Latency"].natural()) { - case 20: latencyCombo.item(0)->setSelected(); break; - case 40: latencyCombo.item(1)->setSelected(); break; - case 60: latencyCombo.item(2)->setSelected(); break; - case 80: latencyCombo.item(3)->setSelected(); break; - case 100: latencyCombo.item(4)->setSelected(); break; + case 0: latencyCombo.item(0)->setSelected(); break; + case 20: latencyCombo.item(1)->setSelected(); break; + case 40: latencyCombo.item(2)->setSelected(); break; + case 60: latencyCombo.item(3)->setSelected(); break; + case 80: latencyCombo.item(4)->setSelected(); break; + case 100: latencyCombo.item(5)->setSelected(); break; } latencyCombo.onChange([&] { update(); }); @@ -36,17 +38,21 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) { volumeLabel.setText("Volume:"); volumeSlider.setLength(201).setPosition(settings["Audio/Volume"].natural()).onChange([&] { updateVolume(); }); + exclusiveMode.setText("Exclusive Mode").setVisible(audio->cap(Audio::Exclusive)); + exclusiveMode.setChecked(settings["Audio/Exclusive"].boolean()).onToggle([&] { updateMode(); }); + update(); } auto AudioSettings::update() -> void { if(auto item = latencyCombo.selected()) { uint latency = 60; - if(item->offset() == 0) latency = 20; - if(item->offset() == 1) latency = 40; - if(item->offset() == 2) latency = 60; - if(item->offset() == 3) latency = 80; - if(item->offset() == 4) latency = 100; + if(item->offset() == 0) latency = 0; + if(item->offset() == 1) latency = 20; + if(item->offset() == 2) latency = 40; + if(item->offset() == 3) latency = 60; + if(item->offset() == 4) latency = 80; + if(item->offset() == 5) latency = 100; settings["Audio/Latency"].setValue(latency); } if(auto item = resamplerCombo.selected()) { @@ -57,7 +63,12 @@ auto AudioSettings::update() -> void { settings["Audio/Resampler"].setValue(resampler); } updateVolume(); - program->updateAudio(); + updateMode(); +} + +auto AudioSettings::updateMode() -> void { + settings["Audio/Exclusive"].setValue(exclusiveMode.checked()); + program->updateAudioMode(); } auto AudioSettings::updateVolume() -> void { diff --git a/higan/target-tomoko/settings/settings.hpp b/higan/target-tomoko/settings/settings.hpp index 61d1399a..cb6a64f9 100644 --- a/higan/target-tomoko/settings/settings.hpp +++ b/higan/target-tomoko/settings/settings.hpp @@ -30,8 +30,10 @@ struct AudioSettings : TabFrameItem { Label volumeLabel{&volumeLayout, Size{80, 0}}; Label volumeValue{&volumeLayout, Size{80, 0}}; HorizontalSlider volumeSlider{&volumeLayout, Size{~0, 0}}; + CheckLabel exclusiveMode{&layout, Size{~0, 0}}; auto update() -> void; + auto updateMode() -> void; auto updateVolume() -> void; }; diff --git a/higan/ws/interface/interface.cpp b/higan/ws/interface/interface.cpp index 0b83808a..1d1ae3b8 100644 --- a/higan/ws/interface/interface.cpp +++ b/higan/ws/interface/interface.cpp @@ -11,7 +11,7 @@ Interface::Interface() { information.manufacturer = "Bandai"; information.name = "WonderSwan"; information.width = 224; //note: technically 224x144; but screen can be rotated - information.height = 144; //by using a square size; this can be done in the core + information.height = 224; //by using a square size; this can be done in the core information.overscan = false; information.aspectRatio = 1.0; information.resettable = false; diff --git a/higan/ws/ppu/ppu.cpp b/higan/ws/ppu/ppu.cpp index 5f1545b9..37d20ac1 100644 --- a/higan/ws/ppu/ppu.cpp +++ b/higan/ws/ppu/ppu.cpp @@ -28,7 +28,10 @@ auto PPU::main() -> void { if(l.screenTwoEnable) renderScreenTwo(); if(l.spriteEnable) renderSprite(); } - output[s.vclk * 224 + s.hclk] = s.pixel.color; + switch(l.orientation) { + case 0: output[(s.vclk + 40) * 224 + s.hclk] = s.pixel.color; break; + case 1: output[(223 - s.hclk) * 224 + (s.vclk + 40)] = s.pixel.color; break; + } step(1); } step(32); @@ -72,7 +75,11 @@ auto PPU::scanline() -> void { auto PPU::frame() -> void { s.field = !s.field; s.vclk = 0; - Emulator::video.refresh(output, 224 * sizeof(uint32), 224, 144); + Emulator::video.refresh(output, 224 * sizeof(uint32), 224, 224); + if(l.orientation != system.orientation()) { + l.orientation = system.orientation(); + memory::fill(output, 224 * 224 * sizeof(uint32)); + } scheduler.exit(Scheduler::Event::Frame); } diff --git a/higan/ws/ppu/ppu.hpp b/higan/ws/ppu/ppu.hpp index 0b36eef9..550137a9 100644 --- a/higan/ws/ppu/ppu.hpp +++ b/higan/ws/ppu/ppu.hpp @@ -34,7 +34,7 @@ struct PPU : Thread, IO { uint12 color; }; - uint32 output[224 * 144]; + uint32 output[224 * 224]; struct State { bool field; @@ -44,6 +44,9 @@ struct PPU : Thread, IO { } s; struct Latches { + //frame(), power() + bool orientation; + //latchRegisters() uint8 backColor; diff --git a/higan/ws/ppu/serialization.cpp b/higan/ws/ppu/serialization.cpp index e0f682c3..5ed28a14 100644 --- a/higan/ws/ppu/serialization.cpp +++ b/higan/ws/ppu/serialization.cpp @@ -7,6 +7,7 @@ auto PPU::serialize(serializer& s) -> void { s.integer((uint&)this->s.pixel.source); s.integer(this->s.pixel.color); + s.integer(l.orientation); s.integer(l.backColor); s.integer(l.screenOneEnable); s.integer(l.screenOneMapBase); diff --git a/higan/ws/ws.hpp b/higan/ws/ws.hpp index f687d2de..b9bf02bf 100644 --- a/higan/ws/ws.hpp +++ b/higan/ws/ws.hpp @@ -30,7 +30,7 @@ namespace WonderSwan { auto create(auto (*entrypoint)() -> void, uint frequency) -> void { if(thread) co_delete(thread); - thread = co_create(65536 * sizeof(void*), entrypoint); + thread = co_create(262'144 * sizeof(void*), entrypoint); this->frequency = frequency; clock = 0; } diff --git a/ruby/audio/alsa.cpp b/ruby/audio/alsa.cpp index 39f6ce83..cc56a4f2 100644 --- a/ruby/audio/alsa.cpp +++ b/ruby/audio/alsa.cpp @@ -65,10 +65,10 @@ struct AudioALSA : Audio { return false; } - auto sample(uint16_t left, uint16_t right) -> void { + auto sample(int16_t left, int16_t right) -> void { if(!device.handle) return; - buffer.data[buffer.length++] = left + (right << 16); + buffer.data[buffer.length++] = (uint16_t)left << 0 | (uint16_t)right << 16; if(buffer.length < device.period_size) return; snd_pcm_sframes_t avail; diff --git a/ruby/audio/ao.cpp b/ruby/audio/ao.cpp index b3112456..22fc6e32 100644 --- a/ruby/audio/ao.cpp +++ b/ruby/audio/ao.cpp @@ -31,8 +31,8 @@ struct AudioAO : Audio { return false; } - auto sample(uint16_t l_sample, uint16_t r_sample) -> void { - uint32_t samp = (l_sample << 0) + (r_sample << 16); + auto sample(int16_t left, int16_t right) -> void { + uint32_t samp = (uint16_t)left << 0 | (uint16_t)right << 0; ao_play(audio_device, (char*)&samp, 4); //This may need to be byte swapped for Big Endian } diff --git a/ruby/audio/directsound.cpp b/ruby/audio/directsound.cpp index 7df84215..6f119607 100644 --- a/ruby/audio/directsound.cpp +++ b/ruby/audio/directsound.cpp @@ -72,8 +72,8 @@ struct AudioDS : Audio { return false; } - auto sample(uint16_t left, uint16_t right) -> void { - device.buffer[device.bufferoffset++] = left + (right << 16); + auto sample(int16_t left, int16_t right) -> void { + device.buffer[device.bufferoffset++] = (uint16_t)left << 0 | (uint16_t)right << 16; if(device.bufferoffset < device.latency) return; device.bufferoffset = 0; diff --git a/ruby/audio/openal.cpp b/ruby/audio/openal.cpp index 5455bd7d..c5bde3b3 100644 --- a/ruby/audio/openal.cpp +++ b/ruby/audio/openal.cpp @@ -66,8 +66,8 @@ struct AudioOpenAL : Audio { return false; } - auto sample(uint16_t left, uint16_t right) -> void { - buffer.data[buffer.length++] = left << 0 | right << 16; + auto sample(int16_t left, int16_t right) -> void { + buffer.data[buffer.length++] = (uint16_t)left << 0 | (uint16_t)right << 16; if(buffer.length < buffer.size) return; ALuint albuffer = 0; diff --git a/ruby/audio/oss.cpp b/ruby/audio/oss.cpp index 8f64b266..eb9628cb 100644 --- a/ruby/audio/oss.cpp +++ b/ruby/audio/oss.cpp @@ -69,8 +69,8 @@ struct AudioOSS : Audio { return false; } - auto sample(uint16_t left, uint16_t right) -> void { - uint32_t sample = left << 0 | right << 16; + auto sample(int16_t left, int16_t right) -> void { + uint32_t sample = (uint16_t)left << 0 | (uint16_t)right << 16; auto unused = write(device.fd, &sample, 4); } diff --git a/ruby/audio/pulseaudio.cpp b/ruby/audio/pulseaudio.cpp index 32cdcf13..e6097493 100644 --- a/ruby/audio/pulseaudio.cpp +++ b/ruby/audio/pulseaudio.cpp @@ -64,9 +64,9 @@ struct AudioPulseAudio : Audio { return false; } - auto sample(uint16_t left, uint16_t right) -> void { + auto sample(int16_t left, int16_t right) -> void { pa_stream_begin_write(device.stream, (void**)&buffer.data, &buffer.size); - buffer.data[buffer.offset++] = left + (right << 16); + buffer.data[buffer.offset++] = (uint16_t)left << 0 | (uint16_t)right << 16; if((buffer.offset + 1) * pa_frame_size(&device.spec) <= buffer.size) return; while(true) { diff --git a/ruby/audio/pulseaudiosimple.cpp b/ruby/audio/pulseaudiosimple.cpp index f3036da0..e361e5c7 100644 --- a/ruby/audio/pulseaudiosimple.cpp +++ b/ruby/audio/pulseaudiosimple.cpp @@ -38,10 +38,10 @@ struct AudioPulseAudioSimple : Audio { return false; } - auto sample(uint16_t left, uint16_t right) -> void { + auto sample(int16_t left, int16_t right) -> void { if(!device.handle) return; - buffer.data[buffer.offset++] = left + (right << 16); + buffer.data[buffer.offset++] = (uint16_t)left << 0 | (uint16_t)right << 16; if(buffer.offset >= 64) { int error; pa_simple_write(device.handle, (const void*)buffer.data, buffer.offset * sizeof(uint32_t), &error); diff --git a/ruby/audio/wasapi.cpp b/ruby/audio/wasapi.cpp index e20f95f2..c9d94a84 100644 --- a/ruby/audio/wasapi.cpp +++ b/ruby/audio/wasapi.cpp @@ -5,19 +5,25 @@ #include #include -#include - struct AudioWASAPI : Audio { ~AudioWASAPI() { term(); } struct { bool exclusive = false; - bool synchronize = false; - uint frequency = 48000; + uint latency = 80; + bool synchronize = true; } settings; + struct { + uint channels = 0; + uint frequency = 0; + uint mode = 0; + uint precision = 0; + } device; + auto cap(const string& name) -> bool { if(name == Audio::Exclusive) return true; + if(name == Audio::Latency) return true; if(name == Audio::Synchronize) return true; if(name == Audio::Frequency) return true; return false; @@ -25,145 +31,132 @@ struct AudioWASAPI : Audio { auto get(const string& name) -> any { if(name == Audio::Exclusive) return settings.exclusive; + if(name == Audio::Latency) return settings.latency; if(name == Audio::Synchronize) return settings.synchronize; - if(name == Audio::Frequency) return settings.frequency; + if(name == Audio::Frequency) return device.frequency; return {}; } auto set(const string& name, const any& value) -> bool { if(name == Audio::Exclusive && value.get()) { + if(audioDevice) term(), init(); settings.exclusive = value.get(); return true; } + if(name == Audio::Latency && value.get()) { + if(audioDevice) term(), init(); + settings.latency = value.get(); + return true; + } + if(name == Audio::Synchronize && value.is()) { settings.synchronize = value.get(); return true; } - if(name == Audio::Frequency && value.is()) { - settings.frequency = value.get(); - dsp.setFrequency(settings.frequency); - return true; - } - return false; } - auto sample(uint16 left, uint16 right) -> void { - int samples[] = {(int16)left, (int16)right}; - dsp.sample(samples); - while(dsp.pending()) { - dsp.read(samples); - write(samples); + auto sample(int16_t left, int16_t right) -> void { + queuedFrames.append((uint16_t)left << 0 | (uint16_t)right << 16); + + if(!available() && queuedFrames.size() >= bufferSize) { + if(settings.synchronize) while(!available()); //wait for free sample slot + else queuedFrames.takeFirst(); //drop sample (run ahead) + } + + uint32_t cachedFrame = 0; + for(auto n : range(available())) { + if(queuedFrames) cachedFrame = queuedFrames.takeFirst(); + write(cachedFrame >> 0, cachedFrame >> 16); } } auto clear() -> void { audioClient->Stop(); - renderClient->GetBuffer(bufferFrameCount, &bufferData); - - renderClient->ReleaseBuffer(bufferFrameCount, 0); + audioClient->Reset(); + for(auto n : range(available())) write(0, 0); audioClient->Start(); } auto init() -> bool { if(CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&enumerator) != S_OK) return false; - if(enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device) != S_OK) return false; - if(device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void**)&audioClient) != S_OK) return false; + if(enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &audioDevice) != S_OK) return false; + if(audioDevice->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void**)&audioClient) != S_OK) return false; if(settings.exclusive) { - if(device->OpenPropertyStore(STGM_READ, &propertyStore) != S_OK) return false; + 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; if(audioClient->GetDevicePeriod(nullptr, &devicePeriod) != S_OK) return false; - if(audioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, 0, devicePeriod, devicePeriod, waveFormat, nullptr) != S_OK) return false; + auto latency = max(devicePeriod, (REFERENCE_TIME)settings.latency * 10'000); //1ms to 100ns units + if(audioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, 0, latency, latency, waveFormat, nullptr) != S_OK) return false; + DWORD taskIndex = 0; taskHandle = AvSetMmThreadCharacteristics(L"Pro Audio", &taskIndex); } else { if(audioClient->GetMixFormat(&waveFormat) != S_OK) return false; if(audioClient->GetDevicePeriod(&devicePeriod, nullptr)) return false; - if(audioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, devicePeriod, 0, waveFormat, nullptr) != S_OK) return false; + auto latency = max(devicePeriod, (REFERENCE_TIME)settings.latency * 10'000); //1ms to 100ns units + if(audioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, latency, 0, waveFormat, nullptr) != S_OK) return false; } if(audioClient->GetService(IID_IAudioRenderClient, (void**)&renderClient) != S_OK) return false; - if(audioClient->GetBufferSize(&bufferFrameCount) != S_OK) return false; + if(audioClient->GetBufferSize(&bufferSize) != S_OK) return false; - switch(((WAVEFORMATEXTENSIBLE*)waveFormat)->SubFormat.Data1) { - case 1: ieee = false; break; //fixed point - case 3: ieee = true; break; //floating point - default: return false; //unknown format; abort - } - - dsp.setChannels(2); - dsp.setPrecision(16); - dsp.setFrequency(settings.frequency); - - dsp.setResampler(DSP::ResampleEngine::Linear); - dsp.setResamplerFrequency(waveFormat->nSamplesPerSec); - dsp.setChannels(waveFormat->nChannels); - dsp.setPrecision(waveFormat->wBitsPerSample); - - print("[WASAPI]\n"); - print("Channels: ", waveFormat->nChannels, "\n"); - print("Precision: ", waveFormat->wBitsPerSample, "\n"); - print("Frequency: ", waveFormat->nSamplesPerSec, "\n"); - print("IEEE-754: ", ieee, "\n"); - print("Exclusive: ", settings.exclusive, "\n\n"); + device.channels = waveFormat->nChannels; + device.frequency = waveFormat->nSamplesPerSec; + device.mode = ((WAVEFORMATEXTENSIBLE*)waveFormat)->SubFormat.Data1; + device.precision = waveFormat->wBitsPerSample; audioClient->Start(); return true; } auto term() -> void { - if(audioClient) { - audioClient->Stop(); - } - - if(taskHandle) { - AvRevertMmThreadCharacteristics(taskHandle); - taskHandle = nullptr; - } + 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(taskHandle) AvRevertMmThreadCharacteristics(taskHandle), taskHandle = nullptr; } private: - auto write(int samples[]) -> void { - while(true) { - uint32 padding = 0; - audioClient->GetCurrentPadding(&padding); - if(bufferFrameCount - padding < 1) { - if(!settings.synchronize) return; - continue; - } - break; + auto available() -> uint { + uint32_t padding = 0; + audioClient->GetCurrentPadding(&padding); + return bufferSize - padding; + } + + auto write(int16_t left, int16_t right) -> void { + if(renderClient->GetBuffer(1, &bufferData) != S_OK) return; + + if(device.channels >= 2 && device.mode == 1 && device.precision == 16) { + auto buffer = (int16_t*)bufferData; + buffer[0] = left; + buffer[1] = right; } - renderClient->GetBuffer(1, &bufferData); - - if(ieee) { + if(device.channels >= 2 && device.mode == 3 && device.precision == 32) { auto buffer = (float*)bufferData; - buffer[0] = (int16)samples[0] / 32768.0; - buffer[1] = (int16)samples[1] / 32768.0; - } else { - auto buffer = (int16*)bufferData; - buffer[0] = (int16)samples[0]; - buffer[1] = (int16)samples[1]; + buffer[0] = left / 32768.0; + buffer[1] = right / 32768.0; } renderClient->ReleaseBuffer(1, 0); } - DSP dsp; IMMDeviceEnumerator* enumerator = nullptr; - IMMDevice* device = nullptr; + IMMDevice* audioDevice = nullptr; IPropertyStore* propertyStore = nullptr; IAudioClient* audioClient = nullptr; IAudioRenderClient* renderClient = nullptr; WAVEFORMATEX* waveFormat = nullptr; PROPVARIANT propVariant; HANDLE taskHandle = nullptr; - DWORD taskIndex = 0; REFERENCE_TIME devicePeriod = 0; - uint32 bufferFrameCount = 0; - uint8* bufferData = nullptr; - bool ieee = false; + uint32_t bufferSize = 0; //in frames + uint8_t* bufferData = nullptr; + vector queuedFrames; }; diff --git a/ruby/audio/xaudio2.cpp b/ruby/audio/xaudio2.cpp index c2c1a97b..97a5a2a8 100644 --- a/ruby/audio/xaudio2.cpp +++ b/ruby/audio/xaudio2.cpp @@ -78,8 +78,8 @@ struct AudioXAudio2 : Audio, public IXAudio2VoiceCallback { pSourceVoice->SubmitSourceBuffer(&xa2buffer); } - auto sample(uint16_t left, uint16_t right) -> void { - device.buffer[device.writebuffer * device.latency + device.bufferoffset++] = left + (right << 16); + auto sample(int16_t left, int16_t right) -> void { + device.buffer[device.writebuffer * device.latency + device.bufferoffset++] = (uint16_t)left << 0 | (uint16_t)right << 16; if(device.bufferoffset < device.latency) return; device.bufferoffset = 0; diff --git a/ruby/audio/xaudio2.hpp b/ruby/audio/xaudio2.hpp index e283f503..62162939 100644 --- a/ruby/audio/xaudio2.hpp +++ b/ruby/audio/xaudio2.hpp @@ -1,12 +1,6 @@ -/* - xaudio2.hpp (2010-08-14) - author: OV2 - - ruby-specific header to provide mingw-friendly xaudio2 interfaces -*/ +#pragma once -#ifndef XAUDIO2_RUBY_H -#define XAUDIO2_RUBY_H +//ruby-specific header to provide mingw-friendly xaudio2 interfaces //64-bit GCC fix #define GUID_EXT EXTERN_C @@ -34,20 +28,6 @@ DECLARE_INTERFACE(IXAudio2Voice); #define XAUDIO2_DEBUG_ENGINE 0x0001 #define XAUDIO2_VOICE_NOSRC 0x0004 -typedef struct -{ - WAVEFORMATEX Format; - union - { - WORD wValidBitsPerSample; - WORD wSamplesPerBlock; - WORD wReserved; - } Samples; - DWORD dwChannelMask; - GUID SubFormat; -} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE, *LPPWAVEFORMATEXTENSIBLE; -typedef const WAVEFORMATEXTENSIBLE* LPCWAVEFORMATEXTENSIBLE; - typedef enum XAUDIO2_DEVICE_ROLE { NotDefaultDevice = 0x0, @@ -337,4 +317,3 @@ __inline HRESULT XAudio2Create(IXAudio2** ppXAudio2, UINT32 Flags X2DEFAULT(0), } return hr; } -#endif diff --git a/ruby/ruby.hpp b/ruby/ruby.hpp index 51a5200e..c933c05d 100644 --- a/ruby/ruby.hpp +++ b/ruby/ruby.hpp @@ -3,10 +3,10 @@ /* ruby * author: byuu * license: ISC - * version: 0.14 (2015-11-19) + * version: 0.15 (2016-04-18) * - * ruby is a cross-platform hardware abstraction layer - * it provides a common interface to video, audio and input devices + * ruby is a cross-platform hardware abstraction layer. + * it provides a common interface to video, audio and input devices. */ #include @@ -62,7 +62,7 @@ struct Audio { virtual auto get(const nall::string& name) -> nall::any { return false; } virtual auto set(const nall::string& name, const nall::any& value) -> bool { return false; } - virtual auto sample(uint16_t left, uint16_t right) -> void {} + virtual auto sample(int16_t left, int16_t right) -> void {} virtual auto clear() -> void {} virtual auto init() -> bool { return true; }