ispeech 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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