Class: OllamaChat::TTS

Inherits:
Object
  • Object
show all
Includes:
Ollama::Handlers::Concern, KramdownANSI, Utils::UTF8Converter, Utils::ValueFormatter
Defined in:
lib/ollama_chat/tts.rb

Overview

Text-to-Speech handler for streaming audio playback.

This class handles converting streamed text responses into spoken audio using a remote TTS server. It manages parallel TTS requests for text blocks while maintaining strict ordering of audio chunks through an OrderedQueue. A coordinator thread ensures chunks are played in the correct sequence, even when multiple TTS requests complete out of order.

Examples:

tts = OllamaChat::TTS.new(chat: chat, voice: 'alloy')
tts.call(response)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from KramdownANSI

#configure_kramdown_ansi_styles, #kramdown_ansi_parse, #kramdown_markdown_remove

Methods included from Utils::ValueFormatter

#format_bytes, #format_tokens

Methods included from Utils::UTF8Converter

#convert_to_utf8

Constructor Details

#initialize(chat:, voice: nil) ⇒ OllamaChat::TTS

Initializes a new TTS handler.

Sets up the audio player, ordered queue for managing audio chunks, and starts a coordinator thread that processes chunks in order.

Parameters:

  • chat (OllamaChat::Chat)

    the chat instance for logging

  • voice (String, nil) (defaults to: nil)

    the voice ID to use for TTS (must be in the list returned by self.voices; falls back to nil if invalid or not provided)



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/ollama_chat/tts.rb', line 50

def initialize(chat:, voice: nil)
  if voice
    if model = @voice_model = chat.config.voice.model?
      self.class.voices(model:).member?(voice) or voice = nil
    else
      self.class.voices.member?(voice) or voice = nil
      @voice_model = 'tts-1'
    end
  end
  @chat              = chat
  @voice             = voice
  @buffer            = +''
  @audio_player      = OllamaChat::Utils::AudioPlayer.new
  @ordered_queue     = OllamaChat::Utils::OrderedQueue.new
  @id_mutex          = Mutex.new
  @next_id           = 0
  @current_thread_id = 1
  @enqueued_count    = 0
  @finished_count    = 0
  @worker_threads    = []
  @coordinator       = Thread.new { run_coordinator }

  super(output: Tins::NULL)
end

Instance Attribute Details

#voiceString? (readonly)

The voice attribute reader returns the voice associated with the object.

Returns:

  • (String, nil)

    the voice ID or nil if not set



79
80
81
# File 'lib/ollama_chat/tts.rb', line 79

def voice
  @voice
end

#voice_modelString? (readonly)

The model attribute reader returns the TTS model ID from config.

Returns:

  • (String, nil)

    the model ID or nil if not configured



84
85
86
# File 'lib/ollama_chat/tts.rb', line 84

def voice_model
  @voice_model
end

Class Method Details

.voices(model: nil) ⇒ Array<String>

Returns a sorted list of available TTS voice IDs.

When model is nil the generic /v1/voices endpoint is queried and a hash array ([{"id": "…"}, …]) is expected. When a model name is given, /v1/audio/voices?model=<id> is queried and a string array (["…", …]) is expected (audio.cpp convention). If the server is unreachable or returns an error, an empty array is returned.

Parameters:

  • model (String, nil) (defaults to: nil)

    the TTS model ID for per-model queries

Returns:

  • (Array<String>)

    a sorted list of unique voice identifiers



29
30
31
32
33
34
35
36
37
# File 'lib/ollama_chat/tts.rb', line 29

def self.voices(model: nil)
  url = model ? OC::OLLAMA::CHAT::TTS_URL +
                  "/v1/audio/voices?model=#{model}"
              : OC::OLLAMA::CHAT::TTS_URL + '/v1/voices'
  voices = JSON.parse(Excon.get(url, expects: 200).body)["voices"]
  (model ? voices : voices.map { |v| v["id"] }).sort
rescue
  []
end

Instance Method Details

#call(response) ⇒ self

Processes a streaming response chunk for TTS conversion.

Buffers incoming text content and extracts complete blocks (separated by blank lines) for immediate TTS processing. When the response is done, finalizes playback by waiting for all pending TTS requests to complete.

Parameters:

  • response (Ollama::Response)

    the streaming response object containing text content and a done flag

Returns:

  • (self)

    returns self for chaining



96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/ollama_chat/tts.rb', line 96

def call(response)
  if content = response.response || response.message&.content

    @buffer << content

    process_pending_blocks # Process and "read" completed blocks immediately! 🏎️💨
  end

  if response.done
    finalize
  end

  self
end