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…...
Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...
C# 表达式和运算符(求值顺序)
求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如,已知表达式3*52,依照子表达式的求值顺序,有两种可能的结果,如图9-3所示。 如果乘法先执行,结果是17。如果5…...
PHP 8.5 即将发布:管道操作符、强力调试
前不久,PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5!作为 PHP 语言的又一次重要迭代,PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是,借助强大的本地开发环境 ServBay&am…...
