Update to v101r07 release.

byuu says:

Added VDP sprite rendering. Can't get any games far enough in to see if
it actually works. So in other words, it doesn't work at all and is 100%
completely broken.

Also added 68K exceptions and interrupts. So far only the VDP interrupt
is present. It definitely seems to be firing in commercial games, so
that's promising. But the implementation is almost certainly completely
wrong. There is fuck all of nothing for documentation on how interrupts
actually work. I had to find out the interrupt vector numbers from
reading the comments from the Sonic the Hedgehog disassembly. I have
literally no fucking clue what I0-I2 (3-bit integer priority value in
the status register) is supposed to do. I know that Vblank=6, Hblank=4,
Ext(gamepad)=2. I know that at reset, SR.I=7. I don't know if I'm
supposed to block interrupts when I is >, >=, <, <= to the interrupt
level. I don't know what level CPU exceptions are supposed to be.

Also implemented VDP regular DMA. No idea if it works correctly since
none of the commercial games run far enough to use it. So again, it's
horribly broken for usre.

Also improved VDP fill mode. But I don't understand how it takes
byte-lengths when the bus is 16-bit. The transfer times indicate it's
actually transferring at the same speed as the 68K->VDP copy, strongly
suggesting it's actually doing 16-bit transfers at a time. In which case,
what happens when you set an odd transfer length?

Also, both DMA modes can now target VRAM, VSRAM, CRAM. Supposedly there's
all kinds of weird shit going on when you target VSRAM, CRAM with VDP
fill/copy modes, but whatever. Get to that later.

Also implemented a very lazy preliminary wait mechanism to to stall out
a processor while another processor exerts control over the bus. This
one's going to be a major work in progress. For one, it totally breaks
the model I use to do save states with libco. For another, I don't
know if a 68K->VDP DMA instantly locks the CPU, or if it the CPU could
actually keep running if it was executing out of RAM when it started
the DMA transfer from ROM (eg it's a bus busy stall, not a hard chip
stall.) That'll greatly change how I handle the waiting.

Also, the OSS driver now supports Audio::Latency. Sound should be
even lower latency now. On FreeBSD when set to 0ms, it's absolutely
incredible. Cannot detect latency whatsoever. The Mario jump sound seems
to happen at the very instant I hear my cherry blue keyswitch activate.
This commit is contained in:
Tim Allen 2016-08-15 14:56:38 +10:00
parent 427bac3011
commit ffd150735b
20 changed files with 364 additions and 93 deletions

View File

@ -12,7 +12,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "101.06";
static const string Version = "101.07";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "http://byuu.org/";

View File

