voicemaker 0.1.0 → 0.2.0
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 +4 -4
- data/README.md +201 -62
- data/lib/sample.yml +9 -11
- data/lib/voicemaker/api.rb +60 -45
- data/lib/voicemaker/cli.rb +17 -2
- data/lib/voicemaker/commands/base.rb +23 -0
- data/lib/voicemaker/commands/new_command.rb +48 -0
- data/lib/voicemaker/commands/project_command.rb +72 -0
- data/lib/voicemaker/commands/tts_command.rb +90 -0
- data/lib/voicemaker/commands/voices_command.rb +63 -0
- data/lib/voicemaker/extensions/file.rb +11 -0
- data/lib/voicemaker/tts.rb +24 -0
- data/lib/voicemaker/tts_params.rb +90 -0
- data/lib/voicemaker/version.rb +1 -1
- data/lib/voicemaker/voices.rb +41 -0
- data/lib/voicemaker.rb +4 -0
- metadata +25 -3
- data/lib/voicemaker/command.rb +0 -111
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ef73c119c97b71f1114e095357f7bfc04d20e73081752fa5bae226c390911622
|
4
|
+
data.tar.gz: 116b47aaa08211a962702f2f2cf1db1134992468a7cb4d296a454f8e23b0f264
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba494699349745471825e4e9af8c92282f5c075b533ae1a9392f6058a9885f617f61e7d3faab48c1a3b3de4f7ea51b29760bf4985d510a3c50d82017d24be93b
|
7
|
+
data.tar.gz: 912165db465f663a2c062206c712bfd35fdc27b5b558fa5a34ee3084618d7565f35bab41cd7e2e02d4c97d9f92808ea2e6def4a923bd3721fc98e6d38944d310
|
data/README.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/voicemaker)
|
4
4
|
[](https://github.com/DannyBen/voicemaker/actions?query=workflow%3ATest)
|
5
|
+
[](https://codeclimate.com/github/DannyBen/voicemaker/maintainability)
|
5
6
|
|
6
7
|
---
|
7
8
|
|
@@ -35,8 +36,7 @@ First, require and initialize with your Voicemaker API key:
|
|
35
36
|
|
36
37
|
```ruby
|
37
38
|
require 'voicemaker'
|
38
|
-
|
39
|
-
api = Voicemaker::API.new api_key
|
39
|
+
Voicemaker::API.key = 'your api key'
|
40
40
|
```
|
41
41
|
|
42
42
|
### Voices list
|
@@ -44,13 +44,14 @@ api = Voicemaker::API.new api_key
|
|
44
44
|
Get the full voices list:
|
45
45
|
|
46
46
|
```ruby
|
47
|
-
|
47
|
+
voices = Voicemaker::Voices.new
|
48
|
+
result = voices.all
|
48
49
|
```
|
49
50
|
|
50
51
|
Search the voices list for one or more strings (AND search):
|
51
52
|
|
52
53
|
```ruby
|
53
|
-
result =
|
54
|
+
result = voices.search "en-us", "female"
|
54
55
|
```
|
55
56
|
|
56
57
|
### Audio generation and download
|
@@ -58,90 +59,221 @@ result = api.voices ["en_US", "female"]
|
|
58
59
|
Make an API call and get the URL to the audio file in return:
|
59
60
|
|
60
61
|
```ruby
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
"LanguageCode" => "en-US",
|
65
|
-
"OutputFormat" => "mp3",
|
66
|
-
"SampleRate" => "48000",
|
67
|
-
"Effect" => "default",
|
68
|
-
"MasterSpeed" => "0",
|
69
|
-
"MasterVolume" => "0",
|
70
|
-
"MasterPitch" => "0",
|
71
|
-
"Text" => "Hello world",
|
72
|
-
}
|
73
|
-
|
74
|
-
result = api.generate params
|
62
|
+
tts = Voicemaker::TTS.new voice: 'Jony', text: 'Hello world'
|
63
|
+
url = tts.url
|
64
|
+
tts.save "output.mp3"
|
75
65
|
```
|
76
66
|
|
77
|
-
|
78
|
-
download the file:
|
67
|
+
or with the full list of available parameters:
|
79
68
|
|
80
69
|
```ruby
|
81
|
-
|
70
|
+
params = {
|
71
|
+
voice: "Jony",
|
72
|
+
output_format: "mp3",
|
73
|
+
sample_rate: 48000,
|
74
|
+
effect: "default",
|
75
|
+
master_speed: 0,
|
76
|
+
master_volume: 0,
|
77
|
+
master_pitch: 0,
|
78
|
+
text: "Hello world"
|
79
|
+
}
|
80
|
+
|
81
|
+
tts = Voicemaker::TTS.new params
|
82
|
+
url = tts.url
|
82
83
|
```
|
83
84
|
|
85
|
+
As the `voice` parameter, you may use either the voice ID (`ai3-Jony`) or the
|
86
|
+
voice web name (`Jony`). Just note that some voices have the same webname (for
|
87
|
+
example, Emily), so in these cases it is best to use the full voice ID.
|
88
|
+
|
84
89
|
## Command line interface
|
85
90
|
|
86
91
|
<!-- USAGE -->
|
92
|
+
### `$ voicemaker `
|
93
|
+
|
94
|
+
|
87
95
|
```
|
88
|
-
|
96
|
+
Voicemaker Text-to-Speech
|
97
|
+
|
98
|
+
Commands:
|
99
|
+
voices Get a list of available voices
|
100
|
+
tts Generate audio files from text
|
101
|
+
new Create a new config file or a project directory
|
102
|
+
project Create multiple audio files
|
103
|
+
|
104
|
+
API Documentation: https://developer.voicemaker.in/apidocs
|
89
105
|
|
90
|
-
|
106
|
+
```
|
91
107
|
|
92
|
-
|
93
|
-
|
108
|
+
### `$ voicemaker voices --help`
|
109
|
+
|
110
|
+
|
111
|
+
```
|
112
|
+
Get a list of available voices
|
94
113
|
|
95
114
|
Usage:
|
96
|
-
voicemaker voices [--save PATH] [SEARCH...]
|
97
|
-
voicemaker
|
98
|
-
voicemaker generate CONFIG [OUTPUT]
|
99
|
-
voicemaker batch INDIR OUTDIR
|
100
|
-
voicemaker (-h|--help|--version)
|
115
|
+
voicemaker voices [--save PATH --count --compact] [SEARCH...]
|
116
|
+
voicemaker voices (-h|--help)
|
101
117
|
|
102
|
-
|
103
|
-
|
104
|
-
|
118
|
+
Options:
|
119
|
+
-s --save PATH
|
120
|
+
Save output to output YAML file
|
121
|
+
|
122
|
+
-t --compact
|
123
|
+
Show each voice in a single line
|
105
124
|
|
106
|
-
|
107
|
-
|
125
|
+
-c --count
|
126
|
+
Add number of voices to the result
|
108
127
|
|
109
|
-
|
110
|
-
|
111
|
-
filename, with the proper mp3 or wav extension
|
128
|
+
-h --help
|
129
|
+
Show this help
|
112
130
|
|
113
|
-
|
114
|
-
|
131
|
+
Parameters:
|
132
|
+
SEARCH
|
133
|
+
Provide one or more text strings to search for (case insensitive AND search)
|
134
|
+
|
135
|
+
Environment Variables:
|
136
|
+
VOICEMAKER_API_KEY
|
137
|
+
Your Voicemaker API key [required]
|
138
|
+
|
139
|
+
VOICEMAKER_API_HOST
|
140
|
+
Override the API host URL
|
141
|
+
|
142
|
+
VOICEMAKER_CACHE_DIR
|
143
|
+
API cache diredctory [default: cache]
|
144
|
+
|
145
|
+
VOICEMAKER_CACHE_LIFE
|
146
|
+
API cache life. These formats are supported:
|
147
|
+
off - No cache
|
148
|
+
20s - 20 seconds
|
149
|
+
10m - 10 minutes
|
150
|
+
10h - 10 hours
|
151
|
+
10d - 10 days
|
152
|
+
|
153
|
+
Examples:
|
154
|
+
voicemaker voices en-us
|
155
|
+
voicemaker voices --save out.yml en-us
|
156
|
+
voicemaker voices en-us female
|
157
|
+
voicemaker voices en-us --compact
|
158
|
+
|
159
|
+
```
|
160
|
+
|
161
|
+
### `$ voicemaker tts --help`
|
162
|
+
|
163
|
+
|
164
|
+
```
|
165
|
+
Generate audio files from text
|
166
|
+
|
167
|
+
Usage:
|
168
|
+
voicemaker tts [options]
|
169
|
+
voicemaker tts (-h|--help)
|
115
170
|
|
116
171
|
Options:
|
117
|
-
-
|
118
|
-
|
172
|
+
-v --voice NAME
|
173
|
+
Voice ID or Webname
|
174
|
+
|
175
|
+
-t --text TEXT
|
176
|
+
Text to say
|
177
|
+
|
178
|
+
-f --file PATH
|
179
|
+
Load text from file
|
180
|
+
|
181
|
+
-c --config PATH
|
182
|
+
Use a YAML configuration file
|
119
183
|
|
120
184
|
-s --save PATH
|
121
|
-
Save
|
185
|
+
Save audio file.
|
186
|
+
If not provided, a URL to the audio file will be printed instead.
|
187
|
+
When used with the --config option, omit the file extension, as it will be
|
188
|
+
determined based on the config file.
|
189
|
+
|
190
|
+
-d --debug
|
191
|
+
Show API parameters
|
122
192
|
|
123
193
|
-h --help
|
124
194
|
Show this help
|
125
195
|
|
126
|
-
|
127
|
-
|
196
|
+
Environment Variables:
|
197
|
+
VOICEMAKER_API_KEY
|
198
|
+
Your Voicemaker API key [required]
|
199
|
+
|
200
|
+
VOICEMAKER_API_HOST
|
201
|
+
Override the API host URL
|
202
|
+
|
203
|
+
VOICEMAKER_CACHE_DIR
|
204
|
+
API cache diredctory [default: cache]
|
205
|
+
|
206
|
+
VOICEMAKER_CACHE_LIFE
|
207
|
+
API cache life. These formats are supported:
|
208
|
+
off - No cache
|
209
|
+
20s - 20 seconds
|
210
|
+
10m - 10 minutes
|
211
|
+
10h - 10 hours
|
212
|
+
10d - 10 days
|
213
|
+
|
214
|
+
Examples:
|
215
|
+
voicemaker tts --voice ai3-Jony --text "Hello world" --save out.mp3
|
216
|
+
voicemaker tts -v ai3-Jony --file hello.txt --save out.mp3
|
217
|
+
voicemaker tts --config english-female.yml -f text.txt -s outfile
|
218
|
+
|
219
|
+
```
|
220
|
+
|
221
|
+
### `$ voicemaker new --help`
|
222
|
+
|
223
|
+
|
224
|
+
```
|
225
|
+
Create a new config file or a project directory
|
226
|
+
|
227
|
+
Usage:
|
228
|
+
voicemaker new config PATH
|
229
|
+
voicemaker new project DIR
|
230
|
+
voicemaker new (-h|--help)
|
231
|
+
|
232
|
+
Commands:
|
233
|
+
config
|
234
|
+
Create a config file to be used with the 'voicemaker tts' command
|
235
|
+
|
236
|
+
project
|
237
|
+
Generate a project directory to be used with the 'voicemaker project'
|
238
|
+
command
|
239
|
+
|
240
|
+
Options:
|
241
|
+
-h --help
|
242
|
+
Show this help
|
128
243
|
|
129
244
|
Parameters:
|
130
|
-
|
131
|
-
|
245
|
+
PATH
|
246
|
+
Path to use for creating the config file
|
247
|
+
|
248
|
+
DIR
|
249
|
+
Directory name for creating the project structure
|
250
|
+
|
251
|
+
Examples:
|
252
|
+
voicemaker new config test.yml
|
253
|
+
voicemaker new project sample-project
|
254
|
+
|
255
|
+
```
|
256
|
+
|
257
|
+
### `$ voicemaker project --help`
|
258
|
+
|
259
|
+
|
260
|
+
```
|
261
|
+
Create multiple audio files
|
132
262
|
|
133
|
-
|
134
|
-
|
263
|
+
Usage:
|
264
|
+
voicemaker project PATH [--debug]
|
265
|
+
voicemaker project (-h|--help)
|
135
266
|
|
136
|
-
|
137
|
-
|
138
|
-
|
267
|
+
Options:
|
268
|
+
--debug
|
269
|
+
Show API parameters
|
139
270
|
|
140
|
-
|
141
|
-
|
271
|
+
-h --help
|
272
|
+
Show this help
|
142
273
|
|
143
|
-
|
144
|
-
|
274
|
+
Parameters:
|
275
|
+
PATH
|
276
|
+
Path to the project directory
|
145
277
|
|
146
278
|
Environment Variables:
|
147
279
|
VOICEMAKER_API_KEY
|
@@ -150,15 +282,22 @@ Environment Variables:
|
|
150
282
|
VOICEMAKER_API_HOST
|
151
283
|
Override the API host URL
|
152
284
|
|
285
|
+
VOICEMAKER_CACHE_DIR
|
286
|
+
API cache diredctory [default: cache]
|
287
|
+
|
288
|
+
VOICEMAKER_CACHE_LIFE
|
289
|
+
API cache life. These formats are supported:
|
290
|
+
off - No cache
|
291
|
+
20s - 20 seconds
|
292
|
+
10m - 10 minutes
|
293
|
+
10h - 10 hours
|
294
|
+
10d - 10 days
|
295
|
+
|
153
296
|
Examples:
|
154
|
-
voicemaker
|
155
|
-
voicemaker voices --save out.yml en-us
|
156
|
-
voicemaker voices en-us female
|
157
|
-
voicemaker new test.yml
|
158
|
-
voicemaker generate test.yml out.mp3
|
159
|
-
voicemaker batch configs out
|
297
|
+
voicemaker project sample-project
|
160
298
|
|
161
299
|
```
|
300
|
+
|
162
301
|
<!-- USAGE -->
|
163
302
|
|
164
303
|
|
data/lib/sample.yml
CHANGED
@@ -1,11 +1,9 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
Text: |
|
11
|
-
Hello world
|
1
|
+
voice: Jony
|
2
|
+
|
3
|
+
# All the below are optional
|
4
|
+
output_format: mp3
|
5
|
+
sample_rate: 48000
|
6
|
+
effect: default
|
7
|
+
master_speed: 0
|
8
|
+
master_volume: 0
|
9
|
+
master_pitch: 0
|
data/lib/voicemaker/api.rb
CHANGED
@@ -1,66 +1,81 @@
|
|
1
|
+
require 'lightly'
|
1
2
|
require 'http'
|
2
|
-
require 'down'
|
3
3
|
|
4
4
|
module Voicemaker
|
5
5
|
class API
|
6
|
-
|
6
|
+
ROOT = 'https://developer.voicemaker.in/voice'
|
7
7
|
|
8
8
|
class << self
|
9
|
-
attr_writer :
|
9
|
+
attr_writer :root, :key, :cache_life, :cache_dir
|
10
10
|
|
11
|
-
def
|
12
|
-
@
|
11
|
+
def root
|
12
|
+
@root ||= ENV['VOICEMAKER_API_ROOT'] || ROOT
|
13
13
|
end
|
14
|
-
end
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
def key
|
16
|
+
@key ||= ENV['VOICEMAKER_API_KEY'] || raise(MissingAuth, "Please set the 'VOICEMAKER_API_KEY' environment variable")
|
17
|
+
end
|
18
|
+
|
19
|
+
def cache_life
|
20
|
+
@cache_life ||= ENV['VOICEMAKER_CACHE_LIFE'] || '4h'
|
21
|
+
end
|
19
22
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
response = HTTP.auth(auth_header).get "#{base_uri}/list"
|
24
|
-
raise BadResponse, "#{response.status}\n#{response.body}" unless response.status.success?
|
25
|
-
voices = response.parse.dig 'data', 'voices_list'
|
26
|
-
raise BadResponse, "Unexpected response: #{response}" unless voices
|
23
|
+
def cache_dir
|
24
|
+
@cache_dir ||= ENV['VOICEMAKER_CACHE_DIR'] || 'cache'
|
25
|
+
end
|
27
26
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
# Performs HTTP GET and cache it
|
28
|
+
# Returns a parsed body on success
|
29
|
+
# Raises BadResponse on error
|
30
|
+
def get(endpoint, params = {})
|
31
|
+
cache.get "#{root}/#{endpoint}+#{params}" do
|
32
|
+
response = get! endpoint, params
|
33
|
+
response.parse
|
32
34
|
end
|
33
|
-
else
|
34
|
-
voices
|
35
35
|
end
|
36
|
-
end
|
37
36
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
37
|
+
# Performs HTTP POST and cache it
|
38
|
+
# Returns a parsed body on success
|
39
|
+
# Raises BadResponse on error
|
40
|
+
def post(endpoint, params = {})
|
41
|
+
cache.get "#{root}/#{endpoint}+#{params}" do
|
42
|
+
response = post! endpoint, params
|
43
|
+
response.parse
|
44
|
+
end
|
45
45
|
end
|
46
|
-
end
|
47
46
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
47
|
+
def cache
|
48
|
+
@cache ||= begin
|
49
|
+
lightly = Lightly.new life: cache_life, dir: cache_dir
|
50
|
+
lightly.disable if cache_life == 'off'
|
51
|
+
lightly
|
52
|
+
end
|
53
|
+
end
|
54
54
|
|
55
|
-
protected
|
55
|
+
protected
|
56
56
|
|
57
|
-
|
58
|
-
|
59
|
-
|
57
|
+
# Performs HTTP GET
|
58
|
+
# Returns a parsed body on success
|
59
|
+
# Raises BadResponse on error
|
60
|
+
def get!(endpoint, params = {})
|
61
|
+
response = HTTP.auth(auth_header).get "#{root}/#{endpoint}", json: params
|
62
|
+
raise BadResponse, "#{response.status}\n#{response.body}" unless response.status.success?
|
63
|
+
response
|
64
|
+
end
|
60
65
|
|
61
|
-
|
62
|
-
|
63
|
-
|
66
|
+
# Performs HTTP POST
|
67
|
+
# Returns a parsed body on success
|
68
|
+
# Raises BadResponse on error
|
69
|
+
def post!(endpoint, params = {})
|
70
|
+
response = HTTP.auth(auth_header).post "#{root}/#{endpoint}", json: params
|
71
|
+
raise BadResponse, "#{response.status}\n#{response.body}" unless response.status.success?
|
72
|
+
response
|
73
|
+
end
|
74
|
+
|
75
|
+
def auth_header
|
76
|
+
"Bearer #{key}"
|
77
|
+
end
|
64
78
|
|
79
|
+
end
|
65
80
|
end
|
66
|
-
end
|
81
|
+
end
|
data/lib/voicemaker/cli.rb
CHANGED
@@ -1,10 +1,25 @@
|
|
1
|
+
require 'lp'
|
1
2
|
require 'mister_bin'
|
2
|
-
require 'voicemaker/
|
3
|
+
require 'voicemaker/commands/base'
|
4
|
+
require 'voicemaker/commands/voices_command'
|
5
|
+
require 'voicemaker/commands/tts_command'
|
6
|
+
require 'voicemaker/commands/new_command'
|
7
|
+
require 'voicemaker/commands/project_command'
|
3
8
|
|
4
9
|
module Voicemaker
|
5
10
|
class CLI
|
6
11
|
def self.runner
|
7
|
-
MisterBin::Runner.new
|
12
|
+
router = MisterBin::Runner.new version: VERSION,
|
13
|
+
header: "Voicemaker Text-to-Speech",
|
14
|
+
footer: "API Documentation: https://developer.voicemaker.in/apidocs"
|
15
|
+
|
16
|
+
router.route "voices", to: Commands::VoicesCommand
|
17
|
+
router.route "tts", to: Commands::TTSCommand
|
18
|
+
router.route "new", to: Commands::NewCommand
|
19
|
+
router.route "project", to: Commands::ProjectCommand
|
20
|
+
|
21
|
+
router
|
22
|
+
|
8
23
|
end
|
9
24
|
end
|
10
25
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Voicemaker
|
2
|
+
module Commands
|
3
|
+
class Base < MisterBin::Command
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def api_environment
|
7
|
+
environment "VOICEMAKER_API_KEY", "Your Voicemaker API key [required]"
|
8
|
+
environment "VOICEMAKER_API_HOST", "Override the API host URL"
|
9
|
+
environment "VOICEMAKER_CACHE_DIR", "API cache diredctory [default: cache]"
|
10
|
+
environment "VOICEMAKER_CACHE_LIFE", <<~EOF
|
11
|
+
API cache life. These formats are supported:
|
12
|
+
off - No cache
|
13
|
+
20s - 20 seconds
|
14
|
+
10m - 10 minutes
|
15
|
+
10h - 10 hours
|
16
|
+
10d - 10 days
|
17
|
+
EOF
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Voicemaker
|
2
|
+
module Commands
|
3
|
+
class NewCommand < Base
|
4
|
+
help "Create a new config file or a project directory"
|
5
|
+
|
6
|
+
usage "voicemaker new config PATH"
|
7
|
+
usage "voicemaker new project DIR"
|
8
|
+
usage "voicemaker new (-h|--help)"
|
9
|
+
|
10
|
+
command "config", "Create a config file to be used with the 'voicemaker tts' command"
|
11
|
+
command "project", "Generate a project directory to be used with the 'voicemaker project' command"
|
12
|
+
|
13
|
+
param "PATH", "Path to use for creating the config file"
|
14
|
+
param "DIR", "Directory name for creating the project structure"
|
15
|
+
|
16
|
+
example "voicemaker new config test.yml"
|
17
|
+
example "voicemaker new project sample-project"
|
18
|
+
|
19
|
+
def config_command
|
20
|
+
copy_config_template args['PATH']
|
21
|
+
end
|
22
|
+
|
23
|
+
def project_command
|
24
|
+
base = args['DIR']
|
25
|
+
copy_config_template "#{base}/config.yml"
|
26
|
+
Dir.mkdir "#{base}/in" unless Dir.exist? "#{base}/in"
|
27
|
+
Dir.mkdir "#{base}/out" unless Dir.exist? "#{base}/out"
|
28
|
+
File.write "#{base}/in/sample1.txt", "hello"
|
29
|
+
File.write "#{base}/in/sample2.txt", "hello"
|
30
|
+
say "created in and out folders in #{base}"
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
def copy_config_template(path)
|
36
|
+
raise InputError, "File already exists: #{path}" if File.exist? path
|
37
|
+
content = File.read template
|
38
|
+
File.deep_write path, content
|
39
|
+
say "saved #{path}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def template
|
43
|
+
@template ||= File.expand_path "../../sample.yml", __dir__
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Voicemaker
|
2
|
+
module Commands
|
3
|
+
class ProjectCommand < Base
|
4
|
+
help "Create multiple audio files"
|
5
|
+
|
6
|
+
usage "voicemaker project PATH [--debug]"
|
7
|
+
usage "voicemaker project (-h|--help)"
|
8
|
+
|
9
|
+
param "PATH", "Path to the project directory"
|
10
|
+
|
11
|
+
option "--debug", "Show API parameters"
|
12
|
+
|
13
|
+
api_environment
|
14
|
+
|
15
|
+
example "voicemaker project sample-project"
|
16
|
+
|
17
|
+
def run
|
18
|
+
text_files.each do |file|
|
19
|
+
text = File.read file
|
20
|
+
tts.params.text = text
|
21
|
+
audio_file = File.basename(file, '.txt') + ".#{tts.params.output_format}"
|
22
|
+
output_path = "#{outdir}/#{audio_file}"
|
23
|
+
|
24
|
+
say "in: !txtblu!#{file}"
|
25
|
+
say "url: !txtblu!#{tts.url}"
|
26
|
+
say "out: !txtblu!#{output_path}"
|
27
|
+
show_details tts if args['--debug']
|
28
|
+
|
29
|
+
tts.save output_path
|
30
|
+
say "---"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def tts
|
37
|
+
@tts ||= Voicemaker::TTS.new **config
|
38
|
+
end
|
39
|
+
|
40
|
+
def show_details(tts, output: nil)
|
41
|
+
lp tts.params.api_params
|
42
|
+
end
|
43
|
+
|
44
|
+
def text_files
|
45
|
+
@text_files ||= Dir["#{indir}/*.txt"]
|
46
|
+
end
|
47
|
+
|
48
|
+
def config
|
49
|
+
@config ||= begin
|
50
|
+
raise InputError, "Cannot find config file: #{config_path}" unless File.exist? config_path
|
51
|
+
YAML.load_file(config_path).transform_keys &:to_sym
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def config_path
|
56
|
+
"#{project_dir}/config.yml"
|
57
|
+
end
|
58
|
+
|
59
|
+
def project_dir
|
60
|
+
args['PATH']
|
61
|
+
end
|
62
|
+
|
63
|
+
def indir
|
64
|
+
"#{project_dir}/in"
|
65
|
+
end
|
66
|
+
|
67
|
+
def outdir
|
68
|
+
"#{project_dir}/out"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Voicemaker
|
2
|
+
module Commands
|
3
|
+
class TTSCommand < Base
|
4
|
+
help "Generate audio files from text"
|
5
|
+
|
6
|
+
usage "voicemaker tts [options]"
|
7
|
+
usage "voicemaker tts (-h|--help)"
|
8
|
+
|
9
|
+
option "-v --voice NAME", "Voice ID or Webname"
|
10
|
+
option "-t --text TEXT", "Text to say"
|
11
|
+
option "-f --file PATH", "Load text from file"
|
12
|
+
option "-c --config PATH", "Use a YAML configuration file"
|
13
|
+
option "-s --save PATH", <<~EOF
|
14
|
+
Save audio file.
|
15
|
+
If not provided, a URL to the audio file will be printed instead.
|
16
|
+
When used with the --config option, omit the file extension, as it will be determined based on the config file.
|
17
|
+
EOF
|
18
|
+
option "-d --debug", "Show API parameters"
|
19
|
+
|
20
|
+
api_environment
|
21
|
+
|
22
|
+
example %q[voicemaker tts --voice ai3-Jony --text "Hello world" --save out.mp3]
|
23
|
+
example %q[voicemaker tts -v ai3-Jony --file hello.txt --save out.mp3]
|
24
|
+
example %q[voicemaker tts --config english-female.yml -f text.txt -s outfile]
|
25
|
+
|
26
|
+
def run
|
27
|
+
verify_args
|
28
|
+
tts = Voicemaker::TTS.new **tts_params
|
29
|
+
say "url: !txtblu!#{tts.url}"
|
30
|
+
|
31
|
+
if output_file
|
32
|
+
tts.save output_file
|
33
|
+
say "out: !txtblu!#{output_file}"
|
34
|
+
end
|
35
|
+
|
36
|
+
show_details tts if args['--debug']
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def show_details(tts, output: nil)
|
42
|
+
lp tts.params.api_params
|
43
|
+
end
|
44
|
+
|
45
|
+
def verify_args
|
46
|
+
raise InputError, "Please provide either --config or --voice" unless args['--config'] || args['--voice']
|
47
|
+
raise InputError, "Please provide either --text or --file" unless args['--text'] || args['--file']
|
48
|
+
end
|
49
|
+
|
50
|
+
def voice
|
51
|
+
args['--voice']
|
52
|
+
end
|
53
|
+
|
54
|
+
def text
|
55
|
+
args['--text'] || text_from_file
|
56
|
+
end
|
57
|
+
|
58
|
+
def output_file
|
59
|
+
args['--save']
|
60
|
+
end
|
61
|
+
|
62
|
+
def tts_params
|
63
|
+
result = if config
|
64
|
+
YAML.load_file(config).transform_keys &:to_sym
|
65
|
+
else
|
66
|
+
{}
|
67
|
+
end
|
68
|
+
|
69
|
+
result[:voice] = voice if voice
|
70
|
+
result[:text] = text
|
71
|
+
result[:output_format] = File.extname(output_file)[1..] if output_file
|
72
|
+
result
|
73
|
+
end
|
74
|
+
|
75
|
+
def config
|
76
|
+
if args['--config']
|
77
|
+
raise InputError, "Cannot find config file #{args['--config']}" unless File.exist? args['--config']
|
78
|
+
args['--config']
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def text_from_file
|
83
|
+
path = args['--file']
|
84
|
+
raise InputError, "Cannot find text file: #{path}" unless File.exist? path
|
85
|
+
File.read path
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Voicemaker
|
2
|
+
module Commands
|
3
|
+
class VoicesCommand < Base
|
4
|
+
help "Get a list of available voices"
|
5
|
+
|
6
|
+
usage "voicemaker voices [--save PATH --count --verbose] [SEARCH...]"
|
7
|
+
usage "voicemaker voices (-h|--help)"
|
8
|
+
|
9
|
+
option "-s --save PATH", "Save output to output YAML file"
|
10
|
+
option "-v --verbose", "Show the full voices data structure"
|
11
|
+
option "-c --count", "Add number of voices to the result"
|
12
|
+
|
13
|
+
param "SEARCH", "Provide one or more text strings to search for (case insensitive AND search)"
|
14
|
+
|
15
|
+
api_environment
|
16
|
+
|
17
|
+
example "voicemaker voices en-us"
|
18
|
+
example "voicemaker voices --save out.yml en-us"
|
19
|
+
example "voicemaker voices en-us female"
|
20
|
+
example "voicemaker voices en-us --verbose"
|
21
|
+
|
22
|
+
def run
|
23
|
+
result = voices.search *args['SEARCH']
|
24
|
+
result['count'] = result.count if args['--count'] and args['--verbose']
|
25
|
+
send_output result
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def voices
|
31
|
+
@voices ||= Voicemaker::Voices.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def send_output(data)
|
35
|
+
save = args['--save']
|
36
|
+
if save
|
37
|
+
say "saved #{save}"
|
38
|
+
File.write save, args['--verbose'] ? data.to_yaml : compact(data)
|
39
|
+
elsif args['--verbose']
|
40
|
+
lp data
|
41
|
+
else
|
42
|
+
puts compact(data)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def compact(data)
|
47
|
+
result = data.values.map do |v|
|
48
|
+
[
|
49
|
+
v['VoiceId'].ljust(16),
|
50
|
+
# v['VoiceWebname'].ljust(10),
|
51
|
+
v['Language'],
|
52
|
+
v['VoiceGender'].ljust(10),
|
53
|
+
v['Engine']
|
54
|
+
].join "\t"
|
55
|
+
end
|
56
|
+
|
57
|
+
result.push "count: #{data.count}" if args['--count']
|
58
|
+
result.join "\n"
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'down'
|
2
|
+
|
3
|
+
module Voicemaker
|
4
|
+
class TTS
|
5
|
+
attr_reader :params
|
6
|
+
|
7
|
+
def initialize(params = {})
|
8
|
+
@params = TTSParams.new params
|
9
|
+
end
|
10
|
+
|
11
|
+
# Returns the URL for the generated file
|
12
|
+
def url
|
13
|
+
response = API.post "api", params.api_params
|
14
|
+
url = response['path']
|
15
|
+
raise BadResponse, "Received empty URL: #{response}" unless url
|
16
|
+
url
|
17
|
+
end
|
18
|
+
|
19
|
+
# Saves the audio file
|
20
|
+
def save(outpath)
|
21
|
+
Down.download url, destination: outpath
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'down'
|
2
|
+
|
3
|
+
module Voicemaker
|
4
|
+
# Provides normalized access to all parameters needed for the TTS endpoint,
|
5
|
+
# including the auto identification of the Engine and Language based on
|
6
|
+
# voice ID, and including sensible defaults.
|
7
|
+
class TTSParams
|
8
|
+
attr_reader :input_params
|
9
|
+
attr_writer :text, :voice, :output_format, :effect, :sample_rate,
|
10
|
+
:master_speed, :master_volume, :master_pitch
|
11
|
+
|
12
|
+
def initialize(input_params = {})
|
13
|
+
@input_params = input_params
|
14
|
+
end
|
15
|
+
|
16
|
+
def inspect
|
17
|
+
%Q[#<Voicemaker::TTSParams api_params="#{api_params}"]
|
18
|
+
end
|
19
|
+
|
20
|
+
def voice
|
21
|
+
@voice ||= find_voice || raise(InputError, "Missing or invalid parameter: voice")
|
22
|
+
end
|
23
|
+
|
24
|
+
def text
|
25
|
+
@text ||= input_params[:text] || raise(InputError, "Missing parameter: text")
|
26
|
+
end
|
27
|
+
|
28
|
+
def output_format
|
29
|
+
@output_format ||= input_params[:output_format] || 'mp3'
|
30
|
+
end
|
31
|
+
|
32
|
+
def effect
|
33
|
+
@effect ||= input_params[:effect] || 'default'
|
34
|
+
end
|
35
|
+
|
36
|
+
def sample_rate
|
37
|
+
@sample_rate ||= (input_params[:sample_rate] || 48000).to_s
|
38
|
+
end
|
39
|
+
|
40
|
+
def master_speed
|
41
|
+
@master_speed ||= (input_params[:master_speed] || 0).to_s
|
42
|
+
end
|
43
|
+
|
44
|
+
def master_volume
|
45
|
+
@master_volume ||= (input_params[:master_volume] || 0).to_s
|
46
|
+
end
|
47
|
+
|
48
|
+
def master_pitch
|
49
|
+
@master_pitch ||= (input_params[:master_pitch] || 0).to_s
|
50
|
+
end
|
51
|
+
|
52
|
+
def api_params
|
53
|
+
{
|
54
|
+
"Engine" => engine,
|
55
|
+
"VoiceId" => voice,
|
56
|
+
"LanguageCode" => language,
|
57
|
+
"OutputFormat" => output_format,
|
58
|
+
"SampleRate" => sample_rate,
|
59
|
+
"Effect" => effect,
|
60
|
+
"MasterSpeed" => master_speed,
|
61
|
+
"MasterVolume" => master_volume,
|
62
|
+
"MasterPitch" => master_pitch,
|
63
|
+
"Text" => text
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
protected
|
68
|
+
|
69
|
+
def engine
|
70
|
+
voice_params['Engine']
|
71
|
+
end
|
72
|
+
|
73
|
+
def language
|
74
|
+
voice_params['Language']
|
75
|
+
end
|
76
|
+
|
77
|
+
def find_voice
|
78
|
+
return nil unless input_params[:voice]
|
79
|
+
voices.find(input_params[:voice])&.dig 'VoiceId'
|
80
|
+
end
|
81
|
+
|
82
|
+
def voice_params
|
83
|
+
voices[voice] || raise(InputError, "Cannot find definition for voice: #{voice}")
|
84
|
+
end
|
85
|
+
|
86
|
+
def voices
|
87
|
+
@voices ||= Voices.new
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/voicemaker/version.rb
CHANGED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Voicemaker
|
4
|
+
# Provides easy and cached access to the list of available voices.
|
5
|
+
class Voices
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
# Enable some of the hash methods idrectly on the Voices object
|
9
|
+
def_delegators :all, :[], :count, :size, :first, :last, :select, :reject,
|
10
|
+
:map, :keys, :values, :each
|
11
|
+
|
12
|
+
# Returns all voices
|
13
|
+
def all
|
14
|
+
@all ||= begin
|
15
|
+
response = API.get "/list"
|
16
|
+
result = response.dig 'data', 'voices_list'
|
17
|
+
raise BadResponse, "Unexpected response: #{response}" unless result
|
18
|
+
result.map { |voice| [voice['VoiceId'], voice] }
|
19
|
+
.sort_by { |_, v| v['VoiceId'] }.to_h
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns a filtered list of voices, with all queries partially included
|
24
|
+
# in the voice parameter values
|
25
|
+
def search(*queries)
|
26
|
+
queries = nil if queries&.empty?
|
27
|
+
return all unless queries
|
28
|
+
|
29
|
+
all.select do |key, data|
|
30
|
+
haystack = data.values.join(' ').downcase
|
31
|
+
queries.all? { |query| haystack.include? query.downcase }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns a single voice, by Voice ID or Voice Webname
|
36
|
+
def find(id_or_webname)
|
37
|
+
all[id_or_webname] || all.find { |_, a| a['VoiceWebname'] == id_or_webname }&.last
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
data/lib/voicemaker.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: voicemaker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Danny Ben Shitrit
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-04-
|
11
|
+
date: 2022-04-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mister_bin
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '5.3'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: lightly
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.3'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.3'
|
69
83
|
description: Easy to use API for Voicemaker.in TTS service, with a command line interface
|
70
84
|
email: db@dannyben.com
|
71
85
|
executables:
|
@@ -79,9 +93,17 @@ files:
|
|
79
93
|
- lib/voicemaker.rb
|
80
94
|
- lib/voicemaker/api.rb
|
81
95
|
- lib/voicemaker/cli.rb
|
82
|
-
- lib/voicemaker/
|
96
|
+
- lib/voicemaker/commands/base.rb
|
97
|
+
- lib/voicemaker/commands/new_command.rb
|
98
|
+
- lib/voicemaker/commands/project_command.rb
|
99
|
+
- lib/voicemaker/commands/tts_command.rb
|
100
|
+
- lib/voicemaker/commands/voices_command.rb
|
83
101
|
- lib/voicemaker/exceptions.rb
|
102
|
+
- lib/voicemaker/extensions/file.rb
|
103
|
+
- lib/voicemaker/tts.rb
|
104
|
+
- lib/voicemaker/tts_params.rb
|
84
105
|
- lib/voicemaker/version.rb
|
106
|
+
- lib/voicemaker/voices.rb
|
85
107
|
homepage: https://github.com/DannyBen/voicemaker
|
86
108
|
licenses:
|
87
109
|
- MIT
|
data/lib/voicemaker/command.rb
DELETED
@@ -1,111 +0,0 @@
|
|
1
|
-
require 'lp'
|
2
|
-
require 'mister_bin'
|
3
|
-
|
4
|
-
module Voicemaker
|
5
|
-
class Command < MisterBin::Command
|
6
|
-
help "Voicemaker API\n\n API Documentation:\n https://developer.voicemaker.in/apidocs"
|
7
|
-
version Voicemaker::VERSION
|
8
|
-
|
9
|
-
usage "voicemaker voices [--save PATH] [SEARCH...]"
|
10
|
-
usage "voicemaker new CONFIG"
|
11
|
-
usage "voicemaker generate CONFIG [OUTPUT]"
|
12
|
-
usage "voicemaker batch INDIR OUTDIR"
|
13
|
-
usage "voicemaker (-h|--help|--version)"
|
14
|
-
|
15
|
-
command "voices", "Get list of voices, optionally in a given language"
|
16
|
-
command "new", "Generate a sample config file"
|
17
|
-
command "generate", "Generate audio file. The output filename will be the same as the config filename, with the proper mp3 or wav extension"
|
18
|
-
command "batch", "Generate multiple audio files from multiple config files"
|
19
|
-
|
20
|
-
option "-l --language LANG", "Limit results to a specific language"
|
21
|
-
option "-s --save PATH", "Save output to output YAML file"
|
22
|
-
|
23
|
-
param "SEARCH", "Provide one or more text strings to search for (case insensitive)"
|
24
|
-
param "CONFIG", "Path to config file"
|
25
|
-
param "OUTPUT", "Path to output mp3/wav file. If not provided, the filename will be the same as the config file, with wav/mp3 extension"
|
26
|
-
param "INDIR", "Path to input directory, containing config YAML files"
|
27
|
-
param "OURDIR", "Path to output directory, where mp3/wav files will be saved"
|
28
|
-
|
29
|
-
environment "VOICEMAKER_API_KEY", "Your Voicemaker API key [required]"
|
30
|
-
environment "VOICEMAKER_API_HOST", "Override the API host URL"
|
31
|
-
|
32
|
-
example "voicemaker voices en-us"
|
33
|
-
example "voicemaker voices --save out.yml en-us"
|
34
|
-
example "voicemaker voices en-us female"
|
35
|
-
example "voicemaker new test.yml"
|
36
|
-
example "voicemaker generate test.yml out.mp3"
|
37
|
-
example "voicemaker batch configs out"
|
38
|
-
|
39
|
-
def voices_command
|
40
|
-
send_output api.voices(args['SEARCH'])
|
41
|
-
end
|
42
|
-
|
43
|
-
def new_command
|
44
|
-
template = File.expand_path "../sample.yml", __dir__
|
45
|
-
content = File.read template
|
46
|
-
File.write args['CONFIG'], content
|
47
|
-
say "Saved #{args['CONFIG']}"
|
48
|
-
end
|
49
|
-
|
50
|
-
def generate_command
|
51
|
-
config_path = args['CONFIG']
|
52
|
-
raise InputError, "Cannot find config file #{config_path}" unless File.exist? config_path
|
53
|
-
outpath = args['OUTPUT'] || outpath_from_config(config_path)
|
54
|
-
generate config_path, outpath
|
55
|
-
end
|
56
|
-
|
57
|
-
def batch_command
|
58
|
-
files = Dir["#{args['INDIR']}/*.yml"].sort
|
59
|
-
raise InputError, "No config files in #{args['INDIR']}" if files.empty?
|
60
|
-
|
61
|
-
files.each do |config_path|
|
62
|
-
extension = extension_from_config(config_path)
|
63
|
-
basename = File.basename config_path, '.yml'
|
64
|
-
outpath = "#{args['OUTDIR']}/#{basename}.#{extension}"
|
65
|
-
generate config_path, outpath
|
66
|
-
puts ""
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
private
|
71
|
-
|
72
|
-
def generate(config_path, outpath)
|
73
|
-
params = YAML.load_file config_path
|
74
|
-
|
75
|
-
say "Config : !txtgrn!#{config_path}"
|
76
|
-
api.download outpath, params do |url|
|
77
|
-
say "URL : !txtblu!#{url}"
|
78
|
-
end
|
79
|
-
|
80
|
-
say "Path : !txtblu!#{outpath}"
|
81
|
-
end
|
82
|
-
|
83
|
-
def send_output(data)
|
84
|
-
save = args['--save']
|
85
|
-
if save
|
86
|
-
say "Saved #{save}"
|
87
|
-
File.write save, data.to_yaml
|
88
|
-
else
|
89
|
-
lp data
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def api
|
94
|
-
@api ||= Voicemaker::API.new(api_key)
|
95
|
-
end
|
96
|
-
|
97
|
-
def api_key
|
98
|
-
ENV['VOICEMAKER_API_KEY'] or raise MissingAuth, "Please set the 'VOICEMAKER_API_KEY' environment variable"
|
99
|
-
end
|
100
|
-
|
101
|
-
def outpath_from_config(config_path)
|
102
|
-
ext = extension_from_config config_path
|
103
|
-
config_path.gsub(/yml$/, ext)
|
104
|
-
end
|
105
|
-
|
106
|
-
def extension_from_config(config_path)
|
107
|
-
YAML.load_file(config_path)['OutputFormat']
|
108
|
-
end
|
109
|
-
|
110
|
-
end
|
111
|
-
end
|