#include #include #include #include //OSS4 soundcard.h includes below SNDCTL defines, but OSS3 does not //However, OSS4 soundcard.h does not reside in //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 #ifndef SNDCTL_DSP_COOKEDMODE #define SNDCTL_DSP_COOKEDMODE _IOW('P', 30, signed) #endif #ifndef SNDCTL_DSP_POLICY #define SNDCTL_DSP_POLICY _IOW('P', 45, signed) #endif namespace ruby { struct pAudioOSS { struct { signed fd = -1; signed format = AFMT_S16_LE; signed channels = 2; } device; struct { string device = "/dev/dsp"; bool synchronize = true; unsigned frequency = 22050; } settings; ~pAudioOSS() { term(); } auto cap(const string& name) -> bool { if(name == Audio::Device) return true; if(name == Audio::Synchronize) return true; if(name == Audio::Frequency) return true; return false; } auto get(const string& name) -> any { if(name == Audio::Device) return settings.device; if(name == Audio::Synchronize) return settings.synchronize; if(name == Audio::Frequency) return settings.frequency; return {}; } auto set(const string& name, const any& value) -> bool { if(name == Audio::Device && value.is()) { settings.device = value.get(); if(!settings.device) settings.device = "/dev/dsp"; return true; } if(name == Audio::Synchronize && value.is()) { settings.synchronize = value.get(); updateSynchronization(); return true; } if(name == Audio::Frequency && value.is()) { settings.frequency = value.get(); if(device.fd >= 0) init(); return true; } return false; } auto sample(uint16_t left, uint16_t right) -> void { uint32_t sample = left << 0 | right << 16; auto unused = write(device.fd, &sample, 4); } auto clear() -> void { } auto init() -> bool { term(); 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 signed cooked = 1, policy = 4; //policy should be 0 - 10, lower = less latency, more CPU usage ioctl(device.fd, SNDCTL_DSP_COOKEDMODE, &cooked); ioctl(device.fd, SNDCTL_DSP_POLICY, &policy); #endif signed freq = settings.frequency; ioctl(device.fd, SNDCTL_DSP_CHANNELS, &device.channels); ioctl(device.fd, SNDCTL_DSP_SETFMT, &device.format); ioctl(device.fd, SNDCTL_DSP_SPEED, &freq); updateSynchronization(); return true; } auto term() -> void { if(device.fd >= 0) { close(device.fd); device.fd = -1; } } private: auto updateSynchronization() -> void { if(device.fd < 0) return; auto flags = fcntl(device.fd, F_GETFL); if(flags < 0) return; settings.synchronize ? flags &=~ O_NONBLOCK : flags |= O_NONBLOCK; fcntl(device.fd, F_SETFL, flags); } }; DeclareAudio(OSS) };