From e1dbbf816561886ca6b1cb22fd2ad916ad6292ef Mon Sep 17 00:00:00 2001 From: weil Date: Sun, 14 Jul 2024 06:22:52 +0200 Subject: [PATCH] Introduce extremely simplistic audio capturing mechanism --- src/AudioCapture.cpp | 149 +++++++++++++++++++++++++++++++++++++++++ src/AudioCapture.h | 39 +++++++++++ src/register_types.cpp | 2 + 3 files changed, 190 insertions(+) create mode 100644 src/AudioCapture.cpp create mode 100644 src/AudioCapture.h diff --git a/src/AudioCapture.cpp b/src/AudioCapture.cpp new file mode 100644 index 0000000..c79459c --- /dev/null +++ b/src/AudioCapture.cpp @@ -0,0 +1,149 @@ +#include "AudioCapture.h" + +#include +#include +#include +#include + +#include + +namespace godot { + + +void AudioCapture::_bind_methods() +{ + ClassDB::bind_method(D_METHOD("process"), &AudioCapture::process); + ClassDB::bind_method(D_METHOD("start"), &AudioCapture::start); + ClassDB::bind_method(D_METHOD("get_frame"), &AudioCapture::getFrame); +} + +AudioCapture::AudioCapture() +{ + UtilityFunctions::print("BEDZIE RUCHANES"); + + HRESULT hr; + IMMDeviceEnumerator* pEnumerator = nullptr; + IMMDevice* pDevice = nullptr; + + hr = CoInitialize(nullptr); + if (FAILED(hr)) { + UtilityFunctions::printerr("CoInitialize failed:", hr); + return; + } + + hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, IID_PPV_ARGS(&pEnumerator)); + if (FAILED(hr)) { + UtilityFunctions::printerr("CoCreateInstance failed:", hr); + return; + } + + hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice); + if (FAILED(hr)) { + UtilityFunctions::printerr("GetDefaultAudioEndpoint failed:", hr); + return; + } + + hr = pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, (void**)&m_audioClient); + if (FAILED(hr)) { + UtilityFunctions::printerr("Device activate failed:", hr); + return; + } + + WAVEFORMATEX* pwfx{nullptr}; + hr = m_audioClient->GetMixFormat(&pwfx); + if (FAILED(hr)) { + UtilityFunctions::printerr("GetMixFormat failed:", hr); + return; + } + + hr = m_audioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, 10000000, 0, pwfx, nullptr); + if (FAILED(hr)) { + UtilityFunctions::printerr("Device initialize failed:", hr); + return; + }; + + hr = m_audioClient->GetService(IID_PPV_ARGS(&m_captureClient)); + if (FAILED(hr)) { + UtilityFunctions::printerr("Get service failed:", hr); + return; + } + + UtilityFunctions::print("Gites majonez"); +} + +Array AudioCapture::getFrame() const +{ + // TODO: common pre-resized buffer? + Array out{}; + + UINT32 len = 0; + UINT32 frames = 0; + DWORD flags = 0; + + m_captureClient->GetNextPacketSize(&len); + + if (len > 0) { + BYTE* pData = nullptr; + m_captureClient->GetBuffer(&pData, &frames, &flags, NULL, NULL); + + if (pData && frames > 0) { + float* pfData = (float*)pData; + for (UINT32 i = 0; i < frames; ++i) { + float leftChannel = pfData[i * 2]; + float rightChannel = pfData[i * 2 + 1]; + out.push_back(Vector2(leftChannel, rightChannel)); + } + } + else { + //out.push_back(Vector2(0, 0)); + } + + m_captureClient->ReleaseBuffer(frames); + m_captureClient->GetNextPacketSize(&len); + } + + return out; +} + +void AudioCapture::process() +{ + UINT32 len = 0; + UINT32 frames = 0; + DWORD flags = 0; + + m_captureClient->GetNextPacketSize(&len); + + while (len > 0) { + BYTE* pData = nullptr; + m_captureClient->GetBuffer(&pData, &frames, &flags, NULL, NULL); + + if (pData && frames > 0) { + float* pfData = (float*)pData; + for (UINT32 i = 0; i < frames; ++i) { + float leftChannel = pfData[i * 2]; + float rightChannel = pfData[i * 2 + 1]; + m_audioPlayback->push_frame(Vector2(leftChannel, rightChannel)); + } + } + else { + m_audioPlayback->push_frame(Vector2(0, 0)); + } + + m_captureClient->ReleaseBuffer(frames); + m_captureClient->GetNextPacketSize(&len); + } + +} + +void AudioCapture::start(Ref playback) +{ + m_audioPlayback = playback; + + const auto hr = m_audioClient->Start(); + if (FAILED(hr)) { + UtilityFunctions::printerr("Start failed:", hr); + return; + }; +} + +} \ No newline at end of file diff --git a/src/AudioCapture.h b/src/AudioCapture.h new file mode 100644 index 0000000..940f589 --- /dev/null +++ b/src/AudioCapture.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +namespace godot { + +class AudioCapture : public Node +{ + GDCLASS(AudioCapture, Node); + +protected: + static void _bind_methods(); + +public: + AudioCapture(); + + Array getFrame() const; + + void process(); + void start(Ref playback); + +private: + IAudioClient* m_audioClient = nullptr; + IAudioCaptureClient* m_captureClient = nullptr; + + Ref m_audioStream; + Ref m_audioPlayback; + + +}; + +} diff --git a/src/register_types.cpp b/src/register_types.cpp index 4079c7f..a278c6d 100644 --- a/src/register_types.cpp +++ b/src/register_types.cpp @@ -13,6 +13,7 @@ #include "GodotOpus.h" #include "AudioProcessor.h" +#include "AudioCapture.h" #include "GodotObs.h" using namespace godot; @@ -25,6 +26,7 @@ void gdextension_initialize(ModuleInitializationLevel p_level) { ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); } }