mac-say 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: befe01288b8556115593d963428671229af0a2e0
4
+ data.tar.gz: 715ed2147a05a67de7c05012c1b4009d909ed0af
5
+ SHA512:
6
+ metadata.gz: 9d48e9271ac17bc24a068c2ce63121a3f9bd2be092c5d3002d63400e2cfc5f24f26c6f6817c24b5b37a823665043fd3e9edeb33cd91712b63bba85b67d02ac59
7
+ data.tar.gz: 916ea15be74ff4d22c461c25117ae8240556bc4d9a09a58c667c0d6469b136621cbc889c123e11b50d1c8eaa9eb88e0f8916fc8ff36ac54f6142543e0d1c0e1d
data/.document ADDED
@@ -0,0 +1,4 @@
1
+ -
2
+ ChangeLog.md
3
+ README.md
4
+ LICENSE.txt
data/.gitignore ADDED
@@ -0,0 +1,40 @@
1
+ /.bundle
2
+ /.yardoc/
3
+ /Gemfile.lock
4
+ /doc/
5
+ /pkg/
6
+ /coverage/
7
+ /vendor/cache/*.gem
8
+
9
+ #********** osx template**********
10
+
11
+ .DS_Store
12
+
13
+ # Thumbnails
14
+ ._*
15
+
16
+ # Files that might appear on external disk
17
+ .Spotlight-V100
18
+ .Trashes
19
+
20
+
21
+ #********** ruby template**********
22
+
23
+ *.gem
24
+ *.rbc
25
+ .bundle
26
+ .config
27
+ coverage
28
+ InstalledFiles
29
+ lib/bundler/man
30
+ pkg
31
+ rdoc
32
+ spec/reports
33
+ test/tmp
34
+ test/version_tmp
35
+ tmp
36
+
37
+ # YARD artifacts
38
+ .yardoc
39
+ _yardoc
40
+ doc/
data/.rubocop.yml ADDED
@@ -0,0 +1,15 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.3
3
+ Exclude:
4
+ - 'test/test_mac-say.rb'
5
+ - 'mac-say.gemspec'
6
+ Metrics/AbcSize:
7
+ Max: 18
8
+ Style/AsciiComments:
9
+ Enabled: false
10
+ Style/MutableConstant:
11
+ Enabled: false
12
+ Metrics/LineLength:
13
+ Max: 130
14
+ Metrics/MethodLength:
15
+ Max: 11
data/.travis.yml ADDED
@@ -0,0 +1,12 @@
1
+ ---
2
+ before_install:
3
+ - gem update bundler
4
+ language: ruby
5
+ script:
6
+ - bundle exec rake test
7
+ env:
8
+ - USE_FAKE_SAY=./test/fake/say
9
+ rvm:
10
+ - 2.0
11
+ - 2.1
12
+ - 2.2
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --markup markdown --markup-provider=redcarpet --title "mac-say Documentation" --protected --asset img:img
data/ChangeLog.md ADDED
@@ -0,0 +1,4 @@
1
+ ### 0.1.0 / 2017-01-22
2
+
3
+ * Initial release:
4
+
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ source 'https://rubygems.org'
3
+
4
+ gemspec
5
+
6
+ group :development do
7
+ gem 'kramdown'
8
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2017 Serge Bedzhyk
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,237 @@
1
+ # mac-say
2
+
3
+ <p align="center">
4
+ <img title="mac-say logo" src ="./img/logo.png" />
5
+ </p>
6
+
7
+ > Ruby wrapper around the modern version of the macOS `say` command. Inspired by the @bratta's [mactts](https://github.com/bratta/mactts)
8
+ >
9
+ > [![Build Status](https://travis-ci.org/smileart/mac-say.svg?branch=dev)](https://travis-ci.org/smileart/mac-say) [![Coverage Status](https://coveralls.io/repos/github/smileart/mac-say/badge.svg?branch=dev)](https://coveralls.io/github/smileart/mac-say?branch=dev) [![Code Climate](https://codeclimate.com/github/smileart/mac-say/badges/gpa.svg)](https://codeclimate.com/github/smileart/mac-say) [![InchCI Status](https://inch-ci.org/github/smileart/mac-say.svg?branch=dev)](https://inch-ci.org/github/smileart/mac-say)
10
+
11
+ * [Homepage](https://rubygems.org/gems/mac-say)
12
+ * [Documentation](http://rubydoc.info/gems/mac-say/frames)
13
+ * [Email](mailto:smileart21 at gmail.com)
14
+
15
+ ## Features
16
+
17
+ * [x] Basic strings reading
18
+ * [x] Basic files reading
19
+ * [x] Multiline strings support
20
+ * [x] Dynamic voices parsing (based on real `say` output)
21
+ * [x] Voices list generation (including samples and ISO information)
22
+ * [x] Voices search (by name / language / country)
23
+ * [x] Simple (class-level) and customisable (instance-level) usage
24
+ * [ ] Observe reading progress line by line❓
25
+ * [ ] Audio output support❓
26
+
27
+ ## Install
28
+
29
+ ```sh
30
+ $ gem install mac-say
31
+ ```
32
+
33
+ ## Examples
34
+
35
+ ```ruby
36
+ require 'pp'
37
+ require 'mac/say'
38
+
39
+ # ===== Class level =====
40
+
41
+ # Get all the voices
42
+ pp Mac::Say.voices
43
+
44
+ # Collect the separate features lists
45
+ pp Mac::Say.voices.collect { |v| v[:name] }
46
+ pp Mac::Say.voices.collect { |v| v[:iso_code] }
47
+ pp Mac::Say.voices.collect { |v| v[:sample] }
48
+
49
+ # Find a voice (returns a Hash)
50
+ pp Mac::Say.voice(:name, :alex)
51
+ pp Mac::Say.voice(:country, :scotland)
52
+
53
+ # Find the voices by the feature (returns an Array)
54
+ pp Mac::Say.voice(:language, :en)
55
+
56
+ # Work with the voices collection
57
+ indian_english = Mac::Say.voice(:country, :in).select { |v| v[:iso_code][:language] == :en }.first[:name]
58
+
59
+ # Use multiline text
60
+ puts Mac::Say.say <<-DATA, indian_english
61
+ Invokes the given block passing in successive elements from self, deleting elements for which the block returns a false value.
62
+ The array may not be changed instantly every time the block is called.
63
+ If changes were made, it will return self, otherwise it returns nil.
64
+ DATA
65
+
66
+ # ===== Instance level =====
67
+
68
+ # with constant name in the constructor and custom rate
69
+ talker = Mac::Say.new(voice: :alex, rate: 300)
70
+ talker.say string: 'Hello world'
71
+
72
+ # with the voice name from the class method + dynamic sample
73
+ talker = Mac::Say.new(voice: Mac::Say.voice(:country, :scotland)[:name])
74
+ talker.say string: talker.voice(:country, :scotland)[:sample]
75
+
76
+ # with the dynamic voice name selected from the multiple voices
77
+ talker = Mac::Say.news
78
+ voice = talker.voice(:language, :en)&.sample(1)&.first&.fetch :name
79
+ talker.say string: 'Hello world!', voice: voice
80
+
81
+ # changing voice in runtime for an instance of talker (while saying something)
82
+ voice = talker.voice(:country, :kr)
83
+ talker.say string: voice[:sample], voice: voice[:name]
84
+
85
+ # or change the voice without saying anything
86
+ talker.say voice: :"ting-ting"
87
+ talker.say string: '您好,我叫Ting-Ting。我讲中文普通话。'
88
+
89
+ # Listen to all the languages with the dynamic voices + dynamic samples
90
+ polyglot = Mac::Say.new
91
+ voices = polyglot.voices
92
+
93
+ voices.each_with_index do |v, i|
94
+ puts "#{i + 1} :: #{v[:name]} :: '#{v[:sample]}'"
95
+ polyglot.say string: v[:sample], voice: v[:name]
96
+ end
97
+
98
+ # Or perform a roll call
99
+ roll_call = Mac::Say.new
100
+ voices = roll_call.voices
101
+
102
+ voices.each_with_index do |v, i|
103
+ puts "#{i + 1} :: #{v[:name]}"
104
+ roll_call.say string: v[:name], voice: v[:name]
105
+ end
106
+
107
+ # ===== Reading files =====
108
+
109
+ # Read the file (prior to the string)
110
+ file_path = File.absolute_path '../test/fixtures/text/en_gb.txt', File.dirname(__FILE__)
111
+
112
+ # with a voice from the class
113
+ voice = Mac::Say.voice(:country, :gb)&.first&.fetch(:name)
114
+ reader = Mac::Say.new(file: file_path, voice: voice)
115
+ reader.read
116
+
117
+ # with a dynamic voice from the class
118
+ voice = Mac::Say.voice(:country, :scotland)[:name]
119
+ reader = Mac::Say.new(file: file_path)
120
+ reader.read voice: voice
121
+
122
+ # with a dynamic voice from the instance
123
+ reader = Mac::Say.new(file: file_path)
124
+ reader.read(voice: reader.voice(:country, :us)[2][:name])
125
+
126
+ # with a dynamic voice from the instance
127
+ new_file_path = File.absolute_path '../test/fixtures/text/en_us.txt', File.dirname(__FILE__)
128
+
129
+ # with a dynamic file change
130
+ reader = Mac::Say.new(file: file_path)
131
+ reader.read voice: :alex
132
+ reader.read file: new_file_path
133
+
134
+ # ===== Exceptions =====
135
+
136
+ # wrong file
137
+ begin
138
+ reader = Mac::Say.new(file: 'wrong')
139
+ reader.read
140
+ rescue Mac::Say::FileNotFound => e
141
+ puts e.message
142
+ end
143
+
144
+ # wrong file
145
+ begin
146
+ reader = Mac::Say.new
147
+ reader.read file: 'too_wrong'
148
+ rescue Mac::Say::FileNotFound => e
149
+ puts e.message
150
+ end
151
+
152
+ # wrong path
153
+ begin
154
+ Mac::Say.new(say_path: '/usr/bin/wrong_say')
155
+ rescue Mac::Say::CommandNotFound => e
156
+ puts e.message
157
+ end
158
+
159
+ # wrong voice
160
+ begin
161
+ talker = Mac::Say.new(voice: :wrong)
162
+ talker.say string: 'OMG! I lost my voice!'
163
+ rescue Mac::Say::VoiceNotFound => e
164
+ puts e.message
165
+ end
166
+
167
+ # wrong voice
168
+ begin
169
+ talker = Mac::Say.new
170
+ talker.say string: 'OMG! I lost my voice!', voice: :too_wrong
171
+ rescue Mac::Say::VoiceNotFound => e
172
+ puts e.message
173
+ end
174
+
175
+ # wrong voice
176
+ begin
177
+ Mac::Say.say 'OMG! I lost my voice!', :still_wrong
178
+ rescue Mac::Say::VoiceNotFound => e
179
+ puts e.message
180
+ end
181
+
182
+ # wrong feature
183
+ begin
184
+ Mac::Say.voice(:tone, :enthusiastic)
185
+ rescue Mac::Say::UnknownVoiceFeature => e
186
+ puts e.message
187
+ end
188
+
189
+ # wrong feature
190
+ begin
191
+ Mac::Say.new.voice(:articulation, :nostalgic)
192
+ rescue Mac::Say::UnknownVoiceFeature => e
193
+ puts e.message
194
+ end
195
+ ```
196
+
197
+ ## Installing & Updating MacOS TTS Voices
198
+
199
+ Open `System Preferences` using Spotlight / Alfred / Dock and follow text or visual instructions:
200
+
201
+ ```
202
+ System Preferences → Accessibility → Speech → System Voice →
203
+ → Customize… → (select voices) → OK → (Wait for download…)
204
+ ```
205
+
206
+ ![Installing & Updating MacOS TTS Voices](./img/voices_manual.png)
207
+
208
+ ## Dev Notes
209
+
210
+ ```sh
211
+ # generated with Ore: https://github.com/ruby-ore/ore
212
+ $ mine mac-say --git --mit --rubygems-tasks --markdown --minitest --travis --yard
213
+
214
+ # generate docs (unless this resolved: https://github.com/rrrene/inch/issues/42)
215
+ $ yard --markup markdown --markup-provider=redcarpet --title "mac-say Documentation" --protected --asset img:img
216
+
217
+ # check the docs
218
+ $ inch --pedantic
219
+
220
+ # test with a fake `say`
221
+ $ USE_FAKE_SAY='./test/fake/say' bundle exec rake test
222
+
223
+ # test with rake
224
+ $ bundle exec rake test
225
+
226
+ # test with m
227
+ $ bundle exec m
228
+
229
+ # run one test by LN
230
+ $ bundle exec m ./test/test_mac-say.rb:34
231
+ ```
232
+
233
+ ## Copyright
234
+
235
+ Copyright (c) 2017 Serge Bedzhyk
236
+
237
+ See [LICENSE.txt](./LICENSE.txt) for details.
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ require 'rubygems'
5
+
6
+ begin
7
+ require 'bundler/setup'
8
+ rescue LoadError => e
9
+ abort e.message
10
+ end
11
+
12
+ require 'rake'
13
+ require 'rubygems/tasks'
14
+ Gem::Tasks.new
15
+
16
+ require 'rake/testtask'
17
+
18
+ Rake::TestTask.new do |test|
19
+ test.libs << 'test'
20
+ test.pattern = 'test/**/test_*.rb'
21
+ test.verbose = true
22
+ end
23
+
24
+ # Wait for https://github.com/rrrene/inch/issues/42 to be resolved
25
+ # require 'yard'
26
+ # YARD::Rake::YardocTask.new
27
+ # task :doc => :yard
data/config.reek ADDED
@@ -0,0 +1,4 @@
1
+ TooManyStatements:
2
+ max_statements: 10
3
+ LongParameterList
4
+ max_params: 5
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+ require 'pp'
3
+ require_relative '../lib/mac/say'
4
+
5
+ # ===== Class level =====
6
+
7
+ # Get all the voices
8
+ pp Mac::Say.voices
9
+
10
+ # Collect the separate features lists
11
+ pp Mac::Say.voices.collect { |v| v[:name] }
12
+ pp Mac::Say.voices.collect { |v| v[:iso_code] }
13
+ pp Mac::Say.voices.collect { |v| v[:sample] }
14
+
15
+ # Find a voice (returns a Hash)
16
+ pp Mac::Say.voice(:name, :alex)
17
+ pp Mac::Say.voice(:country, :scotland)
18
+
19
+ # Find the voices by the feature (returns an Array)
20
+ pp Mac::Say.voice(:language, :en)
21
+
22
+ # Work with the voices collection
23
+ indian_english = Mac::Say.voice(:country, :in).select { |v| v[:iso_code][:language] == :en }.first[:name]
24
+
25
+ # Use multiline text
26
+ puts Mac::Say.say <<-DATA, indian_english
27
+ Invokes the given block passing in successive elements from self, deleting elements for which the block returns a false value.
28
+ The array may not be changed instantly every time the block is called.
29
+ If changes were made, it will return self, otherwise it returns nil.
30
+ DATA
31
+
32
+ # ===== Instance level =====
33
+
34
+ # with constant name in the constructor and custom rate
35
+ talker = Mac::Say.new(voice: :alex, rate: 300)
36
+ talker.say string: 'Hello world'
37
+
38
+ # with the voice name from the class method + dynamic sample
39
+ talker = Mac::Say.new(voice: Mac::Say.voice(:country, :scotland)[:name])
40
+ talker.say string: talker.voice(:country, :scotland)[:sample]
41
+
42
+ # with the dynamic voice name selected from the multiple voices
43
+ talker = Mac::Say.news
44
+ voice = talker.voice(:language, :en)&.sample(1)&.first&.fetch :name
45
+ talker.say string: 'Hello world!', voice: voice
46
+
47
+ # changing voice in runtime for an instance of talker (while saying something)
48
+ voice = talker.voice(:country, :kr)
49
+ talker.say string: voice[:sample], voice: voice[:name]
50
+
51
+ # or change the voice without saying anything
52
+ talker.say voice: :"ting-ting"
53
+ talker.say string: '您好,我叫Ting-Ting。我讲中文普通话。'
54
+
55
+ # Listen to all the languages with the dynamic voices + dynamic samples
56
+ polyglot = Mac::Say.new
57
+ voices = polyglot.voices
58
+
59
+ voices.each_with_index do |v, i|
60
+ puts "#{i + 1} :: #{v[:name]} :: '#{v[:sample]}'"
61
+ polyglot.say string: v[:sample], voice: v[:name]
62
+ end
63
+
64
+ # Or perform a roll call
65
+ roll_call = Mac::Say.new
66
+ voices = roll_call.voices
67
+
68
+ voices.each_with_index do |v, i|
69
+ puts "#{i + 1} :: #{v[:name]}"
70
+ roll_call.say string: v[:name], voice: v[:name]
71
+ end
72
+
73
+ # ===== Reading files =====
74
+
75
+ # Read the file (prior to the string)
76
+ file_path = File.absolute_path '../test/fixtures/text/en_gb.txt', File.dirname(__FILE__)
77
+
78
+ # with a voice from the class
79
+ voice = Mac::Say.voice(:country, :gb)&.first&.fetch(:name)
80
+ reader = Mac::Say.new(file: file_path, voice: voice)
81
+ reader.read
82
+
83
+ # with a dynamic voice from the class
84
+ voice = Mac::Say.voice(:country, :scotland)[:name]
85
+ reader = Mac::Say.new(file: file_path)
86
+ reader.read voice: voice
87
+
88
+ # with a dynamic voice from the instance
89
+ reader = Mac::Say.new(file: file_path)
90
+ reader.read(voice: reader.voice(:country, :us)[2][:name])
91
+
92
+ # with a dynamic voice from the instance
93
+ new_file_path = File.absolute_path '../test/fixtures/text/en_us.txt', File.dirname(__FILE__)
94
+
95
+ # with a dynamic file change
96
+ reader = Mac::Say.new(file: file_path)
97
+ reader.read voice: :alex
98
+ reader.read file: new_file_path
99
+
100
+ # ===== Exceptions =====
101
+
102
+ # wrong file
103
+ begin
104
+ reader = Mac::Say.new(file: 'wrong')
105
+ reader.read
106
+ rescue Mac::Say::FileNotFound => e
107
+ puts e.message
108
+ end
109
+
110
+ # wrong file
111
+ begin
112
+ reader = Mac::Say.new
113
+ reader.read file: 'too_wrong'
114
+ rescue Mac::Say::FileNotFound => e
115
+ puts e.message
116
+ end
117
+
118
+ # wrong path
119
+ begin
120
+ Mac::Say.new(say_path: '/usr/bin/wrong_say')
121
+ rescue Mac::Say::CommandNotFound => e
122
+ puts e.message
123
+ end
124
+
125
+ # wrong voice
126
+ begin
127
+ talker = Mac::Say.new(voice: :wrong)
128
+ talker.say string: 'OMG! I lost my voice!'
129
+ rescue Mac::Say::VoiceNotFound => e
130
+ puts e.message
131
+ end
132
+
133
+ # wrong voice
134
+ begin
135
+ talker = Mac::Say.new
136
+ talker.say string: 'OMG! I lost my voice!', voice: :too_wrong
137
+ rescue Mac::Say::VoiceNotFound => e
138
+ puts e.message
139
+ end
140
+
141
+ # wrong voice
142
+ begin
143
+ Mac::Say.say 'OMG! I lost my voice!', :still_wrong
144
+ rescue Mac::Say::VoiceNotFound => e
145
+ puts e.message
146
+ end
147
+
148
+ # wrong feature
149
+ begin
150
+ Mac::Say.voice(:tone, :enthusiastic)
151
+ rescue Mac::Say::UnknownVoiceFeature => e
152
+ puts e.message
153
+ end
154
+
155
+ # wrong feature
156
+ begin
157
+ Mac::Say.new.voice(:articulation, :nostalgic)
158
+ rescue Mac::Say::UnknownVoiceFeature => e
159
+ puts e.message
160
+ end