123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- #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(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 {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(uint latency) -> bool override { return initialize(); }
- 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);
- 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;
- uint frameCount = 0;
- uint blockCount = 0;
- 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);
- }
|