Driving One's Own Audio Device
I'm a strange guy, and I want my computers to keep silent—that's why I wrote the “Visible-bell mini-howto”, where I suggest speakerectomy surgery be performed. On the other hand, I enjoy playing with the soldering iron to build irrelevant stuff. One of the most irrelevant things I ever conceived is recycling the computer's loudspeaker in a very-low-volume audio device. As you might imagine, the device plugs in the parallel port.
This article describes the driver for such a beast, shows interesting details of the kernel workings and is still short enough to be an easy text for almost any reader. A quick description of the hardware is mandatory, but you can safely skip over the first section and jump directly to the section called “Writing Data”.
The software described here, as well as the electrical drawing, is released according to the GPL and is available as sad-1.0.tar.gz (Standalone Audio Device) from ftp://ftp.systemy.it/pub/develop/, my own ftp site.
Part of this work has been sponsored by “SAD Trasporto Locale” (http://www.sad.it/), the bus company of Bolzano (Bozen), Italy. They plan to bring my hardware on their buses and renamed the company to match my package (smile). (See “Travelling Linux” by Maurizio Cachia, LJ, June 1997.)
My device plugs in the parallel port, and its schematics are depicted in Figure 1; The photograph under the tiele is the only model ever built (Italian buses will run a different flavour of such stuff, the “bus for bus”--ftp://ftp.systemy.it/pub/develop/b4b-X.YY.tar.gz).
I owe the basic idea to Michael Beck, author of the pcsndrv package; the idea sounds like “use the parallel data bits to output audio samples.” My own addition is “use the interrupt signal to strobe samples at the right pace.” Audio samples must flow at 8KHz and any not-so-ancient computer can sustain such an interrupt rate: my almost-ancient development box runs a 33 BogoMips processor and is perfectly happy playing parallel audio. The interrupt-based approach trades higher quality for increased hardware complexity than that needed by Michael's package.
As shown in the schematics, the device is made up of a simple D/A converter built with a few resistors; the signal is then reduced to 1.5V peak-to-peak amplitude and fed through a low-pass filter. The filter I chose is a switched-capacitor device driven by a square wave at ten times the cutoff frequency. The 6142 chip is a dual op-amp with rail-to-rail output, one of several possible choices for low-power single-supply equipment.
The output signal can be brought to a small loudspeaker, but can be listened to only in complete silence; other environments ask for some form of amplification. My preferred alternative to the amplifier is the oscilloscope, the typical hear-by-seeing approach.
The main role of an audio driver is pushing data through the audio device. Several kinds of audio devices exist, and the sad driver only implements the /dev/audio flavour: 8-bit samples flowing at a rate of 8KHz. Each data byte that gets written to /dev/audio should be fed to an 8-bit A/D converter; every 125 microseconds, a new data sample must replace the current one.
Timing issues should be managed by the driver, without intervention from the program writing out the audio data. The output buffer is the software tool that isolates timing issues from user programs.
In sad, the output buffer is allocated at load time using get_free_pages. This function allocates consecutive pages, a power of two of them; the order argument of the function specifies how many pages are requested and is used as a power of two. An order of 1, therefore, represents two pages and an order of 3 represents eight pages. The allocation order of the output buffer is stored in the macro OBUFFER_ORDER, which is 0 in the distributed source file. This accounts for one page, which on the x86 processor corresponds to 4KB, or half a second worth of data.
The output buffer of sad is a circular buffer; the pointers ohead and otail represent its starting and ending points. The kernel uses unsigned long values to represent physical addresses, and the same convention is used in sad:
static unsigned long obuffer = 0; static unsigned long volatile ohead, otail;
Note that the ohead and otail variables are declared as volatile to prevent the compiler from caching their value in processor registers. This is an important caution, as the variables will be modified at interrupt time, asynchronously with respect to the rest of the code.
We'll see later that sad has an input buffer as well; the overall buffer allocation consists of these lines, executed from within init_module:
obuffer = __get_free_pages(GFP_KERNEL,
OBUFFER_ORDER, 0 /* no dma */);
ohead = otail = obuffer;
ibuffer = __get_free_pages(GFP_KERNEL,
IBUFFER_ORDER, 0 /* no dma */);
ihead = itail = ibuffer;
if (!ibuffer || !obuffer) { /* allocation failed
*/
cleanup_module(); /* use your own function */
return -ENOMEM;
}
Any data that a process writes to the device is put in the circular buffer, as long as it fits. When the buffer is full, the writing process is put to sleep, waiting for some space to be freed.
Since the data samples flow out smoothly, the process will eventually be awakened to complete its write system call. Anyway, a good driver is prepared to deal with users hitting the ctrl-C and must deal with SIGINT and other signals.
The following lines are needed to put to sleep and awaken the current process, all the magic is hidden in interruptible_sleep_on:
while (OBUFFER_FREE < OBUFFER_THRESHOLD) {
interruptible_sleep_on(&outq);
if (current->signal & ~current->blocked)
/* tell the fs layer to handle it */
/* a signal arrived */
return -ERESTARTSYS;
/* else, loop */
}
/* the following code writes to
* the circular buffer */
What are OBUFFER_FREE and OBUFFER_THRESHOLD? They are two macros: the former accesses ohead and otail to find out how much free space is in the buffer; the latter is a simple constant, predefined to 1024, a pseudo-random number. The role of such a threshold is to preserve system resources by avoiding too frequent asleep->awake transitions.
If the threshold was 1, the process would need to be awakened as soon as one byte of the buffer was freed, but it would soon be put to sleep again. As a result, the process will always be running, consuming processing power and raising the machine load. A threshold of 1KB assures that when the process goes to sleep it will sleep for at least one tenth of a second, because it won't be awakened before 1KB of data flows through the audio device. You can recompile sad.c with a different threshold value to see how a small value keeps the processor busy. Too big a value can result in jumpy audio, i.e. the sound cuts in and out. The audio stream becomes jumpy because data continues to flow while the kernel schedules execution of the process writing audio data. The more heavily the computer is loaded, the more jumpy the audio is likely to be; if several processes are contending for the processor, the one playing audio might be awakened too late, after all pending data has been transferred to the audio device. In addition to lowering the wakeup threshold, you can also cure the problem by increasing the buffer size.
Naturally, the write device method is only half of the story; the other half is performed by the interrupt handler.
Today’s modular x86 servers are compute-centric, designed as a least common denominator to support a wide range of IT workloads. Those generic, virtualized IT workloads have much different resource optimization requirements than hyperscale and cloud applications. They have resulted in a “one size fits all” enterprise IT architecture that is not optimized for a specific set of IT workloads, and especially not emerging hyperscale workloads, such as web applications, big data, and object storage. In this report, you will learn how shifting the focus from traditional compute-centric IT architectures to an innovative disaggregated fabric-based architecture can optimize and scale your data center.
Sponsored by AMD
Built-in forensics, incident response, and security with Red Hat Enterprise Linux 6
Every security policy provides guidance and requirements for ensuring adequate protection of information and data, as well as high-level technical and administrative security requirements for a system in a given environment. Traditionally, providing security for a system focuses on the confidentiality of the information on it. However, protecting the data integrity and system and data availability is just as important. For example, when processing United States intelligence information, there are three attributes that require protection: confidentiality, integrity, and availability.
Learn more about catching the bad guy in this free white paper.
Sponsored by DLT Solutions
| Making Linux and Android Get Along (It's Not as Hard as It Sounds) | May 16, 2013 |
| Drupal Is a Framework: Why Everyone Needs to Understand This | May 15, 2013 |
| Home, My Backup Data Center | May 13, 2013 |
| Non-Linux FOSS: Seashore | May 10, 2013 |
| Trying to Tame the Tablet | May 08, 2013 |
| Dart: a New Web Programming Experience | May 07, 2013 |
- New Products
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- Drupal Is a Framework: Why Everyone Needs to Understand This
- A Topic for Discussion - Open Source Feature-Richness?
- Home, My Backup Data Center
- What's the tweeting protocol?
- New Products
- RSS Feeds
- Readers' Choice Awards
- Dart: a New Web Programming Experience
Enter to Win an Adafruit Prototyping Pi Plate Kit for Raspberry Pi

It's Raspberry Pi month at Linux Journal. Each week in May, Adafruit will be giving away a Pi-related prize to a lucky, randomly drawn LJ reader. Winners will be announced weekly.
Fill out the fields below to enter to win this week's prize-- a Prototyping Pi Plate Kit for Raspberry Pi.
Congratulations to our winners so far:
- 5-8-13, Pi Starter Pack: Jack Davis
- 5-15-13, Pi Model B 512MB RAM: Patrick Dunn
- Next winner announced on 5-21-13!
Free Webinar: Linux Backup and Recovery
Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.
In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.




13 hours 14 min ago
15 hours 46 min ago
17 hours 3 min ago
17 hours 38 min ago
18 hours 1 min ago
22 hours 49 min ago
23 hours 36 min ago
1 day 1 hour ago
1 day 2 hours ago
1 day 4 hours ago