/* * 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 } }