ispeech 1.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ config/
2
+ vendor/
3
+ Gemfile.lock
4
+ pkg/
5
+ tmp/
6
+ script/voice_enumerator.out
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/LICENCE.txt ADDED
@@ -0,0 +1,21 @@
1
+ Copyright 2012 Birkir A. Barkarson
2
+ Standard MIT licence:
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,88 @@
1
+ Ruby interface to Ispeech's API for generating speech from text.
2
+ More info on their API at http://www.ispeech.org/api/
3
+
4
+ ## Installation
5
+
6
+ gem install ispeech
7
+
8
+ ## Loading
9
+
10
+ require 'ispeech'
11
+
12
+ ## Config
13
+
14
+ Automatically looks for a config/ispeech.yml file.
15
+ Configuration only requires the 'api_key'
16
+
17
+ ### Manual configuration
18
+
19
+ Ispeech.config = Ispeech::Config.new('some_api_key_hash')
20
+
21
+ or
22
+
23
+ Ispeech.config = Ispeech::Config.read(path_to_my_own_yaml_config_file)
24
+
25
+ ## Usage
26
+
27
+ Get the voice service object:
28
+
29
+ service = Ispeech.voice_service
30
+
31
+ or
32
+
33
+ service = Ispeech::VoiceService.new(my_config_object)
34
+
35
+ ### Generate sounds
36
+
37
+ response = service.generate_sound('speak this text', language_or_voice_options)
38
+
39
+ or
40
+
41
+ response = service.generate_with_voice('speak this text', my_voice_object)
42
+
43
+
44
+ ### Language and voice options
45
+
46
+ All available voices are defined in [voices/default.rb](https://github.com/birkirb/ispeech/blob/master/lib/ispeech/voices/default.rb)
47
+
48
+ You can specificy language, gender, voice name in any combination
49
+
50
+ response = service.generate_sound('speak this text', {:language => :en, :gender => :male, :speaker => :usenglishfemale})
51
+
52
+ If you have access to different set of voice there are rake tasks to generate a different voice map and any customization of the voice map should be easy.
53
+ Refer to the spec for details of that.
54
+
55
+ ### Saving generated sounds
56
+
57
+ The response is saved to a tempfile which can be moved, copied or otherwise streamed.
58
+
59
+ response = service.generate_sound('speak this text')
60
+ tempfile = response.download_to_tempfile
61
+
62
+ FileUtils.mv(tempfile.path, 'speak_this_text.mp3')
63
+
64
+ ## Mocking
65
+
66
+ For convinence purposes you can use an inbuilt mock to avoid web request while testing.
67
+
68
+ require 'ispeech/mock'
69
+ Ispeech::Mock.enable!
70
+
71
+ ### Turning off
72
+
73
+ Ispeech::Mock.disable!
74
+
75
+ ### Expectations
76
+
77
+ Only two expectations can be set
78
+
79
+ Ispeech::VoiceService.expect_ok_response
80
+
81
+ or
82
+
83
+ Ispeech::VoiceService.expect_error_response
84
+
85
+
86
+ ## Copyright
87
+
88
+ Copyright (c) 2012 Birkir A. Barkarson. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rake'
4
+ require 'bundler'
5
+ require 'bundler/gem_tasks'
6
+ require 'rspec/core/rake_task'
7
+ require 'ispeech/constants'
8
+
9
+ RSpec::Core::RakeTask.new('spec') do |t|
10
+ t.rspec_opts = ["-fd", "-c"]
11
+ end
12
+
13
+ namespace :generate do
14
+ desc "Generates MP3 with test strings for all voices."
15
+ task :test_voices do
16
+ exec('ruby -I lib script/test_voices.rb')
17
+ end
18
+
19
+ desc "Generate default voice class based on a voice list retrieved from iSpeech's API"
20
+ task :default_voices_class do
21
+ exec('ruby -I lib script/create_voices.rb')
22
+ end
23
+ end
24
+
25
+ desc "Clean up generated and downloaded files"
26
+ task :clean do
27
+ include Ispeech::Scripts
28
+ FileUtils.rm_rf(LOCAL_TEMP_DIR)
29
+ FileUtils.rm(VOICES_ENUMERATOR_FILE) rescue nil
30
+ end
31
+
32
+ task :default => :spec
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'ispeech'
data/ispeech.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "ispeech/constants"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "ispeech"
7
+ s.version = Ispeech::VERSION
8
+ s.authors = ["Birkir A. Barkarson"]
9
+ s.email = ["birkirb@stoicviking.net"]
10
+ s.homepage = "https://github.com/birkirb/ispeech"
11
+ s.summary = %q{Generate speech from the ispeech text to voice service.}
12
+ s.description = %q{Ruby interface to Ispeech's API for generating speech from text. More info at http://www.ispeech.org/api/}
13
+
14
+ s.rubyforge_project = "ispeech"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_runtime_dependency("backports")
22
+ s.add_development_dependency("rspec", '>= 2.6.0')
23
+ s.add_development_dependency("mocha", '>= 0.9.0')
24
+ s.add_development_dependency("webmock", '>= 1.8.0')
25
+ end
data/lib/ispeech.rb ADDED
@@ -0,0 +1,31 @@
1
+ require 'ispeech/error'
2
+ require 'ispeech/config'
3
+ require 'ispeech/voices/default'
4
+ require 'ispeech/voice'
5
+ require 'ispeech/constants'
6
+ require 'ispeech/response'
7
+ require 'ispeech/voice_service'
8
+
9
+ if RUBY_VERSION < "1.9"
10
+ require 'backports'
11
+ end
12
+
13
+ module Ispeech
14
+
15
+ def self.config
16
+ @@config ||= Ispeech::Config.read rescue nil
17
+ end
18
+
19
+ def self.config=(config)
20
+ if config.is_a?(Ispeech::Config)
21
+ @@config = config
22
+ else
23
+ raise Error.new("Ispeech configuration required. Not #{config.class}")
24
+ end
25
+ end
26
+
27
+ def self.voice_service
28
+ VoiceService.new(config)
29
+ end
30
+
31
+ end
@@ -0,0 +1,27 @@
1
+ require 'yaml'
2
+ require 'uri'
3
+
4
+ module Ispeech
5
+ class Config
6
+ attr_reader :api_key,
7
+ :target_url
8
+
9
+ DEFAULT_TARGET_URL = 'http://api.ispeech.org/api/rest'
10
+
11
+ def initialize(api_key, target_url = nil)
12
+ @api_key = api_key
13
+ @target_url = URI.parse(target_url || DEFAULT_TARGET_URL)
14
+ end
15
+
16
+ def self.read(config_file = nil)
17
+ config_file ||= File.join('config', 'ispeech.yml')
18
+ begin
19
+ yaml = YAML.load_file(config_file)
20
+ self.new(yaml['api_key'], yaml['target_url'])
21
+ rescue => err
22
+ raise Error.new("Failed to read configuration file", err)
23
+ end
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ require 'uri'
2
+
3
+ module Ispeech
4
+
5
+ VERSION = "1.0.1"
6
+
7
+ class Voice
8
+ QUALITY_LOW = 8000
9
+ QUALITY_HIGH = 22050
10
+ GENDER_FEMALE = :female
11
+ GENDER_MALE = :male
12
+ end
13
+
14
+ module Scripts
15
+ LOCAL_TEMP_DIR = 'tmp'
16
+ VOICES_ENUMERATOR_FILE = File.join('tmp', 'voice_enumerator.out')
17
+ end
18
+
19
+ end
@@ -0,0 +1,33 @@
1
+ module Ispeech
2
+
3
+ class Error < StandardError
4
+ attr_accessor :original_error
5
+
6
+ def initialize(message, original_error = nil)
7
+ super(message)
8
+ @original_error = original_error
9
+ end
10
+
11
+ def to_s
12
+ if @original_error.nil?
13
+ super
14
+ else
15
+ "#{super}\nCause: #{@original_error.to_s}"
16
+ end
17
+ end
18
+ end
19
+
20
+ class ServiceError < Error
21
+ attr_accessor :code
22
+
23
+ def initialize(message, code = '')
24
+ super(message)
25
+ @code = code
26
+ end
27
+
28
+ def to_s
29
+ "Code: #{code}, Message: #{super}"
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,93 @@
1
+ module Ispeech
2
+ module Mock
3
+
4
+ RESPONSE_MP3_FILE = File.join(File.dirname(__FILE__), '..', '..', 'spec', 'test_data', 'test_file.mp3')
5
+ FAKE_ERROR = Error.new("The mock wants you to fail!")
6
+ EMPTY_PROC = proc {}
7
+
8
+ def self.enable!
9
+ Ispeech::Response.class_eval(<<-EVAL, __FILE__, __LINE__)
10
+ def download_to_tempfile
11
+ download_to_tempfile_with_mock
12
+ end
13
+ def initialize(response)
14
+ initialize_with_fake_response
15
+ end
16
+ EVAL
17
+ Ispeech::VoiceService.class_eval(<<-EVAL, __FILE__, __LINE__)
18
+ def post(params)
19
+ post_with_set_response(params)
20
+ end
21
+ EVAL
22
+ end
23
+
24
+ def self.disable!
25
+ Ispeech::Response.class_eval(<<-EVAL, __FILE__, __LINE__)
26
+ def download_to_tempfile
27
+ download_to_tempfile_without_mock
28
+ end
29
+ def initialize(response)
30
+ initalize_without_fake_response(response)
31
+ end
32
+ EVAL
33
+ Ispeech::VoiceService.class_eval(<<-EVAL, __FILE__, __LINE__)
34
+ def post(params)
35
+ post_without_set_response(params)
36
+ end
37
+ EVAL
38
+ end
39
+
40
+ end
41
+ end
42
+
43
+ module Ispeech
44
+ class Response
45
+
46
+ private
47
+
48
+ alias :download_to_tempfile_without_mock :download_to_tempfile
49
+ alias :initalize_without_fake_response :initialize
50
+
51
+ def download_to_tempfile_with_mock
52
+ tempfile = Tempfile.new(SecureRandom.uuid)
53
+ File.open(Mock::RESPONSE_MP3_FILE) do |f|
54
+ tempfile.write(f.read)
55
+ tempfile.flush
56
+ end
57
+ tempfile
58
+ end
59
+
60
+ def initialize_with_fake_response
61
+ VoiceService.expected_response.call
62
+ end
63
+
64
+ end
65
+ end
66
+
67
+ module Ispeech
68
+ class VoiceService
69
+
70
+ @@expected_response = Mock::EMPTY_PROC
71
+
72
+ def self.expected_response
73
+ @@expected_response
74
+ end
75
+
76
+ def self.expect_ok_response
77
+ @@expected_response = Mock::EMPTY_PROC
78
+ end
79
+
80
+ def self.expect_error_response
81
+ @@expected_response = proc { raise Mock::FAKE_ERROR }
82
+ end
83
+
84
+ private
85
+
86
+ alias :post_without_set_response :post
87
+
88
+ def post_with_set_response(params)
89
+ self.class.expected_response
90
+ end
91
+
92
+ end
93
+ end
@@ -0,0 +1,45 @@
1
+ require 'cgi'
2
+ require 'net/http'
3
+ require 'tempfile'
4
+ require 'securerandom'
5
+
6
+ module Ispeech
7
+ class Response
8
+
9
+ RESPONSE_ERROR_MESSAGE = 'message'
10
+ RESPONSE_ERROR_CODE = 'code'
11
+ ERROR_UNEXPECTED_RESPONSE = Error.new("Response was not a valid HTTP response.")
12
+
13
+ def initialize(response)
14
+ if response.is_a?(Net::HTTPResponse)
15
+ if 200 == response.code.to_i
16
+ @response = response
17
+ else
18
+ if response.content_length.to_i > 0
19
+ params = CGI::parse(response.body)
20
+ raise ServiceError.new(params[RESPONSE_ERROR_MESSAGE].first, params[RESPONSE_ERROR_CODE].first)
21
+ else
22
+ raise ServiceError.new(response.code_type, response.code)
23
+ end
24
+ end
25
+ else
26
+ raise ERROR_UNEXPECTED_RESPONSE
27
+ end
28
+ end
29
+
30
+ def download_to_tempfile
31
+ content = generated_file
32
+ file = Tempfile.new(SecureRandom.uuid)
33
+ file.write(content)
34
+ file.flush
35
+ file # Leaving open. Will be closed once object is finalized.
36
+ end
37
+
38
+ private
39
+
40
+ def generated_file
41
+ @response.body
42
+ end
43
+
44
+ end
45
+ end