/*
* VideoKit
* Copyright © 2025 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 unsafe readonly NativeArray data {
get {
audioBuffer.GetAudioBufferData(out var data).Throw();
audioBuffer.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 => audioBuffer.GetAudioBufferSampleRate(out var sampleRate).Throw() == Status.Ok ? sampleRate : 0;
///
/// Channel count.
///
public readonly int channelCount => audioBuffer.GetAudioBufferChannelCount(out var channelCount).Throw() == Status.Ok ? channelCount : 0;
///
/// Timestamp in nanoseconds.
///
public readonly long timestamp => audioBuffer.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 audioBuffer
).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 () {
audioBuffer.ReleaseSampleBuffer();
UnsafeUtility.Free(audioData, Allocator.Persistent);
}
#endregion
#region --Operations--
private readonly IntPtr audioBuffer;
private readonly float* audioData;
internal AudioBuffer (IntPtr buffer) {
this.audioBuffer = buffer;
this.audioData = null;
}
public static implicit operator IntPtr (AudioBuffer audioBuffer) => audioBuffer.audioBuffer;
#endregion
}
}