lib/punchblock/translator/asterisk/component/mrcp_recog_prompt.rb
# encoding: utf-8
require 'punchblock/translator/asterisk/unimrcp_app'
module Punchblock
module Translator
class Asterisk
module Component
module MRCPRecogPrompt
UniMRCPError = Class.new Punchblock::Error
MRCP_ERRORS = {
'004' => 'RECOGNIZE failed due to grammar load failure.',
'005' => 'RECOGNIZE failed due to grammar compilation failure.',
'006' => 'RECOGNIZE request terminated prematurely due to a recognizer error.',
'007' => 'RECOGNIZE request terminated because speech was too early.',
'009' => 'Failure accessing a URI.',
'010' => 'Language not supported.',
'016' => 'Any DEFINE-GRAMMAR error other than grammar-load-failure and grammar-compilation-failure.',
}
def execute
setup_defaults
validate
send_ref
execute_unimrcp_app
complete
rescue ChannelGoneError
call_ended
rescue UniMRCPError => e
complete_with_error e.message
rescue RubyAMI::Error => e
complete_with_error "Terminated due to AMI error '#{e.message}'"
rescue OptionError => e
with_error 'option error', e.message
end
private
def validate
[:interrupt_on, :start_offset, :start_paused, :repeat_interval, :repeat_times, :max_time].each do |opt|
raise OptionError, "A #{opt} value is unsupported on Asterisk." if output_node.send opt
end
raise OptionError, "An initial-timeout value must be -1 or a positive integer." if @initial_timeout < -1
raise OptionError, "An inter-digit-timeout value must be -1 or a positive integer." if @inter_digit_timeout < -1
raise OptionError, "A recognition-timeout value must be -1, 0, or a positive integer." if @recognition_timeout < -1
raise OptionError, "A max-silence value must be -1, 0, or a positive integer." if @max_silence < -1
raise OptionError, "A speech-complete-timeout value must be -1, 0, or a positive integer." if @speech_complete_timeout < -1
raise OptionError, "A hotword-max-duration value must be -1, 0, or a positive integer." if @hotword_max_duration < -1
raise OptionError, "A hotword-min-duration value must be -1, 0, or a positive integer." if @hotword_min_duration < -1
raise OptionError, "A dtmf-terminate-timeout value must be -1, 0, or a positive integer." if @dtmf_terminate_timeout < -1
raise OptionError, "An n-best-list-length value must be a positive integer." if @n_best_list_length && @n_best_list_length < 1
raise OptionError, "A speed-vs-accuracy value must be a positive integer." if @speed_vs_accuracy && @speed_vs_accuracy < 0
end
def execute_app(app, *args)
UniMRCPApp.new(app, *args, unimrcp_app_options).execute @call
end
def unimrcp_app_options
{uer: 1, b: (@component_node.barge_in == false ? 0 : 1)}.tap do |opts|
opts[:nit] = @initial_timeout if @initial_timeout > -1
opts[:dit] = @inter_digit_timeout if @inter_digit_timeout > -1
opts[:dttc] = input_node.terminator if input_node.terminator
opts[:spl] = input_node.language if input_node.language
opts[:ct] = input_node.min_confidence if input_node.min_confidence
opts[:sl] = input_node.sensitivity if input_node.sensitivity
opts[:t] = input_node.recognition_timeout if @recognition_timeout > -1
opts[:sint] = input_node.max_silence if @max_silence > -1
opts[:sct] = @speech_complete_timeout if @speech_complete_timeout > -1
opts[:sva] = @speed_vs_accuracy if @speed_vs_accuracy
opts[:nb] = @n_best_list_length if @n_best_list_length
opts[:sit] = @start_input_timers unless @start_input_timers.nil?
opts[:dtt] = @dtmf_terminate_timeout if @dtmf_terminate_timeout > -1
opts[:sw] = @save_waveform unless @save_waveform.nil?
opts[:nac] = @new_audio_channel unless @new_audio_channel.nil?
opts[:rm] = @recognition_mode if @recognition_mode
opts[:hmaxd] = @hotword_max_duration if @hotword_max_duration > -1
opts[:hmind] = @hotword_min_duration if @hotword_min_duration > -1
opts[:cdb] = @clear_dtmf_buffer unless @clear_dtmf_buffer.nil?
opts[:enm] = @early_no_match unless @early_no_match.nil?
opts[:iwu] = @input_waveform_uri if @input_waveform_uri
opts[:mt] = @media_type if @media_type
yield opts
end
end
def setup_defaults
@initial_timeout = input_node.initial_timeout || -1
@inter_digit_timeout = input_node.inter_digit_timeout || -1
@recognition_timeout = input_node.recognition_timeout || -1
@max_silence = input_node.max_silence || -1
@speech_complete_timeout = input_node.headers['Speech-Complete-Timeout'] || -1
@speed_vs_accuracy = input_node.headers['Speed-Vs-Accuracy']
@n_best_list_length = input_node.headers['N-Best-List-Length']
@start_input_timers = input_node.headers['Start-Input-Timers']
@dtmf_terminate_timeout = input_node.headers['DTMF-Terminate-Timeout'] || -1
@save_waveform = input_node.headers['Save-Waveform']
@new_audio_channel = input_node.headers['New-Audio-Channel']
@recognition_mode = input_node.headers['Recognition-Mode']
@hotword_max_duration = input_node.headers['Hotword-Max-Duration'] || -1
@hotword_min_duration = input_node.headers['Hotword-Min-Duration'] || -1
@clear_dtmf_buffer = input_node.headers['Clear-DTMF-Buffer']
@early_no_match = input_node.headers['Early-No-Match']
@input_waveform_uri = input_node.headers['Input-Waveform-URI']
@media_type = input_node.headers['Media-Type']
end
def grammars
input_node.grammars.map do |d|
if d.content_type
d.value.to_doc.to_s
else
d.url
end
end.join ','
end
def first_doc
output_node.render_documents.first
end
def audio_filename
first_doc.value.first
end
def output_node
@component_node.output
end
def input_node
@component_node.input
end
def complete
case @call.channel_var('RECOG_STATUS')
when 'INTERRUPTED'
send_complete_event Punchblock::Component::Input::Complete::NoMatch.new
when 'ERROR'
raise UniMRCPError
else
send_complete_event case cause = @call.channel_var('RECOG_COMPLETION_CAUSE')
when '000', '008', '012'
nlsml = RubySpeech.parse URI.decode(@call.channel_var('RECOG_RESULT'))
Punchblock::Component::Input::Complete::Match.new nlsml: nlsml
when '001', '003', '013', '014', '015'
Punchblock::Component::Input::Complete::NoMatch.new
when '002', '011'
Punchblock::Component::Input::Complete::NoInput.new
when *MRCP_ERRORS.keys
raise UniMRCPError, MRCP_ERRORS[cause]
else
raise UniMRCPError
end
end
end
end
end
end
end
end