From 08d632a8bd88b9a79884c2fe1cb36c00ddc400a8 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Mon, 3 Sep 2018 14:14:59 -1000 Subject: [PATCH] Multichannel WAVOut --- include/boo/audiodev/IAudioVoiceEngine.hpp | 4 +- lib/audiodev/WAVOut.cpp | 222 +++++++++++++++------ 2 files changed, 158 insertions(+), 68 deletions(-) diff --git a/include/boo/audiodev/IAudioVoiceEngine.hpp b/include/boo/audiodev/IAudioVoiceEngine.hpp index 19e02f5..9aaf374 100644 --- a/include/boo/audiodev/IAudioVoiceEngine.hpp +++ b/include/boo/audiodev/IAudioVoiceEngine.hpp @@ -108,9 +108,9 @@ struct IAudioVoiceEngine std::unique_ptr NewAudioVoiceEngine(); /** Construct WAV-rendering voice engine */ -std::unique_ptr NewWAVAudioVoiceEngine(const char* path, double sampleRate); +std::unique_ptr NewWAVAudioVoiceEngine(const char* path, double sampleRate, int numChans); #if _WIN32 -std::unique_ptr NewWAVAudioVoiceEngine(const wchar_t* path, double sampleRate); +std::unique_ptr NewWAVAudioVoiceEngine(const wchar_t* path, double sampleRate, int numChans); #endif } diff --git a/lib/audiodev/WAVOut.cpp b/lib/audiodev/WAVOut.cpp index 8bb5c5d..26117aa 100644 --- a/lib/audiodev/WAVOut.cpp +++ b/lib/audiodev/WAVOut.cpp @@ -2,15 +2,18 @@ #include "logvisor/logvisor.hpp" #include "boo/audiodev/IAudioVoiceEngine.hpp" +namespace boo +{ + static logvisor::Module Log("boo::WAVOut"); -struct WAVOutVoiceEngine : boo::BaseAudioVoiceEngine +struct WAVOutVoiceEngine : BaseAudioVoiceEngine { std::vector m_interleavedBuf; - boo::AudioChannelSet _getAvailableSet() + AudioChannelSet _getAvailableSet() { - return boo::AudioChannelSet::Stereo; + return AudioChannelSet::Stereo; } std::string getCurrentAudioOutput() const @@ -38,11 +41,11 @@ struct WAVOutVoiceEngine : boo::BaseAudioVoiceEngine return false; } - boo::ReceiveFunctor* m_midiReceiver = nullptr; + ReceiveFunctor* m_midiReceiver = nullptr; - struct MIDIIn : public boo::IMIDIIn + struct MIDIIn : public IMIDIIn { - MIDIIn(WAVOutVoiceEngine* parent, bool virt, boo::ReceiveFunctor&& receiver) + MIDIIn(WAVOutVoiceEngine* parent, bool virt, ReceiveFunctor&& receiver) : IMIDIIn(parent, virt, std::move(receiver)) {} std::string description() const @@ -51,34 +54,34 @@ struct WAVOutVoiceEngine : boo::BaseAudioVoiceEngine } }; - std::unique_ptr newVirtualMIDIIn(boo::ReceiveFunctor&& receiver) + std::unique_ptr newVirtualMIDIIn(ReceiveFunctor&& receiver) { - std::unique_ptr ret = std::make_unique(nullptr, true, std::move(receiver)); + std::unique_ptr ret = std::make_unique(nullptr, true, std::move(receiver)); m_midiReceiver = &ret->m_receiver; return ret; } - std::unique_ptr newVirtualMIDIOut() + std::unique_ptr newVirtualMIDIOut() { return {}; } - std::unique_ptr newVirtualMIDIInOut(boo::ReceiveFunctor&& receiver) + std::unique_ptr newVirtualMIDIInOut(ReceiveFunctor&& receiver) { return {}; } - std::unique_ptr newRealMIDIIn(const char* name, boo::ReceiveFunctor&& receiver) + std::unique_ptr newRealMIDIIn(const char* name, ReceiveFunctor&& receiver) { return {}; } - std::unique_ptr newRealMIDIOut(const char* name) + std::unique_ptr newRealMIDIOut(const char* name) { return {}; } - std::unique_ptr newRealMIDIInOut(const char* name, boo::ReceiveFunctor&& receiver) + std::unique_ptr newRealMIDIInOut(const char* name, ReceiveFunctor&& receiver) { return {}; } @@ -88,32 +91,120 @@ struct WAVOutVoiceEngine : boo::BaseAudioVoiceEngine FILE* m_fp = nullptr; size_t m_bytesWritten = 0; - void prepareWAV(double sampleRate) + void prepareWAV(double sampleRate, int numChans) { - fwrite("RIFF", 1, 4, m_fp); - uint32_t dataSize = 0; - uint32_t chunkSize = 36 + dataSize; - fwrite(&chunkSize, 1, 4, m_fp); - fwrite("WAVE", 1, 4, m_fp); + uint32_t speakerMask = 0; - fwrite("fmt ", 1, 4, m_fp); - uint32_t sixteen = 16; - fwrite(&sixteen, 1, 4, m_fp); - uint16_t audioFmt = 3; - fwrite(&audioFmt, 1, 2, m_fp); - uint16_t chCount = 2; - fwrite(&chCount, 1, 2, m_fp); - uint32_t sampRate = sampleRate; - fwrite(&sampRate, 1, 4, m_fp); - uint16_t blockAlign = 8; - uint32_t byteRate = sampRate * blockAlign; - fwrite(&byteRate, 1, 4, m_fp); - fwrite(&blockAlign, 1, 2, m_fp); - uint16_t bps = 32; - fwrite(&bps, 1, 2, m_fp); + switch (numChans) + { + default: + case 2: + numChans = 2; + m_mixInfo.m_channels = AudioChannelSet::Stereo; + m_mixInfo.m_channelMap.m_channelCount = 2; + m_mixInfo.m_channelMap.m_channels[0] = AudioChannel::FrontLeft; + m_mixInfo.m_channelMap.m_channels[1] = AudioChannel::FrontRight; + speakerMask = 0x00000001 | 0x00000002; + break; + case 4: + numChans = 4; + m_mixInfo.m_channels = AudioChannelSet::Quad; + m_mixInfo.m_channelMap.m_channelCount = 4; + m_mixInfo.m_channelMap.m_channels[0] = AudioChannel::FrontLeft; + m_mixInfo.m_channelMap.m_channels[1] = AudioChannel::FrontRight; + m_mixInfo.m_channelMap.m_channels[2] = AudioChannel::RearLeft; + m_mixInfo.m_channelMap.m_channels[3] = AudioChannel::RearRight; + speakerMask = 0x00000001 | 0x00000002 | 0x00000010 | 0x00000020; + break; + case 6: + numChans = 6; + m_mixInfo.m_channels = AudioChannelSet::Surround51; + m_mixInfo.m_channelMap.m_channelCount = 6; + m_mixInfo.m_channelMap.m_channels[0] = AudioChannel::FrontLeft; + m_mixInfo.m_channelMap.m_channels[1] = AudioChannel::FrontRight; + m_mixInfo.m_channelMap.m_channels[2] = AudioChannel::FrontCenter; + m_mixInfo.m_channelMap.m_channels[3] = AudioChannel::LFE; + m_mixInfo.m_channelMap.m_channels[4] = AudioChannel::RearLeft; + m_mixInfo.m_channelMap.m_channels[5] = AudioChannel::RearRight; + speakerMask = 0x00000001 | 0x00000002 | 0x00000004 | 0x00000008 | 0x00000010 | 0x00000020; + break; + case 8: + numChans = 8; + m_mixInfo.m_channels = AudioChannelSet::Surround71; + m_mixInfo.m_channelMap.m_channelCount = 8; + m_mixInfo.m_channelMap.m_channels[0] = AudioChannel::FrontLeft; + m_mixInfo.m_channelMap.m_channels[1] = AudioChannel::FrontRight; + m_mixInfo.m_channelMap.m_channels[2] = AudioChannel::FrontCenter; + m_mixInfo.m_channelMap.m_channels[3] = AudioChannel::LFE; + m_mixInfo.m_channelMap.m_channels[4] = AudioChannel::RearLeft; + m_mixInfo.m_channelMap.m_channels[5] = AudioChannel::RearRight; + m_mixInfo.m_channelMap.m_channels[6] = AudioChannel::SideLeft; + m_mixInfo.m_channelMap.m_channels[7] = AudioChannel::SideRight; + speakerMask = 0x00000001 | 0x00000002 | 0x00000004 | 0x00000008 | 0x00000010 | 0x00000020 | 0x00000200 | 0x00000400; + break; + } - fwrite("data", 1, 4, m_fp); - fwrite(&dataSize, 1, 4, m_fp); + if (numChans == 2) + { + fwrite("RIFF", 1, 4, m_fp); + uint32_t dataSize = 0; + uint32_t chunkSize = 36 + dataSize; + fwrite(&chunkSize, 1, 4, m_fp); + + fwrite("WAVE", 1, 4, m_fp); + + fwrite("fmt ", 1, 4, m_fp); + uint32_t sixteen = 16; + fwrite(&sixteen, 1, 4, m_fp); + uint16_t audioFmt = 3; + fwrite(&audioFmt, 1, 2, m_fp); + uint16_t chCount = numChans; + fwrite(&chCount, 1, 2, m_fp); + uint32_t sampRate = sampleRate; + fwrite(&sampRate, 1, 4, m_fp); + uint16_t blockAlign = 4 * numChans; + uint32_t byteRate = sampRate * blockAlign; + fwrite(&byteRate, 1, 4, m_fp); + fwrite(&blockAlign, 1, 2, m_fp); + uint16_t bps = 32; + fwrite(&bps, 1, 2, m_fp); + + fwrite("data", 1, 4, m_fp); + fwrite(&dataSize, 1, 4, m_fp); + } + else + { + fwrite("RIFF", 1, 4, m_fp); + uint32_t dataSize = 0; + uint32_t chunkSize = 60 + dataSize; + fwrite(&chunkSize, 1, 4, m_fp); + + fwrite("WAVE", 1, 4, m_fp); + + fwrite("fmt ", 1, 4, m_fp); + uint32_t forty = 40; + fwrite(&forty, 1, 4, m_fp); + uint16_t audioFmt = 0xFFFE; + fwrite(&audioFmt, 1, 2, m_fp); + uint16_t chCount = numChans; + fwrite(&chCount, 1, 2, m_fp); + uint32_t sampRate = sampleRate; + fwrite(&sampRate, 1, 4, m_fp); + uint16_t blockAlign = 4 * numChans; + uint32_t byteRate = sampRate * blockAlign; + fwrite(&byteRate, 1, 4, m_fp); + fwrite(&blockAlign, 1, 2, m_fp); + uint16_t bps = 32; + fwrite(&bps, 1, 2, m_fp); + uint16_t extSize = 22; + fwrite(&extSize, 1, 2, m_fp); + fwrite(&bps, 1, 2, m_fp); + fwrite(&speakerMask, 1, 4, m_fp); + fwrite("\x03\x00\x00\x00\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71", 1, 16, m_fp); + + fwrite("data", 1, 4, m_fp); + fwrite(&dataSize, 1, 4, m_fp); + } m_mixInfo.m_periodFrames = 512; m_mixInfo.m_sampleRate = sampleRate; @@ -122,21 +213,21 @@ struct WAVOutVoiceEngine : boo::BaseAudioVoiceEngine _buildAudioRenderClient(); } - WAVOutVoiceEngine(const char* path, double sampleRate) + WAVOutVoiceEngine(const char* path, double sampleRate, int numChans) { m_fp = fopen(path, "wb"); if (!m_fp) return; - prepareWAV(sampleRate); + prepareWAV(sampleRate, numChans); } #if _WIN32 - WAVOutVoiceEngine(const wchar_t* path, double sampleRate) + WAVOutVoiceEngine(const wchar_t* path, double sampleRate, int numChans) { m_fp = _wfopen(path, L"wb"); if (!m_fp) return; - prepareWAV(sampleRate); + prepareWAV(sampleRate, numChans); } #endif @@ -144,12 +235,24 @@ struct WAVOutVoiceEngine : boo::BaseAudioVoiceEngine { uint32_t dataSize = m_bytesWritten; - fseek(m_fp, 4, SEEK_SET); - uint32_t chunkSize = 36 + dataSize; - fwrite(&chunkSize, 1, 4, m_fp); + if (m_mixInfo.m_channelMap.m_channelCount == 2) + { + fseek(m_fp, 4, SEEK_SET); + uint32_t chunkSize = 36 + dataSize; + fwrite(&chunkSize, 1, 4, m_fp); - fseek(m_fp, 40, SEEK_SET); - fwrite(&dataSize, 1, 4, m_fp); + fseek(m_fp, 40, SEEK_SET); + fwrite(&dataSize, 1, 4, m_fp); + } + else + { + fseek(m_fp, 4, SEEK_SET); + uint32_t chunkSize = 60 + dataSize; + fwrite(&chunkSize, 1, 4, m_fp); + + fseek(m_fp, 64, SEEK_SET); + fwrite(&dataSize, 1, 4, m_fp); + } fclose(m_fp); } @@ -161,19 +264,8 @@ struct WAVOutVoiceEngine : boo::BaseAudioVoiceEngine void _buildAudioRenderClient() { - m_mixInfo.m_channels = _getAvailableSet(); - unsigned chCount = ChannelCount(m_mixInfo.m_channels); - m_5msFrames = m_mixInfo.m_sampleRate * 5 / 1000; - m_interleavedBuf.resize(2 * m_5msFrames); - - boo::ChannelMap& chMapOut = m_mixInfo.m_channelMap; - chMapOut.m_channelCount = 2; - chMapOut.m_channels[0] = boo::AudioChannel::FrontLeft; - chMapOut.m_channels[1] = boo::AudioChannel::FrontRight; - - while (chMapOut.m_channelCount < chCount) - chMapOut.m_channels[chMapOut.m_channelCount++] = boo::AudioChannel::Unknown; + m_interleavedBuf.resize(m_mixInfo.m_channelMap.m_channelCount * m_5msFrames); } void _rebuildAudioRenderClient(double sampleRate, size_t periodFrames) @@ -186,27 +278,25 @@ struct WAVOutVoiceEngine : boo::BaseAudioVoiceEngine void pumpAndMixVoices() { + size_t frameSz = 4 * m_mixInfo.m_channelMap.m_channelCount; _pumpAndMixVoices(m_5msFrames, m_interleavedBuf.data()); - fwrite(m_interleavedBuf.data(), 1, m_5msFrames * 8, m_fp); - m_bytesWritten += m_5msFrames * 8; + fwrite(m_interleavedBuf.data(), 1, m_5msFrames * frameSz, m_fp); + m_bytesWritten += m_5msFrames * frameSz; } }; -namespace boo +std::unique_ptr NewWAVAudioVoiceEngine(const char* path, double sampleRate, int numChans) { - -std::unique_ptr NewWAVAudioVoiceEngine(const char* path, double sampleRate) -{ - std::unique_ptr ret = std::make_unique(path, sampleRate); + std::unique_ptr ret = std::make_unique(path, sampleRate, numChans); if (!static_cast(*ret).m_fp) return {}; return ret; } #if _WIN32 -std::unique_ptr NewWAVAudioVoiceEngine(const wchar_t* path, double sampleRate) +std::unique_ptr NewWAVAudioVoiceEngine(const wchar_t* path, double sampleRate, int numChans) { - std::unique_ptr ret = std::make_unique(path, sampleRate); + std::unique_ptr ret = std::make_unique(path, sampleRate, numChans); if (!static_cast(*ret).m_fp) return {}; return ret;