Introduce extremely simplistic audio capturing mechanism
This commit is contained in:
parent
c5e4c2989b
commit
e1dbbf8165
|
@ -0,0 +1,149 @@
|
|||
#include "AudioCapture.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmdeviceapi.h>
|
||||
#include <audioclient.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <godot_cpp/variant/utility_functions.hpp>
|
||||
|
||||
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<AudioStreamGeneratorPlayback> playback)
|
||||
{
|
||||
m_audioPlayback = playback;
|
||||
|
||||
const auto hr = m_audioClient->Start();
|
||||
if (FAILED(hr)) {
|
||||
UtilityFunctions::printerr("Start failed:", hr);
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmdeviceapi.h>
|
||||
#include <audioclient.h>
|
||||
|
||||
#include <godot_cpp/classes/node.hpp>
|
||||
#include <godot_cpp/core/class_db.hpp>
|
||||
#include <godot_cpp/classes/audio_stream_generator.hpp>
|
||||
#include <godot_cpp/classes/audio_stream_generator_playback.hpp>
|
||||
|
||||
namespace godot {
|
||||
|
||||
class AudioCapture : public Node
|
||||
{
|
||||
GDCLASS(AudioCapture, Node);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
AudioCapture();
|
||||
|
||||
Array getFrame() const;
|
||||
|
||||
void process();
|
||||
void start(Ref<AudioStreamGeneratorPlayback> playback);
|
||||
|
||||
private:
|
||||
IAudioClient* m_audioClient = nullptr;
|
||||
IAudioCaptureClient* m_captureClient = nullptr;
|
||||
|
||||
Ref<AudioStreamGenerator> m_audioStream;
|
||||
Ref<AudioStreamGeneratorPlayback> m_audioPlayback;
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -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<Opus>();
|
||||
ClassDB::register_class<AudioProcessor>();
|
||||
ClassDB::register_class<AudioCapture>();
|
||||
ClassDB::register_class<Obs>();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue