From 874d11077403dbabca28166e0e5df05403d43ec4 Mon Sep 17 00:00:00 2001 From: jiegeaiai Date: Sun, 29 Sep 2024 00:32:56 +0800 Subject: [PATCH] add first --- AudioRender.sln | 31 +++++ AudioRender/AudioRender.cpp | 27 +++++ AudioRender/AudioRender.h | 21 ++++ AudioRender/AudioRender.vcxproj | 150 ++++++++++++++++++++++++ AudioRender/AudioRender.vcxproj.filters | 54 +++++++++ AudioRender/AudioRender.vcxproj.user | 4 + AudioRender/AudioRenderStd.cpp | 7 ++ AudioRender/AudioRenderStd.h | 12 ++ AudioRender/IAudioRender.h | 30 +++++ AudioRender/Windows/CoreAudioRender.cpp | 1 + AudioRender/Windows/CoreAudioRender.h | 7 ++ AudioRender/Windows/SectionLock.cpp | 22 ++++ AudioRender/Windows/SectionLock.h | 35 ++++++ AudioRender/Windows/WavAudioRender.cpp | 144 +++++++++++++++++++++++ AudioRender/Windows/WavAudioRender.h | 31 +++++ 15 files changed, 576 insertions(+) create mode 100644 AudioRender.sln create mode 100644 AudioRender/AudioRender.cpp create mode 100644 AudioRender/AudioRender.h create mode 100644 AudioRender/AudioRender.vcxproj create mode 100644 AudioRender/AudioRender.vcxproj.filters create mode 100644 AudioRender/AudioRender.vcxproj.user create mode 100644 AudioRender/AudioRenderStd.cpp create mode 100644 AudioRender/AudioRenderStd.h create mode 100644 AudioRender/IAudioRender.h create mode 100644 AudioRender/Windows/CoreAudioRender.cpp create mode 100644 AudioRender/Windows/CoreAudioRender.h create mode 100644 AudioRender/Windows/SectionLock.cpp create mode 100644 AudioRender/Windows/SectionLock.h create mode 100644 AudioRender/Windows/WavAudioRender.cpp create mode 100644 AudioRender/Windows/WavAudioRender.h diff --git a/AudioRender.sln b/AudioRender.sln new file mode 100644 index 0000000..db98028 --- /dev/null +++ b/AudioRender.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35303.130 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AudioRender", "AudioRender\AudioRender.vcxproj", "{095FA653-16F9-4AAE-87E9-3E467A5E8564}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {095FA653-16F9-4AAE-87E9-3E467A5E8564}.Debug|x64.ActiveCfg = Debug|x64 + {095FA653-16F9-4AAE-87E9-3E467A5E8564}.Debug|x64.Build.0 = Debug|x64 + {095FA653-16F9-4AAE-87E9-3E467A5E8564}.Debug|x86.ActiveCfg = Debug|Win32 + {095FA653-16F9-4AAE-87E9-3E467A5E8564}.Debug|x86.Build.0 = Debug|Win32 + {095FA653-16F9-4AAE-87E9-3E467A5E8564}.Release|x64.ActiveCfg = Release|x64 + {095FA653-16F9-4AAE-87E9-3E467A5E8564}.Release|x64.Build.0 = Release|x64 + {095FA653-16F9-4AAE-87E9-3E467A5E8564}.Release|x86.ActiveCfg = Release|Win32 + {095FA653-16F9-4AAE-87E9-3E467A5E8564}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {50A43890-42B5-46B3-AF83-E4977344D4B1} + EndGlobalSection +EndGlobal diff --git a/AudioRender/AudioRender.cpp b/AudioRender/AudioRender.cpp new file mode 100644 index 0000000..f2b4da9 --- /dev/null +++ b/AudioRender/AudioRender.cpp @@ -0,0 +1,27 @@ +#include "AudioRender.h" + +#include +#include + +#include "IAudioRender.h" + +static std::unique_ptr audiosRender_; +bool __stdcall Initialize(const char* sender_name, const char* receiver_name) { + assert(!audiosRender_); + audiosRender_.reset(IAudioRender::Create()); + return true; +} + +bool __stdcall Write(const unsigned char* data, unsigned int len) { + assert(audiosRender_); + + std::vector audio(len, 0); + memcpy(&audio[0], data, len); + AudioFrame frame = { audio, 0 }; + return audiosRender_->Write(frame); +} + +void __stdcall Uninitialize() { + assert(audiosRender_); + audiosRender_.reset(); +} diff --git a/AudioRender/AudioRender.h b/AudioRender/AudioRender.h new file mode 100644 index 0000000..8cdcacd --- /dev/null +++ b/AudioRender/AudioRender.h @@ -0,0 +1,21 @@ +#pragma once + +#if defined(WIN32) +#ifdef AUDIO_RENDER_LIB +#define AUDIO_RENDER_EXPORT __declspec( dllexport ) +#else +#define AUDIO_RENDER_EXPORT __declspec( dllimport ) +#endif +#endif // WIN32 + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + AUDIO_RENDER_EXPORT bool __stdcall Initialize(const char* sender_name, const char* receiver_name); + AUDIO_RENDER_EXPORT bool __stdcall Write(const unsigned char* data, unsigned int len); + AUDIO_RENDER_EXPORT void __stdcall Uninitialize(); + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/AudioRender/AudioRender.vcxproj b/AudioRender/AudioRender.vcxproj new file mode 100644 index 0000000..a1a1d84 --- /dev/null +++ b/AudioRender/AudioRender.vcxproj @@ -0,0 +1,150 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {095fa653-16f9-4aae-87e9-3e467a5e8564} + AudioRender + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + DynamicLibrary + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;WIN32;AUDIO_RENDER_LIB;%(PreprocessorDefinitions) + true + D:\Project\AudioRender\AudioRender;%(AdditionalIncludeDirectories) + + + Console + true + Winmm.lib;%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;WIN32;AUDIO_RENDER_LIB;%(PreprocessorDefinitions) + true + + + Console + true + true + true + Winmm.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AudioRender/AudioRender.vcxproj.filters b/AudioRender/AudioRender.vcxproj.filters new file mode 100644 index 0000000..616cc85 --- /dev/null +++ b/AudioRender/AudioRender.vcxproj.filters @@ -0,0 +1,54 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/AudioRender/AudioRender.vcxproj.user b/AudioRender/AudioRender.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/AudioRender/AudioRender.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/AudioRender/AudioRenderStd.cpp b/AudioRender/AudioRenderStd.cpp new file mode 100644 index 0000000..575ae75 --- /dev/null +++ b/AudioRender/AudioRenderStd.cpp @@ -0,0 +1,7 @@ +#include "AudioRenderStd.h" + +#include "Windows/WavAudioRender.h" + +IAudioRender* IAudioRender::Create() { + return new WavAudioRender; +} \ No newline at end of file diff --git a/AudioRender/AudioRenderStd.h b/AudioRender/AudioRenderStd.h new file mode 100644 index 0000000..aea7328 --- /dev/null +++ b/AudioRender/AudioRenderStd.h @@ -0,0 +1,12 @@ +#pragma once + +#include "IAudioRender.h" + +class AudioRenderStd : public IAudioRender { +public: + ~AudioRenderStd() override = default; + uint64 GetClock() override { + return 0; + } +}; + diff --git a/AudioRender/IAudioRender.h b/AudioRender/IAudioRender.h new file mode 100644 index 0000000..30b126c --- /dev/null +++ b/AudioRender/IAudioRender.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +using int8 = char; +using uint8 = unsigned char; +using int16 = short; +using uint16 = unsigned short; +using int32 = int; +using uint32 = unsigned int; +using int64 = long long; +using uint64 = unsigned long long; + + +struct AudioFrame { + std::vector data_; + uint64 dts_{ 0 }; +}; + + +class IAudioRender { +public: + static IAudioRender* Create(); + +public: + virtual ~IAudioRender() = default; + virtual uint64 GetClock() = 0; + virtual bool Write(const AudioFrame& audioFrame) = 0; + +}; diff --git a/AudioRender/Windows/CoreAudioRender.cpp b/AudioRender/Windows/CoreAudioRender.cpp new file mode 100644 index 0000000..1c6ef2d --- /dev/null +++ b/AudioRender/Windows/CoreAudioRender.cpp @@ -0,0 +1 @@ +#include "CoreAudioRender.h" diff --git a/AudioRender/Windows/CoreAudioRender.h b/AudioRender/Windows/CoreAudioRender.h new file mode 100644 index 0000000..1e60dc7 --- /dev/null +++ b/AudioRender/Windows/CoreAudioRender.h @@ -0,0 +1,7 @@ +#pragma once + +#include "IAudioRender.h" + +class CoreAudioRender : public IAudioRender { +}; + diff --git a/AudioRender/Windows/SectionLock.cpp b/AudioRender/Windows/SectionLock.cpp new file mode 100644 index 0000000..da9304f --- /dev/null +++ b/AudioRender/Windows/SectionLock.cpp @@ -0,0 +1,22 @@ +#include "Windows/SectionLock.h" + +SectionLock::SectionLock() noexcept { + InitializeCriticalSection(§ion_); +} + +SectionLock::~SectionLock() { + DeleteCriticalSection(§ion_); +} + +void SectionLock::Lock() { + EnterCriticalSection(§ion_); +} + +bool SectionLock::TryLock() { + bool success = TryEnterCriticalSection(§ion_); + return success; +} + +void SectionLock::UnLock() { + LeaveCriticalSection(§ion_); +} diff --git a/AudioRender/Windows/SectionLock.h b/AudioRender/Windows/SectionLock.h new file mode 100644 index 0000000..1b702ca --- /dev/null +++ b/AudioRender/Windows/SectionLock.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +class SectionLock { +public: + explicit SectionLock() noexcept; + explicit SectionLock(const SectionLock& ) = delete; + SectionLock& operator= (const SectionLock&) = delete; + ~SectionLock(); + + void Lock(); + bool TryLock(); + void UnLock(); + +private: + CRITICAL_SECTION section_; +}; + +template +class Locker { +public: + explicit Locker(Lock& lock) noexcept : lock_(lock) { + lock_.Lock(); + } + explicit Locker(const Locker&) = delete; + Locker& operator= (const Locker&) = delete; + ~Locker() { + lock_.UnLock(); + } + +private: + Lock& lock_; +}; + diff --git a/AudioRender/Windows/WavAudioRender.cpp b/AudioRender/Windows/WavAudioRender.cpp new file mode 100644 index 0000000..d5c5239 --- /dev/null +++ b/AudioRender/Windows/WavAudioRender.cpp @@ -0,0 +1,144 @@ +#include "Windows/WavAudioRender.h" + +#include +#include + +static WAVEHDR* AllocateBlocks(uint32 size, int32 count) { + uint32 totalBufferSize = (size + sizeof(WAVEHDR)) * count; + + uint8* buffer{ nullptr }; + if ((buffer = reinterpret_cast(HeapAlloc( + GetProcessHeap(), + HEAP_ZERO_MEMORY, + totalBufferSize + ))) == NULL) { + ExitProcess(1); + } + + WAVEHDR* blocks = reinterpret_cast(buffer); + buffer += sizeof(WAVEHDR) * count; + for (int32 index = 0; index < count; ++index) { + blocks[index].dwBufferLength = size; + blocks[index].lpData = (LPSTR)buffer; + blocks[index].dwFlags = 0; + buffer += size; + } + return blocks; +} + +static void FreeBlocks(WAVEHDR* blockArray) { + HeapFree(GetProcessHeap(), 0, blockArray); +} + +WavAudioRender::WavAudioRender() noexcept { + HWAVEOUT hWaveOut = nullptr; + + waveBlocks_ = AllocateBlocks(BlockSize_, BlockCount_); + + constexpr int32 rate = 16000; + constexpr int32 channel = 1; + constexpr int32 bitePerSample = 16; + + WAVEFORMATEX waveform; + waveform.wFormatTag = WAVE_FORMAT_PCM; + waveform.nChannels = channel; + waveform.nSamplesPerSec = rate; + waveform.nAvgBytesPerSec = rate * channel * 2; + waveform.nBlockAlign = channel * 2; + waveform.wBitsPerSample = bitePerSample; + waveform.cbSize = 0; + + + MMRESULT hr = waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveform, + reinterpret_cast(WavAudioRender::waveOutProc), + reinterpret_cast(this), CALLBACK_FUNCTION); + if (MMSYSERR_NOERROR == hr) { + return; + } + + hWavout_ = std::move(hWaveOut); +} + +WavAudioRender::~WavAudioRender() { + if (nullptr == hWavout_) { + return; + } + waveOutReset(hWavout_); + for (int i = 0; i < BlockCount_; i++) { + if (waveBlocks_[i].dwFlags & WHDR_PREPARED) { + waveOutUnprepareHeader(hWavout_, &waveBlocks_[i], sizeof(WAVEHDR)); + } + } + + do { + MMRESULT ret = waveOutClose(hWavout_); + if (MMSYSERR_NOERROR == ret) { + break; + } else if (WAVERR_STILLPLAYING == ret) { + ::Sleep(1); + } else if (MMSYSERR_NOMEM == ret) { + break; + } else { + break; + } + } while (true); + FreeBlocks(waveBlocks_); +} + +bool WavAudioRender::Write(const AudioFrame& audioFrame) { + if (nullptr == hWavout_) { + return false; + } + + WAVEHDR* current; + int32 remain; + current = &waveBlocks_[waveCurrentBlock_]; + uint64 size = audioFrame.data_.size(); + const uint8* data = audioFrame.data_.data(); + while (size > 0) { + if (current->dwFlags & WHDR_PREPARED) { + waveOutUnprepareHeader(hWavout_, current, sizeof(WAVEHDR)); + } + if (size < (int)(BlockSize_ - current->dwUser)) { + memcpy(current->lpData + current->dwUser, data, size); + current->dwUser += size; + break; + } + remain = BlockSize_ - static_cast(current->dwUser); + memcpy(current->lpData + current->dwUser, data, remain); + size -= remain; + data += remain; + current->dwBufferLength = BlockSize_; + waveOutPrepareHeader(hWavout_, current, sizeof(WAVEHDR)); + waveOutWrite(hWavout_, current, sizeof(WAVEHDR)); + { + Locker locker(lock_); + --freeBlockCounter_; + } + + while (!freeBlockCounter_) { + Sleep(1); + } + + ++waveCurrentBlock_; + waveCurrentBlock_ %= BlockCount_; + current = &waveBlocks_[waveCurrentBlock_]; + current->dwUser = 0; + } + return true; +} + +void CALLBACK WavAudioRender::waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, + DWORD_PTR dwParam1, DWORD_PTR dwParam2) { + WavAudioRender* self = reinterpret_cast(dwInstance); + + if (uMsg != WOM_DONE) + return; + + self->FreeBlock(); +} + +void WavAudioRender::FreeBlock() { + Locker locker(lock_); + ++freeBlockCounter_; +} diff --git a/AudioRender/Windows/WavAudioRender.h b/AudioRender/Windows/WavAudioRender.h new file mode 100644 index 0000000..5c48dec --- /dev/null +++ b/AudioRender/Windows/WavAudioRender.h @@ -0,0 +1,31 @@ +#pragma once + +#include "AudioRenderStd.h" + +#include "Windows/SectionLock.h" + +class WavAudioRender : public AudioRenderStd { +public: + explicit WavAudioRender() noexcept; + ~WavAudioRender() override; + + bool Write(const AudioFrame& audioFrame) override; + +private: + static void CALLBACK waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, + DWORD_PTR dwParam1, DWORD_PTR dwParam2); + void FreeBlock(); + +private: + int64_t audioPts_{ 0 }; + + static constexpr int BlockSize_{ 6400 }; + static constexpr int BlockCount_{ 10 }; + + SectionLock lock_; + uint32 freeBlockCounter_{ BlockCount_ }; + uint32 waveCurrentBlock_{ 0 }; + + WAVEHDR* waveBlocks_{ nullptr }; + HWAVEOUT hWavout_{ nullptr }; +};