- Liittynyt
- 17.10.2016
- Viestejä
- 486
Moro! Olisi kysymys ja en oikein tietä miten sen tähän taiteilisi, että se olisi mahdollisimman selvä. Olen saanut äänikaappauksen toimimaan eräässä Unreal Engine projektissa ja ääni olisi tarkoitus lähettää verkon ylitse. Tällä hetkellä ääni toimii hyvin Push-To-Talkissa sekä myös jatkuvassa puheessa, mutta ongelma on se, että jatkuvan puheen ollessa päällä, ääni vie turhaa kaistaa, koska verkon yli lähtee jatkuvasti taulukko, joka on täynnä tavuja.
Pääluokat ovat seuraavat. Windowssin oma legacy kirjasto, jolla windowssin äänikaappausta on tehty. .CPP koodi
Toinen luokka, joka ottaa äänen vastaan ja lähettää sen pelimoottorissa olevalle äänikomponentille:
Olen yrittänyt saada selville puhutaanko mikrofoniin katselemalla AudioData taulukkoa. AudioData taulukko lähetetään eteenpäin kohdassa jossa sanotaan:
Vaikka miten katson niin tuo tavujen taulukko on täysin satunnainen. Vaikka minkäänlaista ääntä ei menisi niin siellä saattaa olla jotain 0 ja usean sadan välistä. Vaikka aiheutan ääntä niin tuo tavujen taulukko ei näytä siitä olevan moksistaan. Kuitenkin tuo nimenomainen taulukko on se, josta pelimoottorissa ääni parsitaan ja toistetaan.
Onko mitään hajua miten noita kahta luokkaa tutkimalla voisi saada selville sen, että kuuleeko mikrofoni ääntä?
Pääluokat ovat seuraavat. Windowssin oma legacy kirjasto, jolla windowssin äänikaappausta on tehty. .CPP koodi
Koodi:
#include "AudioCapturePrivatePCH.h"
#include "LambdaRunnable.h"
#include "FWindowsAudioCapture.h"
#include "AllowWindowsPlatformTypes.h"
#include <iostream>
#include <MMSystem.h>
#include <Windows.h>
using namespace std;
#pragma comment(lib, "winmm.lib")
FWindowsAudioCapture::FWindowsAudioCapture()
{
bRunLoopActive = false;
}
void FWindowsAudioCapture::StartCapture(TFunction<void(const TArray<uint8>&)> OnAudioData /*= nullptr*/, TFunction<void(const TArray<uint8>&)> OnCaptureFinished /*= nullptr*/)
{
//Only attempt to start capture once. If it's active return.
if (bRunLoopActive)
{
return;
}
bRunLoopActive = true;
FThreadSafeBool* bShouldRunPtr = &bRunLoopActive;
FLambdaRunnable::RunLambdaOnBackGroundThread([this, OnAudioData, OnCaptureFinished, bShouldRunPtr]()
{
HWAVEIN hWaveIn;
MMRESULT result;
WAVEFORMATEX pFormat;
pFormat.wFormatTag = WAVE_FORMAT_PCM;
pFormat.nChannels = Options.Channels; //typically 1 or 2
pFormat.nSamplesPerSec = Options.SampleRate;
pFormat.wBitsPerSample = Options.BitsPerSample; // 16 for high quality, 8 for telephone-grade
pFormat.nBlockAlign = pFormat.nChannels * pFormat.wBitsPerSample / 8; // = n.Channels * wBitsPerSample/8
pFormat.nAvgBytesPerSec = Options.SampleRate * pFormat.nBlockAlign; // = nSamplesPerSec * n.Channels * wBitsPerSample/8
pFormat.cbSize = 0;
result = waveInOpen(&hWaveIn, WAVE_MAPPER, &pFormat, 0L, 0L, WAVE_FORMAT_DIRECT);
WAVEHDR hWaveInHdr;
TArray<uint8> AudioBuffer;
AudioBuffer.SetNum(pFormat.nAvgBytesPerSec / 2); //half a second
hWaveInHdr.lpData = (LPSTR)AudioBuffer.GetData();
hWaveInHdr.dwBufferLength = AudioBuffer.Num();
hWaveInHdr.dwBytesRecorded = 0;
hWaveInHdr.dwUser = 0L;
hWaveInHdr.dwFlags = 0L;
hWaveInHdr.dwLoops = 0L;
waveInPrepareHeader(hWaveIn, &hWaveInHdr, sizeof(WAVEHDR));
// Insert a wave input buffer
result = waveInAddBuffer(hWaveIn, &hWaveInHdr, sizeof(WAVEHDR));
result = waveInStart(hWaveIn);
//The headers will now get filled and we should check them periodically for new data
while (*bShouldRunPtr)
{
if (hWaveInHdr.dwFlags & WHDR_DONE)
{
TArray<uint8> OutData;
OutData.SetNum(AudioBuffer.Num());
FMemory::Memcpy(OutData.GetData(), AudioBuffer.GetData(), AudioBuffer.Num());
if (OnAudioData != nullptr)
{
OnAudioData(OutData);
}
//Clear flags
hWaveInHdr.dwFlags = 0;
hWaveInHdr.dwBytesRecorded = 0;
//Re-prep
waveInPrepareHeader(hWaveIn, &hWaveInHdr, sizeof(WAVEHDR));
waveInAddBuffer(hWaveIn, &hWaveInHdr, sizeof(WAVEHDR));
}
}
waveInStop(hWaveIn);
waveInUnprepareHeader(hWaveIn, &hWaveInHdr, sizeof(WAVEHDR));
waveInClose(hWaveIn);
if (OnCaptureFinished != nullptr)
{
//flush whatever is left of the buffer
OnCaptureFinished(AudioBuffer);
}
});
}
void FWindowsAudioCapture::StopCapture()
{
bRunLoopActive = false;
}
void FWindowsAudioCapture::SetOptions(const FAudioCaptureOptions& InOptions)
{
Options = InOptions;
}
#include "HideWindowsPlatformTypes.h"
Toinen luokka, joka ottaa äänen vastaan ja lähettää sen pelimoottorissa olevalle äänikomponentille:
Koodi:
#include "AudioCapturePrivatePCH.h"
#include "ITFAudioCapture.h"
#include "TFAudioCaptureComponent.h"
#include "LambdaRunnable.h"
#include "FWindowsAudioCapture.h"
class FTFAudioCapture : public ITFAudioCapture
{
public:
virtual void StartCapture(TFunction<void(const TArray<uint8>&)> OnAudioData = nullptr, TFunction<void(const TArray<uint8>&)> OnCaptureFinished = nullptr) override;
virtual void StopCapture() override;
virtual void SetOptions(const FAudioCaptureOptions& Options) override;
virtual void AddAudioComponent(const UTFAudioCaptureComponent* Component) override;
virtual void RemoveAudioComponent(const UTFAudioCaptureComponent* Component) override;
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
private:
TSharedPtr<FWindowsAudioCapture> WindowsCapture;
TArray<UTFAudioCaptureComponent*> Components;
};
void FTFAudioCapture::StartupModule()
{
if (!WindowsCapture.IsValid())
{
WindowsCapture = MakeShareable(new FWindowsAudioCapture);
}
}
void FTFAudioCapture::ShutdownModule()
{
}
void FTFAudioCapture::StartCapture(TFunction<void(const TArray<uint8>&)> OnAudioData, TFunction<void(const TArray<uint8>&)> OnCaptureFinished)
{
TFunction<void(const TArray<uint8>&)> OnDataDelegate = [this, OnAudioData] (const TArray<uint8>& AudioData)
{
//Call each added component function inside game thread
FLambdaRunnable::RunShortLambdaOnGameThread([this, AudioData, OnAudioData]
{
for (auto Component : Components)
{
Component->OnAudioData.Broadcast(AudioData);
}
//Also if valid pass it to the new delegate
if (OnAudioData != nullptr)
{
OnAudioData(AudioData);
}
});
};
TFunction<void(const TArray<uint8>&)> OnFinishedDelegate = [this, OnCaptureFinished](const TArray<uint8>& AudioData)
{
//Call each added component function inside game thread
FLambdaRunnable::RunShortLambdaOnGameThread([this, AudioData, OnCaptureFinished]
{
for (auto Component : Components)
{
Component->OnCaptureFinished.Broadcast(AudioData);
}
//Also if valid pass it to the new delegate
if (OnCaptureFinished != nullptr)
{
OnCaptureFinished(AudioData);
}
});
};
WindowsCapture->StartCapture(OnDataDelegate, OnFinishedDelegate);
}
void FTFAudioCapture::StopCapture()
{
if (WindowsCapture.IsValid())
{
WindowsCapture->StopCapture();
}
}
void FTFAudioCapture::SetOptions(const FAudioCaptureOptions& Options)
{
WindowsCapture->SetOptions(Options);
}
void FTFAudioCapture::AddAudioComponent(const UTFAudioCaptureComponent* Component)
{
Components.Add((UTFAudioCaptureComponent*)Component);
}
void FTFAudioCapture::RemoveAudioComponent(const UTFAudioCaptureComponent* Component)
{
Components.Remove((UTFAudioCaptureComponent*)Component);
}
IMPLEMENT_MODULE(FTFAudioCapture, TFAudioCapture)
Olen yrittänyt saada selville puhutaanko mikrofoniin katselemalla AudioData taulukkoa. AudioData taulukko lähetetään eteenpäin kohdassa jossa sanotaan:
Koodi:
for (auto Component : Components)
{
Component->OnCaptureFinished.Broadcast(AudioData);
}
Vaikka miten katson niin tuo tavujen taulukko on täysin satunnainen. Vaikka minkäänlaista ääntä ei menisi niin siellä saattaa olla jotain 0 ja usean sadan välistä. Vaikka aiheutan ääntä niin tuo tavujen taulukko ei näytä siitä olevan moksistaan. Kuitenkin tuo nimenomainen taulukko on se, josta pelimoottorissa ääni parsitaan ja toistetaan.
Onko mitään hajua miten noita kahta luokkaa tutkimalla voisi saada selville sen, että kuuleeko mikrofoni ääntä?