UE4 获取关卡最终合成的音频数据

众所周知,要让某个Actor发出声音,只需要给它添加一个AudioComponent,然后实现相应方法。
但如果反过来,要获取UE关卡中已有的声音,然后将其写成文件或者进行网络广播,该怎么做?
UE给用户提供了 订阅者模式 的框架,大致思路如下:
1)写一个类继承自 ISubmixBufferListener(AudioDevice.h)
2)注册/反注册 Listener
3)实现其唯一的接口 OnNewSubmixBuffer

一、写一个类继承自 ISubmixBufferListener

只需要包含头文件 “AudioDevice.h”
其路径位于 “Runtime/Engine/Public/AudioDevice.h”

class PXSubmixCapturer : public ISubmixBufferListener
{
public:
	PXSubmixCapturer() {};
	virtual ~PXSubmixCapturer() = default;

public:
	bool Initialize();
	bool Uninitialize();
	
	// ~ ISubmixBufferListener Interface
	void OnNewSubmixBuffer(const USoundSubmix* OwningSubmix, float* AudioData, int32 NumSamples, int32 NumChannels, const int32 SampleRate, double AudioClock) override;

private:
	TArray<int16_t> RecordingBuffer;
	FCriticalSection CriticalSection;
};

二、注册/反注册 Listener

  • 注册Listener
    在Initialize函数中将自身注册到AudioDevice的Listener中,具体实现如下:
 bool PXSubmixCapturer::Initialize()
{
	FScopeLock Lock(&CriticalSection);

	if (bInitialized)
	{
		return true;
	}

	if (GEngine == nullptr)
	{
		return false;
	}
	FAudioDeviceHandle AudioDevice = GEngine->GetMainAudioDevice();
	if (!AudioDevice)
	{
		bInitialized = false;
		return false;
	}

	AudioDevice->RegisterSubmixBufferListener(this);

	// 这里修改Engine配置参数,避免Game失去焦点时拿到的音频Buffer是空的
	if (GConfig)
	{
		GConfig->SetFloat(TEXT("Audio"), TEXT("UnfocusedVolumeMultiplier"), 1.0f, GEngineIni);
	}

	bInitialized = true;
	return true;
}
  • 注销
    在Uninitialize函数中注销注册,调用AudioDevice的相应接口:
bool PXSubmixCapturer::Uninitialize()
{
	FScopeLock Lock(&CriticalSection);

	if (!bInitialized)
	{
		return true;
	}

	if (GEngine != nullptr)
	{
		FAudioDeviceHandle AudioDevice = GEngine->GetMainAudioDevice();
		if (AudioDevice)
		{
			AudioDevice->UnregisterSubmixBufferListener(this);
		}
	}

	RecordingBuffer.Empty();
	bInitialized = false;
	return true;
}

三、实现接口

最关键的一步,在你的类中重写 ISubmixBufferListener 的纯虚函数,最后UE运行真正执行回调时,会在这个函数中把音频Buffer及其相关信息吐给你。参考实现如下:

void PXSubmixCapturer::OnNewSubmixBuffer(const USoundSubmix* OwningSubmix, float* AudioData, int32 NumSamples, int32 NumChannels, const int32 SampleRate, double AudioClock)
{
	FScopeLock Lock(&CriticalSection);

	if (!bInitialized)
	{
		return;
	}

	// TSampleBuffer takes in AudioData as float* and internally converts to int16
	Audio::TSampleBuffer<int16> Buffer(AudioData, NumSamples, NumChannels, SampleRate);

	RecordingBuffer.Append(Buffer.GetData(), Buffer.GetNumSamples());
	const int32 ChunkSamples = GetSamplesPerDurationSecs();
	
	while (RecordingBuffer.Num() > ChunkSamples)
	{
		TArray<int16_t> SubmitBuffer(RecordingBuffer.GetData(), ChunkSamples);

		// Do your things with this audio buffer here.
		// ...

		// Remove samples from the recording buffer because it is sent.
		RecordingBuffer.RemoveAt(0, ChunkSamples, false);
	}
}

四、应用

在客户代码中维护一个你刚刚写好的 SubmixCapturer
在合适的地方调用 Initialize / Uninitialize
然后坐享其成, UE的音频框架会自动调用到你重写的 OnNewSubmixBuffer (*仅限-game模式

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值