class in UnityEngine.Experimental.Audio
切换到手册提供对 Unity 对象(如 VideoPlayer)生成的音频样本的访问。
此类可在主线程之外使用,该实现的前提是一次只有一位消费者调用 AudioSampleProvider.ConsumeSampleFrames。通过 C# 和 C++ 均可访问音频数据,具体取决于此数据的用途。
以下示例显示了如何使用从 VideoPlayer 获取的访问器来访问 C# 中的样本:
using UnityEngine; using Unity.Collections; using UnityEngine.Experimental.Video; using UnityEngine.Experimental.Audio; using UnityEngine.Video;
public class ManagedAudioOutput : MonoBehaviour { AudioSampleProvider provider;
void Start() { VideoPlayer vp = GetComponent<VideoPlayer>(); vp.audioOutputMode = VideoAudioOutputMode.APIOnly; vp.prepareCompleted += Prepared; vp.Prepare(); }
void Prepared(VideoPlayer vp) { provider = vp.GetAudioSampleProvider(0); provider.sampleFramesAvailable += SampleFramesAvailable; provider.enableSampleFramesAvailableEvents = true; provider.freeSampleFrameCountLowThreshold = provider.maxSampleFrameCount / 4; vp.Play(); }
void SampleFramesAvailable(AudioSampleProvider provider, uint sampleFrameCount) { using (NativeArray<float> buffer = new NativeArray<float>( (int)sampleFrameCount * provider.channelCount, Allocator.Temp)) { var sfCount = provider.ConsumeSampleFrames(buffer); Debug.LogFormat("SetupSoftwareAudioOut.Available got {0} sample frames.", sfCount); // Do something with the samples here... } } }
以下是有关如何访问 C++ 中的样本的示例:设置必须在 C# 中完成,之后 Unity 的核心插件和原生插件无需借助托管代码即可相互调用。
using System; using System.Runtime.InteropServices; using UnityEngine; using UnityEngine.Experimental.Video; using UnityEngine.Experimental.Audio; using UnityEngine.Video;
public class NativeAudioOutput : MonoBehaviour { AudioSampleProvider provider; AudioSampleProvider.SampleFramesEventNativeFunction sampleFramesAvailableNativeHandler = SampleFramesAvailable;
void Start() { VideoPlayer vp = GetComponent<VideoPlayer>(); vp.audioOutputMode = VideoAudioOutputMode.APIOnly; vp.prepareCompleted += Prepared; vp.Prepare(); }
void Prepared(VideoPlayer vp) { provider = vp.GetAudioSampleProvider(0); provider.freeSampleFrameCountLowThreshold = provider.maxSampleFrameCount - 1024;
SetConsumeSampleFramesFunction( AudioSampleProvider.consumeSampleFramesNativeFunction, provider.id, provider.channelCount, provider.sampleRate); provider.SetSampleFramesAvailableNativeHandler( sampleFramesAvailableNativeHandler, (IntPtr)0);
vp.Play(); }
private const string pluginName = #if UNITY_IPHONE "__Internal" #else "NativeAudioOutputPlugin" #endif ;
[DllImport(pluginName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] private static extern void SetConsumeSampleFramesFunction( AudioSampleProvider.ConsumeSampleFramesNativeFunction cb, uint id, ushort channelCount, uint sampleRate);
[AOT.MonoPInvokeCallback(typeof(AudioSampleProvider.SampleFramesEventNativeFunction))] [DllImport(pluginName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] private static extern void SampleFramesAvailable(IntPtr userData, uint id, uint sampleFrameCount); }
以下是随附的 C++ 插件:
#include <algorithm> #include <stdint.h>
typedef uint32_t(__cdecl *ConsumeSampleFramesFunction)( uint32_t providerId, float* interleavedSampleFrames, uint32_t sampleFrameCount);
ConsumeSampleFramesFunction ConsumeSampleFrames = NULL; uint32_t providerId = -1; float* buffer = NULL; uint32_t bufferSampleFrameCount = 0; uint32_t availableSampleFrameCount = 0;
extern "C" __cdecl void SetConsumeSampleFramesFunction( ConsumeSampleFramesFunction function, uint32_t id, uint16_t channelCount, uint32_t sampleRate) { ConsumeSampleFrames = function; providerId = id; delete[] buffer; buffer = new float[channelCount * sampleRate]; // 1s worth of sample frames. bufferSampleFrameCount = sampleRate; }
extern "C" __cdecl void SampleFramesAvailable(void* userData, uint32_t id, uint32_t sampleFrameCount) { if (ConsumeSampleFrames == NULL) return;
// We consume the sample frames from the handler that tells us that there are some available. // But we could also invoke this regularly from another thread, for example the thread providing // samples to an audio device. const uint32_t consumedSampleFrameCount = ConsumeSampleFrames( providerId, buffer, std::min(bufferSampleFrameCount, sampleFrameCount)); // Do something with the samples here... }
consumeSampleFramesNativeFunction | 指向原生函数(提供了对音频样本帧的访问)的指针。 |
availableSampleFrameCount | 可与 AudioSampleProvider.ConsumeSampleFrames 结合使用的样本帧数。 |
channelCount | 每个样本帧的音频声道数。 |
enableSampleFramesAvailableEvents | 启用 AudioSampleProvider.sampleFramesAvailable 事件。 |
enableSilencePadding | 如果此属性为 true,且可用数据量少于请求数据量,则在静音时填充 ConsumeSampleFrames 生成的缓冲区。否则,缓冲区中的额外样本帧将保持不变。 |
freeSampleFrameCount | 样本生成器在溢出之前仍然可以写入的样本帧数。 |
freeSampleFrameCountLowThreshold | 然后,空闲样本数低于此阈值,这将发出 AudioSampleProvider.sampleFramesAvailable 事件和相关原生事件。 |
id | 此实例的唯一标识符。 |
maxSampleFrameCount | 发出溢出事件之前可在内部缓冲区内累积的最大样本帧数。 |
owner | 此提供程序来自的对象。 |
sampleRate | 此类生成的样本帧的预期播放速率。 |
trackIndex | 创建此提供程序的对象中的轨道索引。 |
valid | 如果对象有效,则此属性为 true。 |
ClearSampleFramesAvailableNativeHandler | 清除通过 AudioSampleProvider.SetSampleFramesAvailableNativeHandler 设置的原生处理程序。 |
ClearSampleFramesOverflowNativeHandler | 清除通过 AudioSampleProvider.SetSampleFramesOverflowNativeHandler 设置的原生处理程序。 |
ConsumeSampleFrames | 使用内部缓冲区中的样本帧。 |
Dispose | 释放内部资源。继承自 IDisposable。 |
SetSampleFramesAvailableNativeHandler | 当可用的样本帧数超过阈值时,为发出的事件设置原生事件处理程序。 |
SetSampleFramesOverflowNativeHandler | 当内部样本帧缓冲区溢出时,为发出的事件设置原生事件处理程序。 |
sampleFramesAvailable | 在可用样本帧数超过通过 AudioSampleProvider.freeSampleFrameCountLowThreshold 设置的阈值时调用。 |
sampleFramesOverflow | 在可用样本帧数超过内部缓冲区所允许的最大值时调用。 |
ConsumeSampleFramesNativeFunction | 表示用于使用样本帧的原生函数指针的类型。 |
SampleFramesEventNativeFunction | 表示用于处理样本帧事件的原生函数指针的类型。 |
SampleFramesHandler | 样本帧事件的委托。 |