2010-08-09 13:28:56 +00:00
|
|
|
#include <dsound.h>
|
|
|
|
|
2015-06-20 05:44:05 +00:00
|
|
|
struct AudioDS : Audio {
|
|
|
|
~AudioDS() { term(); }
|
2010-08-09 13:28:56 +00:00
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
LPDIRECTSOUND ds = nullptr;
|
|
|
|
LPDIRECTSOUNDBUFFER dsb_p = nullptr;
|
|
|
|
LPDIRECTSOUNDBUFFER dsb_b = nullptr;
|
2010-08-09 13:28:56 +00:00
|
|
|
DSBUFFERDESC dsbd;
|
|
|
|
WAVEFORMATEX wfx;
|
|
|
|
|
|
|
|
struct {
|
2015-06-12 13:14:38 +00:00
|
|
|
unsigned rings = 0;
|
|
|
|
unsigned latency = 0;
|
2010-08-09 13:28:56 +00:00
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
uint32_t* buffer = nullptr;
|
|
|
|
unsigned bufferoffset = 0;
|
2010-08-09 13:28:56 +00:00
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
unsigned readring = 0;
|
|
|
|
unsigned writering = 0;
|
|
|
|
int distance = 0;
|
2010-08-09 13:28:56 +00:00
|
|
|
} device;
|
|
|
|
|
|
|
|
struct {
|
2015-06-12 13:14:38 +00:00
|
|
|
HWND handle = nullptr;
|
|
|
|
bool synchronize = false;
|
|
|
|
unsigned frequency = 22050;
|
|
|
|
unsigned latency = 120;
|
2010-08-09 13:28:56 +00:00
|
|
|
} settings;
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto cap(const string& name) -> bool {
|
2010-08-09 13:28:56 +00:00
|
|
|
if(name == Audio::Handle) return true;
|
|
|
|
if(name == Audio::Synchronize) return true;
|
|
|
|
if(name == Audio::Frequency) return true;
|
|
|
|
if(name == Audio::Latency) return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto get(const string& name) -> any {
|
2010-08-09 13:28:56 +00:00
|
|
|
if(name == Audio::Handle) return (uintptr_t)settings.handle;
|
|
|
|
if(name == Audio::Synchronize) return settings.synchronize;
|
|
|
|
if(name == Audio::Frequency) return settings.frequency;
|
|
|
|
if(name == Audio::Latency) return settings.latency;
|
2015-06-12 13:14:38 +00:00
|
|
|
return {};
|
2010-08-09 13:28:56 +00:00
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto set(const string& name, const any& value) -> bool {
|
|
|
|
if(name == Audio::Handle && value.is<uintptr_t>()) {
|
|
|
|
settings.handle = (HWND)value.get<uintptr_t>();
|
2010-08-09 13:28:56 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
if(name == Audio::Synchronize && value.is<bool>()) {
|
|
|
|
settings.synchronize = value.get<bool>();
|
2010-08-09 13:28:56 +00:00
|
|
|
if(ds) clear();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
if(name == Audio::Frequency && value.is<unsigned>()) {
|
|
|
|
settings.frequency = value.get<unsigned>();
|
2010-08-09 13:28:56 +00:00
|
|
|
if(ds) init();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
if(name == Audio::Latency && value.is<unsigned>()) {
|
2015-08-24 09:42:11 +00:00
|
|
|
//latency settings below 40ms causes DirectSound to hang
|
|
|
|
settings.latency = max(40u, value.get<unsigned>());
|
2010-08-09 13:28:56 +00:00
|
|
|
if(ds) init();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto sample(uint16_t left, uint16_t right) -> void {
|
2010-08-09 13:28:56 +00:00
|
|
|
device.buffer[device.bufferoffset++] = left + (right << 16);
|
|
|
|
if(device.bufferoffset < device.latency) return;
|
|
|
|
device.bufferoffset = 0;
|
|
|
|
|
|
|
|
DWORD pos, size;
|
2013-05-02 11:25:45 +00:00
|
|
|
void* output;
|
2010-08-09 13:28:56 +00:00
|
|
|
|
Update to v093r01 release.
byuu says:
Changelog:
- added SA-1 MDR; fixes bug in SD Gundam G-Next where the main
battleship was unable to fire
- added out-of-the-box support for any BSD running Clang 3.3+ (FreeBSD
10+, notably)
- added new video shader, "Display Emulation", which changes the shader
based on the emulated system
- fixed the home button to go to your default library path
- phoenix: Windows port won't send onActivate unless an item is selected
(prevents crashing on pressing enter in file dialog)
- ruby: removed vec4 position from out Vertex {} (helps AMD cards)
- shaders: updated all shaders to use texture() instead of texture2D()
(helps AMD cards)
The "Display Emulation" option works like this: when selected, it tries
to load "<path>/Video Shaders/Emulation/<systemName>.shader/"; otherwise
it falls back to the blur shader. <path> is the usual (next to binary,
then in <config>/higan, then in /usr/share/higan, etc); and <systemName>
is "Famicom", "Super Famicom", "Game Boy", "Game Boy Color", "Game Boy
Advance"
To support BSD, I had to modify the $(platform) variable to
differentiate between Linux and BSD.
As such, the new $(platform) values are:
win -> windows
osx -> macosx
x -> linux or bsd
I am also checking uname -s instead of uname -a now. No reason to
potentially match the hostname to the wrong OS type.
2013-10-21 11:45:39 +00:00
|
|
|
if(settings.synchronize) {
|
2010-08-09 13:28:56 +00:00
|
|
|
//wait until playback buffer has an empty ring to write new audio data to
|
|
|
|
while(device.distance >= device.rings - 1) {
|
|
|
|
dsb_b->GetCurrentPosition(&pos, 0);
|
|
|
|
unsigned activering = pos / (device.latency * 4);
|
Update to v093r01 release.
byuu says:
Changelog:
- added SA-1 MDR; fixes bug in SD Gundam G-Next where the main
battleship was unable to fire
- added out-of-the-box support for any BSD running Clang 3.3+ (FreeBSD
10+, notably)
- added new video shader, "Display Emulation", which changes the shader
based on the emulated system
- fixed the home button to go to your default library path
- phoenix: Windows port won't send onActivate unless an item is selected
(prevents crashing on pressing enter in file dialog)
- ruby: removed vec4 position from out Vertex {} (helps AMD cards)
- shaders: updated all shaders to use texture() instead of texture2D()
(helps AMD cards)
The "Display Emulation" option works like this: when selected, it tries
to load "<path>/Video Shaders/Emulation/<systemName>.shader/"; otherwise
it falls back to the blur shader. <path> is the usual (next to binary,
then in <config>/higan, then in /usr/share/higan, etc); and <systemName>
is "Famicom", "Super Famicom", "Game Boy", "Game Boy Color", "Game Boy
Advance"
To support BSD, I had to modify the $(platform) variable to
differentiate between Linux and BSD.
As such, the new $(platform) values are:
win -> windows
osx -> macosx
x -> linux or bsd
I am also checking uname -s instead of uname -a now. No reason to
potentially match the hostname to the wrong OS type.
2013-10-21 11:45:39 +00:00
|
|
|
if(activering == device.readring) continue;
|
2010-08-09 13:28:56 +00:00
|
|
|
|
|
|
|
//subtract number of played rings from ring distance counter
|
|
|
|
device.distance -= (device.rings + activering - device.readring) % device.rings;
|
|
|
|
device.readring = activering;
|
|
|
|
|
|
|
|
if(device.distance < 2) {
|
|
|
|
//buffer underflow; set max distance to recover quickly
|
|
|
|
device.distance = device.rings - 1;
|
|
|
|
device.writering = (device.rings + device.readring - 1) % device.rings;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
device.writering = (device.writering + 1) % device.rings;
|
|
|
|
device.distance = (device.distance + 1) % device.rings;
|
|
|
|
|
|
|
|
if(dsb_b->Lock(device.writering * device.latency * 4, device.latency * 4, &output, &size, 0, 0, 0) == DS_OK) {
|
|
|
|
memcpy(output, device.buffer, device.latency * 4);
|
|
|
|
dsb_b->Unlock(output, size, 0, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto clear() -> void {
|
2010-08-09 13:28:56 +00:00
|
|
|
device.readring = 0;
|
|
|
|
device.writering = device.rings - 1;
|
|
|
|
device.distance = device.rings - 1;
|
|
|
|
|
|
|
|
device.bufferoffset = 0;
|
|
|
|
if(device.buffer) memset(device.buffer, 0, device.latency * device.rings * 4);
|
|
|
|
|
|
|
|
if(!dsb_b) return;
|
|
|
|
dsb_b->Stop();
|
|
|
|
dsb_b->SetCurrentPosition(0);
|
|
|
|
|
|
|
|
DWORD size;
|
2013-05-02 11:25:45 +00:00
|
|
|
void* output;
|
2010-08-09 13:28:56 +00:00
|
|
|
dsb_b->Lock(0, device.latency * device.rings * 4, &output, &size, 0, 0, 0);
|
|
|
|
memset(output, 0, size);
|
|
|
|
dsb_b->Unlock(output, size, 0, 0);
|
|
|
|
|
|
|
|
dsb_b->Play(0, 0, DSBPLAY_LOOPING);
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto init() -> bool {
|
2015-06-20 05:44:05 +00:00
|
|
|
settings.handle = GetDesktopWindow();
|
2010-08-09 13:28:56 +00:00
|
|
|
|
|
|
|
device.rings = 8;
|
|
|
|
device.latency = settings.frequency * settings.latency / device.rings / 1000.0 + 0.5;
|
|
|
|
device.buffer = new uint32_t[device.latency * device.rings];
|
|
|
|
device.bufferoffset = 0;
|
|
|
|
|
|
|
|
DirectSoundCreate(0, &ds, 0);
|
|
|
|
ds->SetCooperativeLevel((HWND)settings.handle, DSSCL_PRIORITY);
|
|
|
|
|
|
|
|
memset(&dsbd, 0, sizeof(dsbd));
|
|
|
|
dsbd.dwSize = sizeof(dsbd);
|
|
|
|
dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
|
|
|
dsbd.dwBufferBytes = 0;
|
|
|
|
dsbd.lpwfxFormat = 0;
|
|
|
|
ds->CreateSoundBuffer(&dsbd, &dsb_p, 0);
|
|
|
|
|
|
|
|
memset(&wfx, 0, sizeof(wfx));
|
|
|
|
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
|
|
|
wfx.nChannels = 2;
|
|
|
|
wfx.nSamplesPerSec = settings.frequency;
|
|
|
|
wfx.wBitsPerSample = 16;
|
|
|
|
wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels;
|
|
|
|
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
|
|
|
|
dsb_p->SetFormat(&wfx);
|
|
|
|
|
|
|
|
memset(&dsbd, 0, sizeof(dsbd));
|
|
|
|
dsbd.dwSize = sizeof(dsbd);
|
|
|
|
dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLFREQUENCY | DSBCAPS_GLOBALFOCUS | DSBCAPS_LOCSOFTWARE;
|
|
|
|
dsbd.dwBufferBytes = device.latency * device.rings * sizeof(uint32_t);
|
|
|
|
dsbd.guid3DAlgorithm = GUID_NULL;
|
|
|
|
dsbd.lpwfxFormat = &wfx;
|
|
|
|
ds->CreateSoundBuffer(&dsbd, &dsb_b, 0);
|
|
|
|
dsb_b->SetFrequency(settings.frequency);
|
|
|
|
dsb_b->SetCurrentPosition(0);
|
|
|
|
|
|
|
|
clear();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-06-12 13:14:38 +00:00
|
|
|
auto term() -> void {
|
2010-08-09 13:28:56 +00:00
|
|
|
if(device.buffer) {
|
|
|
|
delete[] device.buffer;
|
|
|
|
device.buffer = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(dsb_b) { dsb_b->Stop(); dsb_b->Release(); dsb_b = 0; }
|
|
|
|
if(dsb_p) { dsb_p->Stop(); dsb_p->Release(); dsb_p = 0; }
|
|
|
|
if(ds) { ds->Release(); ds = 0; }
|
|
|
|
}
|
|
|
|
};
|