Wednesday, May 16, 2007

Adding Sound to Your Ruby Apps

Have you ever thought about including sounds in your Ruby application? Used sparingly, sound may enhance your applications by adding audio cues or a custom touch. You could, for example, play a beep or chime that announces the completion of a lengthy process. Perhaps a humorous sound to accompany an error message .

The win32-sound library makes using sounds really simple.

If you installed Ruby with the One-Click Ruby Installer, then you probably already have the win32-sound library installed, along with other win32 utilities. Otherwise, you can install it in seconds via gem. Open up a console window and enter..


gem install win32-sound

To use the win32-sound library, add these require and include statements to the top of your script...

require 'win32/sound'
include Win32

Then, to play a sound file on the PC, call the Sound.play method, passing it the name of the file...

Sound.play('chimes.wav')
Sound.play('c:\sounds\hal9000.wav')

In the example above, you don't have to include the path to the file 'chimes.wav', because 'chimes.wav' is usually installed in the Windows folder. But in most cases, you'll want to include the full path to the sound file.

To generate a simple beep, call the Sound.beep method, passing it the tone frequency (in Hertz, between 37 and 32767) and the duration in milliseconds. For example, to play a low tone for half a second...

Sound.beep(100, 500)

...or to play an annoyingly high-pitched tone for 3 full seconds...

Sound.beep(5000, 3000)

The complete docs for this library can be found here.

Distributing your sound files with your application is also simple. You can embed your sound files in an executable created with RubyScript2Exe, or include them in an install package produced with Inno Setup.

Be careful not to overdo it, though, if your program is to be used by others besides yourself. It's a fine line between clever and annoying.

Thanks for stopping by!

16 comments:

Christian Baumann said...

Interesting to know, but I´m not sure if this really necessary?!
As You said, it´s difficult not to put too much beeps & rings to an application...and I´m sure there more guys outside that put too much sound into there app then others who do it "right".
But I don´t want to get philosophical... ;)
As always: Interesting post, thanks for it! Maybe I´ll use it in one of my next scripts to see how useful it could be for me.

Daniel Berger said...

Updated doc link: http://rubyforge.org/docman/view.php/85/1709/Sound.html

David Mullet said...

Thanks, Daniel!

I have updated the article with the new URL for the docs.

David

Anonymous said...

One possible usage of the beep -
to signal end of long processing activity, when the human goes away from the computer.

Anonymous said...

Do you know a gem that play sound (mp3) on linux?

thanks

David Mullet said...

Rafael-

I don't have Linux to test this out, but the snippet here calls the "aplay" command:

http://pastie.org/pastes/181456

Perhaps something like this will work for you.

David

Anonymous said...

I find the module very useful for (simple) debugging purposes. I even had forgotten I had used it before and in a way "rediscovered" it the next time I needed it.
Both times - thanks to this blog post, so - thanks David!

konung said...

Just wondering if you guys have an idea of how to play tones to a sound card - not the built in speaker. I need to send it to an output line on my card. Or if ti's even possible. I know I can record low volume sounds, and then play them - but I was wondering if I can generate them dynamically and send to our PA system - to signal lunch times and stuff.
Thanks.

Anonymous said...

Can Ruby turn ON/OFF window Audio device?

Usually the Audio is turned OFF as default.

But, want to turn ON and play finish test tone and turn it back OFF.

Possible?

Benjamin said...

I tried the code with a wav-file and I wondered if it is possible to set the duration (and maybe also the volumen) like in the beep tone... any idea?
Thanks for all answers!

David Mullet said...

@Benjamin:

A wav file has a finite length. But you can loop it, wait a while, then stop it:

Sound.play('chimes.wav', Sound::ASYNC|Sound::LOOP)
sleep(5)
Sound.stop()

Benjamin said...

Thank you!

But for my program I would like to play the music simultaneously...

F.e.
# start wav-file
puts "Tell me your solution: "
solution = gets.chomp
# end wav-file afterwards

Is that possible with Win32 sound library?

With the code that I got the music starts...plays a few seconds...stops... and then the program goes on...
any solution with that library?

David Mullet said...

@Benjamin-

Call the play method with the ASYNC and LOOP flags:

Sound.play('jeopardy.wav', Sound::ASYNC|Sound::LOOP)
puts "Tell me your solution: "
solution = gets.chomp
Sound.stop()

The above code starts playing the wav file and immediately returns to the next line of code.

David

Benjamin said...

Now it works, great! Thank you, David!

Unknown said...

This did not work for me, I installed it like it says, and I get this error:

C:\Users\Ted\ruby-sound>ruby ruby-sound.rb
:29:in `require': 126: The specified modul
e could not be found. - C:/Ruby192/lib/ruby/gems/1.9.1/gems/win32-api-1.4.6-x8
6-mingw32/lib/win32/api.so (LoadError)
from :29:in `require'
from C:/Ruby192/lib/ruby/gems/1.9.1/gems/windows-api-0.4.0/lib/windows/a
pi.rb:1:in `'
from :29:in `require'
from :29:in `require'
from C:/Ruby192/lib/ruby/gems/1.9.1/gems/windows-pr-1.0.9/lib/windows/so
und.rb:1:in `'
from :29:in `require'
from :29:in `require'
from C:/Ruby192/lib/ruby/gems/1.9.1/gems/win32-sound-0.4.2/lib/win32/sou
nd.rb:1:in `'
from :33:in `require'
from :33:in `rescue in require'
from :29:in `require'
from ruby-sound.rb:1:in `'

nilanjan said...

been thinking about this for months. I do long cli processing and helps to know when processing is done.