http://www.developer.com/

Back to article

Simple Sounds for Linux


January 23, 2001

Remember the "bad old days", when users had a CLUE (Command Line User Environment) and PC speakers went beep, beep? That all changed in the mid-1980s, when Apple gave us the Mac, and Commodore introduced its Amiga. Since then, new applications have continually pushed the envelope in terms of display resolutions and audio capabilities. While this development is admittedly a good thing, it's also spurred the frequent purchase of expensive hardware, and created a myriad of compatibility problems.

On the flip side of the coin, one of the nice things about Linux is that it allows you to recycle all of your old equipment. Why throw away that slovenly 486, for example, when you can use it as your DNS server or as a telnet-capable workstation? Further, Linux is inherently good at multitasking, and as such, it's ideal for use as a server. Therefore, it's not uncommon to find Linux-based machines running *without* sound cards.

If this happens to be your scenario, does this mean that you have to forego sound entirely? Of course not! It's time to remember an old friend, the internal speaker. No sound card is required; just a PC with a regular speaker in its case.

After all, do you really need symphonic audio when displaying error messages?

This doesn't mean that you have to be content with mere beeps, however. Instead, you'll progress well beyond the standard "bell" sound (ASCII code 007, or Ctrl-G). Using low-grade wizardry, you'll be able to vary the pitch and length of each tone. As such, you'll soon have the ability to play a simple melody by typing a few brief commands. It will also be trivial to include these sounds within your programs.

Getting Re-acquainted With Your Internal Speaker

It's fairly common knowledge that -- with a sound card -- you can play an audio file on Linux by typing:
   cat sample.au > /dev/audio
on the command line, where sample.au is the name of your sound file. However, an even simpler method is to type:
   echo -e "\a"
This sounds the "alarm", i.e., the internal speaker. You can also generate a "beep" by including lines like the following in your code: Perl:
   print "\007";
C:
   char beep[] = {7, '\0'};
   printf("%c", beep);
To move beyond this basic beep, download the console_beep-0.1 package from www.ibiblio.org (formerly metalab.unc.edu, and sunsite.unc.edu before that). Amazingly, the compressed package is only 3K! For your convenience, the direct URL is:

http://www.ibiblio.org/pub/Linux/apps/sound/misc/console_beep-0.1.tar.gz

Once you've downloaded this, unpack it as usual.

Within the tarball, you'll find two programs to make: beep.c, and beep.c.direct. The first, beep.c, takes commands from a file, whereas beep.c.direct can be accessed directly from the command line. To compile beep.c.direct, you'll need to rename it "beep.c" -- after renaming the original, of course.

Usage is simple. Both programs take two arguments: the tonal frequency in Hertz, and the duration in milliseconds. So, let's say you compiled beep.c.direct into "beep". Here's all you'd need to do:

   beep 440 200
This will play a 440 Hz tone for 200 milliseconds, or a fifth of a second.

To play multiple notes, it's often necessary to insert a "rest" between them, like so:

   beep 440 200
   beep 0 200
   beep 700 200
