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

Request a Fusion Trial

Experience Voice Biometrics with the ViGo Demo App
ViGo-on-Google-PlayViGo-on-the-App-Store