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 neededConfiguration:
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:
-
Add scripting define symbol:
- Go to Edit > Project Settings > Player
- Add
VOICEREACT_DISSONANCEto Scripting Define Symbols
-
Configure MicrophoneInputHandler:
// In Inspector:
// - Set Provider Type: Dissonance
// - Assign Dissonance Comms component reference- 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
#endifBenefits:
- 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:
- Add
MyAudioProviderComponentto a GameObject - Assign required references (AudioSource, etc.)
- 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:
- Select GameObject with MicrophoneInputHandler
- Set Provider Type
- 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();
}