Note that the "spacer" note, or rest, plays at zero Hertz (i.e., it doesn't play at all), but it continues for the same duration as the other notes.

Using the beep program, it's easy to play simple melodies. Here are the frequencies for some basic notes:

      262   C - "middle C" 
      277   C# 
      294   D 
      311   D# 
      330   E
      349   F 
      370   F# 
      392   G 
      415   G# 
      440   A
      466   A# 
      494   B 
To find the frequencies of subsequent notes, simply multiply the highest note you know by 1.0595, and then round up or down as appropriate. For example, 494 * 1.0595 = 523.393, or 523 Hz, which should be the "C" note that's one octave above middle C.

Here, then, is a script that plays a portion of Beethoven's F|r Elise:

#!/bin/bash

beep 659 120  #  Treble E
beep 0 120
beep 622 120  #  Treble D#
beep 0 120

beep 659 120  #  Treble E
beep 0 120
beep 622 120  #  Treble D#
beep 0 120
beep 659 120  #  Treble E
beep 0 120
beep 494 120  #  Treble B
beep 0 120
beep 587 120  #  Treble D
beep 0 120
beep 523 120  #  Treble C
beep 0 120

beep 440 120  #  Treble A
beep 0 140
beep 262 120  #  Middle C
beep 0 120
beep 330 120  #  Treble E
beep 0 120
beep 440 120  #  Treble A
beep 0 120

beep 494 120  #  Treble B
beep 0 140
beep 330 120  #  Treble E
beep 0 120
beep 415 120  #  Treble G#
beep 0 120
beep 494 120  #  Treble B
beep 0 120

beep 523 120  #  Treble C
beep 0 140
beep 330 120  #  Treble E
beep 0 120
beep 659 120  #  Treble E
beep 0 120
beep 622 120  #  Treble D#
beep 0 120

beep 659 120  #  Treble E
beep 0 120
beep 622 120  #  Treble D#
beep 0 120
beep 659 120  #  Treble E
beep 0 120
beep 494 120  #  Treble B
beep 0 120
beep 587 120  #  Treble D
beep 0 120
beep 523 120  #  Treble C
beep 0 120

beep 440 120  #  Treble A
beep 0 140
beep 262 120  #  Middle C
beep 0 120
beep 330 120  #  Treble E
beep 0 120
beep 440 120  #  Treble A
beep 0 120

beep 494 120  #  Treble B
beep 0 140
beep 330 120  #  Treble E
beep 0 120
beep 523 120  #  Treble C
beep 0 120
beep 494 120  #  Treble B
beep 0 140
beep 440 120  #  Treble A
The only problem with "beep" is that it's not very efficient. That is, you're consistently calling a program, and as such, the timing might not be absolutely perfect. As I said, Linux is often used for servers, and it's highly likely that other processes will be running at the same time. Therefore, you may experience some delays between notes.

One solution is to include beep's code within your own C program(s). This will definitely speed things up, as the program won't have to be called 400 times to play a tune.

An even better option is to use the following code, provided by programming guru Matan Ziv-Av:

#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/kd.h>

int main(int argc, char *argv[])
{
   int fd, time, freq, arg;
   fd = open("/dev/tty0", O_RDONLY);
   if (argc > 2)
   {
       freq = atoi(argv[1]);
       time = atoi(argv[2]);
   } else {
       freq = 500; /* frequency in Hz */
       time = 50;  /* time in millisec */
   }
   arg = (time<<16)+(1193180/freq);
   return ioctl(fd,KDMKTONE,arg);
}

This simplified program works exactly like beep, only it's much faster. As such, you may need to increase the duration of your tones by a factor of two or more.

Further, it may not be obvious by looking at the code, but this program runs in the background. Therefore, you can move on and do other things while the tone plays uninterrupted for the designated timeframe. This feature can be a detriment, however, if you're trying to play multiple notes (i.e., a melody). In this case, you'll find each note canceling out the next, and no amount of interstitial resting will help.

To overcome this annoyance, you'll need to insert a "rest" within the program itself. I accomplished this with a call to usleep(), like so:

#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/kd.h>

int main(int argc, char *argv[])
{
   int fd, time, freq, arg;
   fd = open("/dev/tty0", O_RDONLY);
   if (argc > 2)
   {
       freq = atoi(argv[1]);
       time = atoi(argv[2]);
   } else {
       freq = 500; /* frequency in Hz */
       time = 50;  /* time in millisec */
   }
   arg = (time<<16)+(1193180/freq);
   ioctl(fd,KDMKTONE,arg);
   usleep(time * 1000);
   return EXIT_SUCCESS;
}
Now the program rests for the same amount of time as the note plays, allowing you to play a chain of notes as before. Remember to #include !

In conclusion, there's no reason to exclude audio completely, just because you don't have a sound card. Simple sound bytes (if you'll pardon the pun) often add to a program's vitality and improve the user's experience. Now that you know how simple it is to code for the internal speaker, why not take advantage of this ever-present, yet often overlooked hardware feature?

Related Resources

1. www.ibiblio.org Massive, web-based repository of all things Linux. Formerly known as metalab.unc.edu and sunsite.unc.edu before that. Check it out!

2. The Sheet Music Archive One place to get free sheet music on the web.

3. The Free Sheet Music Directory Another repository of free sheet music.

4. Classical sheet music Look for the PDF Music link.

5. Free Sheet Music Guide The name says it all!

About Author

Jay Link is twentysomething and lives in Springfield, Illinois. Aside from Linux, his interests include mountain climbing and flying. He administrates InterLink BBS (an unintentionally not-for-profit Internet provider) in his fleeting spare moments, as well as working various odd jobs to pay the rent.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date