1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
|
#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(512);
return initialize();
}
auto driver() -> string override { return "waveOut"; }
auto ready() -> bool override { return true; }
auto hasDevices() -> vector<string> override {
vector<string> devices{"Default"};
for(u32 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<u32> override { return {44100}; }
auto hasLatencies() -> vector<u32> override { return {512, 384, 320, 256, 192, 160, 128, 96, 80, 64, 48, 40, 32}; }
auto setBlocking(bool blocking) -> bool override { return true; }
auto setDynamic(bool dynamic) -> bool override { return initialize(); }
auto setLatency(u32 latency) -> bool override { return initialize(); }
auto clear() -> void override {
for(auto& header : headers) {
memory::fill(header.lpData, frameCount * 4);
}
}
auto level() -> f64 override {
return (f64)((blockQueue * frameCount) + frameIndex) / (blockCount * frameCount);
}
auto output(const f64 samples[]) -> void override {
u16 lsample = sclamp<16>(samples[0] * 32767.0);
u16 rsample = sclamp<16>(samples[1] * 32767.0);
auto block = (u32*)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, (s32)*deviceIndex - 1, &format, (DWORD_PTR)waveOutCallback, (DWORD_PTR)this, CALLBACK_FUNCTION);
frameCount = self.latency;
blockCount = 32;
frameIndex = 0;
blockIndex = 0;
blockQueue = 0;
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));
}
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;
u32 frameCount = 0;
u32 blockCount = 0;
u32 frameIndex = 0;
u32 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);
}
|