Output Audio Player
Play audio responses from AI agents.
Overview
Audio Output Player provides:
Audio playback control
Volume and speed adjustment
Playlist management
Audio effects
Spatial audio support
Basic Setup
Initialize Player
var audioPlayer = agent.AudioController.OutputPlayer;
// Configure settings
audioPlayer.Volume = 1.0f;
audioPlayer.Speed = 1.0f;
audioPlayer.EnableEffects = true;Playback Controls
Play Audio
public class AudioPlayer : MonoBehaviour
{
[SerializeField] private AgentBehaviour agent;
[SerializeField] private AudioSource audioSource;
private AudioOutputPlayer player;
void Start()
{
player = agent.AudioController.OutputPlayer;
player.AudioSource = audioSource;
}
public void PlayAudio(AudioClip clip)
{
player.Play(clip);
Debug.Log($"▶️ Playing: {clip.length}s");
}
public void Pause()
{
player.Pause();
Debug.Log("⏸️ Paused");
}
public void Resume()
{
player.Resume();
Debug.Log("▶️ Resumed");
}
public void Stop()
{
player.Stop();
Debug.Log("⏹️ Stopped");
}
}Auto-Play Agent Responses
public class AutoPlayResponses : MonoBehaviour
{
[SerializeField] private AgentBehaviour agent;
void Start()
{
agent.onAudioResponseReceived.AddListener(PlayResponse);
}
void PlayResponse(AudioClip clip)
{
Debug.Log($"🔊 Playing agent response: {clip.length}s");
var player = agent.AudioController.OutputPlayer;
player.Play(clip);
}
}Volume Control
Set Volume
public class VolumeController : MonoBehaviour
{
[SerializeField] private AgentBehaviour agent;
[SerializeField] private Slider volumeSlider;
private AudioOutputPlayer player;
void Start()
{
player = agent.AudioController.OutputPlayer;
volumeSlider.onValueChanged.AddListener(SetVolume);
volumeSlider.value = player.Volume;
}
void SetVolume(float volume)
{
player.Volume = volume;
Debug.Log($"🔊 Volume: {volume:P0}");
}
}Fade In/Out
public async UniTask FadeIn(float duration)
{
float startVolume = 0;
float targetVolume = player.Volume;
float elapsed = 0;
player.Volume = startVolume;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
player.Volume = Mathf.Lerp(startVolume, targetVolume, elapsed / duration);
await UniTask.Yield();
}
player.Volume = targetVolume;
}
public async UniTask FadeOut(float duration)
{
float startVolume = player.Volume;
float elapsed = 0;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
player.Volume = Mathf.Lerp(startVolume, 0, elapsed / duration);
await UniTask.Yield();
}
player.Volume = 0;
player.Stop();
}Speed Control
Adjust Playback Speed
public class SpeedController : MonoBehaviour
{
[SerializeField] private AgentBehaviour agent;
[SerializeField] private Slider speedSlider;
[SerializeField] private TMP_Text speedText;
private AudioOutputPlayer player;
void Start()
{
player = agent.AudioController.OutputPlayer;
speedSlider.minValue = 0.5f;
speedSlider.maxValue = 2.0f;
speedSlider.value = 1.0f;
speedSlider.onValueChanged.AddListener(SetSpeed);
}
void SetSpeed(float speed)
{
player.Speed = speed;
speedText.text = $"{speed:F1}x";
Debug.Log($"⚡ Speed: {speed}x");
}
}Playlist Management
Audio Queue
public class AudioPlaylist : MonoBehaviour
{
[SerializeField] private AgentBehaviour agent;
private Queue<AudioClip> playlist = new();
private AudioOutputPlayer player;
private bool isPlaying;
void Start()
{
player = agent.AudioController.OutputPlayer;
player.onPlaybackCompleted.AddListener(OnClipFinished);
}
public void AddToQueue(AudioClip clip)
{
playlist.Enqueue(clip);
Debug.Log($"➕ Added to queue: {playlist.Count} clips");
if (!isPlaying)
{
PlayNext();
}
}
void PlayNext()
{
if (playlist.Count == 0)
{
isPlaying = false;
Debug.Log("✓ Playlist finished");
return;
}
AudioClip clip = playlist.Dequeue();
player.Play(clip);
isPlaying = true;
Debug.Log($"▶️ Playing: {clip.name} ({playlist.Count} remaining)");
}
void OnClipFinished()
{
PlayNext();
}
public void ClearQueue()
{
playlist.Clear();
player.Stop();
isPlaying = false;
Debug.Log("🗑️ Queue cleared");
}
}Skip Controls
public void SkipCurrent()
{
player.Stop();
Debug.Log("⏭️ Skipped");
}
public void SkipToEnd()
{
playlist.Clear();
player.Stop();
isPlaying = false;
Debug.Log("⏭️ Skipped to end");
}Audio Effects
Apply Effects
public class AudioEffects : MonoBehaviour
{
[SerializeField] private AudioSource audioSource;
[SerializeField] private bool enableReverb = true;
[SerializeField] private bool enableEcho = false;
void Start()
{
if (enableReverb)
{
var reverb = audioSource.gameObject.AddComponent<AudioReverbFilter>();
reverb.reverbPreset = AudioReverbPreset.Room;
}
if (enableEcho)
{
var echo = audioSource.gameObject.AddComponent<AudioEchoFilter>();
echo.delay = 500f;
echo.decayRatio = 0.5f;
}
}
}Equalizer
public class AudioEqualizer : MonoBehaviour
{
[SerializeField] private AudioSource audioSource;
[Header("EQ Bands")]
[SerializeField] private float lowFreqGain = 1.0f;
[SerializeField] private float midFreqGain = 1.0f;
[SerializeField] private float highFreqGain = 1.0f;
private AudioLowPassFilter lowPass;
private AudioHighPassFilter highPass;
void Start()
{
lowPass = audioSource.gameObject.AddComponent<AudioLowPassFilter>();
highPass = audioSource.gameObject.AddComponent<AudioHighPassFilter>();
ApplyEQ();
}
void ApplyEQ()
{
lowPass.cutoffFrequency = 5000f * lowFreqGain;
highPass.cutoffFrequency = 1000f / highFreqGain;
}
public void SetBand(int band, float gain)
{
switch (band)
{
case 0: lowFreqGain = gain; break;
case 1: midFreqGain = gain; break;
case 2: highFreqGain = gain; break;
}
ApplyEQ();
}
}Spatial Audio
3D Audio Setup
public class SpatialAudio : MonoBehaviour
{
[SerializeField] private AgentBehaviour agent;
[SerializeField] private Transform listenerTransform;
private AudioSource audioSource;
void Start()
{
audioSource = agent.AudioController.OutputPlayer.AudioSource;
// Configure for 3D audio
audioSource.spatialBlend = 1.0f; // Fully 3D
audioSource.rolloffMode = AudioRolloffMode.Linear;
audioSource.minDistance = 1f;
audioSource.maxDistance = 20f;
// Set audio listener
AudioListener.transform = listenerTransform;
}
public void SetPosition(Vector3 position)
{
audioSource.transform.position = position;
}
}Directional Audio
public void ConfigureDirectionalAudio(float spread)
{
audioSource.spatialBlend = 1.0f;
audioSource.spread = spread; // 0-360 degrees
audioSource.dopplerLevel = 1.0f;
}UI Integration
Playback Controls UI
public class PlaybackControlsUI : MonoBehaviour
{
[SerializeField] private AgentBehaviour agent;
[SerializeField] private Button playButton;
[SerializeField] private Button pauseButton;
[SerializeField] private Button stopButton;
[SerializeField] private Slider progressSlider;
[SerializeField] private TMP_Text timeText;
private AudioOutputPlayer player;
void Start()
{
player = agent.AudioController.OutputPlayer;
playButton.onClick.AddListener(() => player.Resume());
pauseButton.onClick.AddListener(() => player.Pause());
stopButton.onClick.AddListener(() => player.Stop());
progressSlider.onValueChanged.AddListener(Seek);
}
void Update()
{
if (player.IsPlaying)
{
float progress = player.PlaybackPosition / player.Duration;
progressSlider.SetValueWithoutNotify(progress);
timeText.text = $"{FormatTime(player.PlaybackPosition)} / {FormatTime(player.Duration)}";
}
}
void Seek(float progress)
{
player.Seek(progress * player.Duration);
}
string FormatTime(float seconds)
{
int minutes = Mathf.FloorToInt(seconds / 60);
int secs = Mathf.FloorToInt(seconds % 60);
return $"{minutes:00}:{secs:00}";
}
}Volume Mixer UI
public class VolumeMixerUI : MonoBehaviour
{
[SerializeField] private Slider masterSlider;
[SerializeField] private Slider voiceSlider;
[SerializeField] private Slider effectsSlider;
void Start()
{
masterSlider.onValueChanged.AddListener(vol =>
AudioListener.volume = vol);
voiceSlider.onValueChanged.AddListener(vol =>
agent.AudioController.OutputPlayer.Volume = vol);
}
}Audio Visualization
Waveform Display
public class WaveformVisualizer : MonoBehaviour
{
[SerializeField] private AgentBehaviour agent;
[SerializeField] private Image waveformImage;
[SerializeField] private int resolution = 256;
private AudioOutputPlayer player;
void Start()
{
player = agent.AudioController.OutputPlayer;
player.onPlaybackStarted.AddListener(GenerateWaveform);
}
void GenerateWaveform(AudioClip clip)
{
float[] samples = new float[clip.samples * clip.channels];
clip.GetData(samples, 0);
Texture2D texture = new Texture2D(resolution, 100);
int samplesPerPixel = samples.Length / resolution;
for (int x = 0; x < resolution; x++)
{
float sum = 0;
for (int i = 0; i < samplesPerPixel; i++)
{
sum += Mathf.Abs(samples[x * samplesPerPixel + i]);
}
float average = sum / samplesPerPixel;
int height = Mathf.RoundToInt(average * 100);
for (int y = 0; y < 100; y++)
{
texture.SetPixel(x, y, y < height ? Color.white : Color.black);
}
}
texture.Apply();
waveformImage.sprite = Sprite.Create(
texture,
new Rect(0, 0, resolution, 100),
new Vector2(0.5f, 0.5f)
);
}
}Error Handling
Handle Playback Errors
player.onPlaybackError.AddListener(error =>
{
Debug.LogError($"Playback error: {error}");
if (error.Contains("audio_source"))
{
ShowMessage("Audio source not configured.");
}
else if (error.Contains("invalid_clip"))
{
ShowMessage("Invalid audio clip.");
}
else
{
ShowMessage("Playback failed.");
}
});Complete Example
using UnityEngine;
using Glitch9.AIDevKit.Agents;
using Cysharp.Threading.Tasks;
public class AudioPlayerManager : MonoBehaviour
{
[SerializeField] private AgentBehaviour agent;
[SerializeField] private AudioSource audioSource;
[Header("UI")]
[SerializeField] private Button playPauseButton;
[SerializeField] private Button stopButton;
[SerializeField] private Slider volumeSlider;
[SerializeField] private Slider speedSlider;
[SerializeField] private TMP_Text statusText;
private AudioOutputPlayer player;
private Queue<AudioClip> playlist = new();
private bool isPlaying;
async void Start()
{
await SetupPlayer();
playPauseButton.onClick.AddListener(TogglePlayPause);
stopButton.onClick.AddListener(Stop);
volumeSlider.onValueChanged.AddListener(SetVolume);
speedSlider.onValueChanged.AddListener(SetSpeed);
}
async UniTask SetupPlayer()
{
player = agent.AudioController.OutputPlayer;
player.AudioSource = audioSource;
// Configure
player.Volume = 1.0f;
player.Speed = 1.0f;
// Listen for events
player.onPlaybackStarted.AddListener(OnPlaybackStarted);
player.onPlaybackCompleted.AddListener(OnPlaybackCompleted);
player.onPlaybackError.AddListener(OnPlaybackError);
// Listen for agent audio responses
agent.onAudioResponseReceived.AddListener(QueueAudio);
Debug.Log("✓ Audio player ready");
}
void QueueAudio(AudioClip clip)
{
playlist.Enqueue(clip);
Debug.Log($"➕ Queued: {playlist.Count} clips");
if (!isPlaying)
{
PlayNext();
}
}
void PlayNext()
{
if (playlist.Count == 0)
{
isPlaying = false;
statusText.text = "Ready";
return;
}
AudioClip clip = playlist.Dequeue();
player.Play(clip);
isPlaying = true;
}
void TogglePlayPause()
{
if (player.IsPlaying)
{
player.Pause();
}
else
{
player.Resume();
}
}
void Stop()
{
player.Stop();
playlist.Clear();
isPlaying = false;
}
void SetVolume(float volume)
{
player.Volume = volume;
}
void SetSpeed(float speed)
{
player.Speed = speed;
}
void OnPlaybackStarted(AudioClip clip)
{
Debug.Log($"▶️ Playing: {clip.name}");
statusText.text = $"Playing: {clip.name}";
playPauseButton.GetComponentInChildren<TMP_Text>().text = "Pause";
}
void OnPlaybackCompleted()
{
Debug.Log("✓ Playback completed");
PlayNext();
}
void OnPlaybackError(string error)
{
Debug.LogError($"Playback error: {error}");
statusText.text = $"<color=red>Error: {error}</color>";
}
}Next Steps
Last updated