Skip to Content
DocsVoicereactCustom Audio Providers

Custom Audio Providers

VoiceReact supports multiple audio sources through the audio provider system. Use built-in providers or create custom ones.

Built-In Providers

Unity Microphone (Default)

Uses Unity’s built-in Microphone API. Works on all platforms.

// Automatically used by default - no configuration needed

Configuration:

var handler = GetComponent<MicrophoneInputHandler>(); // Get available microphone devices string[] devices = handler.GetAvailableDevices(); foreach (var device in devices) { Debug.Log($"Available microphone: {device}"); } // Set specific microphone handler.SetMicrophoneDevice("My Microphone Name"); // Get current device string currentDevice = handler.GetCurrentDevice();

Device selection persists via PlayerPrefs.

Dissonance VoIP

Use Dissonance voice chat as audio input. Perfect for multiplayer games.

Setup:

  1. Add scripting define symbol:

    • Go to Edit > Project Settings > Player
    • Add VOICEREACT_DISSONANCE to Scripting Define Symbols
  2. Configure MicrophoneInputHandler:

// In Inspector: // - Set Provider Type: Dissonance // - Assign Dissonance Comms component reference
  1. VoiceReact will now use Dissonance’s microphone input:
#if VOICEREACT_DISSONANCE using Dissonance; // MicrophoneInputHandler automatically uses Dissonance microphone // Same microphone input for voice chat AND gameplay mechanics #endif

Benefits:

  • Single microphone input for voice chat and gameplay
  • No duplicate microphone access
  • Consistent audio quality
  • Works with Dissonance’s transmission system

Custom Audio Providers

Create custom providers to integrate any audio source.

IAudioProvider Interface

All audio providers implement this interface:

using UnityEngine; using Peek.VoiceReact.Audio; public interface IAudioProvider { // Provider identification string ProviderName { get; } string DeviceName { get; } // Audio stream AudioClip AudioClip { get; } int SampleRate { get; } int Channels { get; } bool IsActive { get; } // Lifecycle bool Initialize(int sampleRate, int lengthSeconds); bool Start(); void Stop(); void Dispose(); // Audio data access int GetCurrentPosition(); bool GetAudioData(float[] samples, int offsetSamples); // Device management string[] GetAvailableDevices(); bool SetDevice(string deviceName); string GetCurrentDevice(); }

Create Custom Provider Component

Extend AudioProviderComponent to create Inspector-assignable providers:

using UnityEngine; using Peek.VoiceReact.Audio; public class MyCustomAudioProvider : AudioProviderComponent { [SerializeField] private AudioSource audioSource; public override IAudioProvider CreateProvider(int sampleRate, int lengthSeconds) { // Return your custom IAudioProvider implementation return new MyAudioProviderImplementation(audioSource, sampleRate, lengthSeconds); } }

Example: AudioSource Provider

Stream audio from an existing AudioSource:

using UnityEngine; using Peek.VoiceReact.Audio; public class AudioSourceProvider : IAudioProvider { private AudioSource _source; private AudioClip _clip; private int _sampleRate; private int _channels; public string ProviderName => "AudioSource Provider"; public string DeviceName => _source != null ? _source.gameObject.name : null; public AudioClip AudioClip => _clip; public int SampleRate => _sampleRate; public int Channels => _channels; public bool IsActive { get; private set; } public AudioSourceProvider(AudioSource source, int sampleRate, int lengthSeconds) { _source = source; _sampleRate = sampleRate; _channels = 1; // Mono } public bool Initialize(int sampleRate, int lengthSeconds) { if (_source == null || _source.clip == null) return false; _clip = _source.clip; _sampleRate = _clip.frequency; _channels = _clip.channels; return true; } public bool Start() { if (_source == null) return false; _source.Play(); IsActive = true; return true; } public void Stop() { if (_source != null) _source.Stop(); IsActive = false; } public int GetCurrentPosition() { return _source != null ? _source.timeSamples : 0; } public bool GetAudioData(float[] samples, int offsetSamples) { if (_clip == null) return false; return _clip.GetData(samples, offsetSamples); } public void Dispose() { Stop(); } // Device management (not applicable for AudioSource) public string[] GetAvailableDevices() => new string[0]; public bool SetDevice(string deviceName) => false; public string GetCurrentDevice() => DeviceName; }

Example: Streaming Audio Provider

Stream audio from external source (network, file, etc):

using System.Collections.Generic; using UnityEngine; using Peek.VoiceReact.Audio; public class StreamingAudioProvider : IAudioProvider { private Queue<float> _audioBuffer = new Queue<float>(); private AudioClip _clip; private int _sampleRate; private int _currentPosition; public string ProviderName => "Streaming Audio Provider"; public string DeviceName => "Network Stream"; public AudioClip AudioClip => _clip; public int SampleRate => _sampleRate; public int Channels => 1; public bool IsActive { get; private set; } public bool Initialize(int sampleRate, int lengthSeconds) { _sampleRate = sampleRate; // Create circular audio buffer _clip = AudioClip.Create( "StreamBuffer", sampleRate * lengthSeconds, 1, sampleRate, true, OnAudioRead ); return true; } public bool Start() { IsActive = true; return true; } public void Stop() { IsActive = false; } // Called by Unity to fill audio buffer private void OnAudioRead(float[] data) { for (int i = 0; i < data.Length; i++) { if (_audioBuffer.Count > 0) { data[i] = _audioBuffer.Dequeue(); } else { data[i] = 0f; } } _currentPosition += data.Length; } // Add streaming audio data public void StreamAudioData(float[] samples) { foreach (float sample in samples) { _audioBuffer.Enqueue(sample); } } public int GetCurrentPosition() => _currentPosition; public bool GetAudioData(float[] samples, int offsetSamples) { return _clip != null && _clip.GetData(samples, offsetSamples); } public void Dispose() { Stop(); _audioBuffer.Clear(); } public string[] GetAvailableDevices() => new string[0]; public bool SetDevice(string deviceName) => false; public string GetCurrentDevice() => DeviceName; }

Using Custom Providers

Option 1: Component-Based (Inspector)

Create a component that can be assigned in Inspector:

using UnityEngine; using Peek.VoiceReact.Audio; public class MyAudioProviderComponent : AudioProviderComponent { [SerializeField] private AudioSource targetAudioSource; public override IAudioProvider CreateProvider(int sampleRate, int lengthSeconds) { return new AudioSourceProvider(targetAudioSource, sampleRate, lengthSeconds); } }

Setup:

  1. Add MyAudioProviderComponent to a GameObject
  2. Assign required references (AudioSource, etc.)
  3. On MicrophoneInputHandler:
    • Set Provider Type to CustomComponent
    • Assign Custom Provider Component reference

Option 2: Code-Based

Create provider directly in code:

using UnityEngine; using Peek.VoiceReact.Audio; public class CustomProviderSetup : MonoBehaviour { void Start() { var handler = GetComponent<MicrophoneInputHandler>(); // This requires modifying MicrophoneInputHandler // to accept runtime provider injection // (not currently supported in base implementation) } }

Note: The base MicrophoneInputHandler creates providers in Awake(). For runtime provider switching, you’d need to extend MicrophoneInputHandler.

Common Use Cases

Network Voice Streaming

Stream voice from network in multiplayer:

public class NetworkVoiceProvider : AudioProviderComponent { private StreamingAudioProvider streamProvider; public override IAudioProvider CreateProvider(int sampleRate, int lengthSeconds) { streamProvider = new StreamingAudioProvider(); streamProvider.Initialize(sampleRate, lengthSeconds); return streamProvider; } // Call this when receiving network audio packets public void OnNetworkAudioReceived(float[] audioSamples) { streamProvider?.StreamAudioData(audioSamples); } }

Pre-Recorded Audio Testing

Test voice detection with pre-recorded audio:

public class RecordedAudioProvider : AudioProviderComponent { [SerializeField] private AudioClip recordedClip; public override IAudioProvider CreateProvider(int sampleRate, int lengthSeconds) { var provider = new RecordedClipProvider(recordedClip); provider.Initialize(sampleRate, lengthSeconds); return provider; } }

Hardware Integration

Integrate specialized audio hardware:

public class HardwareAudioProvider : IAudioProvider { private HardwareAudioDevice _device; // Initialize hardware public bool Initialize(int sampleRate, int lengthSeconds) { _device = new HardwareAudioDevice(); return _device.Connect(sampleRate); } // Stream from hardware public bool GetAudioData(float[] samples, int offsetSamples) { return _device.ReadSamples(samples); } // ... implement other interface members }

Provider Selection

MicrophoneInputHandler selects provider based on configuration:

public enum AudioProviderType { UnityMicrophone, // Unity Microphone API (default) Dissonance, // Dissonance VoIP (requires scripting define) CustomComponent // Custom AudioProviderComponent }

Selection in Inspector:

  1. Select GameObject with MicrophoneInputHandler
  2. Set Provider Type
  3. For CustomComponent, assign Custom Provider Component reference

Best Practices

Thread Safety

Audio providers may be accessed from background threads. Ensure thread-safe operations:

private readonly object _lock = new object(); public bool GetAudioData(float[] samples, int offsetSamples) { lock (_lock) { // Thread-safe data access return _clip.GetData(samples, offsetSamples); } }

Resource Cleanup

Always implement Dispose() properly:

public void Dispose() { Stop(); if (_clip != null) { Object.Destroy(_clip); _clip = null; } _audioBuffer?.Clear(); }

Error Handling

Handle initialization failures gracefully:

public bool Initialize(int sampleRate, int lengthSeconds) { try { // Initialize resources return true; } catch (Exception e) { Debug.LogError($"Provider initialization failed: {e.Message}"); return false; } }

Platform Support

Check platform compatibility:

public bool Initialize(int sampleRate, int lengthSeconds) { #if UNITY_WEBGL Debug.LogError("This provider doesn't support WebGL"); return false; #endif // Initialize for supported platforms return true; }

Debugging Custom Providers

Log provider lifecycle:

public bool Initialize(int sampleRate, int lengthSeconds) { Debug.Log($"[{ProviderName}] Initializing: {sampleRate} Hz, {lengthSeconds}s"); bool success = InitializeInternal(sampleRate, lengthSeconds); Debug.Log($"[{ProviderName}] Initialize result: {success}"); return success; } public bool Start() { Debug.Log($"[{ProviderName}] Starting"); return StartInternal(); } public void Stop() { Debug.Log($"[{ProviderName}] Stopping"); StopInternal(); }

Next Steps

Last updated on