Your game is looking good, so you pass a copy to a friend to try out. Strangely, the sound doesn't work. It turns out your friend runs the GNOME desktop, and under GNOME the sound device is taken over by the esd sound server. GNOME applications are supposed to talk to esd, not directly to the sound device. So you go back, learn the esd API, and add an option to your program to work with esd.
You now pass the game over to a second friend to test, and again sound doesn't work for her. She likes to run the KDE desktop, and under KDE the sound device is managed by the artsd sound server. So you spend a couple more evenings learning the artsd sound API and adding support to your game so it can work with KDE too.
A third friend has heard about this great game and wants to try it too. He is running Solaris on a Sun workstation, but that's okay, he can just recompile the code for his architecture. Unfortunately sound does not work properly, because the Solaris sound device and APIs are not the same as on Linux. You could work on adding support for Solaris too (if you had access to a Solaris machine), but what about the friend who runs AIX, and the one who uses the Network Audio Server (NAS), and the one who uses the ALSA kernel drivers? Just providing basic support for sound is getting to be a lot of work. It's too bad you can't just write the program once and have it work on all platforms.
Enter CSL, the Common Sound Layer.
It currently works on Linux systems using the OSS sound drivers and the aRts sound library (which runs on both KDE and GNOME desktop environments). Applications that use CSL have no further dependencies on any libraries or other components.
CSL was designed to provide similar performance to platform-specific code, with support for latency management and full duplex. This makes it particularly suitable for real-time type applications such as games.
CSL is somewhat unique in that, despite all the talk of "desktop wars" between KDE and GNOME, it is being co-operatively developed by Stefan Westerfeld, a KDE developer, and Tim Janik, a GNOME developer.
CSL has some limitations, mostly by design: it is a C API only, so don't expect an object-oriented interface. It is a low-level API for digital audio only (no MIDI, no mixer, no codecs for complex formats like MP3). If you want features like 3D sound, you should look at something like OpenAL.
Currently, it supports only the OSS sound drivers or the aRts sound server. (aRts ships with KDE, and there is a GNOME version. Many multimedia applications like xmms and the RealVideo player will work under aRts as well).
While CSL is not finished yet, the API is quite stable and most of the functionality is there.
CSL is currently only provided in source format. You need to download the tar archive from http://www.arts-project.org/download/csl-0.1.2.tar.gz (there may be a newer version available by the time you read this).
Building and installing CSL follows the usual GNU build procedure, documented in the file INSTALL. Briefly, you need to run the commands:
%./configure % make % make install
The last must be done as user root. To test CSL, you can run two of the included utility programs. The testsine program generates raw samples for a 440 Hertz sine wave and cslcat accepts raw sound sample from standard input and sends them to the audio output device. Piping them together like this
% tests/testsine | csl/cslcat
should produce one second of a 440 Hertz sine wave tone (the above command assumes you are in the main CSL source directory).
If you have any raw sound files, you can try playing one with the cslcat utility, for example:
% cslcat -r 44100 -w 8 -c 1 /usr/lib/games/koules/start.raw
You can also try the programs in the examples directory.
1 #include <unistd.h> 2 #include <stdio.h> 3 #include <fcntl.h> 4 #include <csl/csl.h> 5 6 int main (int argc, char **argv) 7 { 8 const int size = 1024; 9 CslDriver *driver; 10 CslPcmStream *stream; 11 CslOptions options; 12 short buffer[size]; 13 int i, j, fd; 14 15 options.n_channels = 2; 16 options.rate = 44100; 17 options.pcm_format = CSL_PCM_FORMAT_S16_LE; 18 csl_driver_init (NULL, &driver); 19 csl_pcm_open_output (driver, "cslpcm1", options.rate, options.n_channels, options.pcm_format, &stream); 20 fd = open("/dev/urandom", O_RDONLY); 21 for (i = 0; i < 500; i++) 22 { 23 read(fd, buffer, size); 24 for (j = 0; j < size; j++) 25 buffer[j] = CLAMP(buffer[j], -4000, 4000); 26 csl_pcm_write (stream, size, buffer); 27 } 28 csl_pcm_close (stream); 29 csl_driver_shutdown (driver); 30 return 0; 31 }
In line 4 we include <csl/csl.h>, the header file that defines all of the CSL API functions.
In lines 9-11 we declare variables to hold some of the important CSL data types. A CslDriver is a handle associated with a particular backend driver. A CslPcmStream is a PCM audio stream, associated with a CSLDriver, opened for either input or output, and with specific sampling parameters. It is used much like a file descriptor. The type CslOptions stores options for a CslPcmStream. For convenience, CSL can parse standard command line options for sampling parameters and put them in a CslOptions variable.
Lines 15-17 set the PCM options: number of channels, sampling rate, and data format. In this case two channels (stereo), at a 44100 sample per second rate, using 16-bit signed little-endian samples.
Line 18 obtains a handle to a CslDriver. We could have specified the backend to use (e.g. "oss" or "arts") but the special value of NULL instructs CSL to select a driver automatically. You can also ask CSL to return a list of available drivers.
In line 19, using the driver handle, we pass the sampling options and receive a handle to the CslPcmStream, in this case an output stream for sound playback. If we wanted to perform sound recording we would have opened an input stream.
In line 20 of the example we open the Linux random device. We are going to use it to obtain random numbers which we will send to the sound device.
Lines 22-27 form a loop in which we read data from /dev/urandom into a buffer and then write the data to the PCM stream using csl_pcm_write. Because the data is random, it can contain large sample values which may be quite loud. We use the convenience macro CLAMP provided by CSL to constrain the value within a smaller range (recall that here we are working with 16-bit signed values). The result of writing the random data to the sound device should be a hissing sound from the speaker. This white noise of no particular frequency confirms that the random number generator device is indeed a good source of random data.
In lines 28-29, after looping 500 times (which corresponds to about 3 seconds), we clean up by closing the stream and shutting down the driver.
By studying this and the other examples, and looking at the HTML API documentation, you should quickly get a feel for how to use the library.
There is a mailing list for CSL. You can join the list by sending a
message with the word subscribe as the message body to
csl-request@space.twc.de. The mailing list is archived at http://www.mail-archive.com/csl@space.twc.de.
Jeff Tranter
Jeff Tranter has been using Linux since 1992. He is the author of the
Linux Sound and CD-ROM HOWTOs and the O'Reilly book Linux
Multimedia Guide. He has worked in a number of diverse areas of
Linux, most recently on the KDE desktop environment focusing on
multimedia. He is currently employed at a new Linux company,
Xandros Corporation.
Copyright © 2001, Jeff Tranter.
Copying license http://www.linuxgazette.net/copying.html
Published in Issue 70 of Linux Gazette, September 2001