No, I haven't been burning the oatmeal, but while doing some tests for a new project, I did write another network accessible wrapper to the espeak "text to speech" software. This time, I used the Ruby programming language and the webrick Ruby module which makes writing little web servers a snap.
For the longest time, I had a fairly negative opinion of the Ruby language. However, now that I've written some stuff with Ruby, I actually find the language quite pleasant. There are however, still a few drawbacks for me:
- Rails. Since Ruby on Rails is arguably the flagship Ruby application, searching the web for solutions to ruby issues usually turns up a lot of Rails specific solutions.
- The Gems package management system. Ruby libraries that are not part of the Ruby core or the Ruby standard library are usually distributed as a "Gem" and installed via the Gem package manager (that always seems to be at odds with my operating system)
- Some other stuff because I'm a grump who doesn't like anything.
Actually, none of that is a problem with Ruby itself, so I should really just shut my pie hole. Alright, back to the applications.
What I wanted was a web page I could access with any browser and enter text for the host machine to speak, and a URL that I could hit with curl or wget in order to get the host machine to speak text that I send to it.
Oh wait! There is something I really don't like about ruby: the way multi-line comments are handled. blech! OK, rant over. (seriously, go look it up)
Enter the Ruby
require 'webrick'
#create the html of the 'index' page
@index_html="<html>
<head>
<title>Time Talky Thing</title>
</head>
<body>
<form target='iframe' action='/speak'>
<label for='string'>Text to Speak:</label><br>
<input type='text' name='text'><br>
<label for='delay_minutes'>Delay Minutes:</label><br>"
@index_html += "<select name='delay_minutes'>"
(0..100).each do |i|
@index_html += "<option>#{i}</option>"
end
@index_html += "</select>"
@index_html+="<input type='submit' name='Submit'>
</form>
<iframe name='iframe'></iframe>
</body>
</html>"
#function to run when / is requested
def index (req, resp)
resp['Content-Type'] = 'text/html'
resp.body = @index_html
end
#function to run when /speak is requested
def speak (req, resp)
#get data from the request
text = req.query['text']
delay = req.query['delay_minutes'].to_i * 60
resp['Content-Type'] = 'text/plain'
resp.body = "OK"
speak_string( text, delay)
end
def speak_string( text, delay = 0 )
#build a command
cmd = ""
cmd += "sleep #{delay} && " if delay
cmd += "espeak -a150 -s150 -g7 -ven-us+f3 \"#{text}\"" if text
#if there is a command, run it in a new thread
unless cmd.empty?
Thread.new do
IO.popen(cmd)
end
end
end
# Initialize our WEBrick server
if $0 == __FILE__
config = {:Port => 8000}
server = WEBrick::HTTPServer.new(config)
#what urls do we need to mount?
server.mount_proc('/') do |req, resp|
index( req, resp )
end
server.mount_proc('/speak') do |req, resp|
speak( req, resp )
end
trap "INT" do
server.shutdown
end
server.start
end
Code also available at: http://hoof.jezra.net/snip/o5
What's Happening?
Run the code and point a browser at NAME_OF_MACHINE:8000 and you will be presented with a basic page with a form and an iframe. The form has the iframe as its "target" and the "/speak" url as its 'action'. This keeps things simple from an end users point of view since the page never needs to refresh.
If I don't want to use the form, I can simply send a command such as
to tell me what to do in 4 minutes. ha!
The only downsides to this are:
- having to URLencode strings that need to be spoken
- the length limit of a GET request
Now I need to run this code on the NaNoBox (and install a speaker in the box).
sweet sauce, another internetted thing. Actually, it would be best to run this code on a computer in a toaster.