TTS and PlaybackThread

September 11, 2026 · View on GitHub


TTSFactory

Module: ovos_audio.tts.TTSFactory

Thin wrapper around OVOSTTSFactory from ovos-plugin-manager.

from ovos_audio.tts import TTSFactory

tts = TTSFactory.create()          # reads config from mycroft.conf
tts = TTSFactory.create(config)    # use a specific config dict

TTSFactory.create() resolves config["tts"]["module"] to an installed TTS plugin entry point and instantiates it. The returned object is a TTS instance from ovos_plugin_manager.templates.tts.

After creation, the TTS must be initialised with the bus and playback thread:

tts.init(bus, playback_thread)

PlaybackThread

Module: ovos_audio.playback.PlaybackThread

A daemon Thread that consumes entries from TTS.queue (a Queue) and plays them sequentially. All TTS output and queued sounds pass through this thread to ensure they never overlap.

Queue Entry Format

(audio_path: str, visemes: list, listen: bool, tts_id: str, message: Message)
  • audio_path: path to the synthesized WAV/MP3 file
  • visemes: list of (phoneme, timestamp) pairs for mouth animation. None if unavailable
  • listen: True if the microphone should be activated after playback
  • tts_id: identifier of the TTS plugin that produced the audio. "sounds" for queued sound files
  • message: originating speak message for context forwarding

Lifecycle

PlaybackThread.run()
  └── loop:
        dequeue entry → _play()
            ├── on_start()         → begin_audio()  → emit ovos.audio.output.started
            ├── TTSTransformersService.transform()   (post-process wav)
            ├── emit recognizer_loop:utterance_start
            ├── play_audio(path)   (subprocess via ovos_utils.sound)
            ├── show_visemes()     (if enclosure set)
            └── on_end(listen)     → end_audio()    → emit ovos.audio.output.ended
                                                     → emit ovos.mic.listen  (if listen=True)

OCP Integration

When tts.ocp_cork or tts.ocp_duck is set in config, begin_audio() and end_audio() emit the corresponding OCP control events:

Config keybegin_audio emitsend_audio emits
ocp_cork: trueovos.common_play.corkovos.common_play.uncork
ocp_duck: trueovos.common_play.duckovos.common_play.unduck

If pulse_duck: true, no bus events are emitted. Ducking is handled at the OS PulseAudio level.

G2P Integration

If a G2P (Grapheme-to-Phoneme) plugin is configured (g2p.module in mycroft.conf), PlaybackThread loads it at startup. When viseme data is not provided by the TTS plugin, the G2P plugin generates visemes from the utterance text for mouth animations.

{
  "g2p": {
    "module": "ovos-g2p-plugin-mimic"
  }
}

Key Methods

MethodDescription
set_bus(bus)Attach a bus instance (also propagated to TTSTransformersService)
clear_queue()Drain the queue and terminate any playing subprocess
clear()Alias for clear_queue()
pause()Stop current playback and block the queue
MethodDescription
resume()Resume a paused playback
stop()Terminate thread and clear queue
shutdown()Alias for stop()
show_visemes(pairs)Send viseme data to enclosure (if enclosure is set)

Properties

PropertyTypeDescription
is_runningboolTrue if started and not terminated
_now_playingtuple | NoneThe currently dequeued entry

Bus Events Emitted by PlaybackThread

EventWhen
ovos.audio.output.startedPlayback of a batch of queued audio begins
ovos.audio.output.endedPlayback of a batch of queued audio ends
recognizer_loop:utterance_startEach individual utterance starts playing
ovos.mic.listenAfter speech ends when listen=True
EventWhen
ovos.common_play.corkBefore speech if ocp_cork=True
ovos.common_play.uncorkAfter speech if ocp_cork=True
ovos.common_play.duckBefore speech if ocp_duck=True
ovos.common_play.unduckAfter speech if ocp_duck=True

← playback-service.md · Home · audio-service.md →