修改OpenAudioCaptureStream启动参数为PCM_32,在PC上正常,在Android系统,读取的的数据计算出的音量值在0.4-0.6之间跳动,数据异常。
Audio::FAudioCaptureDeviceParams Params;/** 设置声卡不支持的采样数和通道数开始音频流不会成功,这里不能修改* Params.NumInputChannels = 1;* Params.SampleRate = 16000;* * 可以修改PCM数据格式,默认是32位浮点数FLOATING_POINT_32* 我这里修改为32位整数PCM_32*/Params.PCMAudioEncoding = Audio::EPCMAudioEncoding::PCM_32;// 使用 TFunction 包装成员函数Audio::FOnAudioCaptureFunction OnCapture = [this](const void* InAudio, int32 NumFrames, int32 NumChannels, int32 SampleRate, double StreamTime, bool bOverFlow){this->OnAudioCapture(InAudio, NumFrames, NumChannels, SampleRate, StreamTime, bOverFlow);};bool r = AudioCapture->OpenAudioCaptureStream(Params, MoveTemp(OnCapture), 4800);
修改为FLOATING_POINT_32,按照float值读取数据则是正常的。
Audio::FAudioCaptureDeviceParams Params;/** 设置声卡不支持的采样数和通道数开始音频流不会成功,这里不能修改* Params.NumInputChannels = 1;* Params.SampleRate = 16000;* * 可以修改PCM数据格式,默认是32位浮点数FLOATING_POINT_32* 修改为32位整数PCM_32,在Android系统有问题,还是修改为FLOATING_POINT_32*/Params.PCMAudioEncoding = Audio::EPCMAudioEncoding::FLOATING_POINT_32;// 使用 TFunction 包装成员函数Audio::FOnAudioCaptureFunction OnCapture = [this](const void* InAudio, int32 NumFrames, int32 NumChannels, int32 SampleRate, double StreamTime, bool bOverFlow){this->OnAudioCapture(InAudio, NumFrames, NumChannels, SampleRate, StreamTime, bOverFlow);};bool r = AudioCapture->OpenAudioCaptureStream(Params, MoveTemp(OnCapture), 1920); // 48000采样率可以在重采样是整除3
全部代码
FxAudioCaptureComponent.h
#pragma once#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "AudioCaptureDeviceInterface.h"
#include "HAL/ThreadSafeCounter.h"
#include "HAL/Thread.h"
#include "FxAudioCaptureComponent.generated.h"UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class UFxAudioCaptureComponent : public UActorComponent
{GENERATED_BODY()public:UFxAudioCaptureComponent();UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "XML")int ID;UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "XML")float Intensity;UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "XML")bool bMobile;protected:virtual void BeginPlay() override;virtual void EndPlay(const EEndPlayReason::Type EndPlayReason);// Called every framevirtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;TArray<float> ResampleAndConvert16KHzMono16Bit(const float* inputData, int inputNumSamples, int inputChannels, int inputSampleRate);public:UFUNCTION(BlueprintPure, Category = "FxAudioCapture")int GetAudioDataSize();UFUNCTION(BlueprintCallable, Category = "FxAudioCapture")TArray<uint8> CopyAudioData(int Length, bool bRemove = true);UFUNCTION(BlueprintCallable, Category = "FxAudioCapture")bool StartRecord(float seconds = 10.0f);UFUNCTION(BlueprintCallable, Category = "FxAudioCapture")void StopRecord();UFUNCTION(BlueprintPure, Category = "FxAudioCapture")bool IsRecording();private:FCriticalSection Mutex;TArray<float> m_audioData;bool bRecording;float RecordSeconds;TUniquePtr<Audio::IAudioCaptureStream> AudioCapture;void OnAudioCapture(const void* InAudio, int32 NumFrames, int32 NumChannels, int32 SampleRate, double StreamTime, bool bOverFlow);
};
FxAudioCaptureComponent.cpp
#include "FxAudioCaptureComponent.h"
#include "AudioCaptureDeviceInterface.h"
#include "AudioCaptureCore.h"
#include "AudioMixer.h"UFxAudioCaptureComponent::UFxAudioCaptureComponent(): bRecording(false)
{PrimaryComponentTick.bCanEverTick = true;
}void UFxAudioCaptureComponent::BeginPlay()
{Super::BeginPlay();IModularFeatures::Get().LockModularFeatureList();TArray<Audio::IAudioCaptureFactory*> AudioCaptureStreamFactories = IModularFeatures::Get().GetModularFeatureImplementations<Audio::IAudioCaptureFactory>(Audio::IAudioCaptureFactory::GetModularFeatureName());IModularFeatures::Get().UnlockModularFeatureList();// For now, just return the first audio capture stream implemented. We can make this configurable at a later point.if (AudioCaptureStreamFactories.Num() > 0 && AudioCaptureStreamFactories[0] != nullptr){AudioCapture = AudioCaptureStreamFactories[0]->CreateNewAudioCaptureStream();if (!AudioCapture.IsValid()){GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("CreateNewAudioCaptureStream return null"));}}else {GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("no Audio Capture Stream Factories"));}
}
void UFxAudioCaptureComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
{Super::EndPlay(EndPlayReason);StopRecord();
}
void UFxAudioCaptureComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
}
int UFxAudioCaptureComponent::GetAudioDataSize()
{int len = 0;Mutex.Lock();len = m_audioData.Num();Mutex.Unlock();return len;
}
TArray<uint8> UFxAudioCaptureComponent::CopyAudioData(int numSamples, bool bRemove)
{TArray<uint8> Array;Mutex.Lock();if (0 < numSamples && numSamples <= m_audioData.Num()) {for (int i = 0; i < numSamples; ++i) {int16_t sample16Bit = static_cast<int16_t>(m_audioData[i] * 32767.0f);// 将16位样本存储到Array中Array.Push(static_cast<uint8_t>(sample16Bit & 0xFF));Array.Push(static_cast<uint8_t>((sample16Bit >> 8) & 0xFF));}if (bRemove) {m_audioData.RemoveAt(0, numSamples);}}Mutex.Unlock();return Array;
}
bool UFxAudioCaptureComponent::StartRecord(float seconds)
{StopRecord();RecordSeconds = seconds;if (AudioCapture.IsValid()){Audio::FAudioCaptureDeviceParams Params;/** 设置声卡不支持的采样数和通道数开始音频流不会成功,这里不能修改* Params.NumInputChannels = 1;* Params.SampleRate = 16000;* * 可以修改PCM数据格式,默认是32位浮点数FLOATING_POINT_32* 修改为32位整数PCM_32,在Android系统有问题,还是修改为FLOATING_POINT_32*/Params.PCMAudioEncoding = Audio::EPCMAudioEncoding::FLOATING_POINT_32;// 使用 TFunction 包装成员函数Audio::FOnAudioCaptureFunction OnCapture = [this](const void* InAudio, int32 NumFrames, int32 NumChannels, int32 SampleRate, double StreamTime, bool bOverFlow){this->OnAudioCapture(InAudio, NumFrames, NumChannels, SampleRate, StreamTime, bOverFlow);};bool r = AudioCapture->OpenAudioCaptureStream(Params, MoveTemp(OnCapture), 1920); // 48000采样率可以在重采样是整除3if (r) {r = AudioCapture->StartStream();if (!r) {GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("StartStream return false"));}}else {GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("OpenAudioCaptureStream return false"));}bRecording = r;}return IsRecording();
}
void UFxAudioCaptureComponent::StopRecord()
{if (bRecording && AudioCapture.IsValid()){AudioCapture->StopStream();AudioCapture->CloseStream();}bRecording = false;
}void UFxAudioCaptureComponent::OnAudioCapture(const void* InAudio, int32 NumFrames, int32 NumChannels, int32 SampleRate, double StreamTime, bool bOverFlow)
{// GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("OnAudioCapture - %d,%d,%d,%f,%d"), NumFrames, NumChannels, SampleRate, (float)StreamTime, bOverFlow ? 1 : 0));// 按照16Khz和Mono重采样数据TArray<float> data = ResampleAndConvert16KHzMono16Bit(static_cast<const float*>(InAudio), NumFrames, NumChannels, SampleRate);// 计算强度float total = 0;for (float val : data) {total += FMath::Abs(val);}Intensity = total / (float)(data.Num());// 拷贝数据Mutex.Lock();m_audioData.Append(data.GetData(), data.Num());Mutex.Unlock();
}bool UFxAudioCaptureComponent::IsRecording() {if (!AudioCapture.IsValid()) {return false;}if (!bRecording) {return false;}if (!AudioCapture->IsCapturing()) {return false;}return true;
}
TArray<float> UFxAudioCaptureComponent::ResampleAndConvert16KHzMono16Bit(const float* inputData, int inputNumSamples, int inputChannels, int inputSampleRate)
{int targetSampleRate = 16000;// 计算重采样的步长double resampleRate = static_cast<double>(inputSampleRate) / targetSampleRate;// 临时存储单声道数据std::vector<float> monoSamples;int id = 0;for (int i = 0; i < inputNumSamples; ++i) {float sampleValue = 0;// 如果是多声道,转换为单声道if (inputChannels > 1) {float monoValue = 0;for (int j = 0; j < inputChannels; ++j) {monoValue += inputData[id++];}// 取平均值以避免溢出sampleValue = monoValue / inputChannels;}else {sampleValue = inputData[id++];}monoSamples.push_back(sampleValue);}// 重采样TArray<float> resampledSamples;int targetNumSamples = static_cast<int>(inputNumSamples / resampleRate);for (int i = 0; i < targetNumSamples; ++i) {double srcIndex = i * resampleRate;int srcIndexInt = static_cast<int>(srcIndex);double frac = srcIndex - srcIndexInt;// 线性插值float sample1 = monoSamples[srcIndexInt];float sample2 = monoSamples[std::min(srcIndexInt + 1, inputNumSamples - 1)];float resampledValue = (1.0 - frac) * sample1 + frac * sample2;resampledSamples.Push(resampledValue);}return resampledSamples;
}