/*
* VideoKit
* Copyright © 2026 Yusuf Olokoba. All Rights Reserved.
*/
#nullable enable
namespace VideoKit.Sources {
using System;
using UnityEngine;
using Clocks;
///
/// Media source for generating audio buffers from an `AudioSource` or `AudioListener` component.
///
public sealed class AudioComponentSource : IDisposable {
#region --Client API--
///
/// Create an audio buffer source from an AudioListener.
///
/// Audio listener for the current scene.
/// Handler to receive audio buffers.
/// Clock for generating timestamps.
public AudioComponentSource(
AudioListener listener,
Action handler,
IClock? clock = null
) : this(listener.gameObject, handler, clock) { }
///
/// Create an audio buffer source from an AudioSource.
///
/// Audio source to record.
/// Handler to receive audio buffers.
/// Clock for generating timestamps.
public AudioComponentSource(
AudioSource source,
Action handler,
IClock? clock = null
) : this(source.gameObject, handler, clock) { }
///
/// Close the media source and release resources.
///
public void Dispose() => AudioSourceAttachment.DestroyImmediate(attachment);
#endregion
#region --Operations--
private readonly AudioSourceAttachment attachment;
private AudioComponentSource(GameObject gameObject, Action handler, IClock? clock) {
var sampleRate = AudioSettings.outputSampleRate;
attachment = gameObject.AddComponent();
attachment.sampleBufferDelegate = (data, channels) => {
using var audioBuffer = new AudioBuffer(
sampleRate,
channels,
data,
clock?.timestamp ?? 0L
);
handler(audioBuffer);
};
}
private sealed class AudioSourceAttachment : MonoBehaviour {
public Action? sampleBufferDelegate;
private void OnAudioFilterRead(float[] data, int channels) {
sampleBufferDelegate?.Invoke(data, channels);
}
}
#endregion
}
}