mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-08-31 13:42:33 +02:00
Update to bsnes v107.1 release.
byuu says: Don't let the point release fool you, there are many significant changes in this release. I will be keeping bsnes releases using a point system until the new higan release is ready. Changelog: - GUI: added high DPI support - GUI: fixed the state manager image preview - Windows: added a new waveOut driver with support for dynamic rate control - Windows: corrected the XAudio 2.1 dynamic rate control support [BearOso] - Windows: corrected the Direct3D 9.0 fullscreen exclusive window centering - Windows: fixed XInput controller support on Windows 10 - SFC: added high-level emulation for the DSP1, DSP2, DSP4, ST010, and Cx4 coprocessors - SFC: fixed a slight rendering glitch in the intro to Megalomania If the coprocessor firmware is missing, bsnes will fallback on HLE where it is supported, which is everything other than SD Gundam GX and the two Hayazashi Nidan Morita Shougi games. The Windows dynamic rate control works best with Direct3D in fullscreen exclusive mode. I recommend the waveOut driver over the XAudio 2.1 driver, as it is not possible to target a single XAudio2 version on all Windows OS releases. The waveOut driver should work everywhere out of the box. Note that with DRC, the synchronization source is your monitor, so you will want to be running at 60hz (NTSC) or 50hz (PAL). If you have an adaptive sync monitor, you should instead use the WASAPI (exclusive) or ASIO audio driver.
This commit is contained in:
@@ -34,6 +34,10 @@
|
||||
#include <ruby/audio/wasapi.cpp>
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_WAVEOUT)
|
||||
#include <ruby/audio/waveout.cpp>
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_XAUDIO2)
|
||||
#include <ruby/audio/xaudio2.cpp>
|
||||
#endif
|
||||
@@ -174,6 +178,10 @@ auto Audio::create(string driver) -> bool {
|
||||
if(driver == "WASAPI") self.instance = new AudioWASAPI(*this);
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_WAVEOUT)
|
||||
if(driver == "waveOut") self.instance = new AudioWaveOut(*this);
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_XAUDIO2)
|
||||
if(driver == "XAudio 2.1") self.instance = new AudioXAudio2(*this);
|
||||
#endif
|
||||
@@ -202,6 +210,10 @@ auto Audio::hasDrivers() -> vector<string> {
|
||||
"DirectSound 7.0",
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_WAVEOUT)
|
||||
"waveOut",
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_ALSA)
|
||||
"ALSA",
|
||||
#endif
|
||||
@@ -238,6 +250,8 @@ auto Audio::optimalDriver() -> string {
|
||||
return "XAudio 2.1";
|
||||
#elif defined(AUDIO_DIRECTSOUND)
|
||||
return "DirectSound 7.0";
|
||||
#elif defined(AUDIO_WAVEOUT)
|
||||
return "waveOut";
|
||||
#elif defined(AUDIO_ALSA)
|
||||
return "ALSA";
|
||||
#elif defined(AUDIO_OSS)
|
||||
@@ -256,7 +270,9 @@ auto Audio::optimalDriver() -> string {
|
||||
}
|
||||
|
||||
auto Audio::safestDriver() -> string {
|
||||
#if defined(AUDIO_DIRECTSOUND)
|
||||
#if defined(AUDIO_WAVEOUT)
|
||||
return "waveOut";
|
||||
#elif defined(AUDIO_DIRECTSOUND)
|
||||
return "DirectSound 7.0";
|
||||
#elif defined(AUDIO_WASAPI)
|
||||
return "WASAPI";
|
||||
|
134
ruby/audio/waveout.cpp
Normal file
134
ruby/audio/waveout.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
#include <mmsystem.h>
|
||||
|
||||
auto CALLBACK waveOutCallback(HWAVEOUT handle, UINT message, DWORD_PTR userData, DWORD_PTR, DWORD_PTR) -> void;
|
||||
|
||||
struct AudioWaveOut : AudioDriver {
|
||||
AudioWaveOut& self = *this;
|
||||
AudioWaveOut(Audio& super) : AudioDriver(super) {}
|
||||
~AudioWaveOut() { terminate(); }
|
||||
|
||||
auto create() -> bool override {
|
||||
super.setChannels(2);
|
||||
super.setFrequency(44100);
|
||||
super.setLatency(0);
|
||||
return initialize();
|
||||
}
|
||||
|
||||
auto driver() -> string override { return "waveOut"; }
|
||||
auto ready() -> bool override { return true; }
|
||||
|
||||
auto hasDevices() -> vector<string> override {
|
||||
vector<string> devices{"Default"};
|
||||
for(uint index : range(waveOutGetNumDevs())) {
|
||||
WAVEOUTCAPS caps{};
|
||||
if(waveOutGetDevCaps(index, &caps, sizeof(WAVEOUTCAPS)) == MMSYSERR_NOERROR) {
|
||||
devices.append((const char*)utf8_t(caps.szPname));
|
||||
}
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
auto hasBlocking() -> bool override { return true; }
|
||||
auto hasDynamic() -> bool override { return true; }
|
||||
auto hasFrequencies() -> vector<uint> override { return {44100}; }
|
||||
auto hasLatencies() -> vector<uint> override { return {0}; }
|
||||
|
||||
auto setBlocking(bool blocking) -> bool override { return true; }
|
||||
auto setDynamic(bool dynamic) -> bool override { initialize(); return true; }
|
||||
|
||||
auto clear() -> void override {
|
||||
for(auto& header : headers) {
|
||||
memory::fill(header.lpData, frameCount * 4);
|
||||
}
|
||||
}
|
||||
|
||||
auto level() -> double override {
|
||||
return (double)((blockQueue * frameCount) + frameIndex) / (blockCount * frameCount);
|
||||
}
|
||||
|
||||
auto output(const double samples[]) -> void override {
|
||||
uint16_t lsample = sclamp<16>(samples[0] * 32767.0);
|
||||
uint16_t rsample = sclamp<16>(samples[1] * 32767.0);
|
||||
|
||||
auto block = (uint32_t*)headers[blockIndex].lpData;
|
||||
block[frameIndex] = lsample << 0 | rsample << 16;
|
||||
|
||||
if(++frameIndex >= frameCount) {
|
||||
frameIndex = 0;
|
||||
if(self.dynamic) {
|
||||
while(waveOutWrite(handle, &headers[blockIndex], sizeof(WAVEHDR)) == WAVERR_STILLPLAYING);
|
||||
InterlockedIncrement(&blockQueue);
|
||||
} else while(true) {
|
||||
auto result = waveOutWrite(handle, &headers[blockIndex], sizeof(WAVEHDR));
|
||||
if(!self.blocking || result != WAVERR_STILLPLAYING) break;
|
||||
InterlockedIncrement(&blockQueue);
|
||||
}
|
||||
if(++blockIndex >= blockCount) {
|
||||
blockIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
auto initialize() -> bool {
|
||||
terminate();
|
||||
|
||||
auto deviceIndex = hasDevices().find(self.device);
|
||||
if(!deviceIndex) deviceIndex = 0;
|
||||
|
||||
WAVEFORMATEX format{};
|
||||
format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
format.nChannels = 2;
|
||||
format.nSamplesPerSec = 44100;
|
||||
format.nBlockAlign = 4;
|
||||
format.wBitsPerSample = 16;
|
||||
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
|
||||
format.cbSize = 0; //not sizeof(WAVEFORMAT); size of extra information after WAVEFORMATEX
|
||||
//-1 = default; 0+ = specific device; subtract -1 as hasDevices() includes "Default" entry
|
||||
waveOutOpen(&handle, (int)*deviceIndex - 1, &format, (DWORD_PTR)waveOutCallback, (DWORD_PTR)this, CALLBACK_FUNCTION);
|
||||
|
||||
headers.resize(blockCount);
|
||||
for(auto& header : headers) {
|
||||
memory::fill(&header, sizeof(WAVEHDR));
|
||||
header.lpData = (LPSTR)LocalAlloc(LMEM_FIXED, frameCount * 4);
|
||||
header.dwBufferLength = frameCount * 4;
|
||||
waveOutPrepareHeader(handle, &header, sizeof(WAVEHDR));
|
||||
}
|
||||
|
||||
frameIndex = 0;
|
||||
blockIndex = 0;
|
||||
blockQueue = 0;
|
||||
|
||||
waveOutSetVolume(handle, 0xffff'ffff); //100% volume (65535 left, 65535 right)
|
||||
waveOutRestart(handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto terminate() -> void {
|
||||
if(!handle) return;
|
||||
waveOutPause(handle);
|
||||
waveOutReset(handle);
|
||||
for(auto& header : headers) {
|
||||
waveOutUnprepareHeader(handle, &header, sizeof(WAVEHDR));
|
||||
LocalFree(header.lpData);
|
||||
}
|
||||
waveOutClose(handle);
|
||||
handle = nullptr;
|
||||
headers.reset();
|
||||
}
|
||||
|
||||
HWAVEOUT handle = nullptr;
|
||||
vector<WAVEHDR> headers;
|
||||
const uint frameCount = 512;
|
||||
const uint blockCount = 32;
|
||||
uint frameIndex = 0;
|
||||
uint blockIndex = 0;
|
||||
|
||||
public:
|
||||
LONG blockQueue = 0;
|
||||
};
|
||||
|
||||
auto CALLBACK waveOutCallback(HWAVEOUT handle, UINT message, DWORD_PTR userData, DWORD_PTR, DWORD_PTR) -> void {
|
||||
auto instance = (AudioWaveOut*)userData;
|
||||
if(instance->blockQueue > 0) InterlockedDecrement(&instance->blockQueue);
|
||||
}
|
@@ -20,7 +20,7 @@ struct AudioXAudio2 : AudioDriver, public IXAudio2VoiceCallback {
|
||||
auto ready() -> bool override { return self.isReady; }
|
||||
|
||||
auto hasBlocking() -> bool override { return true; }
|
||||
auto hasDynamic() -> bool override { return false; }
|
||||
auto hasDynamic() -> bool override { return true; }
|
||||
|
||||
auto hasDevices() -> vector<string> override {
|
||||
vector<string> devices;
|
||||
@@ -52,13 +52,13 @@ struct AudioXAudio2 : AudioDriver, public IXAudio2VoiceCallback {
|
||||
self.sourceVoice->Start(0);
|
||||
}
|
||||
|
||||
/*auto level() -> double override {
|
||||
auto level() -> double override {
|
||||
XAUDIO2_VOICE_STATE state{};
|
||||
self.sourceVoice->GetState(&state);
|
||||
uint level = state.BuffersQueued * self.period - state.SamplesPlayed % self.period;
|
||||
uint level = state.BuffersQueued * self.period + buffers[self.index].size() - state.SamplesPlayed % self.period;
|
||||
uint limit = Buffers * self.period;
|
||||
return (double)(limit - level) / limit;
|
||||
}*/
|
||||
return (double)level / limit;
|
||||
}
|
||||
|
||||
auto output(const double samples[]) -> void override {
|
||||
uint32_t frame = 0;
|
||||
|
Reference in New Issue
Block a user