Unreal的Audio::IAudioCaptureStream在Android中录制数据异常
修改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;
}
相关文章:
Unreal的Audio::IAudioCaptureStream在Android中录制数据异常
修改OpenAudioCaptureStream启动参数为PCM_32,在PC上正常,在Android系统,读取的的数据计算出的音量值在0.4-0.6之间跳动,数据异常。 Audio::FAudioCaptureDeviceParams Params;/** 设置声卡不支持的采样数和通道数开始音频流不会成…...
6、AI测试辅助-测试报告编写(生成Bug分析柱状图)
AI测试辅助-测试报告编写(生成Bug分析柱状图) 一、测试报告1. 创建测试报告2. 报告补充优化2.1 Bug图表分析 3. 风险评估 总结 一、测试报告 测试报告内容应该包含: 1、测试结论 2、测试执行情况 3、测试bug结果分析 4、风险评估 5、改进措施…...
【第五节】docker应用系列篇: 使用Docker容器实现ElasticSearch+Kibana部署
系列文章目录 【第五节】docker应用系列篇: 使用Docker容器实现ElasticSearchKibana部署 系列文章目录前言一、 docker运行es二、 docker运行kibina 前言 配一次,真觉得方面 一、 docker运行es docker pull elasticsearch:7.17.0# mkdir -p /opt/es/co…...
openwrt 通过DHCP/DNS(Dnsmasq)屏蔽指定域名(hosts)
1、准备好hosts文件 2、登录openwrt后台:系统-TTYD终端-root登录: cd /etc ls vi hosts_by_me vi hosts_by_me 创建/打开 hosts_by_me文件,把准备好的hosts文件的内容复制粘贴进去,然后保存退出:wq cat hosts_by_me查看确认保…...
opencv——识别图片颜色并绘制轮廓
图像边缘检测 本实验要用到Canny算法,Canny边缘检测方法常被誉为边缘检测的最优方法。 首先,Canny算法的输入端应为图像的二值化结果,接收到二值化图像后,需要按照如下步骤进行: 高斯滤波。计算图像的梯度和方向。非极…...
docker简单私有仓库的创建
1:下载Registry镜像 导入镜像到本地中 [rootlocalhost ~]# docker load -i registry.tag.gz 进行检查 2:开启Registry registry开启的端口号为5000 [rootlocalhost ~]# docker run -d -p 5000:5000 --restartalways registry [rootlocalhost ~]# dock…...
etcd常见运维事件
1、etcd扩/缩容 参考:etcd节点扩/缩容-CSDN博客 2、etcd数据备份/恢复 数据备份 数据备份要从leader节点进行备份,否则可能存在数据同步延迟导致数据不一致; etcdctl --endpoints"https://10.119.48.166:2379" snapshot save /d…...
[代码随想录17]二叉树之最大二叉树、合并二叉树、二搜索树中的搜索、验证二叉搜索树。
前言 二叉树的题目还是要会一流程构造函数之类的。其中还有回溯的思想 题目链接 654. 最大二叉树 - 力扣(LeetCode) 一、最大二叉树 思路:还是考察构造二叉树,简单来说就是给你一个数组去构建一个二叉树,递归来解决就…...
前端三大框架 Vue、React 和 Angular 的市场占比分析
一、引言 ?? 随着前端技术的迅速发展,Vue.js、React 和 Angular 已成为全球最受欢迎的三大前端框架。在国内外,不同的框架在市场中的占比和流行程度存在显著差异。本文将从全球和中国市场的角度,对这三大框架的市场占比进行分析࿰…...
12.3【JAVA-EXP4-DEBUGSTUDY】
java升级版本 JDK 1.8 是 Java Development Kit 的第 8 版本,发布于 2014 年 3 月 18 日。这个版本是 Java SE(Standard Edition)的一部分,包含了 Java 编程语言的实现、编译器、调试工具和其他相关组件 JDK 1.8: 这里的 1.8 表…...
flutter命令行直接指定设备
> flutter driver Found 3 connected devices:sdk gphone16k x86 64 (mobile) • emulator-5554 • android-x64 • Android 15 (API 35) (emulator)Linux (desktop) • linux • linux-x64 • Ubuntu 22.04.5 LTS 6.8.0-49-genericChrome (…...
Spring核心--Bean后处理器
Spring的后处理器(Spring核心重点) Spring的后处理器是Spring对外开发的重要扩展点,允许我们介入到Bean的整个实例化流程中来,以达到动态注册BeanDefinition,动态修改BeanDefinition,以及动态修改Bean的作用。Spring主要有两种后处理器: BeanFactoryPostProcessor: Bean工…...
Windows子系统Ubuntu本地部署xinference以及接入dify详解
背景介绍 系统:Ubuntu 22.04.5 LTSpython环境管理工具:mincondadify版本:0.13.2本文参考 Xinference在线文档和dify的官方介绍模型接入 1. Github拉取Xinference代码 首选去Xinference的Github地址拉取代码 2. 在minconda创建隔离环境 - xinfernce 是我本地创建的环境名…...
如何实现接口继承与实现继承的区别?如何处理多态性与性能的平衡?
在面向对象编程中,接口继承和实现继承是两个重要的概念,同时多态性也是面向对象编程的一大特性,但在追求多态性的同时,我们也需要考虑性能问题。本文将详细探讨接口继承与实现继承的区别,以及如何处理多态性与性能的平…...
VR虚拟展厅的实时互动是如何实现的?
VR虚拟展厅的实时互动是通过一系列技术和流程实现的,这些技术和流程共同确保了用户在虚拟环境中的互动体验能够及时响应和更新。 接下来,由专业从事VR虚拟展厅制作的圆桌3D云展厅平台为大家介绍一下实现VR虚拟展厅实时互动的几个关键要素: 高…...
Java、鸿蒙与嵌入式开发:技术选择与职业发展分析
在当今快速发展的科技领域中,Java、鸿蒙和嵌入式开发代表着不同的技术方向和职业机遇。每个方向都有其独特的市场价值和发展前景,让我们深入分析这三个领域的特点、发展趋势和职业规划。 Java开发方向已经发展了二十多年,仍然在软件开发领域…...
28. Three.js案例-创建圆角矩形并进行拉伸
28. Three.js案例-创建圆角矩形并进行拉伸 实现效果 知识点 WebGLRenderer (WebGL渲染器) WebGLRenderer 是 Three.js 中用于渲染 3D 场景的主要渲染器。 构造器 WebGLRenderer( parameters : Object ) 参数类型描述parametersObject渲染器的配置参数,可选。 …...
Shopee算法分析 - x-sap-ri
去除干扰项 在上篇文章中说到, 我们主动调用了几次,返回结果都是不同的 相同参数, 我们主动多次call. 可以看到结果是不同的. 只有一个Key不同. 接下来, 引用龙哥的文章 引用自龙哥文章, 我仅仅是对关键信息做加粗 1.1 引言 在使用 Unidbg 模拟执行以及辅助算法还原时&#x…...
日志相关的学习记录
Logger.error还不知道怎么传参打印?看完这个你就明白了-CSDN博客 日志使用的方式 使用e.getMessage()、e .toString() 都不会打印堆栈信息,最好直接打印e。 当使用两个参数error(String message, Throwable t),且第二个参数为Throwable(就是…...
HTML和JavaScript实现商品购物系统
下面是一个更全面的商品购物系统示例,包含新增商品、商品的增加删除以及结算找零的功能。这个系统使用HTML和JavaScript实现。 1.功能说明: 这个应用程序使用纯HTML和JavaScript实现。 包含一个商品列表和一个购物车区域。商品列表中有几个示例商品&a…...
PlayCover:跨生态运行iOS应用的性能优化与无缝体验指南
PlayCover:跨生态运行iOS应用的性能优化与无缝体验指南 【免费下载链接】PlayCover Community fork of PlayCover 项目地址: https://gitcode.com/gh_mirrors/pl/PlayCover 价值主张:重新定义Apple生态边界 PlayCover作为专为Apple Silicon Mac设…...
Nunchaku-flux-1-dev性能调优:针对STM32嵌入式设备演示的图片预处理
Nunchaku-flux-1-dev性能调优:针对STM32嵌入式设备演示的图片预处理 最近在折腾一个智能门禁项目,需要在STM32上跑人脸识别。想法挺简单,本地抓拍人脸,然后传给云端的大模型Nunchaku-flux-1-dev去分析。结果一上手就发现…...
4步攻克Dlib库Windows安装难题:从环境诊断到功能验证的完整指南
4步攻克Dlib库Windows安装难题:从环境诊断到功能验证的完整指南 【免费下载链接】Dlib_Windows_Python3.x Dlib compiled binaries (.whl) for Python 3.7-3.14 and Windows x64 项目地址: https://gitcode.com/gh_mirrors/dl/Dlib_Windows_Python3.x 一、环…...
保姆级教程:AI全身全息感知镜像部署,手把手教你实现543点动作捕捉
保姆级教程:AI全身全息感知镜像部署,手把手教你实现543点动作捕捉 1. 引言:全息感知技术的平民化革命 想象一下,只需一台普通电脑,就能实现电影级别的动作捕捉效果——这正是AI全身全息感知技术带来的变革。传统动作…...
Wan2.2-I2V-A14B一键部署教程:Python环境快速配置与模型调用
Wan2.2-I2V-A14B一键部署教程:Python环境快速配置与模型调用 1. 快速开始:部署前的准备工作 在开始之前,确保你已经拥有星图GPU平台的账号并完成登录。这个平台提供了强大的计算资源,特别适合运行图像到视频转换这类计算密集型任…...
G-Helper技术架构解析:华硕笔记本ACPI控制接口的轻量化实现
G-Helper技术架构解析:华硕笔记本ACPI控制接口的轻量化实现 【免费下载链接】g-helper Lightweight, open-source control tool for ASUS laptops and ROG Ally. Manage performance modes, fans, GPU, battery, and RGB lighting across Zephyrus, Flow, TUF, Stri…...
USART串口通信
一、串口 USART USART(Universal Synchronous/Asynchronous Receiver/Transmitter,通用同步 / 异步收发器) 是一种全双工、串行、逐位传输的通信接口,核心是把单片机 / 处理器的并行数据转为串行数据发送,或把串行数据…...
主流AI培训机构评测:关键指标全对比
引言 随着AI技术的飞速发展,AI培训市场也日益繁荣。然而,无论是企业还是创业者在选择AI培训机构时,都面临着诸多挑战。企业端存在缺乏数字化运营团队、不懂AI工具使用、短视频内容生产效率低、打造个人IP能力不足、同城获客成本高且精准度低…...
Linux文件名修改方法大全
在Linux系统中,文件名修改是一个常见且重要的操作。文件名修改可以更好地管理文件和文件夹,使其更具可读性和有序性。通过更改文件名,可以清晰地表达文件的内容和用途,便于快速识别和定位文件。此外,对文件名进行调整还…...
Claude Code交互日志分析:用BERT分割理解AI编程助手的对话逻辑
Claude Code交互日志分析:用BERT分割理解AI编程助手的对话逻辑 你有没有想过,当你向Claude Code这样的AI编程助手提问时,它到底是怎么理解你那一长串话的?比如,你可能会一口气问:“帮我写个Python函数来读…...