@ -21,17 +21,51 @@ auto CPU::main() -> void {
fp.print(pad(disassemble(r.pc), -60, ' '), " ", disassembleRegisters().replace("\n", " "), "\n");
#endif
if(state.interruptPending) {
if(state.interruptPending.bit((uint)Interrupt::HorizontalBlank)) {
state.interruptPending.bit((uint)Interrupt::HorizontalBlank) = 0;
r.i = 4;
return exception(Exception::Interrupt, Vector::HorizontalBlank);
}
if(state.interruptPending.bit((uint)Interrupt::VerticalBlank)) {
state.interruptPending.bit((uint)Interrupt::VerticalBlank) = 0;
r.i = 6;
return exception(Exception::Interrupt, Vector::VerticalBlank);
}
}
instruction();
}
auto CPU::step(uint clocks) -> void {
while(wait) {
Thread::step(1);
synchronize();
}
Thread::step(clocks);
synchronize();
}
auto CPU::synchronize() -> void {
synchronize(apu);
synchronize(vdp);
synchronize(psg);
synchronize(ym2612);
}
auto CPU::raise(Interrupt interrupt) -> void {
if(!state.interruptLine.bit((uint)interrupt)) {
state.interruptLine.bit((uint)interrupt) = 1;
state.interruptPending.bit((uint)interrupt) = 1;
}
}
auto CPU::lower(Interrupt interrupt) -> void {
state.interruptLine.bit((uint)interrupt) = 0;
}
auto CPU::power() -> void {
M68K::power();
@ -41,7 +75,8 @@ auto CPU::power() -> void {
auto CPU::reset() -> void {
M68K::reset();
create(CPU::Enter, system.colorburst() * 15.0 / 7.0);
cycles = 0;
memory::fill(&state, sizeof(State));
}
auto CPU::readByte(uint24 addr) -> uint8 {

View File

@ -1,10 +1,21 @@
//Motorola 68000
struct CPU : Processor::M68K, Thread {
enum class Interrupt : uint {
HorizontalBlank,
VerticalBlank,
};
using Thread::synchronize;
static auto Enter() -> void;
auto boot() -> void;
auto main() -> void;
auto step(uint clocks) -> void override;
auto synchronize() -> void;
auto raise(Interrupt) -> void;
auto lower(Interrupt) -> void;
auto power() -> void;
auto reset() -> void;
@ -17,7 +28,10 @@ struct CPU : Processor::M68K, Thread {
private:
uint8 ram[64 * 1024];
uint cycles = 0;
struct State {
uint32 interruptLine;
uint32 interruptPending;
} state;
};
extern CPU cpu;

View File

@ -15,15 +15,24 @@ namespace MegaDrive {
using Scheduler = Emulator::Scheduler;
extern Scheduler scheduler;
struct Wait {
enum : uint {
VDP_DMA = 1 << 0,
};
};
struct Thread : Emulator::Thread {
auto create(auto (*entrypoint)() -> void, double frequency) -> void {
Emulator::Thread::create(entrypoint, frequency);
scheduler.append(*this);
wait = 0;
}
inline auto synchronize(Thread& thread) -> void {
if(clock() >= thread.clock()) scheduler.resume(thread);
}
uint wait = 0;
};
#include <md/cpu/cpu.hpp>

View File

@ -1,4 +1,4 @@
auto VDP::Background::scanline() -> void {
auto VDP::Background::scanline(uint y) -> void {
}
auto VDP::Background::run(uint x, uint y) -> void {
@ -17,9 +17,9 @@ auto VDP::Background::run(uint x, uint y) -> void {
tileAddress += pixelY << 1 | pixelX >> 2;
uint16 tileData = vdp.vram[tileAddress];
uint4 palette = tileData >> (((pixelX & 3) ^ 3) << 2);
if(palette) {
output.color = tileAttributes.bits(13,14) << 4 | palette;
uint4 color = tileData >> (((pixelX & 3) ^ 3) << 2);
if(color) {
output.color = tileAttributes.bits(13,14) << 4 | color;
output.priority = tileAttributes.bit(15);
}
}

View File

@ -1,18 +1,36 @@
auto VDP::dmaRun() -> void {
if(!io.dmaEnable) return;
if(!io.dmaActive) return;
if(io.command.bits(4,5) != 2) return;
if(io.dmaMode == 2) return dmaFillVRAM();
if(io.dmaMode <= 1) return dmaLoad();
if(io.dmaMode == 2) return dmaFill();
if(io.dmaMode == 3) return dmaCopy();
}
auto VDP::dmaFillVRAM() -> void {
auto address = io.address.bits(1,15);
auto data = io.dmaFillWord;
if(io.address.bit(0)) data = data >> 8 | data << 8;
vram[address] = data;
io.address += io.dataIncrement;
auto VDP::dmaLoad() -> void {
cpu.wait |= Wait::VDP_DMA;
auto data = cpu.readWord(io.dmaSource);
writeDataPort(data);
io.dmaSource.bits(0,15) += 2;
if(--io.dmaLength == 0) {
io.dmaActive = false;
io.command.bit(5) = 0;
cpu.wait &=~ Wait::VDP_DMA;
}
}
auto VDP::dmaFill() -> void {
if(io.dmaFillWait) return;
auto data = io.dmaFillWord;
writeDataPort(data);
io.dmaSource.bits(0,15) += 2;
if(--io.dmaLength == 0 || --io.dmaLength == 0) {
io.command.bit(5) = 0;
}
}
auto VDP::dmaCopy() -> void {
}

View File

@ -72,8 +72,7 @@ auto VDP::writeDataPort(uint16 data) -> void {
io.commandPending = false;
//DMA VRAM fill
if(io.command.bits(4,5) == 2) {
io.dmaActive = true;
if(io.dmaFillWait.lower()) {
io.dmaFillWord = data;
return;
}
@ -113,7 +112,10 @@ auto VDP::readControlPort() -> uint16 {
io.commandPending = false;
uint16 result = 0b0011'0100'0000'0000;
result |= io.dmaActive << 1;
result |= 1 << 9; //FIFO empty
result |= (state.y >= 240) << 3; //vertical blank
result |= (state.y >= 240 || state.x >= 320) << 2; //horizontal blank
result |= io.command.bit(5) << 1; //DMA active
return result;
}
@ -126,7 +128,7 @@ auto VDP::writeControlPort(uint16 data) -> void {
io.command.bits(2,5) = data.bits(4,7);
io.address.bits(14,15) = data.bits(0,1);
io.dmaFillWait = io.dmaMode == 2 && io.command.bits(4,5) == 2;
return;
}
@ -147,7 +149,7 @@ auto VDP::writeControlPort(uint16 data) -> void {
case 0x00: {
io.displayOverlayEnable = data.bit(0);
io.counterLatch = data.bit(1);
io.horizontalInterruptEnable = data.bit(4);
io.horizontalBlankInterruptEnable = data.bit(4);
io.leftColumnBlank = data.bit(5);
return;
}
@ -186,13 +188,13 @@ auto VDP::writeControlPort(uint16 data) -> void {
//sprite attribute table location
case 0x05: {
io.attrtableSprite = data.bits(0,7);
sprite.io.attributeAddress = data.bits(0,7) << 8;
return;
}
//sprite pattern base address
case 0x06: {
io.nametableBaseSprite = data.bit(5);
sprite.io.nametableAddressBase = data.bit(5);
return;
}

View File

@ -3,6 +3,17 @@ auto VDP::scanline() -> void {
if(++state.y >= 262) state.y = 0;
if(state.y == 0) scheduler.exit(Scheduler::Event::Frame);
if(state.y == 0) {
sprite.frame();
}
if(state.y < 240) {
planeA.scanline(state.y);
window.scanline(state.y);
planeB.scanline(state.y);
sprite.scanline(state.y);
}
state.output = buffer + (state.y * 2 + 0) * 1280;
}
@ -16,12 +27,15 @@ auto VDP::run() -> void {
planeA.run(state.x, state.y);
planeB.run(state.x, state.y);
sprite.run(state.x, state.y);
auto output = io.backgroundColor;
if(auto color = planeB.output.color) output = color;
if(auto color = planeA.output.color) output = color;
if(auto color = sprite.output.color) output = color;
if(planeB.output.priority) if(auto color = planeB.output.color) output = color;
if(planeA.output.priority) if(auto color = planeA.output.color) output = color;
if(sprite.output.priority) if(auto color = sprite.output.color) output = color;
outputPixel(cram[output]);
state.x++;

76
higan/md/vdp/sprite.cpp Normal file
View File

@ -0,0 +1,76 @@
auto VDP::Sprite::frame() -> void {
uint15 address = io.attributeAddress;
uint7 link = 0;
oam.reset();
while(oam.size() < 80) {
uint64 attributes;
attributes |= (uint64)vdp.vram[address + (link << 2) + 0] << 48;
attributes |= (uint64)vdp.vram[address + (link << 2) + 1] << 32;
attributes |= (uint64)vdp.vram[address + (link << 2) + 2] << 16;
attributes |= (uint64)vdp.vram[address + (link << 2) + 3] << 0;
auto& object = oam.append();
object.x = attributes.bits( 0, 9) - 128;
object.address = attributes.bits(16,26) << 4;
object.horizontalFlip = attributes.bit (27);
object.verticalFlip = attributes.bit (28);
object.palette = attributes.bits(29,30);
object.priority = attributes.bit (31);
object.height = attributes.bits(40,41) << 3;
object.width = attributes.bits(42,43) << 3;
object.y = attributes.bits(48,57) - 128;
link = attributes.bits(32,38);
if(!link) break;
}
}
auto VDP::Sprite::scanline(uint y) -> void {
object.reset();
for(auto& o : oam) {
if((uint9)(o.y + o.height - 1) < y) continue;
if((uint9)(y + o.height - 1) < o.y) continue;
if(o.x == 0) break;
object.append(o);
if(object.size() >= object.capacity()) break;
}
}
auto VDP::Sprite::run(uint x, uint y) -> void {
output.priority = 0;
output.color = 0;
for(auto& o : object) {
if((uint9)(o.x + o.width - 1) < x) continue;
if((uint9)(y + o.width - 1) < o.x) continue;
auto objectX = (uint9)(x - o.x);
auto objectY = (uint9)(y - o.y);
if(o.horizontalFlip) objectX = (o.width - 1) - objectX;
if(o.verticalFlip) objectY = (o.height - 1) - objectY;
uint tileX = objectX >> 3;
uint tileY = objectY >> 3;
uint tileNumber = tileX * (o.width >> 3) + tileY;
uint15 tileAddress = o.address + (tileNumber << 4);
uint pixelX = objectX & 7;
uint pixelY = objectY & 7;
tileAddress += pixelY << 1 | pixelX >> 2;
uint16 tileData = vdp.vram[tileAddress];
uint4 color = tileData >> (((pixelX & 3) ^ 3) << 2);
if(color) {
output.color = o.palette << 4 | color;
output.priority = o.priority;
}
}
}
auto VDP::Sprite::power() -> void {
}
auto VDP::Sprite::reset() -> void {
memory::fill(&io, sizeof(IO));
}

View File

@ -10,6 +10,7 @@ VDP vdp;
#include "dma.cpp"
#include "render.cpp"
#include "background.cpp"
#include "sprite.cpp"
auto VDP::Enter() -> void {
while(true) scheduler.synchronize(), vdp.main();
@ -18,14 +19,26 @@ auto VDP::Enter() -> void {
auto VDP::main() -> void {
scanline();
if(state.y < 240) {
if(state.y == 0) {
cpu.lower(CPU::Interrupt::VerticalBlank);
}
cpu.lower(CPU::Interrupt::HorizontalBlank);
for(uint x : range(320)) {
run();
step(1);
}
if(io.horizontalBlankInterruptEnable) {
cpu.raise(CPU::Interrupt::HorizontalBlank);
}
step(22);
} else {
if(state.y == 240) {
if(io.verticalBlankInterruptEnable) {
cpu.raise(CPU::Interrupt::VerticalBlank);
}
}
step(342);
}
step(22);
}
auto VDP::step(uint clocks) -> void {
@ -44,6 +57,7 @@ auto VDP::power() -> void {
planeA.power();
window.power();
planeB.power();
sprite.power();
}
auto VDP::reset() -> void {
@ -54,6 +68,7 @@ auto VDP::reset() -> void {
planeA.reset();
window.reset();
planeB.reset();
sprite.reset();
}
}

View File

@ -24,7 +24,9 @@ struct VDP : Thread {
//dma.cpp
auto dmaRun() -> void;
auto dmaFillVRAM() -> void;
auto dmaLoad() -> void;
auto dmaFill() -> void;
auto dmaCopy() -> void;
//render.cpp
auto scanline() -> void;
@ -33,7 +35,7 @@ struct VDP : Thread {
//background.cpp
struct Background {
auto scanline() -> void;
auto scanline(uint y) -> void;
auto run(uint x, uint y) -> void;
auto power() -> void;
@ -54,15 +56,51 @@ struct VDP : Thread {
Background window;
Background planeB;
//sprite.cpp
struct Sprite {
auto frame() -> void;
auto scanline(uint y) -> void;
auto run(uint x, uint y) -> void;
auto power() -> void;
auto reset() -> void;
struct IO {
uint15 attributeAddress;
uint1 nametableAddressBase;
} io;
struct Object {
uint10 x;
uint10 y;
uint width;
uint height;
bool horizontalFlip;
bool verticalFlip;
uint2 palette;
uint1 priority;
uint15 address;
};
struct Output {
uint6 color;
boolean priority;
} output;
array<Object, 80> oam;
array<Object, 20> object;
};
Sprite sprite;
private:
uint16 vram[32768];
uint16 vramExpansion[32768]; //not present in stock Mega Drive hardware
uint9 cram[64];
uint10 vsram[40];
private:
struct IO {
//internal state
boolean dmaActive;
boolean dmaFillWait;
uint8 dmaFillWord;
//command
@ -73,7 +111,7 @@ private:
//$00 mode register 1
uint1 displayOverlayEnable;
uint1 counterLatch;
uint1 horizontalInterruptEnable;
uint1 horizontalBlankInterruptEnable;
uint1 leftColumnBlank;
//$01 mode register 2
@ -84,12 +122,6 @@ private:
uint1 displayEnable;
uint1 externalVRAM;
//$05 sprite attribute table location
uint8 attrtableSprite;
//$06 sprite pattern base address
uint1 nametableBaseSprite;
//$07 background color
uint6 backgroundColor;

View File

@ -46,6 +46,16 @@ auto M68K::supervisor() -> bool {
}
auto M68K::exception(uint exception, uint vector) -> void {
auto pc = r.pc;
auto sr = readSR();
r.s = 1;
r.t = 0;
push<Long>(pc);
push<Word>(sr);
r.pc = read<Long>(vector << 2);
}
}

View File

@ -32,16 +32,19 @@ struct M68K {
Unprivileged,
Trap,
Interrupt,
};};
struct Vector { enum : uint {
Illegal = 4,
DivisionByZero = 5,
BoundsCheck = 6,
Overflow = 7,
Unprivileged = 8,
IllegalLineA = 10,
IllegalLineF = 11,
Illegal = 4,
DivisionByZero = 5,
BoundsCheck = 6,
Overflow = 7,
Unprivileged = 8,
IllegalLineA = 10,
IllegalLineF = 11,
HorizontalBlank = 28,
VerticalBlank = 30,
};};
M68K();

View File

@ -199,7 +199,28 @@ auto Presentation::updateEmulator() -> void {
emulator->set("Scanline Emulation", scanlineEmulation.checked());
}
auto Presentation::clearViewport() -> void {
if(!video) return;
uint32_t* output;
uint length = 0;
uint width = viewport.geometry().width();
uint height = viewport.geometry().height();
if(video->lock(output, length, width, height)) {
for(uint y : range(height)) {
auto dp = output + y * (length >> 2);
for(uint x : range(width)) *dp++ = 0xff000000;
}
video->unlock();
video->refresh();
}
}
auto Presentation::resizeViewport() -> void {
//clear video area before resizing to avoid seeing distorted video momentarily
clearViewport();
uint scale = 2;
if(settings["Video/Scale"].text() == "Small" ) scale = 2;
if(settings["Video/Scale"].text() == "Medium") scale = 3;
@ -219,7 +240,6 @@ auto Presentation::resizeViewport() -> void {
if(!emulator) {
viewport.setGeometry({0, 0, windowWidth, windowHeight});
draw(Resource::Logo::higan);
} else {
auto videoSize = emulator->videoSize(windowWidth, windowHeight, aspectCorrection);
viewport.setGeometry({
@ -227,6 +247,9 @@ auto Presentation::resizeViewport() -> void {
videoSize.width, videoSize.height
});
}
//clear video area again to ensure entire viewport area has been painted in
clearViewport();
}
auto Presentation::toggleFullScreen() -> void {
@ -243,43 +266,9 @@ auto Presentation::toggleFullScreen() -> void {
menuBar.setVisible(true);
statusBar.setVisible(settings["UserInterface/ShowStatusBar"].boolean());
}
Application::processEvents();
resizeViewport();
}
auto Presentation::draw(image logo) -> void {
if(!video) return;
uint32_t* output;
uint length = 0;
uint width = viewport.geometry().width();
uint height = viewport.geometry().height();
if(video->lock(output, length, width, height)) {
uint cx = (width - logo.width()) - 10;
uint cy = (height - logo.height()) - 10;
image backdrop;
backdrop.allocate(width, height);
if(logo && !program->hasQuit) {
backdrop.sphericalGradient(0xff0000bf, 0xff000000, logo.width(), logo.height() / 2, width, height);
backdrop.impose(image::blend::sourceAlpha, cx, cy, logo, 0, 0, logo.width(), logo.height());
} else {
backdrop.fill(0xff000000);
}
auto data = (uint32_t*)backdrop.data();
for(auto y : range(height)) {
auto dp = output + y * (length >> 2);
auto sp = data + y * width;
for(auto x : range(width)) *dp++ = *sp++;
}
video->unlock();
video->refresh();
}
}
auto Presentation::loadShaders() -> void {
auto pathname = locate("Video Shaders/");

View File

@ -11,9 +11,9 @@ struct AboutWindow : Window {
struct Presentation : Window {
Presentation();
auto updateEmulator() -> void;
auto clearViewport() -> void;
auto resizeViewport() -> void;
auto toggleFullScreen() -> void;
auto draw(image logo = {}) -> void;
auto loadShaders() -> void;
MenuBar menuBar{this};

View File

@ -29,7 +29,6 @@ auto Program::loadMedium(Emulator::Interface& interface, const Emulator::Interfa
}
updateAudioDriver();
updateAudioEffects();
presentation->draw();
emulator->power();
presentation->resizeViewport();
@ -45,14 +44,13 @@ auto Program::loadMedium(Emulator::Interface& interface, const Emulator::Interfa
auto Program::unloadMedium() -> void {
if(!emulator) return;
presentation->draw();
presentation->clearViewport();
toolsManager->cheatEditor.saveCheats();
emulator->unload();
emulator = nullptr;
mediumPaths.reset();
presentation->resizeViewport();
presentation->draw(Resource::Logo::higan);
presentation->setTitle({"higan v", Emulator::Version});
presentation->systemMenu.setVisible(false);
presentation->toolsMenu.setVisible(false);

View File

@ -31,7 +31,7 @@ Program::Program(string_vector args) {
video->set(Video::Synchronize, settings["Video/Synchronize"].boolean());
if(!video->init()) video = Video::create("None");
presentation->draw(Resource::Logo::higan);
presentation->clearViewport();
audio = Audio::create(settings["Audio/Driver"].text());
audio->set(Audio::Device, settings["Audio/Device"].text());

53
nall/array.hpp Normal file
View File

@ -0,0 +1,53 @@
#pragma once
#include <nall/range.hpp>
namespace nall {
template<typename T, uint Capacity>
struct array {
auto capacity() const -> uint { return Capacity; }
auto size() const -> uint { return _size; }
auto reset() -> void {
for(uint n : range(_size)) _pool.t[n].~T();
_size = 0;
}
auto operator[](uint index) -> T& {
return _pool.t[index];
}
auto operator[](uint index) const -> const T& {
return _pool.t[index];
}
auto append() -> T& {
new(_pool.t + _size) T;
return _pool.t[_size++];
}
auto append(const T& value) -> void {
new(_pool.t + _size++) T(value);
}
auto append(T&& value) -> void {
new(_pool.t + _size++) T(move(value));
}
auto begin() { return &_pool.t[0]; }
auto end() { return &_pool.t[_size]; }
auto begin() const { return &_pool.t[0]; }
auto end() const { return &_pool.t[_size]; }
private:
union U {
U() {}
~U() {}
T t[Capacity];
} _pool;
uint _size = 0;
};
}

View File

@ -15,6 +15,7 @@
#include <nall/algorithm.hpp>
#include <nall/any.hpp>
#include <nall/array.hpp>
#include <nall/atoi.hpp>
#include <nall/bit.hpp>
#include <nall/bit-field.hpp>

View File

@ -3,12 +3,7 @@
#include <sys/ioctl.h>
#include <sys/soundcard.h>
//OSS4 soundcard.h includes below SNDCTL defines, but OSS3 does not
//However, OSS4 soundcard.h does not reside in <sys/>
//Therefore, attempt to manually define SNDCTL values if using OSS3 header
//Note that if the defines below fail to work on any specific platform, one can point soundcard.h
//above to the correct location for OSS4 (usually /usr/lib/oss/include/sys/soundcard.h)
//Failing that, one can disable OSS4 ioctl calls inside init() and remove the below defines
//OSSv4 features: define fallbacks for OSSv3 (where these ioctls are ignored)
#ifndef SNDCTL_DSP_COOKEDMODE
#define SNDCTL_DSP_COOKEDMODE _IOW('P', 30, int)
@ -31,12 +26,14 @@ struct AudioOSS : Audio {
string device = "/dev/dsp";
bool synchronize = true;
uint frequency = 48000;
uint latency = 60;
} settings;
auto cap(const string& name) -> bool {
if(name == Audio::Device) return true;
if(name == Audio::Synchronize) return true;
if(name == Audio::Frequency) return true;
if(name == Audio::Latency) return true;
return false;
}
@ -44,6 +41,7 @@ struct AudioOSS : Audio {
if(name == Audio::Device) return settings.device;
if(name == Audio::Synchronize) return settings.synchronize;
if(name == Audio::Frequency) return settings.frequency;
if(name == Audio::Latency) return settings.latency;
return {};
}
@ -66,6 +64,12 @@ struct AudioOSS : Audio {
return true;
}
if(name == Audio::Latency && value.is<uint>()) {
settings.latency = value.get<uint>();
if(device.fd >= 0) init();
return true;
}
return false;
}
@ -81,13 +85,11 @@ struct AudioOSS : Audio {
device.fd = open(settings.device, O_WRONLY, O_NONBLOCK);
if(device.fd < 0) return false;
#if 1 //SOUND_VERSION >= 0x040000
//attempt to enable OSS4-specific features regardless of version
//OSS3 ioctl calls will silently fail, but sound will still work
int cooked = 1, policy = 4; //policy should be 0 - 10, lower = less latency, more CPU usage
int cooked = 1;
ioctl(device.fd, SNDCTL_DSP_COOKEDMODE, &cooked);
//policy: 0 = minimum latency (higher CPU usage); 10 = maximum latency (lower CPU usage)
int policy = min(10, settings.latency / 20); //note: latency measurement isn't exact
ioctl(device.fd, SNDCTL_DSP_POLICY, &policy);
#endif
int frequency = settings.frequency;
ioctl(device.fd, SNDCTL_DSP_CHANNELS, &device.channels);
ioctl(device.fd, SNDCTL_DSP_SETFMT, &device.format);