Class: OllamaChat::Utils::AudioPlayer
- Inherits:
-
Object
- Object
- OllamaChat::Utils::AudioPlayer
- Defined in:
- lib/ollama_chat/utils/audio_player.rb
Overview
A robust audio player that streams raw PCM data to ffplay.
This class solves the issue of ffplay hanging when data arrives late by injecting silence chunks into the pipe. It uses a background thread and a Queue to decouple audio fetching from playback, ensuring smooth continuous audio even if the TTS backend is slow.
Instance Method Summary collapse
-
#initialize(command: nil, frequency: nil, bits: nil, pause: nil) ⇒ AudioPlayer
constructor
Creates a new AudioPlayer instance.
-
#inspect ⇒ String
Returns a detailed inspection string for debugging.
-
#play {|player| ... } ⇒ Object
Plays audio using a block-based iterator pattern.
-
#playing? ⇒ Boolean
Checks if the player is currently active or has pending audio.
-
#push(chunk) ⇒ self
(also: #<<)
Pushes an audio chunk to the playback queue.
-
#silent_bytes(duration) ⇒ String
private
Generates a silence chunk of PCM null bytes for the given duration.
-
#start ⇒ self
Starts the background playback thread.
-
#stop ⇒ self
Stops the playback and waits for the background thread to finish.
-
#to_s ⇒ String
Returns a string representation of the player's current state.
Constructor Details
#initialize(command: nil, frequency: nil, bits: nil, pause: nil) ⇒ AudioPlayer
Creates a new AudioPlayer instance.
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/ollama_chat/utils/audio_player.rb', line 21 def initialize(command: nil, frequency: nil, bits: nil, pause: nil) config = OC::OLLAMA::CHAT::AUDIO_PLAYER_CONFIG? or raise 'env var %s is required' % ( OC::OLLAMA::CHAT::AUDIO_PLAYER_CONFIG!.env_var_name.inspect ) command ||= config.command frequency ||= config.frequency bits ||= config.bits pause ||= config.pause @audio_queue = Queue.new @playing = false @command = command @frequency = frequency.to_f @bits = bits @pause = pause.to_f @silence_chunk = silent_bytes(@pause) end |
Instance Method Details
#inspect ⇒ String
Returns a detailed inspection string for debugging.
135 136 137 |
# File 'lib/ollama_chat/utils/audio_player.rb', line 135 def inspect "#<#{self.class}: #{to_s}>" end |
#play {|player| ... } ⇒ Object
Plays audio using a block-based iterator pattern.
This method simplifies lifecycle management by automatically starting the player before yielding and stopping it afterwards, ensuring the playback thread is properly cleaned up.
83 84 85 86 87 88 |
# File 'lib/ollama_chat/utils/audio_player.rb', line 83 def play(&block) block or raise ArgumentError, 'require &block argument' start block.(self) stop end |
#playing? ⇒ Boolean
Checks if the player is currently active or has pending audio.
The player is considered "playing" if the @playing flag is true
or if there are still audio chunks waiting in the queue.
96 97 98 |
# File 'lib/ollama_chat/utils/audio_player.rb', line 96 def @playing || @audio_queue.present? end |
#push(chunk) ⇒ self Also known as: <<
Pushes an audio chunk to the playback queue.
116 117 118 119 |
# File 'lib/ollama_chat/utils/audio_player.rb', line 116 def push(chunk) @audio_queue.push(chunk) start end |
#silent_bytes(duration) ⇒ String (private)
Generates a silence chunk of PCM null bytes for the given duration.
145 146 147 |
# File 'lib/ollama_chat/utils/audio_player.rb', line 145 def silent_bytes(duration) "\x00" * ((@frequency * (@bits / 8) * duration)).ceil end |
#start ⇒ self
Starts the background playback thread.
This method launches a thread that opens a pipe to ffplay and begins consuming audio chunks from the internal queue. If the queue is empty, it writes silence to keep the stream alive.
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/ollama_chat/utils/audio_player.rb', line 46 def start @playing and return self @playing = true @thread = Thread.new do IO.popen("#@command >/dev/null 2>&1", "w") do |io| io.binmode io.sync = true while chunk = @audio_queue.shift(true) rescue nil if chunk io.write(chunk) else io.write(@silence_chunk) sleep @pause end end end end @thread.report_on_exception = false self end |
#stop ⇒ self
Stops the playback and waits for the background thread to finish.
This sets the playing flag to false and joins the thread, ensuring all resources are cleaned up properly.
106 107 108 109 110 |
# File 'lib/ollama_chat/utils/audio_player.rb', line 106 def stop @playing = false @thread&.join self end |
#to_s ⇒ String
Returns a string representation of the player's current state.
128 129 130 |
# File 'lib/ollama_chat/utils/audio_player.rb', line 128 def to_s "playing=#{} pause=#@pause #{@silence_chunk.bytesize} B" end |