Kuinka saada puhutun äänen kovuus selville (mikrofoni kaappaus, Unreal Engine).

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
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ä?
 
Jos käytät 16-bittisiä sampleja, niin taulukossa sataset eivät ole paljon, vaan luokkaa 1% maksimista (16bit maksimiarvo 32768).

Jos sanot mikrofoniin tasaisella voimakkaalla äänellä "AAAAAAAAAAAAA" jotakuinkin niin matalalla äänellä kuin "normaalisti" pystyt, tallennat lukuarvot taulukkoon ja tarkastelet (nSamplesPerSec/20) mittaista pätkää tuosta ääninäytteestä, niin mitä sieltä löytyy?

Normaalisti miehen äänialan bassopää on jossain 80-160Hz välimaastossa, joten sieltä pitäisi löytyä about tuolla taajuudella oskillointia. Jos siis sample ratesi on vaikka 44kHz, niin 2200 peräkkäistä samplea excelissä (tai pelienginessä) piirtämällä pitäisi löytyä ainakin jonkin sorttinen järki siihen nauhoitetun signaalin voimakkuuteen ja sitten voitkin ruveta miettimään, että mikä triggeröintiehto on sopiva ja millä aikajaksolla se muutos pitäisi sieltä havaita.

Jos haluat valmiita koodeja, niin github on täynnä voice activation libraryitä. Kriittinen hakusana on "VAD". Minulla ei ole mitään käryä, että mikä noista olisi hyvä.

Jos haluat hypätä syvään päätyyn, niin lähde vaikka tästä eteenpäin:
https://ieeexplore.ieee.org/document/8309294/
jtkim-kaist/VAD
 
Jos käytät 16-bittisiä sampleja, niin taulukossa sataset eivät ole paljon, vaan luokkaa 1% maksimista (16bit maksimiarvo 32768).

Jos sanot mikrofoniin tasaisella voimakkaalla äänellä "AAAAAAAAAAAAA" jotakuinkin niin matalalla äänellä kuin "normaalisti" pystyt, tallennat lukuarvot taulukkoon ja tarkastelet (nSamplesPerSec/20) mittaista pätkää tuosta ääninäytteestä, niin mitä sieltä löytyy?

Normaalisti miehen äänialan bassopää on jossain 80-160Hz välimaastossa, joten sieltä pitäisi löytyä about tuolla taajuudella oskillointia. Jos siis sample ratesi on vaikka 44kHz, niin 2200 peräkkäistä samplea excelissä (tai pelienginessä) piirtämällä pitäisi löytyä ainakin jonkin sorttinen järki siihen nauhoitetun signaalin voimakkuuteen ja sitten voitkin ruveta miettimään, että mikä triggeröintiehto on sopiva ja millä aikajaksolla se muutos pitäisi sieltä havaita.

Jos haluat valmiita koodeja, niin github on täynnä voice activation libraryitä. Kriittinen hakusana on "VAD". Minulla ei ole mitään käryä, että mikä noista olisi hyvä.

Jos haluat hypätä syvään päätyyn, niin lähde vaikka tästä eteenpäin:
https://ieeexplore.ieee.org/document/8309294/
jtkim-kaist/VAD
Kiitos. Minäpä huomenna tarkastelen noita töissä. Mistä tuo luku 20 tulee? nSamplesPerSec/20?
 
Kiitos. Minäpä huomenna tarkastelen noita töissä. Mistä tuo luku 20 tulee? nSamplesPerSec/20?
Asia ei minulle kuulu enkä asiasta mitään tiedä, mutta pakko heittää silti arvaus. :D Arvaan siis, että se on mielivaltainen luku mutta käytännössä sillä saa jossain mielessä sopivan mittaisen tarkastelujakson.
 
20 tulee siitä, että saat sellaisen määrän sampleja joissa tuollainen 100hz ääni näkyy kivana aaltona. Käytännössä siis 0.05sek pituinen pätkä ääntä.
 

Statistiikka

Viestiketjuista
261 844
Viestejä
4 548 944
Jäsenet
74 855
Uusin jäsen
Nåksuu

Hinta.fi

Back
Ylös Bottom