Ruby code snippets that provide insight into building voice biometric registration and verification into a Ruby-based application.
Note that you need to insert your developer credentials (VV_ORG_ID
, VV_USERNAME
, etc) that you will have received when you self-registered for Fusion Enterprise.
This Ruby sample code for Fusion Enterprise uses a directory with pre-recorded 16-bit little-endian PCM files for digits 0-9. These audio files can be downloaded along with the Ruby code below.
require_relative "api_helper" require_relative "audio_helper" # This is a short sample demonstrating the registration and enrollment of a new "claimant" using the VoiceVault Fusion APIs. # For the purposes of this sample, we have a directory with pre-recorded 16-bit little-endian PCM files for digits 0-9, # which we concatenate to form the speech samples. # Set up some constant values... CALL_REFERENCE = "Ruby REST API sample" LANGUAGE = "EnglishUnitedStates" AUDIO_FORMAT = "LittleEndian" # VoiceVault also supports compressed 8-bit "ALaw" or "MuLaw" audio, and "BigEndian" 16-bit PCM format. begin # Input your registration details here (from your self-registration welcome email) rest_api_url_base = "..." username = "..." password = "..." organisation_unit = "..." configuration_id = "..." # Initialize a new instance of the API helper with the information provided above api_helper = ApiHelper.new rest_api_url_base, username, password, organisation_unit, configuration_id, LANGUAGE, AUDIO_FORMAT # Initialize the cache of digit-level audio samples audio_helper = AudioHelper.new # Attempt to register a new claimant rc_hash = api_helper.register_claimant raise "Failed to register claimant: #{rc_hash["message"]}" if rc_hash["status_code"] != "0" claimant_id = rc_hash["claimant_id"] puts "Registered claimant id: #{claimant_id}" # Start a new dialogue for the claimant sd_hash = api_helper.start_dialogue claimant_id, CALL_REFERENCE raise "Failed to start dialogue: #{sd_hash["message"]}" if sd_hash["status_code"] != "0" dialogue_id = sd_hash["dialogue_id"] puts "Started dialogue id: #{dialogue_id}" # Determine which file needs to be submitted next_prompt = sd_hash["prompt_hint"] # Loop until the dialogue is complete begin # As mentioned above, for the purposes of this sample we generate speech samples programmatically from pre-recorded audio files audio_data = audio_helper.generate_speech_from_prompt next_prompt # Submit the generated audio file to the new dialogue ds_hash = api_helper.submit_phrase dialogue_id, audio_data, next_prompt raise "Failed to submit phrase: #{ds_hash["message"]}" if ds_hash["status_code"] != "0" puts "Submitted phrase: #{next_prompt}" # Poll until processing completes. until ds_hash["request_status"] != "TooManyUnprocessedPhrases" puts "... waiting for processing to complete" sleep 1 ds_hash = api_helper.get_dialogue_summary dialogue_id raise "Failed to get dialogue summary: #{ds_hash["message"]}" if ds_hash["status_code"] != "0" end # Now that processing is complete, we should have a prompt hint for the next file (if we're not done) next_prompt = ds_hash["prompt_hint"] end while ds_hash["dialogue_status"] == "Started" # Print the final status of the dialogue puts "Dialogue completed. Status = #{ds_hash["dialogue_status"]}" rescue Exception => e puts "An error occurred. #{e.message}" end
Helper functions:
# The following code uses functionality that is available in most standard Ruby # distributions. Note that it can be greatly simplified using gems for processes # such as multipart form submission and XML parsing. require "net/http" require "uri" require "rexml/document" include REXML class ApiHelper FORM_BOUNDARY = "AaB03x" @uri_root # The base path for any API requests @creds # A hash containing the username and password for this instance @ou # The organisation unit ID (GUID) representing the customer for this instance @config # The configuration ID (GUID) representing the settings to use for this instance @language # The spoken language of all prompts submitted using this instance @format # The format of the recorded audio files that will be submitted # Instantiate the API helper def initialize uri_root, username, password, ou, config, language, format # Set up our instance members from the supplied parameters @uri_root = uri_root @creds = {"username" => username, "password" => password} @ou = ou @config = config @language = language @format = format end # Register a new "claimant" to be enrolled within VoiceVault Fusion def register_claimant params = { "organisation_unit" => @ou } do_post "RegisterClaimant.ashx", params, nil end # Begin a new enrollment or verification dialogue def start_dialogue claimant_id, reference params = { "configuration_id" => @config, "claimant_id" => claimant_id, "external_ref" => reference, "language" => @language } do_post "StartDialogue.ashx", params, nil end # Submit audio to an existing VoiceVault Fusion dialogue def submit_phrase dialogue_id, phrase, prompt params = { "dialogue_id" => dialogue_id, "prompt" => prompt, "format" => @format } do_post "SubmitPhrase.ashx", params, phrase end # Retrieve the status of a dialogue def get_dialogue_summary dialogue_id params = { "dialogue_id" => dialogue_id } do_post "GetDialogueSummary.ashx", params, nil end private # An internal method that takes care of the HTTP POST to the VoiceVault Fusion REST API def do_post page, params, audio_file # Set up our HTTP request uri = URI::join(@uri_root, page) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true request = Net::HTTP::Post.new(uri.request_uri) # Add our credentials to the parameters before we construct the form params.update @creds # If we have audio, we need to construct the multipart form by hand. # Note: gems such as "multipart" or "rest_client" can greatly simplify this process if (audio_file != nil) post_body = [] params.each do |k, v| post_body << "--#{FORM_BOUNDARY}\r\n" post_body << "Content-Disposition: form-data; name=\"#{k}\"\r\n\r\n#{v}\r\n" end post_body << "--#{FORM_BOUNDARY}\r\n" post_body << "Content-Disposition: form-data; name=\"utterance\"; filename=\"audio.wav\"\r\n" post_body << "Content-Type: audio/wav\r\n\r\n" post_body << audio_file post_body << "\r\n--#{FORM_BOUNDARY}--\r\n" request.content_type = "multipart/form-data, boundary=#{FORM_BOUNDARY}" request.body = post_body.join else # Much simpler if we're not doing a multipart submission! request.set_form_data(params) end # Do the actual HTTP post response = http.request(request) # And parse the XML response into a hash response_xml = Document.new(response.body) hash = {} response_xml.elements.each("response_info/*") do |e| hash[e.name()] = e.text end # Return the populated hash hash end end
Audio helper functions:
# A small helper class to assist with the generation of audio files for submission within the demo. # Uses a cache of 16-bit little-endian audio files representing digits 0 through 9 to build the phrases. class AudioHelper AUDIO_FILE_PATH = "Speech/" AUDIO_FILE_EXTENSION = ".wav" @cache # We want to pre-load all the digit audio files to speed things up def initialize @cache = {} ('0'..'9').each do |c| @cache[c] = open(File.join(AUDIO_FILE_PATH, c + AUDIO_FILE_EXTENSION), "rb") { |io| io.read } end end # Helper function to concatenate digit-level audio files into an n-digit speech sample representing the supplied prompt def generate_speech_from_prompt prompt output = [] prompt.each_char do |c| output << @cache[c] end return output.join end end