pocketsphinx-ruby 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +45 -7
- data/lib/pocketsphinx.rb +7 -1
- data/lib/pocketsphinx/api/call_helpers.rb +16 -0
- data/lib/pocketsphinx/api/pocketsphinx.rb +4 -0
- data/lib/pocketsphinx/configuration/default.rb +10 -0
- data/lib/pocketsphinx/configuration/grammar.rb +21 -0
- data/lib/pocketsphinx/decoder.rb +58 -15
- data/lib/pocketsphinx/grammar/jsgf.rb +37 -0
- data/lib/pocketsphinx/grammar/jsgf_builder.rb +27 -0
- data/lib/pocketsphinx/microphone.rb +4 -10
- data/lib/pocketsphinx/version.rb +1 -1
- data/spec/assets/grammars/goforward.gram +15 -0
- data/spec/assets/grammars/invalid.gram +1 -0
- data/spec/assets/grammars/sentences.gram +5 -0
- data/spec/configuration_spec.rb +9 -5
- data/spec/decoder_spec.rb +77 -1
- data/spec/grammar_spec.rb +43 -0
- data/spec/integration/default_recognition_spec.rb +21 -0
- data/spec/integration/{speech_recognizer_spec.rb → grammar_recognition_spec.rb} +4 -3
- metadata +18 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d3479a20de1bf075e2a4d2efcf8721c05a650751
|
4
|
+
data.tar.gz: a3ea68aaf9e0dc60149c2992dd8316233d8568f3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e732d04275721ed223776c7d90b97894e8a778e73e334a036561715efbf868b65e8b20809730013fcaf020f21ab5de2fa646af6b7bc7aeb2f467345b6492f33e
|
7
|
+
data.tar.gz: a6670826d281b272ea29d4d609805328d2da8af4753d66349e78f222c5c99eaf05a7dde19faad8ac070b57e03aacab20396c5686c8b7365e4d586591bc958add
|
data/README.md
CHANGED
@@ -56,7 +56,7 @@ Or install it yourself as:
|
|
56
56
|
The `LiveSpeechRecognizer` is modeled on the same class in [Sphinx4](http://cmusphinx.sourceforge.net/wiki/tutorialsphinx4). It uses the `Microphone` and `Decoder` classes internally to provide a simple, high-level recognition interface:
|
57
57
|
|
58
58
|
```ruby
|
59
|
-
require 'pocketsphinx-ruby'
|
59
|
+
require 'pocketsphinx-ruby' # Omitted in subsequent examples
|
60
60
|
|
61
61
|
Pocketsphinx::LiveSpeechRecognizer.new.recognize do |speech|
|
62
62
|
puts speech
|
@@ -106,7 +106,7 @@ The `Microphone` class uses Pocketsphinx's libsphinxad to record audio for speec
|
|
106
106
|
For example, to record and save a 5 second raw audio file:
|
107
107
|
|
108
108
|
```ruby
|
109
|
-
microphone = Microphone.new
|
109
|
+
microphone = Pocketsphinx::Microphone.new
|
110
110
|
|
111
111
|
File.open("test.raw", "wb") do |file|
|
112
112
|
microphone.record do
|
@@ -130,7 +130,7 @@ To open this audio file take a look at [this wiki page](https://github.com/watso
|
|
130
130
|
The `Decoder` class uses Pocketsphinx's libpocketsphinx to decode audio data into text. For example to decode a single utterance:
|
131
131
|
|
132
132
|
```ruby
|
133
|
-
decoder = Decoder.new(Configuration.default)
|
133
|
+
decoder = Pocketsphinx::Decoder.new(Pocketsphinx::Configuration.default)
|
134
134
|
decoder.decode 'spec/assets/audio/goforward.raw'
|
135
135
|
|
136
136
|
puts decoder.hypothesis # => "go forward ten years"
|
@@ -142,16 +142,54 @@ puts decoder.hypothesis # => "go forward ten years"
|
|
142
142
|
Keyword spotting is another feature that is not in the current stable (0.8) releases of Pocketsphinx, having been [merged into trunk](https://github.com/cmusphinx/pocketsphinx/commit/f562f9356cc7f1ade4941ebdde0c377642a023e3) early in 2014. In can be useful for detecting an activation keyword in a command and control application, while ignoring all other speech. Set up a recognizer as follows:
|
143
143
|
|
144
144
|
```ruby
|
145
|
-
configuration = Configuration::KeywordSpotting.new('Okay computer')
|
146
|
-
recognizer = LiveSpeechRecognizer.new(configuration)
|
145
|
+
configuration = Pocketsphinx::Configuration::KeywordSpotting.new('Okay computer')
|
146
|
+
recognizer = Pocketsphinx::LiveSpeechRecognizer.new(configuration)
|
147
147
|
```
|
148
148
|
|
149
|
-
The `KeywordSpotting` configuration accepts a second argument for adjusting the sensitivity of the keyword detection. Note that this is just a wrapper which sets the `keyphrase` and `kws_threshold` settings on the default configuration
|
149
|
+
The `KeywordSpotting` configuration accepts a second argument for adjusting the sensitivity of the keyword detection. Note that this is just a wrapper which sets the `keyphrase` and `kws_threshold` settings on the default configuration:
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
Pocketsphinx::Configuration::KeywordSpotting.new('keyword', 2).changes
|
153
|
+
# => [
|
154
|
+
# { :name => "keyphrase", :type => :string, :default => nil, :required => false, :value => "keyword", :info => "Keyphrase to spot" },
|
155
|
+
# { :name => "kws_threshold", :type => :float, :default => 1.0, :required => false, :value => 2.0, :info => "Threshold for p(hyp)/p(alternatives) ratio" },
|
156
|
+
# { :name => "lm", :type => :string, :default => "/usr/local/Cellar/cmu-pocketsphinx/HEAD/share/pocketsphinx/model/lm/en_US/hub4.5000.DMP", :required => false, :value => nil, :info => "Word trigram language model input file" }
|
157
|
+
# ]
|
158
|
+
```
|
159
|
+
|
160
|
+
|
161
|
+
### Grammars
|
162
|
+
|
163
|
+
Another way of configuring Pocketsphinx is with a grammar, which is normally used to describe very simple types of languages for command and control. Restricting the set of possible utterances in this way can greatly improve recognition accuracy for these types of application.
|
164
|
+
|
165
|
+
Load a [JSGF](http://www.w3.org/TR/jsgf/) grammar from a file:
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
configuration = Pocketsphinx::Configuration::Grammar.new('sentences.gram')
|
169
|
+
```
|
170
|
+
|
171
|
+
Or build one dynamically with this simple DSL (currently only supports sentence lists):
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
configuration = Pocketsphinx::Configuration::Grammar.new do
|
175
|
+
sentence "Go forward ten meters"
|
176
|
+
sentence "Go backward ten meters"
|
177
|
+
end
|
178
|
+
```
|
179
|
+
|
180
|
+
|
181
|
+
## Troubleshooting
|
182
|
+
|
183
|
+
This gem has been tested with a manual Pocketsphinx installation on Ubuntu 14.04 and a Homebrew Pocketsphinx installation on OSX 10.9.4 Mavericks. Take a look at the following common problems before opening an issue.
|
184
|
+
|
185
|
+
**`attach_function': Function 'ps_default_search_args' not found in [libpocketsphinx.so] (FFI::NotFoundError)**
|
186
|
+
|
187
|
+
An error like this probably means that you have an old version of the Pocketsphinx libraries installed. If necessary, replace them with a recent development version which supports the features available in this gem.
|
150
188
|
|
151
189
|
|
152
190
|
## Contributing
|
153
191
|
|
154
|
-
1. Fork it ( https://github.com/
|
192
|
+
1. Fork it ( https://github.com/watsonbox/pocketsphinx-ruby/fork )
|
155
193
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
156
194
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
157
195
|
4. Push to the branch (`git push origin my-new-feature`)
|
data/lib/pocketsphinx.rb
CHANGED
@@ -6,12 +6,18 @@ require "pocketsphinx/version"
|
|
6
6
|
require "pocketsphinx/api/sphinxbase"
|
7
7
|
require "pocketsphinx/api/sphinxad"
|
8
8
|
require "pocketsphinx/api/pocketsphinx"
|
9
|
+
require "pocketsphinx/api/call_helpers"
|
10
|
+
|
11
|
+
# Grammar
|
12
|
+
require "pocketsphinx/grammar/jsgf"
|
13
|
+
require "pocketsphinx/grammar/jsgf_builder"
|
9
14
|
|
10
15
|
# Configuration
|
11
|
-
require
|
16
|
+
require "pocketsphinx/configuration/setting_definition"
|
12
17
|
require "pocketsphinx/configuration/base"
|
13
18
|
require "pocketsphinx/configuration/default"
|
14
19
|
require "pocketsphinx/configuration/keyword_spotting"
|
20
|
+
require "pocketsphinx/configuration/grammar"
|
15
21
|
|
16
22
|
require "pocketsphinx/audio_file"
|
17
23
|
require "pocketsphinx/microphone"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Pocketsphinx
|
2
|
+
module API
|
3
|
+
Error = Class.new(StandardError)
|
4
|
+
|
5
|
+
module CallHelpers
|
6
|
+
def api_call(method, *args)
|
7
|
+
calling_method = caller[0][/`.*'/][1..-2]
|
8
|
+
ps_api.send(method, *args).tap do |result|
|
9
|
+
if result < 0
|
10
|
+
raise Error, "#{self.class.to_s.split('::').last}##{calling_method} failed with error code #{result}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -17,6 +17,10 @@ module Pocketsphinx
|
|
17
17
|
attach_function :ps_end_utt, [:decoder], :int
|
18
18
|
attach_function :ps_get_in_speech, [:decoder], :uint8
|
19
19
|
attach_function :ps_get_hyp, [:decoder, :pointer, :pointer], :string
|
20
|
+
attach_function :ps_set_jsgf_string, [:decoder, :string, :string], :int
|
21
|
+
attach_function :ps_unset_search, [:decoder, :string], :int
|
22
|
+
attach_function :ps_get_search, [:decoder], :string
|
23
|
+
attach_function :ps_set_search, [:decoder, :string], :int
|
20
24
|
end
|
21
25
|
end
|
22
26
|
end
|
@@ -7,6 +7,16 @@ module Pocketsphinx
|
|
7
7
|
# Sets default grammar and language model if they are not set explicitly and
|
8
8
|
# are present in the default search path.
|
9
9
|
API::Pocketsphinx.ps_default_search_args(@ps_config)
|
10
|
+
|
11
|
+
# Treat ps_default_search_args settings as defaults
|
12
|
+
changes.each do |details|
|
13
|
+
setting_definitions[details[:name]].deflt = details[:value]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Show details for settings which don't match Pocketsphinx defaults
|
18
|
+
def changes
|
19
|
+
details.reject { |d| d[:default] == d[:value] }
|
10
20
|
end
|
11
21
|
end
|
12
22
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Pocketsphinx
|
2
|
+
module Configuration
|
3
|
+
class Grammar < Default
|
4
|
+
attr_accessor :grammar
|
5
|
+
|
6
|
+
def initialize(*args, &block)#(grammar_path = nil)
|
7
|
+
super()
|
8
|
+
|
9
|
+
@grammar = ::Grammar::Jsgf.new(*args, &block)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Since JSGF strings are not supported in Pocketsphinx configuration (only files),
|
13
|
+
# we use the post_init_decoder hook to configure the JSGF
|
14
|
+
def post_init_decoder(decoder)
|
15
|
+
decoder.unset_search
|
16
|
+
decoder.set_jsgf_string(grammar.raw)
|
17
|
+
decoder.set_search
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/pocketsphinx/decoder.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Pocketsphinx
|
2
2
|
class Decoder < Struct.new(:configuration)
|
3
|
-
|
3
|
+
include API::CallHelpers
|
4
4
|
|
5
5
|
attr_writer :ps_api
|
6
6
|
|
@@ -13,10 +13,7 @@ module Pocketsphinx
|
|
13
13
|
# nil, the previous configuration will be reloaded, with any changes applied.
|
14
14
|
def reconfigure(configuration = nil)
|
15
15
|
self.configuration = configuration if configuration
|
16
|
-
|
17
|
-
ps_api.ps_reinit(ps_decoder, self.configuration.ps_config).tap do |result|
|
18
|
-
raise Error, "Decoder#reconfigure failed with error code #{result}" if result < 0
|
19
|
-
end
|
16
|
+
reinit_decoder
|
20
17
|
end
|
21
18
|
|
22
19
|
# Decode a raw audio stream as a single utterance, opening a file if path given
|
@@ -64,9 +61,7 @@ module Pocketsphinx
|
|
64
61
|
# worth of data. This may allow the recognizer to produce more accurate results.
|
65
62
|
# @return Number of frames of data searched
|
66
63
|
def process_raw(buffer, size, no_search = false, full_utt = false)
|
67
|
-
|
68
|
-
raise Error, "Decoder#process_raw failed with error code #{result}" if result < 0
|
69
|
-
end
|
64
|
+
api_call :ps_process_raw, ps_decoder, buffer, size, no_search ? 1 : 0, full_utt ? 1 : 0
|
70
65
|
end
|
71
66
|
|
72
67
|
# Start utterance processing.
|
@@ -77,16 +72,12 @@ module Pocketsphinx
|
|
77
72
|
#
|
78
73
|
# @param [String] name String uniquely identifying this utterance. If nil, one will be created.
|
79
74
|
def start_utterance(name = nil)
|
80
|
-
|
81
|
-
raise Error, "Decoder#start_utterance failed with error code #{result}" if result < 0
|
82
|
-
end
|
75
|
+
api_call :ps_start_utt, ps_decoder, name
|
83
76
|
end
|
84
77
|
|
85
78
|
# End utterance processing
|
86
79
|
def end_utterance
|
87
|
-
|
88
|
-
raise Error, "Decoder#end_utterance failed with error code #{result}" if result < 0
|
89
|
-
end
|
80
|
+
api_call :ps_end_utt, ps_decoder
|
90
81
|
end
|
91
82
|
|
92
83
|
# Checks if the last feed audio buffer contained speech
|
@@ -102,12 +93,64 @@ module Pocketsphinx
|
|
102
93
|
ps_api.ps_get_hyp(ps_decoder, nil, nil)
|
103
94
|
end
|
104
95
|
|
96
|
+
# Adds new search using JSGF model.
|
97
|
+
#
|
98
|
+
# Convenience method to parse JSGF model from string and create a search.
|
99
|
+
#
|
100
|
+
# @param [String] jsgf_string The JSGF grammar
|
101
|
+
# @param [String] name The search name
|
102
|
+
def set_jsgf_string(jsgf_string, name = 'default')
|
103
|
+
api_call :ps_set_jsgf_string, ps_decoder, name, jsgf_string
|
104
|
+
end
|
105
|
+
|
106
|
+
# Returns name of curent search in decoder
|
107
|
+
def get_search
|
108
|
+
ps_api.ps_get_search(ps_decoder)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Actives search with the provided name.
|
112
|
+
#
|
113
|
+
# Activates search with the provided name. The search must be added before
|
114
|
+
# using either ps_set_fsg(), ps_set_lm() or ps_set_kws().
|
115
|
+
def set_search(name = 'default')
|
116
|
+
api_call :ps_set_search, ps_decoder, name
|
117
|
+
end
|
118
|
+
|
119
|
+
# Unsets the search and releases related resources.
|
120
|
+
#
|
121
|
+
# Unsets the search previously added with
|
122
|
+
# using either ps_set_fsg(), ps_set_lm() or ps_set_kws().
|
123
|
+
def unset_search(name = 'default')
|
124
|
+
api_call :ps_unset_search, ps_decoder, name
|
125
|
+
end
|
126
|
+
|
105
127
|
def ps_api
|
106
128
|
@ps_api || API::Pocketsphinx
|
107
129
|
end
|
108
130
|
|
109
131
|
def ps_decoder
|
110
|
-
@ps_decoder
|
132
|
+
init_decoder if @ps_decoder.nil?
|
133
|
+
@ps_decoder
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
def init_decoder
|
139
|
+
@ps_decoder = ps_api.ps_init(configuration.ps_config)
|
140
|
+
post_init_decoder
|
141
|
+
end
|
142
|
+
|
143
|
+
def reinit_decoder
|
144
|
+
ps_api.ps_reinit(ps_decoder, configuration.ps_config).tap do |result|
|
145
|
+
raise API::Error, "Decoder#reconfigure failed with error code #{result}" if result < 0
|
146
|
+
post_init_decoder
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def post_init_decoder
|
151
|
+
if configuration.respond_to?(:post_init_decoder)
|
152
|
+
configuration.post_init_decoder(self)
|
153
|
+
end
|
111
154
|
end
|
112
155
|
end
|
113
156
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Pocketsphinx
|
2
|
+
module Grammar
|
3
|
+
class Jsgf
|
4
|
+
attr_reader :raw
|
5
|
+
|
6
|
+
def initialize(path = nil, &block)
|
7
|
+
if path.nil? && !block_given?
|
8
|
+
raise "Either a path or block is required to create a JSGF grammar"
|
9
|
+
end
|
10
|
+
|
11
|
+
if block_given?
|
12
|
+
@raw = grammar_from_block(&block)
|
13
|
+
else
|
14
|
+
@raw = grammar_from_file(path)
|
15
|
+
check_grammar
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def grammar_from_file(path)
|
20
|
+
File.read path
|
21
|
+
end
|
22
|
+
|
23
|
+
def grammar_from_block(&block)
|
24
|
+
builder = JsgfBuilder.new
|
25
|
+
builder.instance_eval(&block)
|
26
|
+
builder.jsgf
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def check_grammar
|
32
|
+
# Simple header check for now
|
33
|
+
raise 'Invalid JSGF grammar' unless raw.lines.first.strip == "#JSGF V1.0;"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Pocketsphinx
|
2
|
+
module Grammar
|
3
|
+
class JsgfBuilder
|
4
|
+
def initialize
|
5
|
+
@sentences = []
|
6
|
+
end
|
7
|
+
|
8
|
+
def sentence(sentence)
|
9
|
+
@sentences << sentence
|
10
|
+
end
|
11
|
+
|
12
|
+
def jsgf
|
13
|
+
header + sentences_rule
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def header
|
19
|
+
"#JSGF V1.0;\n\ngrammar default;\n\n"
|
20
|
+
end
|
21
|
+
|
22
|
+
def sentences_rule
|
23
|
+
"public <sentence> = #{@sentences.map(&:downcase).join(' | ')};"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -3,7 +3,7 @@ module Pocketsphinx
|
|
3
3
|
#
|
4
4
|
# Implements Recordable interface (#record and #read_audio)
|
5
5
|
class Microphone
|
6
|
-
|
6
|
+
include API::CallHelpers
|
7
7
|
|
8
8
|
attr_reader :ps_audio_device
|
9
9
|
attr_writer :ps_api
|
@@ -36,15 +36,11 @@ module Pocketsphinx
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def start_recording
|
39
|
-
|
40
|
-
raise Error, "Microphone#start_recording failed with error code #{result}" if result < 0
|
41
|
-
end
|
39
|
+
api_call :ad_start_rec, @ps_audio_device
|
42
40
|
end
|
43
41
|
|
44
42
|
def stop_recording
|
45
|
-
|
46
|
-
raise Error, "Microphone#stop_recording failed with error code #{result}" if result < 0
|
47
|
-
end
|
43
|
+
api_call :ad_stop_rec, @ps_audio_device
|
48
44
|
end
|
49
45
|
|
50
46
|
# Read next block of audio samples while recording; read upto max samples into buf.
|
@@ -69,9 +65,7 @@ module Pocketsphinx
|
|
69
65
|
end
|
70
66
|
|
71
67
|
def close_device
|
72
|
-
|
73
|
-
raise Error, "Microphone#close_device failed with error code #{result}" if result < 0
|
74
|
-
end
|
68
|
+
api_call :ad_close, @ps_audio_device
|
75
69
|
end
|
76
70
|
|
77
71
|
def ps_api
|
data/lib/pocketsphinx/version.rb
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
#JSGF V1.0;
|
2
|
+
|
3
|
+
/**
|
4
|
+
* JSGF Grammar for Turtle example
|
5
|
+
*/
|
6
|
+
|
7
|
+
grammar goforward;
|
8
|
+
|
9
|
+
public <move> = go forward ten meters;
|
10
|
+
|
11
|
+
public <move2> = go <direction> <distance> [meter | meters];
|
12
|
+
|
13
|
+
<direction> = forward | backward;
|
14
|
+
|
15
|
+
<distance> = one | two | three | four | five | six | seven | eight | nine | ten;
|
@@ -0,0 +1 @@
|
|
1
|
+
This isn't a valid grammar
|
data/spec/configuration_spec.rb
CHANGED
@@ -90,12 +90,16 @@ describe Configuration do
|
|
90
90
|
context 'keyword spotting configuration' do
|
91
91
|
subject { Configuration::KeywordSpotting.new('Okay computer') }
|
92
92
|
|
93
|
-
it '
|
94
|
-
|
95
|
-
|
93
|
+
it 'modifies the default configuration keyphrase and language model' do
|
94
|
+
changes = subject.changes
|
95
|
+
|
96
|
+
expect(changes.count).to be(2)
|
97
|
+
|
98
|
+
expect(changes[0][:name]).to eq('keyphrase')
|
99
|
+
expect(changes[0][:value]).to eq('okay computer')
|
96
100
|
|
97
|
-
|
98
|
-
expect(
|
101
|
+
expect(changes[1][:name]).to eq('lm')
|
102
|
+
expect(changes[1][:value]).to be_nil
|
99
103
|
end
|
100
104
|
|
101
105
|
it 'exposes the keyphrase setting as #keyword' do
|
data/spec/decoder_spec.rb
CHANGED
@@ -12,12 +12,18 @@ describe Decoder do
|
|
12
12
|
end
|
13
13
|
|
14
14
|
describe '#reconfigure' do
|
15
|
-
it 'calls libpocketsphinx' do
|
15
|
+
it 'calls libpocketsphinx and the configuration post initialize hook' do
|
16
16
|
expect(ps_api)
|
17
17
|
.to receive(:ps_reinit)
|
18
18
|
.with(subject.ps_decoder, configuration.ps_config)
|
19
19
|
.and_return(0)
|
20
20
|
|
21
|
+
configuration.define_singleton_method(:post_init_decoder) { |decoder| }
|
22
|
+
|
23
|
+
expect(configuration)
|
24
|
+
.to receive(:post_init_decoder)
|
25
|
+
.with(subject)
|
26
|
+
|
21
27
|
subject.reconfigure
|
22
28
|
end
|
23
29
|
|
@@ -133,4 +139,74 @@ describe Decoder do
|
|
133
139
|
expect(subject.hypothesis).to eq("Hypothesis")
|
134
140
|
end
|
135
141
|
end
|
142
|
+
|
143
|
+
describe '#set_jsgf_string' do
|
144
|
+
it 'calls libpocketsphinx' do
|
145
|
+
expect(ps_api)
|
146
|
+
.to receive(:ps_set_jsgf_string)
|
147
|
+
.with(subject.ps_decoder, 'default', 'JSGF')
|
148
|
+
.and_return(0)
|
149
|
+
|
150
|
+
subject.set_jsgf_string('JSGF')
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'raises an exception on error' do
|
154
|
+
expect(ps_api)
|
155
|
+
.to receive(:ps_set_jsgf_string)
|
156
|
+
.and_return(-1)
|
157
|
+
|
158
|
+
expect { subject.set_jsgf_string('JSGF') }
|
159
|
+
.to raise_exception "Decoder#set_jsgf_string failed with error code -1"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe '#set_search' do
|
164
|
+
it 'calls libpocketsphinx' do
|
165
|
+
expect(ps_api)
|
166
|
+
.to receive(:ps_set_search)
|
167
|
+
.with(subject.ps_decoder, 'search')
|
168
|
+
.and_return(0)
|
169
|
+
|
170
|
+
subject.set_search('search')
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'raises an exception on error' do
|
174
|
+
expect(ps_api)
|
175
|
+
.to receive(:ps_set_search)
|
176
|
+
.and_return(-1)
|
177
|
+
|
178
|
+
expect { subject.set_search('search') }
|
179
|
+
.to raise_exception "Decoder#set_search failed with error code -1"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe '#unset_search' do
|
184
|
+
it 'calls libpocketsphinx' do
|
185
|
+
expect(ps_api)
|
186
|
+
.to receive(:ps_unset_search)
|
187
|
+
.with(subject.ps_decoder, 'search')
|
188
|
+
.and_return(0)
|
189
|
+
|
190
|
+
subject.unset_search('search')
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'raises an exception on error' do
|
194
|
+
expect(ps_api)
|
195
|
+
.to receive(:ps_unset_search)
|
196
|
+
.and_return(-1)
|
197
|
+
|
198
|
+
expect { subject.unset_search('search') }
|
199
|
+
.to raise_exception "Decoder#unset_search failed with error code -1"
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe '#get_search' do
|
204
|
+
it 'calls libpocketsphinx' do
|
205
|
+
expect(ps_api)
|
206
|
+
.to receive(:ps_get_search)
|
207
|
+
.and_return(:search)
|
208
|
+
|
209
|
+
expect(subject.get_search).to eq(:search)
|
210
|
+
end
|
211
|
+
end
|
136
212
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Grammar::Jsgf do
|
4
|
+
it "raises an exception when neither a file or block are given" do
|
5
|
+
expect { Grammar::Jsgf.new }.to raise_exception "Either a path or block is required to create a JSGF grammar"
|
6
|
+
end
|
7
|
+
|
8
|
+
context "reading a grammar from a file" do
|
9
|
+
let(:grammar_path) { grammar :goforward }
|
10
|
+
subject { Grammar::Jsgf.new(grammar_path) }
|
11
|
+
|
12
|
+
it "reads a grammar from a file" do
|
13
|
+
expect(subject.raw.lines.count).to eq(15)
|
14
|
+
end
|
15
|
+
|
16
|
+
context "the grammar file is invalid" do
|
17
|
+
let(:grammar_path) { grammar :invalid }
|
18
|
+
|
19
|
+
it "raises an exception" do
|
20
|
+
expect { subject }.to raise_exception "Invalid JSGF grammar"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "building a grammer from a block" do
|
26
|
+
subject do
|
27
|
+
Grammar::Jsgf.new do
|
28
|
+
sentence "Go forward ten meters"
|
29
|
+
sentence "Go backward ten meters"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it "builds a grammar from a block" do
|
34
|
+
expect(subject.raw).to eq(File.read grammar(:sentences))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def grammar(name)
|
41
|
+
"spec/assets/grammars/#{name}.gram"
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'speech recognition with default configuration' do
|
4
|
+
subject do
|
5
|
+
AudioFileSpeechRecognizer.new.tap do |speech_recognizer|
|
6
|
+
speech_recognizer.decoder = @decoder
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# Share decoder across all examples for speed
|
11
|
+
before :all do
|
12
|
+
@decoder = Decoder.new(Configuration.default)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#recognize' do
|
16
|
+
it 'should decode speech in raw audio' do
|
17
|
+
expect { |b| subject.recognize('spec/assets/audio/goforward.raw', 4096, &b) }.
|
18
|
+
to yield_with_args("go forward ten years")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe
|
3
|
+
describe 'speech recognition with a grammar' do
|
4
4
|
let(:recordable) { AudioFile.new('spec/assets/audio/goforward.raw') }
|
5
5
|
|
6
6
|
subject do
|
@@ -12,12 +12,13 @@ describe SpeechRecognizer do
|
|
12
12
|
|
13
13
|
# Share decoder across all examples for speed
|
14
14
|
before :all do
|
15
|
-
@
|
15
|
+
@configuration = Configuration::Grammar.new('spec/assets/grammars/goforward.gram')
|
16
|
+
@decoder = Decoder.new(@configuration)
|
16
17
|
end
|
17
18
|
|
18
19
|
describe '#recognize' do
|
19
20
|
it 'should decode speech in raw audio' do
|
20
|
-
expect { |b| subject.recognize(4096, &b) }.to yield_with_args("go forward ten
|
21
|
+
expect { |b| subject.recognize(4096, &b) }.to yield_with_args("go forward ten meters")
|
21
22
|
end
|
22
23
|
end
|
23
24
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pocketsphinx-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Howard Wilson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-10-
|
11
|
+
date: 2014-10-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|
@@ -100,6 +100,7 @@ files:
|
|
100
100
|
- examples/record_audio_file.rb
|
101
101
|
- lib/pocketsphinx-ruby.rb
|
102
102
|
- lib/pocketsphinx.rb
|
103
|
+
- lib/pocketsphinx/api/call_helpers.rb
|
103
104
|
- lib/pocketsphinx/api/pocketsphinx.rb
|
104
105
|
- lib/pocketsphinx/api/sphinxad.rb
|
105
106
|
- lib/pocketsphinx/api/sphinxbase.rb
|
@@ -107,19 +108,27 @@ files:
|
|
107
108
|
- lib/pocketsphinx/audio_file_speech_recognizer.rb
|
108
109
|
- lib/pocketsphinx/configuration/base.rb
|
109
110
|
- lib/pocketsphinx/configuration/default.rb
|
111
|
+
- lib/pocketsphinx/configuration/grammar.rb
|
110
112
|
- lib/pocketsphinx/configuration/keyword_spotting.rb
|
111
113
|
- lib/pocketsphinx/configuration/setting_definition.rb
|
112
114
|
- lib/pocketsphinx/decoder.rb
|
115
|
+
- lib/pocketsphinx/grammar/jsgf.rb
|
116
|
+
- lib/pocketsphinx/grammar/jsgf_builder.rb
|
113
117
|
- lib/pocketsphinx/live_speech_recognizer.rb
|
114
118
|
- lib/pocketsphinx/microphone.rb
|
115
119
|
- lib/pocketsphinx/speech_recognizer.rb
|
116
120
|
- lib/pocketsphinx/version.rb
|
117
121
|
- pocketsphinx-ruby.gemspec
|
118
122
|
- spec/assets/audio/goforward.raw
|
123
|
+
- spec/assets/grammars/goforward.gram
|
124
|
+
- spec/assets/grammars/invalid.gram
|
125
|
+
- spec/assets/grammars/sentences.gram
|
119
126
|
- spec/configuration_spec.rb
|
120
127
|
- spec/decoder_spec.rb
|
128
|
+
- spec/grammar_spec.rb
|
121
129
|
- spec/integration/decoder_spec.rb
|
122
|
-
- spec/integration/
|
130
|
+
- spec/integration/default_recognition_spec.rb
|
131
|
+
- spec/integration/grammar_recognition_spec.rb
|
123
132
|
- spec/microphone_spec.rb
|
124
133
|
- spec/spec_helper.rb
|
125
134
|
- spec/speech_recognizer_spec.rb
|
@@ -149,10 +158,15 @@ specification_version: 4
|
|
149
158
|
summary: Ruby FFI pocketsphinx bindings
|
150
159
|
test_files:
|
151
160
|
- spec/assets/audio/goforward.raw
|
161
|
+
- spec/assets/grammars/goforward.gram
|
162
|
+
- spec/assets/grammars/invalid.gram
|
163
|
+
- spec/assets/grammars/sentences.gram
|
152
164
|
- spec/configuration_spec.rb
|
153
165
|
- spec/decoder_spec.rb
|
166
|
+
- spec/grammar_spec.rb
|
154
167
|
- spec/integration/decoder_spec.rb
|
155
|
-
- spec/integration/
|
168
|
+
- spec/integration/default_recognition_spec.rb
|
169
|
+
- spec/integration/grammar_recognition_spec.rb
|
156
170
|
- spec/microphone_spec.rb
|
157
171
|
- spec/spec_helper.rb
|
158
172
|
- spec/speech_recognizer_spec.rb
|