mac-say 0.1.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/ChangeLog.md +26 -2
- data/LICENSE.txt +1 -1
- data/README.md +35 -13
- data/examples/examples.rb +15 -6
- data/img/voices_manual.png +0 -0
- data/lib/mac/say/version.rb +2 -2
- data/lib/mac/say/voices_attributes.rb +647 -0
- data/lib/mac/say.rb +120 -52
- data/mac-say.gemspec +11 -11
- data/test/fake/say +3 -1
- data/test/helper.rb +7 -3
- data/test/test_mac-say.rb +71 -59
- metadata +26 -26
data/lib/mac/say.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require_relative 'say/version'
|
3
|
+
require_relative 'say/voices_attributes'
|
4
|
+
|
3
5
|
require 'English'
|
4
6
|
|
5
7
|
# Wrapper namespace module for a Say class
|
@@ -8,7 +10,7 @@ module Mac
|
|
8
10
|
# Allows to use simple TTS on Mac right from Ruby scripts
|
9
11
|
class Say
|
10
12
|
# A regex pattern to parse say voices list output
|
11
|
-
|
13
|
+
VOICE_PATTERN = /(^[\w\s-]+)\s+([\w-]+)\s+#\s([\p{Graph}\p{Zs}]+$)/i
|
12
14
|
|
13
15
|
# An error raised when `say` command couldn't be found
|
14
16
|
class CommandNotFound < StandardError; end
|
@@ -19,8 +21,20 @@ module Mac
|
|
19
21
|
# An error raised when the given voice isn't valid
|
20
22
|
class VoiceNotFound < StandardError; end
|
21
23
|
|
22
|
-
# An error raised when there is no a
|
23
|
-
class
|
24
|
+
# An error raised when there is no a attribute of voice to match
|
25
|
+
class UnknownVoiceAttribute < StandardError; end
|
26
|
+
|
27
|
+
# The list of the voice attributes available
|
28
|
+
VOICE_ATTRIBUTES = [
|
29
|
+
:name,
|
30
|
+
:language,
|
31
|
+
:country,
|
32
|
+
:sample,
|
33
|
+
:gender,
|
34
|
+
:joke,
|
35
|
+
:quality,
|
36
|
+
:singing
|
37
|
+
]
|
24
38
|
|
25
39
|
# Current voices list
|
26
40
|
#
|
@@ -29,20 +43,24 @@ module Mac
|
|
29
43
|
# Mac::Say.voices #=>
|
30
44
|
# [
|
31
45
|
# {
|
32
|
-
# :name
|
33
|
-
# :
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
# :
|
46
|
+
# :name => :agnes,
|
47
|
+
# :language => :en,
|
48
|
+
# :country => :us,
|
49
|
+
# :sample => "Isn't it nice to have a computer that will talk to you?",
|
50
|
+
# :gender => :female,
|
51
|
+
# :joke => false,
|
52
|
+
# :quality => :low,
|
53
|
+
# :singing => false
|
38
54
|
# },
|
39
55
|
# {
|
40
|
-
# :name
|
41
|
-
# :
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
# :
|
56
|
+
# :name => :albert,
|
57
|
+
# :language => :en,
|
58
|
+
# :country => :us,
|
59
|
+
# :sample => "I have a frog in my throat. No, I mean a real frog!",
|
60
|
+
# :gender => :male,
|
61
|
+
# :joke => true,
|
62
|
+
# :quality => :medium,
|
63
|
+
# :singing => false
|
46
64
|
# },
|
47
65
|
# ...
|
48
66
|
# ]
|
@@ -94,7 +112,7 @@ module Mac
|
|
94
112
|
# Providing file, voice or rate arguments changes instance state and influence
|
95
113
|
# all the subsequent #say calls unless they have their own custom arguments
|
96
114
|
#
|
97
|
-
# @param string [String] a text to read using say command
|
115
|
+
# @param string [String] a text to read using say command (default: nil)
|
98
116
|
# @param file [String] path to the file to read (default: nil)
|
99
117
|
# @param voice [Symbol] voice to be used by the say command (default: :alex)
|
100
118
|
# @param rate [Integer] speech rate in words per minute (default: 175) accepts values in (175..720)
|
@@ -126,31 +144,67 @@ module Mac
|
|
126
144
|
execute_command(string)
|
127
145
|
end
|
128
146
|
|
129
|
-
#
|
147
|
+
# Look for voices by their attributes (e.g. :name, :language, :country, :gender, etc.)
|
148
|
+
#
|
149
|
+
# @overload voice(attribute, value)
|
150
|
+
# @param attribute [Symbol] the attribute to search voices by
|
151
|
+
# @param value [Symbol, String] the value of the attribute to search voices by
|
152
|
+
# @overload voice(&block)
|
153
|
+
# @yield [voice] Passes the given block to @voices.find_all
|
154
|
+
#
|
155
|
+
# @return [Array<Hash>, Hash, nil] an array with all the voices matched by the attribute or
|
156
|
+
# a voice Hash if only one voice corresponds to the attribute, nil if no voices found
|
157
|
+
#
|
158
|
+
# @example Find voices by one or more attributes
|
159
|
+
#
|
160
|
+
# Mac::Say.new.voice(:joke, false)
|
161
|
+
# Mac::Say.new.voice(:gender, :female)
|
130
162
|
#
|
131
|
-
#
|
132
|
-
#
|
163
|
+
# Mac::Say.new.voice { |v| v[:joke] == true && v[:gender] == :female }
|
164
|
+
# Mac::Say.new.voice { |v| v[:language] == :en && v[:gender] == :male && v[:quality] == :high && v[:joke] == false }
|
133
165
|
#
|
134
|
-
# @raise [
|
135
|
-
def self.voice(
|
166
|
+
# @raise [UnknownVoiceAttribute] if the voice attribute isn't supported def self.voice(attribute = nil, value = nil, &block)
|
167
|
+
def self.voice(attribute = nil, value = nil, &block)
|
136
168
|
mac = new
|
137
|
-
|
169
|
+
|
170
|
+
if block_given?
|
171
|
+
mac.voice(&block)
|
172
|
+
else
|
173
|
+
mac.voice(attribute, value)
|
174
|
+
end
|
138
175
|
end
|
139
176
|
|
140
|
-
#
|
177
|
+
# Look for voices by their attributes (e.g. :name, :language, :country, :gender, etc.)
|
141
178
|
#
|
142
|
-
# @
|
143
|
-
#
|
179
|
+
# @overload voice(attribute, value)
|
180
|
+
# @param attribute [Symbol] the attribute to search voices by
|
181
|
+
# @param value [Symbol, String] the value of the attribute to search voices by
|
182
|
+
# @overload voice(&block)
|
183
|
+
# @yield [voice] Passes the given block to @voices.find_all
|
144
184
|
#
|
145
|
-
# @
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
185
|
+
# @return [Array<Hash>, Hash, nil] an array with all the voices matched by the attribute or
|
186
|
+
# a voice Hash if only one voice corresponds to the attribute, nil if no voices found
|
187
|
+
#
|
188
|
+
# @example Find voices by one or more attributes
|
189
|
+
#
|
190
|
+
# Mac::Say.new.voice(:joke, false)
|
191
|
+
# Mac::Say.new.voice(:gender, :female)
|
192
|
+
#
|
193
|
+
# Mac::Say.new.voice { |v| v[:joke] == true && v[:gender] == :female }
|
194
|
+
# Mac::Say.new.voice { |v| v[:language] == :en && v[:gender] == :male && v[:quality] == :high && v[:joke] == false }
|
195
|
+
#
|
196
|
+
# @raise [UnknownVoiceAttribute] if the voice attribute isn't supported
|
197
|
+
def voice(attribute = nil, value = nil, &block)
|
198
|
+
return unless (attribute && !value.nil?) || block_given?
|
199
|
+
raise UnknownVoiceAttribute, "Voice has no '#{attribute}' attribute" if attribute && !VOICE_ATTRIBUTES.include?(attribute)
|
200
|
+
|
201
|
+
if block_given?
|
202
|
+
found_voices = @voices.find_all(&block)
|
203
|
+
else
|
204
|
+
found_voices = @voices.find_all {|voice| voice[attribute] === value }
|
205
|
+
end
|
151
206
|
|
152
207
|
return if found_voices.empty?
|
153
|
-
|
154
208
|
found_voices.count == 1 ? found_voices.first : found_voices
|
155
209
|
end
|
156
210
|
|
@@ -161,20 +215,24 @@ module Mac
|
|
161
215
|
# Mac::Say.voices #=>
|
162
216
|
# [
|
163
217
|
# {
|
164
|
-
# :name
|
165
|
-
# :
|
166
|
-
#
|
167
|
-
#
|
168
|
-
#
|
169
|
-
# :
|
218
|
+
# :name => :agnes,
|
219
|
+
# :language => :en,
|
220
|
+
# :country => :us,
|
221
|
+
# :sample => "Isn't it nice to have a computer that will talk to you?",
|
222
|
+
# :gender => :female,
|
223
|
+
# :joke => false,
|
224
|
+
# :quality => :low,
|
225
|
+
# :singing => false
|
170
226
|
# },
|
171
227
|
# {
|
172
|
-
# :name
|
173
|
-
# :
|
174
|
-
#
|
175
|
-
#
|
176
|
-
#
|
177
|
-
# :
|
228
|
+
# :name => :albert,
|
229
|
+
# :language => :en,
|
230
|
+
# :country => :us,
|
231
|
+
# :sample => "I have a frog in my throat. No, I mean a real frog!",
|
232
|
+
# :gender => :male,
|
233
|
+
# :joke => true,
|
234
|
+
# :quality => :medium,
|
235
|
+
# :singing => false
|
178
236
|
# },
|
179
237
|
# ...
|
180
238
|
# ]
|
@@ -213,7 +271,7 @@ module Mac
|
|
213
271
|
# @raise [FileNotFound] if the given file wasn't found or isn't readable by the current user
|
214
272
|
def generate_command
|
215
273
|
say_path = @config[:say_path]
|
216
|
-
file
|
274
|
+
file = @config[:file]
|
217
275
|
|
218
276
|
raise CommandNotFound, "Command `say` couldn't be found by '#{@config[:say_path]}' path" unless valid_command_path? say_path
|
219
277
|
|
@@ -234,22 +292,30 @@ module Mac
|
|
234
292
|
# @raise [CommandNotFound] if the say command wasn't found
|
235
293
|
def load_voices
|
236
294
|
return if @voices
|
295
|
+
|
237
296
|
say_path = @config[:say_path]
|
238
297
|
raise CommandNotFound, "Command `say` couldn't be found by '#{say_path}' path" unless valid_command_path? say_path
|
239
298
|
|
240
|
-
@voices = `#{say_path} -v '?'`.scan(
|
299
|
+
@voices = `#{say_path} -v '?'`.scan(VOICE_PATTERN).map do |voice|
|
241
300
|
lang = voice[1].split(/[_-]/)
|
301
|
+
name = voice[0].strip.downcase.to_sym
|
302
|
+
|
303
|
+
additional_attributes = ADDITIONAL_VOICE_ATTRIBUTES[name] || ADDITIONAL_VOICE_ATTRIBUTES[:_unknown_voice]
|
304
|
+
|
242
305
|
{
|
243
|
-
name:
|
244
|
-
|
245
|
-
|
246
|
-
|
306
|
+
name: name,
|
307
|
+
language: lang[0].downcase.to_sym,
|
308
|
+
country: lang[1].downcase.to_sym,
|
309
|
+
sample: voice[2].strip
|
310
|
+
}.merge(additional_attributes)
|
247
311
|
end
|
248
312
|
end
|
249
313
|
|
250
314
|
# Checks voice existence by the name
|
251
315
|
# Loads voices if they weren't loaded before
|
252
316
|
#
|
317
|
+
# @param name [String, Symbol] the name of the voice to validate
|
318
|
+
#
|
253
319
|
# @return [Boolean] if the voices name in the list of voices
|
254
320
|
#
|
255
321
|
# @raise [CommandNotFound] if the say command wasn't found
|
@@ -260,6 +326,8 @@ module Mac
|
|
260
326
|
|
261
327
|
# Checks say command existence by the path
|
262
328
|
#
|
329
|
+
# @param path [String] the path of the `say` command to validate
|
330
|
+
#
|
263
331
|
# @return [Boolean] if the command exists and if it is executable
|
264
332
|
def valid_command_path?(path)
|
265
333
|
File.exist?(path) && File.executable?(path)
|
@@ -267,11 +335,11 @@ module Mac
|
|
267
335
|
|
268
336
|
# Checks text file existence by the path
|
269
337
|
#
|
338
|
+
# @param path [String] the path of the text file validate
|
339
|
+
#
|
270
340
|
# @return [Boolean] if the file exists and if it is readable
|
271
341
|
def valid_file_path?(path)
|
272
342
|
path && File.exist?(path) && File.readable?(path)
|
273
343
|
end
|
274
344
|
end
|
275
345
|
end
|
276
|
-
|
277
|
-
p Mac::Say.voice(:name, 'fuck')
|
data/mac-say.gemspec
CHANGED
@@ -30,16 +30,16 @@ Gem::Specification.new do |gem|
|
|
30
30
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
31
31
|
gem.require_paths = ['lib']
|
32
32
|
|
33
|
-
gem.add_development_dependency 'bundler', '~>
|
34
|
-
gem.add_development_dependency 'minitest', '~> 5.
|
35
|
-
gem.add_development_dependency 'minitest-reporters', '~> 1.
|
36
|
-
gem.add_development_dependency 'rake', '~>
|
37
|
-
gem.add_development_dependency 'simplecov', '~> 0.
|
33
|
+
gem.add_development_dependency 'bundler', '~> 2.3'
|
34
|
+
gem.add_development_dependency 'minitest', '~> 5.15'
|
35
|
+
gem.add_development_dependency 'minitest-reporters', '~> 1.5'
|
36
|
+
gem.add_development_dependency 'rake', '~> 13.0'
|
37
|
+
gem.add_development_dependency 'simplecov', '~> 0.16'
|
38
38
|
gem.add_development_dependency 'rubygems-tasks', '~> 0.2'
|
39
|
-
gem.add_development_dependency 'yard', '~> 0.
|
40
|
-
gem.add_development_dependency 'inch', '~> 0.
|
41
|
-
gem.add_development_dependency 'redcarpet', '~> 3.
|
42
|
-
gem.add_development_dependency 'github-markup', '~>
|
43
|
-
gem.add_development_dependency 'm', '~> 1.
|
44
|
-
gem.add_development_dependency 'coveralls', '~> 0.8'
|
39
|
+
gem.add_development_dependency 'yard', '~> 0.9'
|
40
|
+
gem.add_development_dependency 'inch', '~> 0.8'
|
41
|
+
gem.add_development_dependency 'redcarpet', '~> 3.5'
|
42
|
+
gem.add_development_dependency 'github-markup', '~> 4.0'
|
43
|
+
gem.add_development_dependency 'm', '~> 1.6'
|
44
|
+
gem.add_development_dependency 'coveralls', '~> 0.8.23'
|
45
45
|
end
|
data/test/fake/say
CHANGED
@@ -9,6 +9,7 @@ Fiona en-scotland # Hello, my name is Fiona. I am a Scottish-Engli
|
|
9
9
|
Serena en_GB # Hello, my name is Serena. I am a British-English voice.
|
10
10
|
Ting-Ting zh_CN # 您好,我叫Ting-Ting。我讲中文普通话。
|
11
11
|
Veena en_IN # Hello, my name is Veena. I am an Indian-English voice.
|
12
|
+
Test en_GB # Well I'm the most mysterious voice round here, believe me.
|
12
13
|
VOICES
|
13
14
|
|
14
15
|
VOICES_NAMES = [
|
@@ -16,7 +17,8 @@ VOICES_NAMES = [
|
|
16
17
|
:daniel,
|
17
18
|
:fiona,
|
18
19
|
:'ting-ting',
|
19
|
-
:veena
|
20
|
+
:veena,
|
21
|
+
:test
|
20
22
|
]
|
21
23
|
|
22
24
|
options = {
|
data/test/helper.rb
CHANGED
@@ -4,15 +4,19 @@ require 'rubygems'
|
|
4
4
|
require 'coveralls'
|
5
5
|
Coveralls.wear!
|
6
6
|
|
7
|
+
require 'simplecov'
|
8
|
+
SimpleCov.formatters = SimpleCov::Formatter::MultiFormatter.new [
|
9
|
+
SimpleCov::Formatter::HTMLFormatter,
|
10
|
+
Coveralls::SimpleCov::Formatter
|
11
|
+
]
|
12
|
+
SimpleCov.start
|
13
|
+
|
7
14
|
begin
|
8
15
|
require 'bundler/setup'
|
9
16
|
rescue LoadError => error
|
10
17
|
abort error.message
|
11
18
|
end
|
12
19
|
|
13
|
-
require 'simplecov'
|
14
|
-
SimpleCov.start
|
15
|
-
|
16
20
|
require 'minitest/autorun'
|
17
21
|
require 'minitest/reporters'
|
18
22
|
|
data/test/test_mac-say.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'helper'
|
3
3
|
require 'mac/say'
|
4
|
+
require 'mac/say/voices_attributes'
|
4
5
|
|
5
6
|
describe 'Mac::Say as a macOS `say` wrapper' do
|
6
7
|
describe 'On a class level' do
|
@@ -9,71 +10,72 @@ describe 'Mac::Say as a macOS `say` wrapper' do
|
|
9
10
|
end
|
10
11
|
|
11
12
|
it 'must have a VERSION constant' do
|
12
|
-
Mac::Say.const_get('VERSION').wont_be_empty
|
13
|
+
_(Mac::Say.const_get('VERSION')).wont_be_empty
|
13
14
|
end
|
14
15
|
|
15
16
|
it 'must return available voices as an Array of Hashes' do
|
16
|
-
Mac::Say.voices.wont_be_empty
|
17
|
-
Mac::Say.voices.must_be_kind_of Array
|
17
|
+
_(Mac::Say.voices).wont_be_empty
|
18
|
+
_(Mac::Say.voices).must_be_kind_of Array
|
18
19
|
end
|
19
20
|
|
20
21
|
it 'must return specific Hash structure for a voice' do
|
21
22
|
voice = Mac::Say.voice(:name, :alex)
|
22
|
-
voice.wont_be_empty
|
23
|
-
voice.must_be_kind_of Hash
|
24
|
-
voice.keys.must_equal
|
23
|
+
_(voice).wont_be_empty
|
24
|
+
_(voice).must_be_kind_of Hash
|
25
|
+
_(voice.keys).must_equal Mac::Say::VOICE_ATTRIBUTES
|
25
26
|
end
|
26
27
|
|
27
|
-
it 'must return
|
28
|
-
|
29
|
-
voice.
|
30
|
-
|
31
|
-
voice[:iso_code].keys.must_equal [:language, :country]
|
32
|
-
end
|
28
|
+
it 'must return additional attributes for known voices' do
|
29
|
+
voice_name = :alex
|
30
|
+
voice = Mac::Say.voice(:name, voice_name)
|
31
|
+
additional_voice_attributes = ADDITIONAL_VOICE_ATTRIBUTES[voice_name]
|
33
32
|
|
34
|
-
|
35
|
-
voice
|
36
|
-
voice[:
|
33
|
+
_(voice[:gender]).must_equal additional_voice_attributes[:gender]
|
34
|
+
_(voice[:joke]).must_equal additional_voice_attributes[:joke]
|
35
|
+
_(voice[:quality]).must_equal additional_voice_attributes[:quality]
|
37
36
|
end
|
38
37
|
|
39
|
-
it '.voice must search for a voice
|
40
|
-
voice = Mac::Say.voice(:
|
41
|
-
voice[:name].must_equal :
|
38
|
+
it '.voice must search for a voice using single attribute' do
|
39
|
+
voice = Mac::Say.voice(:name, :alex)
|
40
|
+
_(voice[:name]).must_equal :alex
|
42
41
|
end
|
43
42
|
|
44
|
-
it '.voice must search for a voice
|
45
|
-
voices = Mac::Say.voice
|
46
|
-
voices.
|
43
|
+
it '.voice must search for a voice using block given' do
|
44
|
+
voices = Mac::Say.voice {|voice| voice[:language] == :en && voice[:joke] == false }
|
45
|
+
_(voices).must_be_kind_of Array
|
47
46
|
end
|
48
47
|
|
49
48
|
it '.voice must return one voice as a Hash' do
|
50
49
|
voice = Mac::Say.voice(:name, :alex)
|
51
|
-
voice.must_be_kind_of Hash
|
50
|
+
_(voice).must_be_kind_of Hash
|
52
51
|
end
|
53
52
|
|
53
|
+
# For this test to pass you'd have to have more than
|
54
|
+
# one British voice on your machine!
|
54
55
|
it '.voice must return an Array of voices if > 1' do
|
55
56
|
voices = Mac::Say.voice(:country, :gb)
|
56
|
-
voices.must_be_kind_of Array
|
57
|
+
_(voices).must_be_kind_of Array
|
57
58
|
end
|
58
59
|
|
59
60
|
it ".voice must return nil if voice wasn't found" do
|
60
61
|
voices = Mac::Say.voice(:name, :xxx)
|
61
|
-
voices.must_be_nil
|
62
|
+
_(voices).must_be_nil
|
62
63
|
end
|
63
64
|
|
65
|
+
|
64
66
|
it '.say must return 0 in successive speech' do
|
65
67
|
expectation = ["#{@say_path} -v 'alex' -r 175", 0]
|
66
|
-
Mac::Say.say('42').must_equal expectation
|
68
|
+
_(Mac::Say.say('42')).must_equal expectation
|
67
69
|
end
|
68
70
|
|
69
71
|
it '.say must use custom voice' do
|
70
72
|
expectation = ["#{@say_path} -v 'alex' -r 175", 0]
|
71
|
-
Mac::Say.say('42', :alex).must_equal expectation
|
73
|
+
_(Mac::Say.say('42', :alex)).must_equal expectation
|
72
74
|
end
|
73
75
|
|
74
76
|
it '.say must work with multiple lines' do
|
75
77
|
expectation = ["#{@say_path} -v 'alex' -r 175", 0]
|
76
|
-
Mac::Say.say(<<-TEXT, :alex).must_equal expectation
|
78
|
+
_(Mac::Say.say(<<-TEXT, :alex)).must_equal expectation
|
77
79
|
1
|
78
80
|
2
|
79
81
|
3
|
@@ -81,15 +83,15 @@ describe 'Mac::Say as a macOS `say` wrapper' do
|
|
81
83
|
end
|
82
84
|
|
83
85
|
it '.say must fail on wrong voice' do
|
84
|
-
|
86
|
+
_{
|
85
87
|
Mac::Say.say 'OMG! I lost my voice!', :wrong
|
86
88
|
}.must_raise Mac::Say::VoiceNotFound
|
87
89
|
end
|
88
90
|
|
89
|
-
it '.voice must fail on wrong voice
|
90
|
-
|
91
|
+
it '.voice must fail on wrong voice attribute' do
|
92
|
+
_{
|
91
93
|
Mac::Say.voice(:tone, :enthusiastic)
|
92
|
-
}.must_raise Mac::Say::
|
94
|
+
}.must_raise Mac::Say::UnknownVoiceAttribute
|
93
95
|
end
|
94
96
|
end
|
95
97
|
|
@@ -100,24 +102,24 @@ describe 'Mac::Say as a macOS `say` wrapper' do
|
|
100
102
|
end
|
101
103
|
|
102
104
|
it 'must instantiate Mac::Say' do
|
103
|
-
@reader.must_be_instance_of Mac::Say
|
105
|
+
_(@reader).must_be_instance_of Mac::Say
|
104
106
|
end
|
105
107
|
|
106
108
|
it '#say must return 0 on successive speech' do
|
107
109
|
expectation = ["#{@say_path} -v 'alex' -r 175", 0]
|
108
|
-
@reader.say(string: '42').must_equal expectation
|
110
|
+
_(@reader.say(string: '42')).must_equal expectation
|
109
111
|
end
|
110
112
|
|
111
113
|
it '#read must be a synonym of #say' do
|
112
114
|
expectation = ["#{@say_path} -v 'alex' -r 175", 0]
|
113
|
-
@reader.read(string: '42').must_equal expectation
|
115
|
+
_(@reader.read(string: '42')).must_equal expectation
|
114
116
|
end
|
115
117
|
|
116
118
|
it '#say must support :file' do
|
117
119
|
absolute_path = File.absolute_path './fixtures/text/en_gb_test.txt', File.dirname(__FILE__)
|
118
120
|
expectation = ["#{@say_path} -f #{absolute_path} -v 'alex' -r 175", 0]
|
119
121
|
|
120
|
-
@reader.say(file: absolute_path).must_equal expectation
|
122
|
+
_(@reader.say(file: absolute_path)).must_equal expectation
|
121
123
|
end
|
122
124
|
|
123
125
|
it '#say must read :file from initial config' do
|
@@ -125,7 +127,17 @@ describe 'Mac::Say as a macOS `say` wrapper' do
|
|
125
127
|
expectation = ["#{@say_path} -f #{absolute_path} -v 'alex' -r 175", 0]
|
126
128
|
|
127
129
|
@reader = Mac::Say.new(file: absolute_path)
|
128
|
-
@reader.say.must_equal expectation
|
130
|
+
_(@reader.say).must_equal expectation
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'must return nil additional attrs for unknown voices' do
|
134
|
+
if ENV['USE_FAKE_SAY']
|
135
|
+
voice = @reader.voice(:name, :test)
|
136
|
+
|
137
|
+
_(voice[:gender]).must_be_nil
|
138
|
+
_(voice[:joke]).must_be_nil
|
139
|
+
_(voice[:quality]).must_be_nil
|
140
|
+
end
|
129
141
|
end
|
130
142
|
|
131
143
|
it '#say must change :file from initial config' do
|
@@ -136,90 +148,90 @@ describe 'Mac::Say as a macOS `say` wrapper' do
|
|
136
148
|
|
137
149
|
# init
|
138
150
|
@reader = Mac::Say.new(file: gb_absolute_path)
|
139
|
-
@reader.config[:file].must_equal gb_absolute_path
|
151
|
+
_(@reader.config[:file]).must_equal gb_absolute_path
|
140
152
|
|
141
153
|
# change
|
142
|
-
@reader.say(file: us_absolute_path).must_equal expectation
|
143
|
-
@reader.config[:file].must_equal us_absolute_path
|
154
|
+
_(@reader.say(file: us_absolute_path)).must_equal expectation
|
155
|
+
_(@reader.config[:file]).must_equal us_absolute_path
|
144
156
|
end
|
145
157
|
|
146
158
|
it '#say must prioritise :file over :string' do
|
147
159
|
absolute_path = File.absolute_path './fixtures/text/en_gb_test.txt', File.dirname(__FILE__)
|
148
160
|
expectation = ["#{@say_path} -f #{absolute_path} -v 'alex' -r 175", 0]
|
149
161
|
|
150
|
-
@reader.say(string: 'test', file: absolute_path).must_equal expectation
|
162
|
+
_(@reader.say(string: 'test', file: absolute_path)).must_equal expectation
|
151
163
|
end
|
152
164
|
|
153
165
|
it '#say must support custom :rate' do
|
154
166
|
expectation = ["#{@say_path} -v 'alex' -r 250", 0]
|
155
|
-
@reader.say(string: '42', rate: 250).must_equal expectation
|
167
|
+
_(@reader.say(string: '42', rate: 250)).must_equal expectation
|
156
168
|
end
|
157
169
|
|
158
170
|
it '#say must support custom :voice' do
|
159
171
|
expectation = ["#{@say_path} -v 'fiona' -r 175", 0]
|
160
|
-
@reader.say(string: '42', voice: :fiona).must_equal expectation
|
172
|
+
_(@reader.say(string: '42', voice: :fiona)).must_equal expectation
|
161
173
|
end
|
162
174
|
|
163
175
|
it '#say must change the :voice' do
|
164
176
|
expectation = ["#{@say_path} -v 'fiona' -r 175", 0]
|
165
|
-
@reader.config[:voice].must_equal :alex
|
177
|
+
_(@reader.config[:voice]).must_equal :alex
|
166
178
|
|
167
|
-
@reader.say(string: '42', voice: :fiona).must_equal expectation
|
168
|
-
@reader.config[:voice].must_equal :fiona
|
179
|
+
_(@reader.say(string: '42', voice: :fiona)).must_equal expectation
|
180
|
+
_(@reader.config[:voice]).must_equal :fiona
|
169
181
|
|
170
|
-
@reader.say(string: '13').must_equal expectation
|
182
|
+
_(@reader.say(string: '13')).must_equal expectation
|
171
183
|
end
|
172
184
|
|
173
185
|
it '#say must change the :rate' do
|
174
186
|
expectation = ["#{@say_path} -v 'alex' -r 300", 0]
|
175
|
-
@reader.config[:rate].must_equal 175
|
187
|
+
_(@reader.config[:rate]).must_equal 175
|
176
188
|
|
177
|
-
@reader.say(string: '42', rate: 300).must_equal expectation
|
178
|
-
@reader.config[:rate].must_equal 300
|
189
|
+
_(@reader.say(string: '42', rate: 300)).must_equal expectation
|
190
|
+
_(@reader.config[:rate]).must_equal 300
|
179
191
|
|
180
|
-
@reader.say(string: '13').must_equal expectation
|
192
|
+
_(@reader.say(string: '13')).must_equal expectation
|
181
193
|
end
|
182
194
|
|
183
195
|
it '#say must fail on wrong initial voice' do
|
184
|
-
|
196
|
+
_{
|
185
197
|
talker = Mac::Say.new(voice: :wrong)
|
186
198
|
talker.say string: 'OMG! I lost my voice!'
|
187
199
|
}.must_raise Mac::Say::VoiceNotFound
|
188
200
|
end
|
189
201
|
|
190
202
|
it '#say must fail on wrong dynamic voice' do
|
191
|
-
|
203
|
+
_{
|
192
204
|
talker = Mac::Say.new
|
193
205
|
talker.say string: 'OMG! I lost my voice!', voice: :wrong
|
194
206
|
}.must_raise Mac::Say::VoiceNotFound
|
195
207
|
end
|
196
208
|
|
197
209
|
it '#voice must fail on wrong say path' do
|
198
|
-
|
210
|
+
_{
|
199
211
|
Mac::Say.new(say_path: '/wrong/wrong/path').voice(:name, :alex)
|
200
212
|
}.must_raise Mac::Say::CommandNotFound
|
201
213
|
end
|
202
214
|
|
203
215
|
it '#say must fail on wrong say path' do
|
204
|
-
|
216
|
+
_{
|
205
217
|
Mac::Say.new(say_path: '/wrong/wrong/path').say 'test'
|
206
218
|
}.must_raise Mac::Say::CommandNotFound
|
207
219
|
end
|
208
220
|
|
209
221
|
it '#say must fail on wrong file path' do
|
210
|
-
|
222
|
+
_{
|
211
223
|
Mac::Say.new.say(file: '/wrong/wrong/path')
|
212
224
|
}.must_raise Mac::Say::FileNotFound
|
213
225
|
end
|
214
226
|
|
215
|
-
it '#voice must fail on wrong
|
216
|
-
|
227
|
+
it '#voice must fail on wrong attribute' do
|
228
|
+
_{
|
217
229
|
Mac::Say.new.voice(:articulation, :nostalgic)
|
218
|
-
}.must_raise Mac::Say::
|
230
|
+
}.must_raise Mac::Say::UnknownVoiceAttribute
|
219
231
|
end
|
220
232
|
|
221
233
|
it '#say must fail on initial wrong file path' do
|
222
|
-
|
234
|
+
_{
|
223
235
|
Mac::Say.new(file: '/wrong/wrong/path').say
|
224
236
|
}.must_raise Mac::Say::FileNotFound
|
225
237
|
end
|