class WebSocketClient

Class for interacting with the WebSocket API

Public Class Methods

new(audio: nil, chunk_data:, options:, recognize_callback:, service_url:, headers:, disable_ssl_verification: false) click to toggle source
# File lib/ibm_watson/websocket/speech_to_text_websocket_listener.rb, line 14
def initialize(audio: nil, chunk_data:, options:, recognize_callback:, service_url:, headers:, disable_ssl_verification: false)
  @audio = audio
  @options = options
  @callback = recognize_callback
  @bytes_sent = 0
  @headers = headers
  @is_listening = false
  @service_url = service_url
  @timer = nil
  @chunk_data = chunk_data
  @mic_running = false
  @data_size = audio.nil? ? 0 : @audio.size
  @queue = Queue.new
  @disable_ssl_verification = disable_ssl_verification
end

Public Instance Methods

add_audio_chunk(chunk:) click to toggle source
# File lib/ibm_watson/websocket/speech_to_text_websocket_listener.rb, line 98
def add_audio_chunk(chunk:)
  @data_size += chunk.size
  @queue << chunk
end
start() click to toggle source
# File lib/ibm_watson/websocket/speech_to_text_websocket_listener.rb, line 30
def start
  on_open = lambda do |event|
    on_connect(event)
    @client.send(build_start_message(options: @options))
    @mic_running = true if @chunk_data
    send_audio(data: @audio)
  end

  on_message = lambda do |event|
    json_object = JSON.parse(event.data)
    if json_object.key?("error")
      error = json_object["error"]
      if error.start_with?(TIMEOUT_PREFIX)
        @callback.on_inactivity_timeout(error: error)
      else
        @callback.on_error(error: error)
      end
    elsif json_object.key?("state")
      if !@is_listening
        @is_listening = true
      else
        @client.send(build_close_message)
        @callback.on_transcription_complete
        @client.close(CLOSE_SIGNAL)
      end
    elsif json_object.key?("results") || json_object.key?("speaker_labels")
      hypothesis = ""
      unless json_object["results"].nil? && json_object["speaker_labels"].nil?
        hypothesis = json_object.dig("results", 0, "alternatives", 0, "transcript")
        b_final = json_object.dig("results", 0, "final")
        transcripts = extract_transcripts(alternatives: json_object.dig("results", 0, "alternatives"))

        @callback.on_hypothesis(hypothesis: hypothesis) if b_final

        @callback.on_transcription(transcript: transcripts)
        @callback.on_data(data: json_object)
      end
    end
  end

  on_close = lambda do |_event|
    @client = nil
    EM.stop_event_loop
  end

  on_error = lambda do |event|
    @callback.on_error(error: event)
  end

  EM&.reactor_thread&.join
  EM.run do
    if @disable_ssl_verification
      @service_url = @service_url.sub("wss:", "ws:")
      @client = Faye::WebSocket::Client.new(@service_url, nil, tls: { verify_peer: false, fail_if_no_peer_cert: false }, headers: @headers)
    else
      @client = Faye::WebSocket::Client.new(@service_url, nil, headers: @headers)
    end
    @client.onclose = on_close
    @client.onerror = on_error
    @client.onmessage = on_message
    @client.onopen = on_open
    @client.add_listener(Faye::WebSocket::API::Event.create("open"))
    @client.add_listener(Faye::WebSocket::API::Event.create("message"))
    @client.add_listener(Faye::WebSocket::API::Event.create("close"))
    @client.add_listener(Faye::WebSocket::API::Event.create("error"))
  end
end
stop_audio() click to toggle source
# File lib/ibm_watson/websocket/speech_to_text_websocket_listener.rb, line 103
def stop_audio
  @mic_running = false
end

Private Instance Methods

build_close_message() click to toggle source
# File lib/ibm_watson/websocket/speech_to_text_websocket_listener.rb, line 118
def build_close_message
  { "action" => "close" }.to_json
end
build_start_message(options:) click to toggle source
# File lib/ibm_watson/websocket/speech_to_text_websocket_listener.rb, line 113
def build_start_message(options:)
  options["action"] = "start"
  options.to_json
end
extract_transcripts(alternatives:) click to toggle source
# File lib/ibm_watson/websocket/speech_to_text_websocket_listener.rb, line 145
def extract_transcripts(alternatives:)
  transcripts = []
  unless alternatives&.nil?.nil?
    alternatives.each do |alternative|
      transcript = {}
      transcript["confidence"] = alternative["confidence"] if alternative.key?("confidence")
      transcript["transcript"] = alternative["transcript"]
      transcripts << transcript
    end
  end
  transcripts
end
on_connect(_response) click to toggle source
# File lib/ibm_watson/websocket/speech_to_text_websocket_listener.rb, line 109
def on_connect(_response)
  @callback.on_connected
end
send_audio(data:) click to toggle source
# File lib/ibm_watson/websocket/speech_to_text_websocket_listener.rb, line 122
def send_audio(data:)
  if @chunk_data
    if @mic_running
      @queue.empty? ? send_chunk(chunk: nil, final: false) : send_chunk(chunk: @queue.pop(true), final: false)
    elsif @queue.length == 1
      send_chunk(chunk: @queue.pop(true), final: true)
      @queue.close
      @timer.cancel if @timer.respond_to?(:cancel)
      return
    else
      send_chunk(chunk: @queue.pop(true), final: false) unless @queue.empty?
    end
  else
    if @bytes_sent + ONE_KB >= @data_size
      send_chunk(chunk: data.read(ONE_KB), final: true)
      @timer.cancel if @timer.respond_to?(:cancel)
      return
    end
    send_chunk(chunk: data.read(ONE_KB), final: false)
  end
  @timer = EventMachine::Timer.new(TEN_MILLISECONDS) { send_audio(data: data) }
end
send_chunk(chunk:, final: false) click to toggle source
# File lib/ibm_watson/websocket/speech_to_text_websocket_listener.rb, line 158
def send_chunk(chunk:, final: false)
  return if chunk.nil?

  @bytes_sent += chunk.size
  @client.send(chunk.bytes)
  @client.send({ "action" => "stop" }.to_json) if final
  @timer.cancel if @timer.respond_to?(:cancel) && final
end