Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add Configuration wrapper for cmd_ln.c
  • Loading branch information
watsonbox committed Oct 18, 2014
1 parent 5c6c0c5 commit ca107c6
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 65 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -20,3 +20,5 @@ tmp
*.o
*.a
mkmf.log
.DS_Store
log
2 changes: 2 additions & 0 deletions lib/pocketsphinx.rb
Expand Up @@ -5,6 +5,8 @@
require "pocketsphinx/api/sphinxad"
require "pocketsphinx/api/pocketsphinx"

require "pocketsphinx/configuration"

module Pocketsphinx
# Your code goes here...
end
59 changes: 59 additions & 0 deletions lib/pocketsphinx/configuration.rb
@@ -0,0 +1,59 @@
require 'pocketsphinx/configuration/setting_definition'

module Pocketsphinx
class Configuration
private_class_method :new

def initialize(ps_arg_defs)
@ps_arg_defs = ps_arg_defs
@setting_definitions = SettingDefinition.from_arg_defs(ps_arg_defs)

# Sets default settings based on definitions
@ps_config = API::Sphinxbase.cmd_ln_parse_r(nil, ps_arg_defs, 0, nil, 1)
end

def self.default
new(API::Pocketsphinx.ps_args)
end

def [](name)
unless definition = @setting_definitions[name]
raise "Configuration setting '#{name}' does not exist"
end

case definition.type
when :integer
API::Sphinxbase.cmd_ln_int_r(@ps_config, "-#{name}")
when :float
API::Sphinxbase.cmd_ln_float_r(@ps_config, "-#{name}")
when :string
API::Sphinxbase.cmd_ln_str_r(@ps_config, "-#{name}")
when :boolean
API::Sphinxbase.cmd_ln_int_r(@ps_config, "-#{name}") != 0
when :string_list
raise NotImplementedException
end
end

def []=(name, value)
unless definition = @setting_definitions[name]
raise "Configuration setting '#{name}' does not exist"
end

case definition.type
when :integer
raise "Configuration setting '#{name}' must be a Fixnum" unless value.respond_to?(:to_i)
API::Sphinxbase.cmd_ln_set_int_r(@ps_config, "-#{name}", value.to_i)
when :float
raise "Configuration setting '#{name}' must be a Float" unless value.respond_to?(:to_i)
API::Sphinxbase.cmd_ln_set_float_r(@ps_config, "-#{name}", value.to_f)
when :string
API::Sphinxbase.cmd_ln_set_str_r(@ps_config, "-#{name}", value.to_s)
when :boolean
API::Sphinxbase.cmd_ln_set_int_r(@ps_config, "-#{name}", value ? 1 : 0)
when :string_list
raise NotImplementedException
end
end
end
end
40 changes: 40 additions & 0 deletions lib/pocketsphinx/configuration/setting_definition.rb
@@ -0,0 +1,40 @@
module Pocketsphinx
class Configuration
class SettingDefinition
TYPES = [:integer, :float, :string, :boolean, :string_list]

def initialize(name, type_code, default, doc)
@name, @type_code, @default, @doc = name, type_code, default, doc
end

def type
# Remove the required bit if it exists and find type from log2 of code
TYPES[Math.log2(@type_code - @type_code%2) - 1]
end

def required?
@type_code % 2 == 1
end

# Build setting definitions from pocketsphinx argument definitions
#
# @param [FFI::Pointer] ps_arg_defs A pointer to the Pocketsphinx argument definitions
#
# @return [Hash] A hash of setting definitions (name -> definition)
def self.from_arg_defs(ps_arg_defs)
{}.tap do |setting_defs|
arg_array = FFI::Pointer.new(API::Sphinxbase::Argument, ps_arg_defs)

0.upto(Float::INFINITY) do |i|
arg = API::Sphinxbase::Argument.new(arg_array[i])
break if arg[:name].nil?

# Remove '-' from argument name
name = arg[:name][1..-1]
setting_defs[name] = new(name, arg[:type], arg[:deflt], arg[:doc])
end
end
end
end
end
end
1 change: 1 addition & 0 deletions pocketsphinx-ruby.gemspec
Expand Up @@ -22,4 +22,5 @@ Gem::Specification.new do |spec|

