freetts 0.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 +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
|
+
...
|