freetts 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/.rvmrc.example +1 -0
- data/README.md +67 -0
- data/examples/hello_world.rb +5 -0
- data/examples/rss_reader.rb +20 -0
- data/examples/voice_play.rb +133 -0
- data/lib/freetts.rb +18 -0
- data/lib/freetts/version.rb +3 -0
- data/lib/freetts/voice.rb +47 -0
- data/test/freetts_test.rb +19 -0
- data/test/voice_test.rb +64 -0
- data/vendor/cmu_time_awb.jar +0 -0
- data/vendor/cmu_us_kal.jar +0 -0
- data/vendor/cmudict04.jar +0 -0
- data/vendor/cmulex.jar +0 -0
- data/vendor/cmutimelex.jar +0 -0
- data/vendor/en_us.jar +0 -0
- data/vendor/freetts.jar +0 -0
- data/vendor/voices.txt +13 -0
- metadata +66 -0
data/.gitignore
ADDED
data/.rvmrc.example
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm --create jruby-X.Y.Z@freetts
|
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# Integration Exercise: Java Library Wrapper
|
2
|
+
|
3
|
+
## Exercise Summary
|
4
|
+
|
5
|
+
- You should create a gem using JRuby that wraps an existing Java library.
|
6
|
+
- Your gem should work with a Java library that doesn't already have
|
7
|
+
a good wrapper.
|
8
|
+
- You should make the API for your library look and feel like Ruby, not Java.
|
9
|
+
|
10
|
+
|
11
|
+
## A JRuby wrapper for FreeTTS
|
12
|
+
FreeTTS is a speech synthesis system written entirely in the JavaTM programming
|
13
|
+
language. It is based upon Flite: a small run-time speech synthesis engine
|
14
|
+
developed at Carnegie Mellon University. Flite is derived from the Festival
|
15
|
+
Speech Synthesis System from the University of Edinburgh and the FestVox project
|
16
|
+
from Carnegie Mellon University.
|
17
|
+
|
18
|
+
This wrapper exposes the most essential functionality required for processing
|
19
|
+
text-to-speech requests.
|
20
|
+
|
21
|
+
|
22
|
+
### Sample Code
|
23
|
+
It's easy enough to pass a String to FreeTTS:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
require "lib/freetts"
|
27
|
+
|
28
|
+
FreeTTS.speak "hello world"
|
29
|
+
```
|
30
|
+
|
31
|
+
With a little bit of manipulation, FreeTTS can read the news to you:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
require "uri"
|
35
|
+
require "rss"
|
36
|
+
require "lib/freetts"
|
37
|
+
|
38
|
+
uri = URI.parse("http://feeds.feedburner.com/RubyInside")
|
39
|
+
feed_items = RSS::Parser.parse(uri.read, false).items.first(5)
|
40
|
+
feed_items.each do |item|
|
41
|
+
puts item.title.content
|
42
|
+
FreeTTS.speak item.title.content
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
You can change the currently selected voice and make modifications that
|
47
|
+
will affect pitch, rate of speech, and so on.
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
voice = FreeTTS::Voice.for_name("alan")
|
51
|
+
voice.pitch *= 1.25
|
52
|
+
voice.rate += 20
|
53
|
+
```
|
54
|
+
|
55
|
+
Feel free to run any of the sample programs in the `examples/` directory
|
56
|
+
to see the library in action.
|
57
|
+
|
58
|
+
```bash
|
59
|
+
examples/hello_world.rb
|
60
|
+
examples/rss_reader.rb
|
61
|
+
examples/voice_play.rb
|
62
|
+
```
|
63
|
+
|
64
|
+
|
65
|
+
## Future Enhancements
|
66
|
+
- An adapter for plugging IO objects directly into the speech synthesizer
|
67
|
+
- Whack out a gem version of the library
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env jruby
|
2
|
+
|
3
|
+
|
4
|
+
$:.unshift File.join(File.dirname(__FILE__), "../lib")
|
5
|
+
require "uri"
|
6
|
+
require "rss"
|
7
|
+
require "freetts"
|
8
|
+
|
9
|
+
|
10
|
+
feed_url = "http://feeds.feedburner.com/RailsInside"
|
11
|
+
|
12
|
+
uri = URI.parse(feed_url)
|
13
|
+
|
14
|
+
feed_items = RSS::Parser.parse(uri.read, false).items.first(5)
|
15
|
+
|
16
|
+
feed_items.each do |item|
|
17
|
+
title = item.title.content
|
18
|
+
puts title
|
19
|
+
FreeTTS.speak(title)
|
20
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
#!/usr/bin/env jruby
|
2
|
+
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__), "..")
|
4
|
+
require "lib/freetts"
|
5
|
+
|
6
|
+
|
7
|
+
# A summary of the available commands
|
8
|
+
HELP_MESSAGE = "Enter one of the following commands:\n" +
|
9
|
+
" v select a voice\n" +
|
10
|
+
" i view information for currently selected voice\n" +
|
11
|
+
" c change a setting for currently selected voice\n" +
|
12
|
+
" s speak a phrase\n" +
|
13
|
+
" h/? view this help message\n" +
|
14
|
+
" q/x quit"
|
15
|
+
|
16
|
+
|
17
|
+
# Display a summary of the available commands.
|
18
|
+
def display_help_message
|
19
|
+
puts HELP_MESSAGE
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
# Prompt the user to enter a command and return his input.
|
24
|
+
def prompt_for_input(prompt_string="Command")
|
25
|
+
$stdout.write("#{ prompt_string }:>> ")
|
26
|
+
$stdin.gets.chomp
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
# Allow the user to select a new current_voice from a list of options.
|
31
|
+
def select_voice
|
32
|
+
voices = FreeTTS::Voice.all
|
33
|
+
options_with_indices = voices.each_with_index.map { |v, i| "(#{ i }) #{ v }" }
|
34
|
+
puts "Select a voice by entering the appropriate number:"
|
35
|
+
puts " #{ options_with_indices.join(" ") }"
|
36
|
+
|
37
|
+
user_input = prompt_for_input("Selection")
|
38
|
+
|
39
|
+
if user_input =~ /\A\d+\Z/ and
|
40
|
+
(selected_name = voices[user_input.to_i] rescue nil)
|
41
|
+
FreeTTS.current_voice = FreeTTS::Voice.for_name(selected_name)
|
42
|
+
puts "Voice \"#{ selected_name }\" has been selected."
|
43
|
+
else
|
44
|
+
puts "Sorry, but that is not a valid selection."
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
# Display attribute values for the currently selected voice.
|
50
|
+
def show_voice_info
|
51
|
+
attributes = %w(age description domain duration_stretch gender locale)
|
52
|
+
attributes += %w(organization pitch pitch_range pitch_shift rate style volume)
|
53
|
+
|
54
|
+
voice = FreeTTS.current_voice
|
55
|
+
puts "Current settings for voice \"#{ voice.name } \":"
|
56
|
+
attributes.each do |attribute|
|
57
|
+
puts " #{ attribute.ljust(20) }#{ voice.send(attribute.to_sym) }"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Change an attribute for the currently selected voice.
|
62
|
+
def change_voice_setting
|
63
|
+
update_attributes = %w( duration_stretch pitch pitch_range pitch_shift )
|
64
|
+
update_attributes += %w( rate volume )
|
65
|
+
|
66
|
+
options_with_indices = update_attributes.each_with_index.
|
67
|
+
map { |v, i| "(#{ i }) #{ v }" }
|
68
|
+
puts "Select an attribute to update by entering the appropriate number:"
|
69
|
+
puts " #{ options_with_indices.join(" ") }"
|
70
|
+
|
71
|
+
user_input = prompt_for_input("Selection")
|
72
|
+
|
73
|
+
if user_input =~ /\A\d+\Z/ and
|
74
|
+
(0..update_attributes.size).include?(user_input.to_i)
|
75
|
+
selected_attribute = update_attributes[user_input.to_i]
|
76
|
+
current_value = FreeTTS.current_voice.send(selected_attribute.to_sym)
|
77
|
+
|
78
|
+
user_input = prompt_for_input("New value (current: #{ current_value })")
|
79
|
+
|
80
|
+
#begin
|
81
|
+
new_value = Float(user_input)
|
82
|
+
FreeTTS.current_voice.send((selected_attribute + "=").to_sym, new_value)
|
83
|
+
puts "Set \"selected_attribute\" to value #{ new_value }"
|
84
|
+
#rescue Exception => e
|
85
|
+
#puts e
|
86
|
+
#puts "Illegal argument: requested value may be out of range for this attribute"
|
87
|
+
#end
|
88
|
+
else
|
89
|
+
puts "Sorry, but that is not a valid selection."
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Prompt the user to enter text to speak, and pass it to FreeTTS.
|
94
|
+
def prompt_for_speakable
|
95
|
+
puts "Enter text to be spoken:>> "
|
96
|
+
user_input = $stdin.gets.chomp
|
97
|
+
FreeTTS.speak(user_input)
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
def unknown_command(command)
|
102
|
+
puts "Sorry, I don't know how to '#{ command }'."
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
display_help_message
|
107
|
+
|
108
|
+
loop do
|
109
|
+
entered_command = prompt_for_input
|
110
|
+
|
111
|
+
case entered_command.downcase.chars.to_a[0]
|
112
|
+
when "v"
|
113
|
+
select_voice
|
114
|
+
when "i"
|
115
|
+
show_voice_info
|
116
|
+
when "c"
|
117
|
+
change_voice_setting
|
118
|
+
when "s"
|
119
|
+
prompt_for_speakable
|
120
|
+
when "h", "?"
|
121
|
+
display_help_message
|
122
|
+
when "q", "x"
|
123
|
+
break
|
124
|
+
else
|
125
|
+
unknown_command(entered_command)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
|
131
|
+
|
132
|
+
|
133
|
+
|
data/lib/freetts.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
require "java"
|
4
|
+
require "vendor/freetts.jar"
|
5
|
+
|
6
|
+
require "freetts/voice"
|
7
|
+
require "freetts/version"
|
8
|
+
|
9
|
+
|
10
|
+
module FreeTTS
|
11
|
+
@current_voice = Voice.for_name(Voice::DEFAULT_NAME)
|
12
|
+
class << self; attr_accessor :current_voice; end
|
13
|
+
|
14
|
+
|
15
|
+
def self.speak(speakable)
|
16
|
+
self.current_voice.speak(speakable)
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module FreeTTS
|
2
|
+
class Voice
|
3
|
+
DEFAULT_NAME = "kevin16"
|
4
|
+
|
5
|
+
@voice_manager = com.sun.speech.freetts.VoiceManager.get_instance
|
6
|
+
class << self; attr_reader :voice_manager; end
|
7
|
+
|
8
|
+
|
9
|
+
def initialize(voice)
|
10
|
+
@voice_impl = voice
|
11
|
+
end
|
12
|
+
|
13
|
+
def speak(saying)
|
14
|
+
@voice_impl.speak(saying)
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing(method, *args, &block)
|
18
|
+
method = method.to_s
|
19
|
+
java_method_name = case method
|
20
|
+
when /\A(.*)=\Z/
|
21
|
+
"set_#{ $1 }"
|
22
|
+
when /\A(.*)\Z/
|
23
|
+
"get_#{ $1 }"
|
24
|
+
end
|
25
|
+
|
26
|
+
if java_method_name and @voice_impl.respond_to?(java_method_name.to_sym)
|
27
|
+
return @voice_impl.send(java_method_name.to_sym, *args)
|
28
|
+
else
|
29
|
+
error_message = "undefined method #{ method } for #{ self }:Voice"
|
30
|
+
raise NoMethodError.new(error_message)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.all
|
35
|
+
voice_manager.get_voices.map { |voice| voice.get_name }
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.for_name(voice_name)
|
39
|
+
if voice = voice_manager.get_voice(voice_name)
|
40
|
+
voice.allocate
|
41
|
+
Voice.new(voice)
|
42
|
+
else
|
43
|
+
raise "no voice found for name \"#{ voice_name }\""
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), "../lib")
|
2
|
+
|
3
|
+
require "freetts"
|
4
|
+
require "test/unit"
|
5
|
+
|
6
|
+
|
7
|
+
class FreeTTSTest < Test::Unit::TestCase
|
8
|
+
def test_setting_current_voice
|
9
|
+
start_voice = FreeTTS.current_voice
|
10
|
+
all_voices = FreeTTS::Voice.all
|
11
|
+
|
12
|
+
new_voice_name = all_voices.reject { |name| name == start_voice.name }.first
|
13
|
+
FreeTTS.current_voice = FreeTTS::Voice.for_name(new_voice_name)
|
14
|
+
new_voice = FreeTTS.current_voice
|
15
|
+
|
16
|
+
assert_not_nil(new_voice)
|
17
|
+
assert_equal(new_voice_name, new_voice.name)
|
18
|
+
end
|
19
|
+
end
|
data/test/voice_test.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), "../lib")
|
2
|
+
|
3
|
+
require "freetts"
|
4
|
+
require "test/unit"
|
5
|
+
|
6
|
+
|
7
|
+
class VoiceTest < Test::Unit::TestCase
|
8
|
+
def valid_voice
|
9
|
+
FreeTTS.current_voice
|
10
|
+
end
|
11
|
+
|
12
|
+
# Calling valid accessor methods should result in a successful invocation.
|
13
|
+
def test_valid_accessor_methods
|
14
|
+
valid_accessors = %w( description gender locale pitch rate style )
|
15
|
+
valid_accessors.each do |method|
|
16
|
+
assert_nothing_raised do
|
17
|
+
valid_voice.send(method.to_sym)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Calling unknown accessor methods should raise a NoMethodException.
|
23
|
+
def test_invalid_accessor_methods
|
24
|
+
invalid_accessors = %w( something voice wallaby wombat parakeet )
|
25
|
+
invalid_accessors.each do |method|
|
26
|
+
assert_raises(NoMethodError) do
|
27
|
+
valid_voice.send(method.to_sym)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# A request for all voices should get an array of names.
|
33
|
+
def test_fetch_voices
|
34
|
+
voice_names = FreeTTS::Voice.all
|
35
|
+
assert_instance_of(Array, voice_names)
|
36
|
+
|
37
|
+
voice_names.each do |voice_name|
|
38
|
+
assert_instance_of(String, voice_name)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Requesting an available voice by name should return the relevant voice.
|
43
|
+
def test_fetch_any_single_valid_voice
|
44
|
+
voice_names = FreeTTS::Voice.all
|
45
|
+
|
46
|
+
voice_names.each do |voice_name|
|
47
|
+
assert_nothing_raised do
|
48
|
+
voice = FreeTTS::Voice.for_name(voice_name)
|
49
|
+
assert_instance_of(FreeTTS::Voice, voice)
|
50
|
+
assert_equal(voice_name, voice.name)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Attempting to fetch a non-existent voice should raise an exception.
|
56
|
+
def test_cannot_fetch_fake_voices
|
57
|
+
fake_voices = ["Ed McMahon", "Casey Kasem", "Rick Dees", "Frank Sinatra"]
|
58
|
+
fake_voices.each do |voice_name|
|
59
|
+
assert_raises(RuntimeError) do
|
60
|
+
FreeTTS::Voice.for_name(voice_name)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
data/vendor/cmulex.jar
ADDED
Binary file
|
Binary file
|
data/vendor/en_us.jar
ADDED
Binary file
|
data/vendor/freetts.jar
ADDED
Binary file
|
data/vendor/voices.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# This file lists the voice directories avialable to FreeTTs.
|
2
|
+
# To get the name of a voice directory from a voice jar file,
|
3
|
+
# type "java -jar voicefile.jar".
|
4
|
+
#
|
5
|
+
# Entries should look like:
|
6
|
+
#com.sun.speech.freetts.en.us.cmu_time_awb.AlanVoiceDirectory
|
7
|
+
#com.sun.speech.freetts.en.us.cmu_us_kal.KevinVoiceDirectory
|
8
|
+
|
9
|
+
com.sun.speech.freetts.en.us.cmu_time_awb.AlanVoiceDirectory
|
10
|
+
com.sun.speech.freetts.en.us.cmu_us_kal.KevinVoiceDirectory
|
11
|
+
|
12
|
+
# Uncomment to allow MBROLA voices:
|
13
|
+
#de.dfki.lt.freetts.en.us.MbrolaVoiceDirectory
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: freetts
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Chris kottom
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-09-19 00:00:00.000000000 +02:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
description: FreeTTS provides a simple wrapper around the Java library of the same name in order to expose a voice synthesis API.
|
16
|
+
email: chris@chriskottom.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- .gitignore
|
22
|
+
- .rvmrc.example
|
23
|
+
- README.md
|
24
|
+
- examples/hello_world.rb
|
25
|
+
- examples/rss_reader.rb
|
26
|
+
- examples/voice_play.rb
|
27
|
+
- lib/freetts.rb
|
28
|
+
- lib/freetts/version.rb
|
29
|
+
- lib/freetts/voice.rb
|
30
|
+
- test/freetts_test.rb
|
31
|
+
- test/voice_test.rb
|
32
|
+
- vendor/cmu_time_awb.jar
|
33
|
+
- vendor/cmu_us_kal.jar
|
34
|
+
- vendor/cmudict04.jar
|
35
|
+
- vendor/cmulex.jar
|
36
|
+
- vendor/cmutimelex.jar
|
37
|
+
- vendor/en_us.jar
|
38
|
+
- vendor/freetts.jar
|
39
|
+
- vendor/voices.txt
|
40
|
+
has_rdoc: true
|
41
|
+
homepage: ''
|
42
|
+
licenses: []
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ! '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
none: false
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ! '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
none: false
|
59
|
+
requirements: []
|
60
|
+
rubyforge_project: freetts
|
61
|
+
rubygems_version: 1.5.1
|
62
|
+
signing_key:
|
63
|
+
specification_version: 3
|
64
|
+
summary: A voice synthesizer in JRuby
|
65
|
+
test_files: []
|
66
|
+
...
|