vocalware 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +20 -0
- data/README.markdown +67 -0
- data/data/voices.csv +83 -0
- data/lib/vocalware.rb +24 -0
- data/lib/vocalware/client.rb +158 -0
- data/lib/vocalware/errors.rb +40 -0
- data/lib/vocalware/languages.rb +73 -0
- data/lib/vocalware/request.rb +88 -0
- data/lib/vocalware/voice.rb +94 -0
- metadata +156 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4a00fc81a9d6dde4488d2b51d17bf9ebd35d0132
|
4
|
+
data.tar.gz: 8b930de1d5e591a8516a12720d714532571a0c93
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 99f87cc818f7c778673254b66abe5b562c98ec9a9dea8c638331c038b21d236466399638097abe8cb91d2e382765b3f7da12b09b443f31844ba9dead1ceacf0a
|
7
|
+
data.tar.gz: 786b3fcea77f7464a61513fa3daa9071d1a4298bd885e44798a1c846c8213601faf79e5e31cf66e588a60d0e7f8befe0fa483f464cbf6a5cd9b84eb190a5cda8
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 TMX Credit, author Sergey Potapov
|
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.markdown
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# Vocalware
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/TMXCredit/vocalware.png?branch=master)](https://travis-ci.org/TMXCredit/vocalware)
|
4
|
+
|
5
|
+
Ruby client for the [Vocalware](https://www.vocalware.com/) REST API.
|
6
|
+
|
7
|
+
## Install
|
8
|
+
|
9
|
+
Add vocalware to your Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'vocalware'
|
13
|
+
```
|
14
|
+
|
15
|
+
Run `bundle install`.
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
Lookup a voice by its attributes:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
voice = Vocalware::Voice.find(:lang => :en, :name => 'Kate')
|
23
|
+
```
|
24
|
+
As attributes you can also use `engine_id`, `lang_id`, `voice_id`, `gender`.
|
25
|
+
|
26
|
+
Initialize a client:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
client = Vocalware::Client.new(
|
30
|
+
:secret_phrase => SECRET_PHRASE,
|
31
|
+
:api_id => API_ID,
|
32
|
+
:account_id => ACCOUNT_ID,
|
33
|
+
:voice => voice
|
34
|
+
)
|
35
|
+
```
|
36
|
+
|
37
|
+
Generate speech from text:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
audio_data = client.gen('Say hello on a night like this!')
|
41
|
+
File.binwrite('./cure.mp3', audio_data)
|
42
|
+
```
|
43
|
+
|
44
|
+
### Override attributes for a single request
|
45
|
+
|
46
|
+
If you need to say few words in Spanish, you can override the `:voice`
|
47
|
+
attribute for one single request:
|
48
|
+
|
49
|
+
```
|
50
|
+
voice = Vocalware::Vocalware.find(:lang => :es, :name => 'Juan')
|
51
|
+
client.gen('Hola! Qué tal estás?', :voice => voice)
|
52
|
+
```
|
53
|
+
|
54
|
+
## Running tests
|
55
|
+
|
56
|
+
```sh
|
57
|
+
rake spec
|
58
|
+
```
|
59
|
+
|
60
|
+
|
61
|
+
## Credits
|
62
|
+
|
63
|
+
* [Sergey Potapov](https://github.com/greyblake)
|
64
|
+
|
65
|
+
## Copyright
|
66
|
+
|
67
|
+
Copyright (c) 2013 TMX Credit. See LICENSE.txt for further details.
|
data/data/voices.csv
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
engine_id , lang , name , voice_id , gender , description
|
2
|
+
2 , en , Susan , 1 , F , US
|
3
|
+
2 , en , Dave , 2 , M , US
|
4
|
+
2 , en , Elizabeth , 4 , F , UK
|
5
|
+
2 , en , Simon , 5 , M , UK
|
6
|
+
2 , en , Catherine , 6 , F , UK
|
7
|
+
2 , en , Allison , 7 , F , US
|
8
|
+
2 , en , Steven , 8 , M , US
|
9
|
+
2 , en , Alan , 9 , M , Australian
|
10
|
+
2 , en , Grace , 10 , F , Australian
|
11
|
+
2 , en , Veena , 11 , F , Indian
|
12
|
+
2 , es , Carmen , 1 , F , Castilian
|
13
|
+
2 , es , Juan , 2 , M , Castilian
|
14
|
+
2 , es , Francisca , 3 , F , Chilean
|
15
|
+
2 , es , Diego , 4 , M , Argentine
|
16
|
+
2 , es , Esperanza , 5 , F , Mexican
|
17
|
+
2 , es , Jorge , 6 , M , Castilian
|
18
|
+
2 , es , Carlos , 7 , M , American
|
19
|
+
2 , es , Soledad , 8 , F , American
|
20
|
+
2 , es , Leonor , 9 , F , Castilian
|
21
|
+
2 , es , Ximena , 10 , F , American
|
22
|
+
2 , de , Stefan , 2 , M
|
23
|
+
2 , de , Katrin , 3 , F
|
24
|
+
2 , fr , Bernard , 2 , M , European
|
25
|
+
2 , fr , Jolie , 3 , F , European
|
26
|
+
2 , fr , Florence , 4 , F , European
|
27
|
+
2 , fr , Charlotte , 5 , F , Canadian
|
28
|
+
2 , fr , Olivier , 6 , M , Canadian
|
29
|
+
2 , ca , Montserrat , 1 , F
|
30
|
+
2 , ca , Jordi , 2 , M
|
31
|
+
2 , ca , Empar , 3 , F , Valencian
|
32
|
+
2 , pt , Gabriela , 1 , F , Brasilian
|
33
|
+
2 , pt , Amalia , 2 , F , European
|
34
|
+
2 , pt , Eusebio , 3 , M , European
|
35
|
+
2 , pt , Fernanda , 4 , F , Brazilian
|
36
|
+
2 , pt , Felipe , 5 , M , Brazilian
|
37
|
+
2 , it , Paola , 1 , F
|
38
|
+
2 , it , Silvana , 2 , F
|
39
|
+
2 , it , Valentia , 3 , F
|
40
|
+
2 , it , Luca , 5 , M
|
41
|
+
2 , it , Marcello , 6 , M
|
42
|
+
2 , it , Roberto , 7 , M
|
43
|
+
2 , it , Matteo , 8 , M
|
44
|
+
2 , it , Giulia , 9 , F
|
45
|
+
2 , it , Federica , 10 , F
|
46
|
+
2 , el , Afroditi , 1 , F
|
47
|
+
2 , el , Nikos , 3 , M
|
48
|
+
2 , sv , Annika , 1 , F
|
49
|
+
2 , sv , Sven , 2 , M
|
50
|
+
2 , zh , Linlin , 1 , F , Mandarin
|
51
|
+
2 , zh , Lisheng , 2 , F , Mandarin
|
52
|
+
2 , nl , Willem , 1 , M
|
53
|
+
2 , nl , Saskia , 2 , F
|
54
|
+
2 , pl , Zosia , 1 , F
|
55
|
+
2 , pl , Krzysztof , 2 , M
|
56
|
+
2 , gl , Carmela , 1 , F
|
57
|
+
2 , tr , Kerem , 1 , M
|
58
|
+
2 , tr , Zeynep , 2 , F
|
59
|
+
2 , tr , Selin , 3 , F
|
60
|
+
2 , da , Frida , 1 , F
|
61
|
+
2 , da , Magnus , 2 , M
|
62
|
+
2 , no , Vilde , 1 , F
|
63
|
+
2 , no , Henrik , 2 , M
|
64
|
+
2 , ru , Olga , 1 , F
|
65
|
+
2 , ru , Dmitri , 2 , M
|
66
|
+
2 , fi , Milla , 1 , F
|
67
|
+
2 , fi , Marko , 2 , M
|
68
|
+
2 , ar , Tarik , 1 , M
|
69
|
+
2 , ar , Laila , 2 , F
|
70
|
+
2 , ro , Ioana , 1 , F
|
71
|
+
2 , eo , Ludoviko , 1 , M
|
72
|
+
3 , en , Kate , 1 , F , US
|
73
|
+
3 , en , Paul , 2 , M , US
|
74
|
+
3 , en , Julie , 3 , F , US
|
75
|
+
3 , en , Bridget , 4 , F , UK
|
76
|
+
3 , es , Violeta , 1 , F
|
77
|
+
3 , zh , Lily , 1 , F , Mandarin
|
78
|
+
3 , zh , Hui , 3 , F , Mandarin
|
79
|
+
3 , zh , Liang , 4 , M , Mandarin
|
80
|
+
3 , ja , Show , 2 , M
|
81
|
+
3 , ja , Misaki , 3 , F
|
82
|
+
3 , ko , Yumi , 1 , F
|
83
|
+
3 , ko , Junwoo , 2 , M
|
data/lib/vocalware.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# Standard library
|
2
|
+
require 'csv'
|
3
|
+
require 'digest'
|
4
|
+
require 'cgi'
|
5
|
+
|
6
|
+
# Gems
|
7
|
+
require 'faraday'
|
8
|
+
|
9
|
+
# Vocalware
|
10
|
+
require 'vocalware/errors'
|
11
|
+
require 'vocalware/client'
|
12
|
+
require 'vocalware/languages'
|
13
|
+
require 'vocalware/voice'
|
14
|
+
require 'vocalware/request'
|
15
|
+
|
16
|
+
|
17
|
+
# Client for Vocalware REST API.
|
18
|
+
module Vocalware
|
19
|
+
# Path to directory with data.
|
20
|
+
DATA_PATH = File.expand_path('../../data', __FILE__)
|
21
|
+
|
22
|
+
# Path to CSV file with voices information.
|
23
|
+
VOICES_CSV_FILE = File.join(DATA_PATH, 'voices.csv')
|
24
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
module Vocalware
|
2
|
+
# Vocalware client, which uses Vocalware's REST API to generate speech.
|
3
|
+
#
|
4
|
+
# @example
|
5
|
+
# # Look up voice
|
6
|
+
# voice = Vocalware::Voice.find(:lang => :en, :name => 'Kate')
|
7
|
+
#
|
8
|
+
# # Initialize client
|
9
|
+
# client =
|
10
|
+
# Vocalware::Client.new(
|
11
|
+
# :secret_phrase => SECRET_PHRASE,
|
12
|
+
# :api_id => API_ID,
|
13
|
+
# :account_id => ACCOUNT_ID,
|
14
|
+
# :voice => voice
|
15
|
+
# )
|
16
|
+
#
|
17
|
+
# # Generate mp3
|
18
|
+
# client.gen("I love ruby!") # => mp3 audio as binary string
|
19
|
+
class Client
|
20
|
+
# @attribute account_id [Integer, String] account id (ACC)
|
21
|
+
attr_accessor :account_id
|
22
|
+
|
23
|
+
# @attribute api_id [Integer, String] API id (API)
|
24
|
+
attr_accessor :api_id
|
25
|
+
|
26
|
+
# @attribute secret_phrase [String] secret phrase
|
27
|
+
attr_accessor :secret_phrase
|
28
|
+
|
29
|
+
# @attribute voice [Vocalware::Voice] voice
|
30
|
+
attr_accessor :voice
|
31
|
+
|
32
|
+
# @attribute ext [String] whether "mp3" or "swf"
|
33
|
+
attr_accessor :ext
|
34
|
+
|
35
|
+
# @attribute host [String] host to which request will be set
|
36
|
+
attr_accessor :host
|
37
|
+
|
38
|
+
# @attribute path [String] path as part of URL to send request
|
39
|
+
attr_accessor :path
|
40
|
+
|
41
|
+
# @attribute protocol [String] whether "http" or "https"
|
42
|
+
attr_accessor :protocol
|
43
|
+
|
44
|
+
# @attribute port [Integer]
|
45
|
+
attr_accessor :port
|
46
|
+
|
47
|
+
# @attribute http_client [Faraday::Connection]
|
48
|
+
attr_accessor :http_client
|
49
|
+
|
50
|
+
|
51
|
+
# Default attributes
|
52
|
+
DEFAULT_ATTRS = {
|
53
|
+
:protocol => 'http',
|
54
|
+
:host => 'www.vocalware.com',
|
55
|
+
:path => '/tts/gen.php',
|
56
|
+
:ext => 'mp3'
|
57
|
+
}
|
58
|
+
|
59
|
+
# @param attrs [Hash<Symbol, Object>] client attributes
|
60
|
+
def initialize(attrs = {})
|
61
|
+
DEFAULT_ATTRS.merge(attrs).each do |attr_name, value|
|
62
|
+
send("#{attr_name}=", value)
|
63
|
+
end
|
64
|
+
|
65
|
+
@http_client ||= Faraday.new
|
66
|
+
validate!
|
67
|
+
end
|
68
|
+
|
69
|
+
# Generate speech from passed text.
|
70
|
+
#
|
71
|
+
# @param text [String] text to generate speech
|
72
|
+
# @param opts [Hash<Symbol, Object>] options which override client
|
73
|
+
# attributes for one particular request.
|
74
|
+
#
|
75
|
+
# @return [String] audio data in format defined by +:ext+ attribute
|
76
|
+
def gen(text, opts = {})
|
77
|
+
url = build_url(text, opts)
|
78
|
+
send_request(url)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Build URL where request will be sent.
|
82
|
+
#
|
83
|
+
# @param text [String] text to generate speech
|
84
|
+
# @param opts [Hash<Symbol, Object>] options which override client
|
85
|
+
# attributes
|
86
|
+
#
|
87
|
+
# @return [String] url
|
88
|
+
def build_url(text, opts = {})
|
89
|
+
attrs = to_hash.merge(opts)
|
90
|
+
attrs[:text] = text.strip
|
91
|
+
Request.new(attrs).to_url
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
# Issue request to the remote service using HTTP client.
|
96
|
+
# Handle potential errors and raise {Vocalware::RequestError} with
|
97
|
+
# request information.
|
98
|
+
#
|
99
|
+
# @param url [String] end point with encoded GET parameters to send request
|
100
|
+
#
|
101
|
+
# @return [String] audio data
|
102
|
+
def send_request(url)
|
103
|
+
response = @http_client.get(url)
|
104
|
+
|
105
|
+
# If response has other status than success
|
106
|
+
unless response.status.between?(200, 299)
|
107
|
+
raise RequestError.from_url_and_response(
|
108
|
+
url, response, "Unexpected response status"
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
response_body = response.body
|
113
|
+
|
114
|
+
# In case of an error, Vocalware still returns a 200 HTTP status,
|
115
|
+
# but with an error message in the response body, instead of audio data.
|
116
|
+
case response.headers['Content-Type']
|
117
|
+
when 'audio/mpeg', 'application/x-shockwave-flash'
|
118
|
+
return response_body
|
119
|
+
else
|
120
|
+
raise RequestError.from_url(url, response_body)
|
121
|
+
end
|
122
|
+
rescue Faraday::Error::ConnectionFailed => err
|
123
|
+
raise RequestError.from_url(url, err.message)
|
124
|
+
end
|
125
|
+
private :send_request
|
126
|
+
|
127
|
+
# Ensure all required attributes are present.
|
128
|
+
#
|
129
|
+
# @return [void]
|
130
|
+
def validate!
|
131
|
+
raise(Error, 'secret_phrase is missing') unless secret_phrase
|
132
|
+
raise(Error, 'api_id is missing') unless api_id
|
133
|
+
raise(Error, 'account_id is missing') unless account_id
|
134
|
+
raise(Error, 'voice is missing') unless voice
|
135
|
+
|
136
|
+
raise(Error,
|
137
|
+
'voice must be a Vocalware::Voice'
|
138
|
+
) unless voice.is_a?(Voice)
|
139
|
+
end
|
140
|
+
private :validate!
|
141
|
+
|
142
|
+
# Represent all client attributes as a hash.
|
143
|
+
#
|
144
|
+
# @return [Hash<Symbol, Object>]
|
145
|
+
def to_hash
|
146
|
+
{ :host => host,
|
147
|
+
:path => path,
|
148
|
+
:protocol => protocol,
|
149
|
+
:port => port,
|
150
|
+
:account_id => account_id,
|
151
|
+
:api_id => api_id,
|
152
|
+
:secret_phrase => secret_phrase,
|
153
|
+
:voice => voice,
|
154
|
+
:ext => ext }
|
155
|
+
end
|
156
|
+
private :to_hash
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Vocalware
|
2
|
+
# Basic Vocalware error.
|
3
|
+
class Error < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
# Raised when no voices or more than one are found.
|
7
|
+
class FindVoiceError < Error
|
8
|
+
end
|
9
|
+
|
10
|
+
# Raised on sending and processing HTTP request to Vocalware service.
|
11
|
+
class RequestError < Error
|
12
|
+
# @attribute url [String] URL where request was sent
|
13
|
+
attr_accessor :url
|
14
|
+
|
15
|
+
# @attribute response [Faraday::Response] recevied response
|
16
|
+
attr_accessor :response
|
17
|
+
|
18
|
+
# Create instance with request URL and error message.
|
19
|
+
#
|
20
|
+
# @return [Vocalware::RequestError]
|
21
|
+
def self.from_url(url, message)
|
22
|
+
message << "\nREQUEST URL: #{url}"
|
23
|
+
new(message)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Create instance with request URL, response and error message.
|
27
|
+
#
|
28
|
+
# @return [Vocalware::RequestError]
|
29
|
+
def self.from_url_and_response(url, response, message)
|
30
|
+
message << "\nREQUEST URL: #{url}"
|
31
|
+
message << "\nRESPONSE STATUS: #{response.status}"
|
32
|
+
message << "\nRESPONSE BODY: #{response.body}"
|
33
|
+
new(message)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Raised when required parameters for request are missing.
|
38
|
+
class BuildRequestError < RequestError
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Vocalware
|
2
|
+
# Maps language ISO 639-1 code to Vocalware language id.
|
3
|
+
LANGUAGES = {
|
4
|
+
# Arabic
|
5
|
+
:ar => 27,
|
6
|
+
|
7
|
+
# Catalan
|
8
|
+
:ca => 5,
|
9
|
+
|
10
|
+
# Chinese
|
11
|
+
:zh => 10,
|
12
|
+
|
13
|
+
# Danish
|
14
|
+
:da => 19,
|
15
|
+
|
16
|
+
# Dutch
|
17
|
+
:nl => 11,
|
18
|
+
|
19
|
+
# English
|
20
|
+
:en => 1,
|
21
|
+
|
22
|
+
# Esperanto
|
23
|
+
:eo => 31,
|
24
|
+
|
25
|
+
# Finnish
|
26
|
+
:fi => 23,
|
27
|
+
|
28
|
+
# French
|
29
|
+
:fr => 4,
|
30
|
+
|
31
|
+
# Galician
|
32
|
+
:gl => 15,
|
33
|
+
|
34
|
+
# German
|
35
|
+
:de => 3,
|
36
|
+
|
37
|
+
# Greek
|
38
|
+
:el => 8,
|
39
|
+
|
40
|
+
# Italian
|
41
|
+
:it => 7,
|
42
|
+
|
43
|
+
# Japanese
|
44
|
+
:ja => 12,
|
45
|
+
|
46
|
+
# Korean
|
47
|
+
:ko => 13,
|
48
|
+
|
49
|
+
# Norwegian
|
50
|
+
:no => 20,
|
51
|
+
|
52
|
+
# Polish
|
53
|
+
:pl => 14,
|
54
|
+
|
55
|
+
# Portuguese
|
56
|
+
:pt => 6,
|
57
|
+
|
58
|
+
# Romanian
|
59
|
+
:ro => 30,
|
60
|
+
|
61
|
+
# Russian
|
62
|
+
:ru => 21,
|
63
|
+
|
64
|
+
# Spanish
|
65
|
+
:es => 2,
|
66
|
+
|
67
|
+
# Swedish
|
68
|
+
:sv => 9,
|
69
|
+
|
70
|
+
# Turkish
|
71
|
+
:tr => 16
|
72
|
+
}.freeze
|
73
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Vocalware
|
2
|
+
# Request to send to Vocalware's remote service.
|
3
|
+
# Builds a URL with all necessary parameters according to the API.
|
4
|
+
class Request
|
5
|
+
# Required parameters according to the Vocalware API reference.
|
6
|
+
REQUIRED_PARAMETERS = ['EID', 'LID', 'VID', 'TXT', 'ACC', 'API', 'CS']
|
7
|
+
|
8
|
+
# @param attrs [Hash<Symbol, Object>]
|
9
|
+
def initialize(attrs)
|
10
|
+
@attrs = attrs
|
11
|
+
validate!
|
12
|
+
end
|
13
|
+
|
14
|
+
# Build a query as a URL with GET parameters.
|
15
|
+
#
|
16
|
+
# @return [String]
|
17
|
+
def to_url
|
18
|
+
port = @attrs[:port]
|
19
|
+
url = "#{@attrs[:protocol]}://#{@attrs[:host]}"
|
20
|
+
url << ":#{port}" if port
|
21
|
+
url << "#{@attrs[:path]}?"
|
22
|
+
|
23
|
+
params_str = params.map {|name, value|
|
24
|
+
"#{CGI.escape(name)}=#{CGI.escape(value)}"
|
25
|
+
}.join('&')
|
26
|
+
url << params_str
|
27
|
+
end
|
28
|
+
|
29
|
+
# Validate all required parameters are present.
|
30
|
+
#
|
31
|
+
# @return [void]
|
32
|
+
def validate!
|
33
|
+
REQUIRED_PARAMETERS.each do |name|
|
34
|
+
if params[name].empty?
|
35
|
+
raise(BuildRequestError, "Vocalware: Parameter #{name} is required")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
private :validate!
|
40
|
+
|
41
|
+
# Build GET parameters.
|
42
|
+
#
|
43
|
+
# @return [Hash<String, String>]
|
44
|
+
def params
|
45
|
+
@params ||= begin
|
46
|
+
voice = @attrs[:voice]
|
47
|
+
{ 'EID' => voice.engine_id.to_s,
|
48
|
+
'LID' => voice.lang_id.to_s,
|
49
|
+
'VID' => voice.voice_id.to_s,
|
50
|
+
'TXT' => @attrs[:text].to_s,
|
51
|
+
'EXT' => @attrs[:ext].to_s,
|
52
|
+
'FX_TYPE' => @attrs[:fx_type].to_s,
|
53
|
+
'FX_LEVEL' => @attrs[:fx_level].to_s,
|
54
|
+
'ACC' => @attrs[:account_id].to_s,
|
55
|
+
'API' => @attrs[:api_id].to_s,
|
56
|
+
'SESSION' => @attrs[:session].to_s,
|
57
|
+
'CS' => checksum }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
private :params
|
61
|
+
|
62
|
+
# Calculate checksum. The following formula is used:
|
63
|
+
# CS = md5(EID + LID + VID + TXT + EXT + FX_TYPE + FX_LEVEL +
|
64
|
+
# + fACC + API + SESSION + SECRET PHRASE)
|
65
|
+
#
|
66
|
+
# @return [String] MD5 hex digest
|
67
|
+
def checksum
|
68
|
+
@checksum ||= begin
|
69
|
+
voice = @attrs[:voice]
|
70
|
+
data = [
|
71
|
+
voice.engine_id.to_s,
|
72
|
+
voice.lang_id.to_s,
|
73
|
+
voice.voice_id.to_s,
|
74
|
+
@attrs[:text].to_s,
|
75
|
+
@attrs[:ext].to_s,
|
76
|
+
@attrs[:fx_type].to_s,
|
77
|
+
@attrs[:fx_level].to_s,
|
78
|
+
@attrs[:account_id].to_s,
|
79
|
+
@attrs[:api_id].to_s,
|
80
|
+
@attrs[:session].to_s,
|
81
|
+
@attrs[:secret_phrase].to_s
|
82
|
+
].join('')
|
83
|
+
Digest::MD5.hexdigest(data)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
private :checksum
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Vocalware
|
2
|
+
# Voice encapsulates voice parameters like +engine_id+, +voice_id+, +lang_id+
|
3
|
+
# etc. And provides class method to look up voice by parameters.
|
4
|
+
#
|
5
|
+
# @example
|
6
|
+
# # Find a voice with name Juan. Exception will be raise if the voice with
|
7
|
+
# # such name is not unique. As well as if the voice not found.
|
8
|
+
# voice = Voice.find(:name => 'Juan')
|
9
|
+
class Voice
|
10
|
+
# @attr_reader engine_id [Integer, String] engine id which is used to
|
11
|
+
# generate speech
|
12
|
+
attr_reader :engine_id
|
13
|
+
|
14
|
+
# @attr_reader lang [Symbol, String] language ISO 639-1 code (2 chars)
|
15
|
+
attr_reader :lang
|
16
|
+
|
17
|
+
# @attr_reader name [String] voice name
|
18
|
+
attr_reader :name
|
19
|
+
|
20
|
+
# @attr_reader voice_id [Integer, String] unique only in scope of
|
21
|
+
# +engine_id+ and +lang+
|
22
|
+
attr_reader :voice_id
|
23
|
+
|
24
|
+
# @attr_reader gender [String] whether "M" or "F"
|
25
|
+
attr_reader :gender
|
26
|
+
|
27
|
+
# @attr_reader description [String] regions, dialects, etc
|
28
|
+
attr_reader :description
|
29
|
+
|
30
|
+
# @attr_reader lang_id [Integer, String] language id
|
31
|
+
attr_reader :lang_id
|
32
|
+
|
33
|
+
# Get all voices.
|
34
|
+
#
|
35
|
+
# @return [Array<Vocalware::Voice>]
|
36
|
+
def self.all
|
37
|
+
@all ||= send(:load_all)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Find a voice by attributes.
|
41
|
+
#
|
42
|
+
# @param attrs [Hash] attributes to find a voice
|
43
|
+
#
|
44
|
+
# @return
|
45
|
+
def self.find(attrs)
|
46
|
+
voices = all.select {|voice| voice.match?(attrs) }
|
47
|
+
|
48
|
+
raise(FindVoiceError,
|
49
|
+
"No voice found using #{attrs.inspect}"
|
50
|
+
) if voices.empty?
|
51
|
+
raise(FindVoiceError,
|
52
|
+
"More than 1 voice found using #{attrs.inspect}"
|
53
|
+
) if voices.size > 1
|
54
|
+
|
55
|
+
voices.first
|
56
|
+
end
|
57
|
+
|
58
|
+
# Load and instantiate voices from CSV file.
|
59
|
+
#
|
60
|
+
# @return [Array<Vocalware::Voice>]
|
61
|
+
def self.load_all
|
62
|
+
converter = lambda { |str| str ? str.strip! : nil }
|
63
|
+
data = CSV.read( VOICES_CSV_FILE,
|
64
|
+
:headers => true,
|
65
|
+
:skip_blanks => true,
|
66
|
+
:converters => converter,
|
67
|
+
:header_converters => converter )
|
68
|
+
data.map { |row| Voice.new(row.to_hash) }
|
69
|
+
end
|
70
|
+
private_class_method :load_all
|
71
|
+
|
72
|
+
|
73
|
+
# @param attributes [Hash] voice attributes
|
74
|
+
def initialize(attributes)
|
75
|
+
attributes.each do |attr, value|
|
76
|
+
self.instance_variable_set("@#{attr}", value)
|
77
|
+
end
|
78
|
+
@lang_id = LANGUAGES[lang.to_sym] ||
|
79
|
+
raise(Error, "Unknown lang #{lang.inspect}")
|
80
|
+
end
|
81
|
+
|
82
|
+
# Verify that the voice matches the passed attributes.
|
83
|
+
#
|
84
|
+
# @param attributes [Hash]
|
85
|
+
#
|
86
|
+
# @return [Boolean]
|
87
|
+
def match?(attributes)
|
88
|
+
attributes.each do |attr, val|
|
89
|
+
return false if send(attr).to_s != val.to_s
|
90
|
+
end
|
91
|
+
true
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
metadata
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vocalware
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- TMX Credit
|
8
|
+
- Potapov Sergey
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-10-25 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: faraday
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - '>='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - '>='
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: bundler
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ~>
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '1.0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ~>
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '1.0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: jeweler
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ~>
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: 1.8.7
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ~>
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 1.8.7
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: yard
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: metric_fu
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: pry
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - '>='
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: pry-nav
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - '>='
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - '>='
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
description: Ruby client for Vocalware REST API
|
113
|
+
email:
|
114
|
+
- rubygems@tmxcredit.com
|
115
|
+
- blake131313@gmail.com
|
116
|
+
executables: []
|
117
|
+
extensions: []
|
118
|
+
extra_rdoc_files:
|
119
|
+
- LICENSE.txt
|
120
|
+
- README.markdown
|
121
|
+
files:
|
122
|
+
- LICENSE.txt
|
123
|
+
- README.markdown
|
124
|
+
- data/voices.csv
|
125
|
+
- lib/vocalware.rb
|
126
|
+
- lib/vocalware/client.rb
|
127
|
+
- lib/vocalware/errors.rb
|
128
|
+
- lib/vocalware/languages.rb
|
129
|
+
- lib/vocalware/request.rb
|
130
|
+
- lib/vocalware/voice.rb
|
131
|
+
homepage: http://github.com/TMXCredit/vocalware
|
132
|
+
licenses:
|
133
|
+
- MIT
|
134
|
+
metadata: {}
|
135
|
+
post_install_message:
|
136
|
+
rdoc_options: []
|
137
|
+
require_paths:
|
138
|
+
- lib
|
139
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - '>='
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
145
|
+
requirements:
|
146
|
+
- - '>='
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: '0'
|
149
|
+
requirements: []
|
150
|
+
rubyforge_project:
|
151
|
+
rubygems_version: 2.0.3
|
152
|
+
signing_key:
|
153
|
+
specification_version: 4
|
154
|
+
summary: Ruby client for Vocalware REST API
|
155
|
+
test_files: []
|
156
|
+
has_rdoc:
|