spec.add_development_dependency "bundler", "~> 1.6"
spec.add_development_dependency "rake"
spec.add_development_dependency "rspec", "~> 3.1.0"
end
47 changes: 47 additions & 0 deletions spec/configuration_spec.rb
@@ -0,0 +1,47 @@
require 'spec_helper'

describe Pocketsphinx::Configuration do
subject { Pocketsphinx::Configuration.default }

it "provides a default pocketsphinx configuration" do
expect(subject).to be_a(Pocketsphinx::Configuration)
end

it "supports integer settings" do
expect(subject['frate']).to eq(100)
expect(subject['frate']).to be_a(Fixnum)

subject['frate'] = 50
expect(subject['frate']).to eq(50)
end

it "supports float settings" do
expect(subject['samprate']).to eq(16000)
expect(subject['samprate']).to be_a(Float)

subject['samprate'] = 8000
expect(subject['samprate']).to eq(8000)
end

it "supports getting strings" do
expect(subject['warp_type']).to eq('inverse_linear')

subject['warp_type'] = 'different_type'
expect(subject['warp_type']).to eq('different_type')
end

it "supports getting booleans" do
expect(subject['smoothspec']).to eq(false)

subject['smoothspec'] = true
expect(subject['smoothspec']).to eq(true)
end

it 'raises exceptions when setting with incorrectly typed values' do
expect { subject['frate'] = true }.to raise_exception "Configuration setting 'frate' must be a Fixnum"
end

it 'raises exceptions when a setting is unknown' do
expect { subject['unknown'] = true }.to raise_exception "Configuration setting 'unknown' does not exist"
end
end
74 changes: 9 additions & 65 deletions spec/spec_helper.rb
@@ -1,19 +1,12 @@
# This file was generated by the `rspec --init` command. Conventionally, all
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
# The generated `.rspec` file contains `--require spec_helper` which will cause this
# file to always be loaded, without a need to explicitly require it in any files.
#
# Given that it is always loaded, you are encouraged to keep this file as
# light-weight as possible. Requiring heavyweight dependencies from this file
# will add to the boot time of your test suite on EVERY test run, even for an
# individual file that may not need all of that loaded. Instead, consider making
# a separate helper file that requires the additional dependencies and performs
# the additional setup, and require it from the spec files that actually need it.
#
# The `.rspec` file also contains a few flags that are not defaults but that
# users commonly want.
#
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
require 'pocketsphinx'

POCKETSPHINX_LOG_FILE = "./log/pocketsphinx_test.log"

# Set up pocketsphinx logging to a file rather than stdout
FileUtils.makedirs File.dirname(POCKETSPHINX_LOG_FILE)
FileUtils.touch POCKETSPHINX_LOG_FILE
Pocketsphinx::API::Sphinxbase.err_set_logfile POCKETSPHINX_LOG_FILE

RSpec.configure do |config|
# rspec-expectations config goes here. You can use an alternate
# assertion/expectation library such as wrong or the stdlib/minitest
Expand All @@ -37,53 +30,4 @@
# `true` in RSpec 4.
mocks.verify_partial_doubles = true
end

# The settings below are suggested to provide a good initial experience
# with RSpec, but feel free to customize to your heart's content.
=begin
# These two settings work together to allow you to limit a spec run
# to individual examples or groups you care about by tagging them with
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
# get run.
config.filter_run :focus
config.run_all_when_everything_filtered = true
# Limits the available syntax to the non-monkey patched syntax that is recommended.
# For more details, see:
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
# - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
config.disable_monkey_patching!
# This setting enables warnings. It's recommended, but in some cases may
# be too noisy due to issues in dependencies.
config.warnings = true
# Many RSpec users commonly either run the entire suite or an individual
# file, and it's useful to allow more verbose output when running an
# individual spec file.
if config.files_to_run.one?
# Use the documentation formatter for detailed output,
# unless a formatter has already been configured
# (e.g. via a command-line flag).
config.default_formatter = 'doc'
end
# Print the 10 slowest examples and example groups at the
# end of the spec run, to help surface which specs are running
# particularly slow.
config.profile_examples = 10
# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
config.order = :random
# Seed global randomization in this process using the `--seed` CLI option.
# Setting this allows you to use `--seed` to deterministically reproduce
# test failures related to randomization by passing the same `--seed` value
# as the one that triggered the failure.
Kernel.srand config.seed
=end
end

0 comments on commit ca107c6

Please sign in to comment.