Vocalis
Local speech synthesis into Unity AudioClip output.
Vocalis wraps a native TTS engine, converts returned float samples into an AudioClip and can play that clip through an assigned AudioSource. It also contains a StreamingAssets voice catalog and PCM helpers.
Overview
Vocalis is for local NPC dialogue, narration and accessibility paths where text should become an AudioClip without a cloud request. VocalisSpeaker owns the inspector-facing voice and playback fields, while NativeTtsEngine calls the native synthesis functions.
The source also includes VoiceCatalog for JSON voice definitions and PcmUtility for clip conversion and a simple speed operation. Native synthesis is the implemented path; voice catalog loading and utility methods are separate APIs.
Requirements
- Target Unity baseline: 2021.3 LTS and newer.
- A native TTS library is required. The source imports
vocalis_ttsand comments that platform libraries are placed in the Unity native plugin folder. NativeTtsEngine.IsNativeAvailablereturns true for Windows, macOS and Linux editor/player platforms only.- Voice model data is read from
Assets/StreamingAssets/Vocalis/model.onnxby the native engine. - Mono/IL2CPP native import settings and stripping behavior require target validation. The source does not include a link.xml or preservation attribute.
Setup
- Copy the plugin folder into
Assets/Denisitree/Vocalis/. - Provide the native library and the model path expected by
NativeTtsEngine.Synthesize. - Add
VocalisSpeakerto a GameObject and assigntarget,voiceId,speedandpitch. - For voice definitions, place JSON files under a StreamingAssets subfolder and call
VoiceCatalog.Loadwith its root path.
SynthesizeAsync logs a warning and returns null.Quick start
The sample constructs a speaker with Inspector-assigned playback and explicit voice settings.
var speaker = new VocalisSpeaker { target = target, voiceId = "en-US-voice-01", speed = 1f, pitch = 0f };
await speaker.SpeakAsync(prompt);- Assign an AudioSource to
target. - Choose a voice ID.
- Set speed and pitch.
- Await
SpeakAsync.
Core concepts
Native synthesis
NativeTtsEngine initializes the ONNX model path, calls native synthesis with text, voice ID, speed and pitch, copies the output buffer and frees the native context.
AudioClip conversion
VocalisSpeaker.SynthesizeAsync runs native synthesis in Task.Run, then calls PcmUtility.ToAudioClip. SpeakAsync assigns and plays the result only when both clip and target are non-null.
Voice catalog
VoiceCatalog.Load combines Application.streamingAssetsPath with a root path, reads JSON files and deserializes VoiceDefinition values containing id, name and language.
Speed helper
PcmUtility.ApplySpeed allocates a new array based on speed and linearly interpolates neighboring samples. It is not called by VocalisSpeaker in the current source.
API reference
VocalisSpeaker
| Name | Type | Description | Default |
|---|---|---|---|
| voiceId | string | Voice identifier passed to the native engine. | en-US-voice-01 |
| speed | float | Native synthesis speed. | 1f |
| pitch | float | Native synthesis pitch. | 0f |
| target | AudioSource | Playback target. | null |
| SynthesizeAsync | Task<AudioClip> | Runs native synthesis and converts samples into a clip. | - |
| SpeakAsync | Task | Synthesizes, assigns and plays when possible. | - |
public string voiceId = "en-US-voice-01";
public float speed = 1f;
public float pitch = 0f;
public AudioSource target;
Task<AudioClip> SynthesizeAsync(string text)
Task SpeakAsync(string text)
ITtsEngine and NativeTtsEngine
| Name | Type | Description | Default |
|---|---|---|---|
| Initialize | void | Interface method accepting a model path. | - |
| Synthesize | (float[] samples, int sampleRate) | Interface method for text, voice ID, speed and pitch. | - |
| GetVoices | IReadOnlyList<string> | Interface method returning voice IDs. | - |
| IsNativeAvailable | static bool | Checks supported desktop runtime platforms. | - |
| NativeTtsEngine.Synthesize | (float[] samples, int sampleRate) | Calls native synthesis and copies the result. | - |
void Initialize(string modelPath)
(float[] samples, int sampleRate) Synthesize(string text, string voiceId, float speed, float pitch)
IReadOnlyList<string> GetVoices()
static bool IsNativeAvailable()
(float[] samples, int sampleRate) Synthesize(string text, string voiceId, float speed, float pitch)
VoiceCatalog and VoiceDefinition
| Name | Type | Description | Default |
|---|---|---|---|
| id | string | Serialized voice identifier. | null |
| name | string | Serialized display name. | null |
| language | string | Serialized language value. | null |
| Load | IReadOnlyList<VoiceDefinition> | Reads JSON voice files below a StreamingAssets root path. | - |
public string id;
public string name;
public string language;
IReadOnlyList<VoiceDefinition> Load(string rootPath)
PcmUtility
| Name | Type | Description | Default |
|---|---|---|---|
| ToAudioClip | static AudioClip | Creates a one-channel clip and sets its samples. | - |
| ApplySpeed | static float[] | Creates an interpolated sample array based on speed. | - |
static AudioClip ToAudioClip(float[] samples, int sampleRate, string clipName)
static float[] ApplySpeed(float[] samples, float speed)VocalisDemo
The public sample class exposes two public fields:
| Name | Type | Description | Default |
|---|---|---|---|
| prompt | string | Text passed to SpeakAsync. | Welcome to the den, traveler. |
| target | AudioSource | Playback target. | null |
Recipes
Speak with Inspector values
Use VocalisSpeaker with an assigned AudioSource, then call SpeakAsync. The sample's default voice, speed and pitch are explicit in its object initializer.
Change voice speed at runtime
Set the public speed field before calling SynthesizeAsync or SpeakAsync. The value is passed to the native engine.
Load voice definitions
Instantiate VoiceCatalog and call Load(rootPath). The returned list contains only successfully deserialized JSON definitions.
Apply the PCM speed helper
Call PcmUtility.ApplySpeed directly when you own a float sample buffer. This helper is available but not wired into the speaker path.
Performance and platform notes
Native synthesis is placed inside Task.Run; clip creation and AudioSource playback happen after the task returns. The source provides no benchmark or memory guarantee.
Native availability is limited to the desktop platforms listed in the source. StreamingAssets paths and native library import settings need target-specific validation, especially for IL2CPP.
Troubleshooting / FAQ
Empty text returns null, and unavailable native platforms also return null after logging a warning. Check both conditions.
Provide the library named by the P/Invoke binding and configure it for the target platform. This prototype requires a native library.
The source passes Assets/StreamingAssets/Vocalis/model.onnx to the native initializer.
SpeakAsync only assigns and plays when both the generated clip and target are non-null.
The computed StreamingAssets directory must exist and contain JSON files. Invalid or null definitions are skipped.
The source has no link.xml or preservation attribute. Validate native imports and stripping in the target build.
Planned
- Validated native packaging and target-platform coverage.
- Wiring
ApplySpeedinto the speaker path, if that becomes part of the API contract. - Expanded voice catalog validation and runtime voice selection behavior.
Something unclear?