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.
134 135 136 |
# File 'lib/ollama_chat/utils/audio_player.rb', line 134 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.
82 83 84 85 86 87 |
# File 'lib/ollama_chat/utils/audio_player.rb', line 82 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.
95 96 97 |
# File 'lib/ollama_chat/utils/audio_player.rb', line 95 def @playing || @audio_queue.present? end |
#push(chunk) ⇒ self Also known as: <<
Pushes an audio chunk to the playback queue.
115 116 117 118 |
# File 'lib/ollama_chat/utils/audio_player.rb', line 115 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.
144 145 146 |
# File 'lib/ollama_chat/utils/audio_player.rb', line 144 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 |
# 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, "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 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.
105 106 107 108 109 |
# File 'lib/ollama_chat/utils/audio_player.rb', line 105 def stop @playing = false @thread&.join self end |
#to_s ⇒ String
Returns a string representation of the player's current state.
127 128 129 |
# File 'lib/ollama_chat/utils/audio_player.rb', line 127 def to_s "playing=#{} pause=#@pause #{@silence_chunk.bytesize} B" end |