Voice Output
Let NPCs speak their responses aloud with text-to-speech. Voice output is optional - LoreMindNPC works without it.
Providers
LoreMind supports two TTS providers:
| Provider | Type | Cost | Quality | Latency |
|---|---|---|---|---|
| Kokoro | Local | FREE | High (#1 on HuggingFace) | ~50-200ms |
| ElevenLabs | Cloud | Paid | Very High | ~200-500ms |
Recommendation:
- Development/Testing: Use Kokoro (free, offline, no API costs)
- Production: Either works - Kokoro is viable for PC/console, ElevenLabs for mobile/WebGL
Prerequisites
Kokoro (Local TTS)
Kokoro runs entirely on-device using the Kokoro-82M model (~80MB).
- Install KokoroSharp NuGet package
- Add
LOREMIND_KOKOROscripting define symbol
ElevenLabs (Cloud TTS)
- Create account at elevenlabs.io
- Get API key from your dashboard
- Enter API key in Window > LoreMind > Control Panel > Voice
Installation
1. Add Scripting Define (Kokoro only)
- Go to Edit > Project Settings > Player
- Expand Other Settings > Script Compilation
- Add
LOREMIND_KOKOROto Scripting Define Symbols - Click Apply
2. Enable in Project Settings
- Open Window > LoreMind > Control Panel
- Go to Voice tab
- Enable Text-to-Speech
- Select TTS Provider (Kokoro or ElevenLabs)
- For ElevenLabs: Enter your API key
Quick Setup
Add the Component
- Select your NPC GameObject (with
LoreMindNPC) - Add Component > LoreMind > Voice > Voice Output
The component auto-adds an AudioSource for playback.
Enable Auto-Speak
The easiest way to use voice output:
- On your
LoreMindNPC, enable Auto-Speak Responses - Assign the Voice Output component
Now every NPC response is automatically spoken!
Manual Control
For more control, trigger speech from code:
using UnityEngine;
using Peek.LoreMind;
using Peek.LoreMind.Voice;
public class NPCVoiceDemo : MonoBehaviour
{
[SerializeField] private LoreMindNPC npc;
[SerializeField] private LoreMindVoiceOutput voiceOutput;
void Start()
{
// Speak when NPC responds
npc.OnResponseReceived.AddListener(response =>
{
voiceOutput.Speak(response);
});
}
}Per-NPC Voice Configuration
Each NPC can have its own unique voice:
// Set voice in code
voiceOutput.VoiceId = "am_adam"; // Kokoro voice ID
// Or override provider for this NPC only
voiceOutput.OverrideProvider = true;
voiceOutput.ProviderOverride = TTSProviderType.ElevenLabs;Kokoro Voices
| Voice ID | Name | Accent | Gender |
|---|---|---|---|
af_bella | Bella | American | Female |
af_nicole | Nicole | American | Female |
af_sarah | Sarah | American | Female |
af_sky | Sky | American | Female |
am_adam | Adam | American | Male |
am_michael | Michael | American | Male |
bf_emma | Emma | British | Female |
bf_isabella | Isabella | British | Female |
bm_george | George | British | Male |
bm_lewis | Lewis | British | Male |
ElevenLabs Voices
Voice IDs are fetched from your ElevenLabs account. Check the Voice Output inspector or call:
TTSVoiceInfo[] voices = voiceOutput.GetAvailableVoices();
foreach (var voice in voices)
{
Debug.Log($"{voice.name}: {voice.id}");
}Complete Example
using UnityEngine;
using UnityEngine.UI;
using Peek.LoreMind;
using Peek.LoreMind.Voice;
public class TalkingNPC : MonoBehaviour
{
[SerializeField] private LoreMindNPC npc;
[SerializeField] private LoreMindVoiceOutput voiceOutput;
[Header("UI")]
[SerializeField] private Text subtitleText;
[SerializeField] private Image speakingIndicator;
void Start()
{
// Configure voice
voiceOutput.VoiceId = "am_adam"; // Male voice
// Wire up voice events
voiceOutput.OnSpeechStarted.AddListener(() =>
{
speakingIndicator.color = Color.green;
});
voiceOutput.OnSpeechCompleted.AddListener(() =>
{
speakingIndicator.color = Color.gray;
});
voiceOutput.OnError.AddListener(error =>
{
Debug.LogError($"TTS Error: {error}");
speakingIndicator.color = Color.red;
});
// Show subtitles and speak
npc.OnResponseReceived.AddListener(response =>
{
subtitleText.text = response;
voiceOutput.Speak(response);
});
}
// Public method for UI button
public void StopSpeaking()
{
voiceOutput.Stop();
}
}Configuration
Playback Settings
| Setting | Description | Default |
|---|---|---|
| Voice ID | Provider-specific voice identifier | (default) |
| Use Streaming | Start playback before synthesis completes | true |
| Speech Rate | Playback speed (0.5 = slow, 2.0 = fast) | 1.0 |
| Volume | Audio volume | 1.0 |
Provider Override
| Setting | Description | Default |
|---|---|---|
| Override Provider | Use different provider for this NPC | false |
| Provider Override | Provider to use when overriding | None |
Events
// Speech lifecycle
voiceOutput.OnSpeechStarted.AddListener(() => ShowSpeakingUI());
voiceOutput.OnSpeechCompleted.AddListener(() => HideSpeakingUI());
// Error handling
voiceOutput.OnError.AddListener(error => ShowErrorMessage(error));API Summary
Properties
bool IsSpeaking { get; }
bool IsSynthesizing { get; }
string LastSpokenText { get; }
AudioSource AudioSource { get; }
string VoiceId { get; set; }
bool OverrideProvider { get; set; }
TTSProviderType ProviderOverride { get; set; }Methods
void Speak(string text)
Task SpeakAsync(string text)
void Stop()
TTSVoiceInfo[] GetAvailableVoices()
string GetProviderName()
Task ReinitializeProviderAsync()Streaming vs Non-Streaming
Streaming (Default)
Audio starts playing while still being synthesized. Lower perceived latency.
voiceOutput.UseStreaming = true; // Inspector or codeNon-Streaming
Waits for complete audio before playing. Useful for short responses or when you need the full audio clip.
voiceOutput.UseStreaming = false;Async/Await Pattern
For precise control over speech completion:
public async void HandleResponse(string response)
{
// Wait for speech to complete
await voiceOutput.SpeakAsync(response);
// Speech finished, continue with next action
ShowNextDialogueOption();
}Combining Voice Input and Output
Create a full voice conversation loop:
using UnityEngine;
using Peek.LoreMind;
using Peek.LoreMind.Voice;
public class VoiceConversation : MonoBehaviour
{
[SerializeField] private LoreMindNPC npc;
[SerializeField] private LoreMindVoiceInput voiceInput;
[SerializeField] private LoreMindVoiceOutput voiceOutput;
void Start()
{
// Player speaks -> NPC responds -> NPC speaks
voiceInput.OnTranscription.AddListener(playerText =>
{
npc.Respond(playerText);
});
npc.OnResponseReceived.AddListener(response =>
{
voiceOutput.Speak(response);
});
}
}Troubleshooting
”TTS provider not available”
- Check TTS is enabled in Control Panel > Voice
- Verify scripting define (
LOREMIND_KOKORO) is set - For ElevenLabs: Check API key is valid
No audio plays
- Verify AudioSource component exists and is enabled
- Check AudioSource volume is not zero
- Check AudioListener exists in scene
First synthesis is slow
Kokoro loads the model on first use (~1-2 seconds). Subsequent calls are fast.
// Pre-warm at scene start
await voiceOutput.SpeakAsync(""); // Empty string, just initializesAudio sounds choppy (streaming)
Try disabling streaming for this NPC:
voiceOutput.UseStreaming = false;ElevenLabs rate limit
ElevenLabs has usage limits. If you hit rate limits:
- Reduce speech frequency
- Cache common responses
- Consider Kokoro for development
Platform Support
| Platform | Kokoro | ElevenLabs |
|---|---|---|
| Windows | Native | API |
| macOS | Native | API |
| Linux | Native | API |
| iOS/Android | Via ONNX | API |
| WebGL | Not supported | API |
Best Practices
Provide Visual Feedback
Always indicate when the NPC is speaking:
voiceOutput.OnSpeechStarted.AddListener(() =>
{
npcNameplate.color = Color.green;
speakingBubble.SetActive(true);
});
voiceOutput.OnSpeechCompleted.AddListener(() =>
{
npcNameplate.color = Color.white;
speakingBubble.SetActive(false);
});Show Subtitles
Not all players can use audio. Always show text:
npc.OnResponseReceived.AddListener(response =>
{
subtitleText.text = response;
voiceOutput.Speak(response);
});Handle Interruptions
Let players skip or interrupt speech:
void Update()
{
if (Input.GetKeyDown(KeyCode.Space) && voiceOutput.IsSpeaking)
{
voiceOutput.Stop();
}
}Different Voices for Different NPCs
Give each NPC character a unique voice:
// Gruff blacksmith - male British
blacksmithVoice.VoiceId = "bm_george";
// Young apprentice - female American
apprenticeVoice.VoiceId = "af_sky";
// Wise elder - male American
elderVoice.VoiceId = "am_adam";Next Steps
- Voice Input - Let players speak to NPCs
- LoreMindNPC Component - Main component guide
- API Reference - Complete API documentation