/*
* VideoKit
* Copyright © 2026 Yusuf Olokoba. All Rights Reserved.
*/
#nullable enable
namespace VideoKit {
using System;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Internal;
using Status = Internal.VideoKit.Status;
///
/// Audio buffer.
/// The audio buffer always contains a linear PCM audio interleaved by channel.
///
public unsafe readonly struct AudioBuffer : IDisposable {
#region --Client API--
///
/// Audio data.
/// This is always linear PCM audio interleaved by channel.
///
public readonly NativeArray data {
get {
handle.GetAudioBufferData(out var data).Throw();
handle.GetAudioBufferSampleCount(out var sampleCount).Throw();
var nativeArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(
data,
sampleCount,
Allocator.None
);
#if ENABLE_UNITY_COLLECTIONS_CHECKS
NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref nativeArray, AtomicSafetyHandle.Create());
#endif
return nativeArray;
}
}
///
/// Sample rate.
///
public readonly int sampleRate => handle.GetAudioBufferSampleRate(out var sampleRate).Throw() == Status.Ok ? sampleRate : 0;
///
/// Channel count.
///
public readonly int channelCount => handle.GetAudioBufferChannelCount(out var channelCount).Throw() == Status.Ok ? channelCount : 0;
///
/// Timestamp in nanoseconds.
///
public readonly long timestamp => handle.GetSampleBufferTimestamp(out var timestamp).Throw() == Status.Ok ? timestamp : 0L;
///
/// Create an audio buffer from a linear PCM sample buffer.
/// NOTE: This overload makes a copy of the input buffer, so prefer using the other overloads instead.
///
/// Sample rate.
/// Channel count.
/// Audio data.
/// Timestamp in nanoseconds.
public AudioBuffer(
int sampleRate,
int channelCount,
float[] data,
long timestamp = 0L
) {
// Copy
var byteSize = data.Length * sizeof(float);
audioData = (float*)UnsafeUtility.Malloc(byteSize, 16, Allocator.Persistent);
fixed (float* src = data)
UnsafeUtility.MemCpy(audioData, src, byteSize);
// Create
VideoKit.CreateAudioBuffer(
sampleRate,
channelCount,
audioData,
data.Length,
timestamp,
out handle
).Throw();
}
///
/// Create an audio buffer from a linear PCM sample buffer.
///
/// Sample rate.
/// Channel count.
/// Audio data.
/// Timestamp in nanoseconds.
public unsafe AudioBuffer(
int sampleRate,
int channelCount,
NativeArray data,
long timestamp = 0L
) : this(sampleRate, channelCount, (float*)data.GetUnsafePtr(), data.Length, timestamp) { }
///
/// Create an audio buffer from a linear PCM sample buffer.
///
/// Sample rate.
/// Channel count.
/// Audio data.
/// Total number of samples in sample buffer.
/// Timestamp in nanoseconds.
public unsafe AudioBuffer(
int sampleRate,
int channelCount,
float* data,
int sampleCount,
long timestamp = 0L
) : this(VideoKit.CreateAudioBuffer(
sampleRate,
channelCount,
data,
sampleCount,
timestamp,
out var buffer
).Throw() == Status.Ok ? buffer : default) { }
///
/// Dispose the audio buffer and release resources.
///
public void Dispose() {
handle.ReleaseSampleBuffer();
UnsafeUtility.Free(audioData, Allocator.Persistent);
}
#endregion
#region --Operations--
private readonly IntPtr handle;
private readonly float* audioData;
internal AudioBuffer(IntPtr buffer) {
this.handle = buffer;
this.audioData = null;
}
public static implicit operator IntPtr(AudioBuffer audioBuffer) => audioBuffer.handle;
#endregion
}
}