Events
VoiceReact provides both UnityEvents (Inspector-friendly) and C# events (code-based) for maximum flexibility.
VoiceReactController Events
VoiceReactController fires high-level gameplay events.
OnSpeechStart (UnityEvent)
Fires when player starts speaking.
using UnityEngine;
using Peek.VoiceReact;
public class GameplayController : MonoBehaviour
{
[SerializeField] private VoiceReactController voiceController;
void Start()
{
voiceController.OnSpeechStart.AddListener(OnPlayerStartedSpeaking);
}
void OnPlayerStartedSpeaking()
{
Debug.Log("Player started speaking!");
// Trigger gameplay events
}
}Inspector Setup:
- Select GameObject with script
- In Inspector, find OnSpeechStart event
- Click + to add listener
- Drag target GameObject
- Select function to call
OnSpeechEnd (UnityEvent<float>)
Fires when player stops speaking. Provides speech duration.
void Start()
{
voiceController.OnSpeechEnd.AddListener(OnPlayerStoppedSpeaking);
}
void OnPlayerStoppedSpeaking(float duration)
{
Debug.Log($"Player spoke for {duration} seconds");
if (duration > 2f)
{
// Long speech - big noise event
AlertNearbyEnemies();
}
}OnTranscriptionReady (UnityEvent<string>)
Fires when speech-to-text transcription is ready (requires Whisper integration).
void Start()
{
voiceController.OnTranscriptionReady.AddListener(OnTranscriptionReceived);
}
void OnTranscriptionReceived(string text)
{
Debug.Log($"Player said: {text}");
// Voice command system
ProcessVoiceCommand(text);
}Advanced C# Events
For custom processing with access to detailed event data, use advanced C# events.
OnSpeechStartAdvanced
Provides detailed speech start information:
using System;
using Peek.VoiceReact;
void Start()
{
voiceController.OnSpeechStartAdvanced += HandleSpeechStart;
}
void OnDestroy()
{
voiceController.OnSpeechStartAdvanced -= HandleSpeechStart;
}
void HandleSpeechStart(object sender, VoiceReactEventArgs e)
{
Debug.Log($"Speech started at {e.DecibelLevel} dB");
Debug.Log($"Normalized volume: {e.NormalizedVolume}");
// Access to raw audio data
if (e.AudioData != null)
{
ProcessAudioData(e.AudioData, e.SampleRate);
}
}VoiceReactEventArgs Properties:
DecibelLevel- Current audio level in dBNormalizedVolume- 0-1 volume for UI displaysDuration- Speech duration (only in OnSpeechEnd)AudioData- Raw audio samples (float array)SampleRate- Audio sample rate in HzTranscription- Speech-to-text result (if available)
OnSpeechEndAdvanced
Provides detailed speech end information including recorded audio:
void Start()
{
voiceController.OnSpeechEndAdvanced += HandleSpeechEnd;
}
void HandleSpeechEnd(object sender, VoiceReactEventArgs e)
{
Debug.Log($"Speech ended. Duration: {e.Duration}s");
Debug.Log($"Final volume: {e.DecibelLevel} dB");
// Save audio recording
if (e.AudioData != null)
{
SaveAudioClip(e.AudioData, e.SampleRate);
}
}OnSpeechDataAdvanced
Fires during speech with real-time audio data. Use for live processing or visualization.
void Start()
{
voiceController.OnSpeechDataAdvanced += HandleSpeechData;
}
void HandleSpeechData(object sender, VoiceReactEventArgs e)
{
// Real-time audio processing
UpdateAudioVisualization(e.AudioData);
// Current speech duration
Debug.Log($"Speaking for {e.Duration}s at {e.DecibelLevel} dB");
}VoiceListener Events
VoiceListener provides events for enemy/NPC hearing.
OnPlayerHeard (UnityEvent<Vector3, float>)
Fires when player becomes audible to this listener.
[SerializeField] private VoiceListener listener;
void Start()
{
listener.OnPlayerHeard.AddListener(OnHeardPlayer);
}
void OnHeardPlayer(Vector3 playerPosition, float decibelLevel)
{
Debug.Log($"Heard player at {playerPosition}");
Debug.Log($"Volume: {decibelLevel} dB");
// React to player voice
InvestigatePosition(playerPosition);
}Event Parameters:
playerPosition- Player’s 3D world positiondecibelLevel- Volume in decibels (-60 to 0 dB)
OnPlayerLost (UnityEvent)
Fires when player becomes inaudible (went silent or out of range).
void Start()
{
listener.OnPlayerLost.AddListener(OnLostPlayer);
}
void OnLostPlayer()
{
Debug.Log("Can no longer hear player");
ReturnToPatrol();
}OnDecibelLevelChanged (UnityEvent<float>)
Fires when player’s volume changes significantly while being heard.
void Start()
{
listener.OnDecibelLevelChanged.AddListener(OnVolumeChanged);
}
void OnVolumeChanged(float newDecibelLevel)
{
Debug.Log($"Player volume changed to {newDecibelLevel} dB");
// React to volume changes
AdjustBehaviorBasedOnVolume(newDecibelLevel);
}Low-Level Audio Events
MicrophoneInputHandler provides low-level audio capture events. Use these for custom audio processing.
OnVoiceCaptureStarted
Fires when voice capture begins (low-level).
var inputHandler = GetComponent<MicrophoneInputHandler>();
void Start()
{
inputHandler.OnVoiceCaptureStarted += HandleCaptureStarted;
}
void OnDestroy()
{
inputHandler.OnVoiceCaptureStarted -= HandleCaptureStarted;
}
void HandleCaptureStarted(object sender, AudioCaptureEventArgs e)
{
Debug.Log($"Voice capture started");
Debug.Log($"Sample rate: {e.SampleRate} Hz");
Debug.Log($"Noise floor: {e.NoiseFloor} dB");
}AudioCaptureEventArgs Properties:
AudioData- Raw audio samples (float array)SampleRate- Audio sample rate in HzDecibelLevel- Current dB levelNoiseFloor- Background noise level in dBIsVoiceDetected- Is voice currently detected
OnVoiceCaptureStopped
Fires when voice capture ends.
void Start()
{
inputHandler.OnVoiceCaptureStopped += HandleCaptureStopped;
}
void HandleCaptureStopped(object sender, AudioCaptureEventArgs e)
{
Debug.Log("Voice capture stopped");
// Process final audio buffer
ProcessRecording(e.AudioData, e.SampleRate);
}OnVoiceCaptureData
Fires during capture with real-time audio data chunks.
void Start()
{
inputHandler.OnVoiceCaptureData += HandleCaptureData;
}
void HandleCaptureData(object sender, AudioCaptureEventArgs e)
{
// Live audio processing
AnalyzeAudioChunk(e.AudioData);
}Common Event Patterns
UI Feedback
Show visual feedback when player speaks:
using UnityEngine;
using UnityEngine.UI;
public class VoiceFeedbackUI : MonoBehaviour
{
[SerializeField] private VoiceReactController voiceController;
[SerializeField] private Image micIndicator;
void Start()
{
voiceController.OnSpeechStart.AddListener(ShowMicActive);
voiceController.OnSpeechEnd.AddListener(HideMicActive);
}
void ShowMicActive()
{
micIndicator.color = Color.red;
}
void HideMicActive(float duration)
{
micIndicator.color = Color.gray;
}
}Audio Recording
Save player speech recordings:
using System.IO;
public class SpeechRecorder : MonoBehaviour
{
[SerializeField] private VoiceReactController voiceController;
void Start()
{
voiceController.OnSpeechEndAdvanced += RecordSpeech;
}
void RecordSpeech(object sender, VoiceReactEventArgs e)
{
if (e.AudioData == null || e.AudioData.Length == 0)
return;
// Create AudioClip from samples
var clip = AudioClip.Create(
"Recording",
e.AudioData.Length,
1, // mono
e.SampleRate,
false
);
clip.SetData(e.AudioData, 0);
// Save to file (requires additional WAV encoding)
SaveClipToFile(clip);
}
}Volume-Based Animations
Animate character based on volume:
public class VoiceAnimator : MonoBehaviour
{
[SerializeField] private VoiceReactController voiceController;
[SerializeField] private Animator animator;
void Start()
{
voiceController.OnSpeechStart.AddListener(OnStartSpeaking);
voiceController.OnSpeechEnd.AddListener(OnStopSpeaking);
}
void Update()
{
if (voiceController.IsPlayerSpeaking)
{
// Animate based on volume
float volume = voiceController.NormalizedVolume;
animator.SetFloat("VoiceIntensity", volume);
}
}
void OnStartSpeaking()
{
animator.SetBool("IsSpeaking", true);
}
void OnStopSpeaking(float duration)
{
animator.SetBool("IsSpeaking", false);
}
}Event Chaining
Chain events for complex behavior:
public class ChainedEventExample : MonoBehaviour
{
[SerializeField] private VoiceReactController voiceController;
private int consecutiveSpeechCount = 0;
void Start()
{
voiceController.OnSpeechEnd.AddListener(OnSpeechEnded);
}
void OnSpeechEnded(float duration)
{
// Count consecutive short speeches
if (duration < 1f)
{
consecutiveSpeechCount++;
if (consecutiveSpeechCount >= 3)
{
Debug.Log("Player is spamming short noises!");
TriggerPenalty();
consecutiveSpeechCount = 0;
}
}
else
{
consecutiveSpeechCount = 0;
}
}
}Multi-Listener Coordination
Coordinate multiple listeners:
public class ListenerCoordinator : MonoBehaviour
{
private VoiceListener[] allListeners;
void Start()
{
allListeners = FindObjectsByType<VoiceListener>(FindObjectsSortMode.None);
foreach (var listener in allListeners)
{
listener.OnPlayerHeard.AddListener(OnAnyListenerHeard);
}
}
void OnAnyListenerHeard(Vector3 position, float db)
{
// Count how many can hear
int hearingCount = 0;
foreach (var listener in allListeners)
{
if (listener.CanHearPlayer)
hearingCount++;
}
Debug.Log($"{hearingCount} enemies can hear the player!");
if (hearingCount >= 3)
{
TriggerSurroundedEvent();
}
}
}Event Cleanup
Always unsubscribe from C# events to prevent memory leaks:
void Start()
{
voiceController.OnSpeechStartAdvanced += HandleSpeechStart;
}
void OnDestroy()
{
// IMPORTANT: Unsubscribe when destroyed
if (voiceController != null)
{
voiceController.OnSpeechStartAdvanced -= HandleSpeechStart;
}
}UnityEvents are automatically cleaned up by Unity, but C# events require manual unsubscription.
Performance Considerations
Event Rate Limiting
Limit how often events are processed:
private float lastEventTime = 0f;
private float eventCooldown = 0.5f;
void HandleSpeechData(object sender, VoiceReactEventArgs e)
{
if (Time.time - lastEventTime < eventCooldown)
return;
lastEventTime = Time.time;
// Process event
DoExpensiveOperation(e.AudioData);
}Conditional Event Processing
Only process events when needed:
void HandleSpeechStart(object sender, VoiceReactEventArgs e)
{
// Only process if player is in dangerous area
if (!IsInDangerZone())
return;
// Process event
AlertEnemies();
}