虚幻引擎其实对实时音频的采集支持得非常好。不过由于对音频概念的学习,还是花了一些时间进行研究。本文主要介绍如何基于虚幻引擎采集实时麦克风的音频数据。
在虚幻引擎中,我没有找到动态修改音频采样率的方法。下面的方法设置是不行的,不知道有没有哪位高手可以指点一下。单双通道可以自己进行处理。针对双通道转单通道,可以尝试如下思路。单通道:在单通道PCM数据中,音频样本按时间顺序连续排列。数据流看起来像这样:S1, S2, S3, S4, ...,其中每个S代表一个样本。
双通道:在双通道PCM数据中,通常使用交错排列方式。这意味着左右声道的样本交替出现。数据流看起来像这样:L1, R1, L2, R2, L3, R3, ...,其中L和R分别代表左声道和右声道的样本。
将双通道转成单通道可以尝试如下方案:
单声道样本 = (左声道样本 + 右声道样本) / 2
PCM数据可以以大端字节序(Big Endian)或小端字节序(Little Endian)存储
WAV文件通常使用小端字节序
另外 Windows系统使用的是小端字节序(Little Endian)。在小端字节序中,多字节值的低位字节存储在内存中的低地址处,而高位字节则存储在高地址处。
例如,在小端系统中,一个16位整数0x1234将以34 12的形式存储在内存中(假设内存地址从左到右增加):0x34是低位字节,存储在较低的地址,而0x12是高位字节,存储在较高的地址。
所以在虚幻音频当中我就默认小端字节序了。
在虚幻引擎中使用FFileHelper类写文件时,如果目标文件已经存在,那么默认行为是覆盖该文件。这意味着原始文件的内容将被新内容完全替换。如果你需要根据文件是否存在来采取不同的行动,可以在写文件之前先检查文件是否存在。
#include "HAL/PlatformFilemanager.h"
void AppendOrCreateFile(const FString& FilePath, const TArray& Data)
{
// 获取平台文件管理器的引用
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
// 检查文件是否存在
if (PlatformFile.FileExists(*FilePath))
{
// 文件已存在,追加内容
IFileHandle* FileHandle = PlatformFile.OpenWrite(*FilePath, true);
if (FileHandle)
{
FileHandle->Write(Data.GetData(), Data.Num());
delete FileHandle; // 关闭文件
}
}
else
{
// 文件不存在,创建新文件并写入内容
FFileHelper::SaveArrayToFile(Data, *FilePath);
}
}
主要通过 UAudioCapture 对象进行采集,代码调用也非常简洁。创建一个UAudioCapture对象
m_AudioCapture = UAudioCaptureFunctionLibrary::CreateAudioCapture();
注册音频回调函数
m_AudioGeneratorHandle = m_AudioCapture->AddGeneratorDelegate(MyFunction);
启动音频采集
m_AudioCapture->StartCapturingAudio();
启动成功之后就可以获取到相关音频参数如采样率和通道
auto samplerate = m_AudioCapture->GetSampleRate();
auto channelnum = m_AudioCapture->GetNumChannels();
音频处理方面,在回调函数中,音频被设置在0-1之间,如果我们想保存为32bit的音频,可以自己等比例变换。
void ATestVoiceChatAudioCapture::OnAudioGenerate(const float* InAudio, int32 NumSamples)
这里我转成了32bit的pcm编码,转换如下
outAudio[i] = InAudio[i] * MAX_int32;
之后在保存为文件即可。有了实时音频采集的加持,我们可以做很多工作,比如语音识别,语音聊天等等。
请关注公众号g0415shenw 加入知识星球。
星球地址 https://t.zsxq.com/15EvfoA7n