\chapter{Linux Audio Programming}
\label{ch:linuxaudio}

Hardware manufacturers are often reluctant to release programming specifications to independent developers. This has impeded Linux's development at times and has resulted in less than optimal drivers for certain devices. However, the recent explosion in Linux's popularity has drawn attention to the project, and hardware support has improved considerably of late. Most consumer audio hardware is now fully supported under Linux, and some manufacturers have even contributed their own open source drivers for their hardware. This chapter discusses the ups and downs of Linux sound programming with several important APIs.

If you haven't yet read Chapter \ref{ch:masteringsdl}, it would be a good idea to flip back to its basic explanation of sound samples, buffers, and frequencies (beginning on page \pageref{sect:sdlaudio}). This chapter will assume that you are familiar with these basics, and we won't spend any more time on the subject.

This chapter describes the development of a complete Linux sound file player, dubbed Multi-Play. In the course of developing this real-world application, we will look at how to load sound data from disk and discuss four common methods of playing sound under Linux. By the end of the chapter, you will know how to integrate sound into a Linux game using any of the major APIs. The chapter ends with a discussion of the OpenAL environmental audio library, which is very useful for producing realistic sound effects in 3D environments.

\section{Competing APIs}
Linux is home to two competing sets of sound drivers. While Linux skeptics are likely to shout, ``Aha! Fragmentation!'' upon hearing this, the competition has raised the bar and has resulted in a much higher-quality set of sound drivers. Linux now supports almost every sound card on the market. One set of drivers is very consistent, complete, and stable, while the other set frequently breaks compatibility in order to cleanly integrate cutting-edge features. These two sets are largely interoperable, and so Linux users can enjoy the best of both worlds.

\index{OSS!overview}The original Linux sound API is the Open Sound System \index{OSS}(OSS). OSS consists of a set of kernel modules that provide a common programming interface to hundreds of different sound cards. Some of these modules \index{OSS/Free}(OSS/Free) are distributed for free with the Linux kernel, and some are available in binary-only form for a fee from \index{4Front Technologies}\website{4Front Technologies}{http://www.4front-tech.com}.  The OSS modules are well written and commercially supported, but the OSS programming interface leaves something to be desired. Nonetheless, OSS is more or less a de facto standard for Linux audio, and supporting OSS virtually guarantees that your application will work on most sound hardware.

\index{ALSA!overview}The \index{ALSA}\website{Advanced Linux Sound Architecture (ALSA) project}{http://www.alsa-project.org} has created an alternate set of Linux sound drivers. ALSA consists of a set of kernel modules as well as a programming library, providing support for a substantial and ever increasing number of sound cards. The ALSA library is much more convenient than the OSS's \code{ioctl}-based interface, and there is a simple emulation driver to support programs that don't use the native ALSA API. Perhaps the most significant difference between ALSA and OSS is that ALSA is a free software project maintained by volunteers, whereas OSS is a commercial venture that can support the latest hardware through nondisclosure agreements. Each approach has advantages. ALSA's biggest problem is that it is not quite ready to go mainstream yet; its programming interface changes with every major release (but this is slowing down). Many people use ALSA exclusively for its OSS compatibility mode, since many applications don't support ALSA directly.

With this brief comparison in mind, which sound interface should your games use? ALSA is a bit more programmer-friendly, if you can tolerate its evolving API. It has a well-designed (albeit changing) interface with lots of bells and whistles. OSS currently has a wider base of users but a rather crude programming interface. If providing support for both APIs is out of the question, I recommend coding for OSS and then testing with the ALSA emulation facility. An alternate approach would be to use a higher-level library such as SDL or OpenAL that can work with either interface.

\section{Introducing Multi-Play}
\index{Multi-Play|(}Multi-Play is a simple command-line sound file player. It works with normal OSS, OSS using direct DMA buffer access, ESD, and ALSA and supports a large number of sound file formats. Since it is designed to clearly demonstrate audio programming, Multi-Play lacks a few of the features one might find in an end-user player program, such as support for nonstandard frequencies and sample sizes. Feel free to use pieces of the Multi-Play code in your own projects or even to develop the complete player into a finished product. It's meant to be a Rosetta stone of sorts, performing the same basic task (sound playback) in several different ways.

Since Multi-Play is meant to demonstrate the mechanics of audio programming (and is not really intended for day-to-day use), it doesn't compensate for certain types of ``errors.'' For instance, \index{ESD}ESD does not properly handle 8-bit samples in some cases, but Multi-Play will attempt to use them anyway (whereas an end-user player might automatically change 8-bit samples into 16-bit samples for ESD playback). It also blindly tries to set the driver to the sample rate indicated by the sound file. This will not work in some cases, since drivers often don't support oddball sample rates. A high-quality sound player should try to compensate for this problem. However, doing so shouldn't be necessary for most game development situations.

The complete code for Multi-Play is available on the Web site; it is excerpted as appropriate here. It is a good idea to obtain and compile a copy of the code. Experimentation is the best way to learn any new area of programming.

\section{Loading Sound Files}
\index{sndfile library|see{libsndfile}}
\index{audiofile library|see{libaudiofile}}
SDL provides a convenient \index{wav files@\filename{.wav} files}\codei{SDL\_LoadWAV} function, but it is of little use to programs that don't use SDL, and it reads only the wave (\filename{.wav}) file format. A better option is the \index{libsndfile}libsndfile library maintained by Erik de Castro Lopo, which contains routines for loading nearly every common sound format. This library is easy to use, and it is available under the GNU LGPL (formerly the more restrictive GPL). You might also consider Michael Pruett's \index{libaudiofile}libaudiofile library, a free implementation of an API originally developed by Silicon Graphics. It is a bit less straightforward than libsndfile, however.

\subsection{Using libsndfile}
\index{libsndfile!using|(}
The libsndfile library makes it easy to read sample data from a large number of sound file formats. Sound files in any supported format are opened with a single library function, and individual samples can then be read with a common interface, regardless of the file format's encoding or compression. Your program must still take the size and signedness of the sample data into account, but libsndfile provides the necessary information.

\index{libsndfile!opening files}The \codei{sf\_open\_read} function opens a sound file for reading. This function accepts a filename and a pointer to an \codei{SF\_INFO} structure and returns a pointer to a \codei{SNDFILE} structure. The \code{SF\_INFO} structure represents the format of the sound data: its sample rate, sample size, and signedness. \code{sf\_open\_read} fills in this structure; your program does not need to supply this information in most cases. The \code{SNDFILE} structure returned by \code{sf\_open\_read} is a file handle; its contents are used internally by libsndfile, and they are not important to us. \code{sf\_open\_read} returns \code{NULL} if the requested file cannot be opened. For the curious, libsndfile does provide a \code{sf\_open\_write} facility for writing sound files, but this facility is beyond our present needs.

\index{libsndfile!reading data}After opening a sound file and obtaining a \code{SNDFILE} pointer, your program can read samples from it. libsndfile provides functions for reading samples as short integers, integers, or doubles. Each sample (8- or 16-bit) is called an \firstterm{item}, and a pair of stereo samples (or one mono sample) is a \index{samples!versus frames}\index{frame}\firstterm{frame}. libsndfile allows you to read individual items or complete frames. The \index{sf\_readf\_type@\code{sf\_readf\_\textit{type}}}\code{sf\_readf\_short}, \code{sf\_readf\_int}, and \code{sf\_readf\_double} functions read frames from a \code{SNDFILE} into buffers of various types. \code{sf\_readf\_double} can optionally scale samples to the interval $[-1..1]$, which is convenient for advanced audio processing. We will demonstrate this function in the next example.

\index{libsndfile!closing files}When your program is finished reading sound data from a \code{SNDFILE}, it should close the file with the \codei{sf\_close} function.

\begin{apiref}
\indexc{sf\_open\_read}
\apifunction{sf\_open\_read(filename, info)}
\apisynopsis{Opens a sound file, such as a \filename{.wav} or \filename{.au} file, for reading.}
\apireturns{Pointer to a \code{SNDFILE} structure that represents the open file. Fills in the provided \code{SF\_INFO} structure with information about the sound data.}
\apifirstparm{filename}{The name of the file to open.}
\apinextparm{info}{Pointer to the \code{SF\_INFO} structure that should receive information about the sound data.}
\apiendparms
\end{apiref}

\begin{apiref}
\indexc{SF\_INFO}
\structname{SF\_INFO}
\structsynopsis{Information about the data contained in a sound file.}
\structfirstmember{samplerate}{Sample rate of the sound data, in hertz.}
\structnextmember{samples}{Number of samples contained in each channel of the sound file. (The total number of samples is \code{channels * samples}.)}
\structnextmember{channels}{Number of channels contained in the sound file. This is usually 1 for mono or 2 for stereo.}
\structnextmember{pcmbitwidth}{Number of bits per sample.}
\structnextmember{format}{Sample format. Format constants are defined in \filename{sndfile.h}. This chapter only uses a small portion of libsndfile's capabilities---it can understand a wide variety of encoding formats, not just raw PCM.}
\apiendparms
\end{apiref}

\begin{apiref}
\indexc{sf\_readf\_type}
\apifunction{sf\_readf\_\emph{type}(sndfile, buffer, frames)}
\apisynopsis{Reads PCM data from an open sound file and returns it as a particular data type (regardless of the sample's original format). Possible types are \code{short}, \code{int}, and \code{double}. The \code{double} version of this function takes an extra boolean flag that indicates whether libsndfile should normalize sample values to the range $[-1..1]$.}
\apireturns{Number of frames successfully read.}
\apifirstparm{sndfile}{Pointer to an open \code{SNDFILE} handle.}
\apinextparm{buffer}{Pointer to a buffer for the data.}
\apinextparm{frames}{Number of frames to read. (A frame consists of one sample from each channel.)}
\apiendparms
\end{apiref}

\begin{apiref}
\indexc{sf\_close}
\apifunction{sf\_close(sndfile)}
\apisynopsis{Closes a sound file.}
\apifirstparm{sndfile}{Pointer to the \code{SNDFILE} to close.}
\apiendparms
\end{apiref}

\index{libsndfile!example|(}
\index{Multi-Play!example|(}
\begin{codelisting}{mp-loadsound.c}{code:mploadsound}
\begin{verbatim}
/* Sound loader for Multi-Play. */

#include <stdio.h>
#include <stdlib.h>
#include <sndfile.h>

/* Loads a sound file from disk into a newly allocated buffer.
   Sets *rate to the sample rate of the sound, *channels to the
   number of channels (1 for mono, 2 for stereo), *bits to the
   sample size in bits (8 or 16), *buf to the address of the
   buffer, and *buflen to the length of the buffer in bytes.
   16-bit samples will be stored using the host machine's
   endianness (little endian on Intel-based machines, big
   endian on PowerPC, etc.)
   
   8-bit samples will always be unsigned. 16-bit will always
   be signed.

   Channels are interleaved.
   
   Requires libsndfile to be linked into the program.
   
   Returns 0 on success and nonzero on failure. Prints an error
   message on failure. */
   
int LoadSoundFile(char *filename, int *rate, int *channels,
                  int *bits, u_int8_t **buf, int *buflen)
{
    SNDFILE *file;
    SF_INFO file_info;
    short *buffer_short = NULL;
    u_int8_t *buffer_8 = NULL;
    int16_t *buffer_16 = NULL;
    int i;
        
    /* Open the file and retrieve sample information. */
    file = sf_open_read(filename, &file_info);
    if (file == NULL) {
        printf("Unable to open '%s'.\n", filename);
        return -1;
    }
        
    /* Make sure the format is acceptable. */
    if ((file_info.format & 0x0F) != SF_FORMAT_PCM) {
        printf("'%s' is not a PCM-based audio file.\n",
               filename);
        sf_close(file);
        return -1;
    }
        
    if ((file_info.pcmbitwidth != 8) &&
        (file_info.pcmbitwidth != 16)) {
        printf("'%s' uses an unrecognized sample size.\n",
               filename);
        sf_close(file);
        return -1;
    }
        
    /* Allocate buffers. */
    buffer_short = (short *)malloc(file_info.samples *
                                   file_info.channels * 
                                   sizeof (short));

    buffer_8 = (u_int8_t *)malloc(file_info.samples *
                                  file_info.channels *
                                  file_info.pcmbitwidth / 8);

    buffer_16 = (int16_t *)buffer_8;

    if (buffer_short == NULL || buffer_8 == NULL) {
        printf("Unable to allocate enough memory for '%s'.\n",
               filename);
        fclose(file);
        free(buffer_short);
        free(buffer_8);
        return -1;
    }

    /* Read the entire sound file. */
    if (sf_readf_short(file,buffer_short,file_info.samples) ==
        (size_t)-1) {
        printf("Error while reading samples from '%s'.\n",
               filename);
        fclose(file);
        free(buffer_short);
        free(buffer_8);
        return -1;
    }
        
    /* Convert the data to the correct format. */
    for (i = 0; i < file_info.samples * file_info.channels; i++) {
        if (file_info.pcmbitwidth == 8) {
            /* Convert the sample from a signed short to an
               unsigned byte */
            buffer_8[i] = (u_int8_t)((short)buffer_short[i] + 128);
        } else {
            buffer_16[i] = (int16_t)buffer_short[i];
        }
    }
        
    /* Return the sound data. */
    *rate = file_info.samplerate;
    *channels = file_info.channels;
    *bits = file_info.pcmbitwidth;
    *buf = buffer_8;
    *buflen = file_info.samples * file_info.channels *
              file_info.pcmbitwidth / 8;
        
    /* Close the file and return success. */
    sf_close(file);
    free(buffer_short);

    return 0;
}
\end{verbatim}
\end{codelisting}
\index{Multi-Play!example|)}
\index{libsndfile!example|)}

This routine serves as Multi-Play's file loader. It begins by opening a sound file with \code{sf\_open}, checking that the samples are in plain PCM format (libsndfile does support other sample types), and verifying that the samples are either 8-bit or 16-bit (again, other sample types are possible but less common). It then allocates memory for the samples. Since the size of \code{short} integers can vary from platform to platform, this routine takes a safe approach of allocating a temporary buffer of type \code{short} for loading the file and then copying the samples to a buffer of either \code{int16\_t} or \code{u\_int8\_t}. This is a bit wasteful, and a production-quality sound player should probably take a different approach (such as using \code{sizeof} to determine the size of \code{short} and handling particular cases). With the sample data now in memory, the routine passes the relevant data back to its caller and returns zero.

\index{samples!signed versus unsigned}libsndfile treats all PCM samples as signed and reads them as \code{short}, \code{int}, or \code{double}. The actual number of bits used by each sample is indicated in the \code{pcmbitwidth} field of the \code{SF\_INFO} structure. Since our sound player will treat all 8-bit samples as unsigned and all 16-bit samples as signed (the usual convention for sound playback), our loader converts 8-bit samples to unsigned by adding 128 (bringing them into the range $[0..255]$). Sixteen-bit samples need no special handling, since libsndfile returns samples in the host machine's \index{endianness}endianness (little endian on Intel-based machines and big endian on many others).
\index{libsndfile!using|)}

\subsection{Other Options}
If for some reason libsndfile doesn't appeal to you, there are several other options. You could use the \index{libaudiofile}libaudiofile library,  which has a slightly different API and similar capabilities. libaudiofile has been around for a long time, and it is widely used. You could implement your own wave file loader, but this is extra work. The original wave file format has a very simple structure, consisting of little more than a RIFF header and a block of raw sample data. Unfortunately, some wave files are encoded in strange ways, and these must be either specifically handled or rejected. Libraries such as libsndfile and libaudiofile handle these quirks automatically.

Another option is to convert your sound files into a simpler format that is trivial to load. The \index{SoX}SoX (Sound eXchange) utility is handy for this purpose. SoX can rewrite sound files as raw, headerless data, which you can then load with the C library's \code{fopen} and \code{fread} functions. The main drawbacks to this approach are that the sound will be completely uncompressed (assuming that it was compressed to begin with) and that you will have to keep track of the sound's sample format by hand. This is a general nuisance, and so it's better to use a library for loading sound files.

\section{Using OSS}
\index{OSS!using|(}The OSS API is based on device files and the \code{ioctl} facility. UNIX \index{device files}\firstterm{device files} are files that represent devices in the system rather than ordinary data storage space. To use a device file, an application typically opens it by name with the C library's \code{open} function and then proceeds to exchange data with it using the standard \code{read} and \code{write} functions. The kernel recognizes the file as a device and intercepts the data. In the instance of a sound card, this data would consist of raw PCM samples. Device files are traditionally located in the \filename{/dev} directory on UNIX systems, but they can technically exist anywhere in the filesystem. The main OSS device files are \filename{/dev/dsp} and \filename{/dev/audio}.

This explains how a program can get samples into the sound card, but what about the sound card's other attributes, such as the sample format and sampling rate? This is where \label{ioctl}\codei{ioctl} comes in. The \code{ioctl} function (a system call, just like \code{read} or \code{write}) allows you to send special commands to device files, usually to configure the device before sending data with \code{write}. \code{ioctl} is a low-level interface, but there is really nothing difficult about it, so long as you have plenty of documentation about the device file you are working with (hence this book). OSS provides a header file (\filename{soundcard.h}) with symbols for the various \code{ioctl} calls it supports. Be careful with \code{ioctl}; its data goes directly to the kernel, and you could possibly throw a wrench into the system by sending unexpected data (though this would be considered a bug in the kernel; if you find such a bug, please report it).

Before we discuss ways to improve OSS's performance for gaming, let's examine the code for Multi-Play's OSS back end.

\index{Multi-Play!OSS back end|(}
\index{OSS!simple example|(}
\begin{codelisting}{mp-oss.c}{code:mposs}
\begin{verbatim}
/* Basic sound playback with OSS. */

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include <sys/mman.h>

/* Plays a sound with OSS (/dev/dsp), using default options.
   samples - raw 8-bit unsigned or 16-bit signed sample data
   bits - 8 or 16, indicating the sample size
   channels - 1 or 2, indicating mono or stereo
   rate - sample frequency
   bytes - length of sound data in bytes
   Returns 0 on successful playback, nonzero on error. */

int PlayerOSS(u_int8_t *samples, int bits, int channels,
              int rate, int bytes)
{
    /* file handle for /dev/dsp */
    int dsp = 0;

    /* Variables for ioctl's. */
    unsigned int requested, ioctl_format;
    unsigned int ioctl_channels, ioctl_rate;
        
    /* Playback status variables. */
    int position;

    /* Attempt to open /dev/dsp for playback (writing). */
    dsp = open("/dev/dsp",O_WRONLY);
        
    /* This could very easily fail, so we must handle errors. */
    if (dsp == -1) {
        perror("OSS player: error opening /dev/dsp for playback");
        return -1;
    }

    /* Select the appropriate sample format. */
    switch (bits) {
    case 8: ioctl_format = AFMT_U8; break;                
    case 16: ioctl_format = AFMT_S16_NE; break;                
    default: printf("OSS player: unknown sample size.\n");
        return -1;
    }

    /* We've decided on a format. We now need to pass it to OSS.
       ioctl is a very generalized interface. We always pass data
       to it by reference, not by value, even if the data is a
       simple integer. */
    requested = ioctl_format;
    if (ioctl(dsp,SNDCTL_DSP_SETFMT,&ioctl_format) == -1) {
        perror("OSS player: format selection failed");
        close(dsp);
        return -1;
    }
        
    /* ioctl's usually modify their arguments. SNDCTL_DSP_SETFMT
       sets its integer argument to the sample format that OSS
       actually gave us. This could be different than what we
       requested. For simplicity, we will not handle this
       situation. */
    if (requested != ioctl_format) {
        printf("OSS player: unsupported sample format.\n");
        close(dsp);
        return -1;
    }

    /* We must inform OSS of the number of channels
       (mono or stereo) before we set the sample rate. This is
       due to limitations in some (older) sound cards. */
    ioctl_channels = channels;
    if (ioctl(dsp,SNDCTL_DSP_CHANNELS,&ioctl_channels) == -1) {
        perror("OSS player: unable to set the number of channels");
        close(dsp);
        return -1;
    }

    /* OSS might not have granted our request, even if the ioctl
       succeeded. */
    if (channels != ioctl_channels) {
        printf("OSS player: unable to set the number of channels.\n");
        close(dsp);
        return -1;
    }

    /* We can now set the sample rate. */
    ioctl_rate = rate;
    if (ioctl(dsp,SNDCTL_DSP_SPEED,&ioctl_rate) == -1) {
        perror("OSS player: unable to set sample rate");
        close(dsp);
        return -1;
    }
        
    /* OSS sets the SNDCTL_DSP_SPEED argument to the actual
       sample rate, which may be different from the requested rate.
       In this case, a production-quality player would upsample or
       downsample the sound data. We'll simply report an error. */
    if (rate != ioctl_rate) {
        printf("OSS player: unable to set the sample rate.\n");
        close(dsp);
        return -1;
    }

    /* Feed the sound data to OSS. */
    position = 0;
    while (position < bytes) {
        int written, blocksize;

                
        /* We'll send audio data in 4096-byte chunks.
           This is arbitrary, but it should be a power
           of two if possible. This conditional just makes
           sure we properly handle the last chunk in the
           buffer. */
        if (bytes-position < 4096)
            blocksize = bytes-position;
        else
            blocksize = 4096;
                                
        /* Write to the sound device. */
        written = write(dsp,&samples[position],blocksize);
        if (written == -1) {
            perror("\nOSS player: error writing to sound device");
            close(dsp);
            return -1;
        }

        /* Update the position. */
        position += written;

        /* Print some information. */
        WritePlaybackStatus(position, bytes, channels, bits, rate);
        printf("\r");
        fflush(stdout);
    }

    printf("\n");
    close(dsp);

    return 0;
}
\end{verbatim}
\end{codelisting}
\index{Multi-Play!OSS back end|)}
\index{OSS!simple example|)}

This is the first of five Multi-Play player back ends, and one of three relating to OSS. These players are self-contained and handle all necessary initialization and cleanup internally. Their parameters are self-explanatory: the sound buffer to play is passed as a \code{u\_int8\_t} pointer, and various information about the sample format and playback rate is passed in as integers.

Our player begins by opening the \filename{/dev/dsp} device file. A production-quality player would likely provide some sort of command-line option to specify a different OSS device, but \filename{/dev/dsp} is valid on most systems. If the file is opened successfully, the player begins to set the appropriate playback parameters with the \code{ioctl} interface.

\index{OSS!setting sample format}\index{endianness}The first step in configuring the driver is to set the sample size. Our loader recognizes both 8-bit unsigned and 16-bit signed samples, and these are the only cases our player needs to account for. After selecting the appropriate constant (code{AFMT\_S16\_NE} for 16-bit signed with default endianness, or \code{AFMT\_U8} for 8-bit unsigned), it calls \code{ioctl} with \code{SNDCTL\_DSP\_SETFMT}. This \code{ioctl} call could fail if the driver does not support the requested format, but this is unlikely to happen unless the user has an extremely old sound card. We could instead use \code{AFMT\_S16\_LE} or \code{AFMT\_S16\_BE} to explicitly set little or big endianness, respectively. If this \code{ioctl} succeeds, the player sets the number of channels and the sample rate in a similar fashion. It is important to set the number of channels before the sample rate, because some sound cards have different limitations in mono or stereo mode.

The sound driver is now configured, and our player can begin sending samples to the device. Our player uses the standard UNIX \code{write} function to transfer the samples in 4,096-byte increments. Since sound cards play samples at a limited rate (specified by the sampling frequency), it is possible that these \code{write} calls will \index{OSS!blocking}\firstterm{block} (delay until the sound card is ready for more samples) and thereby limit the speed of the rest of the program. This would not be acceptable in a game, since games typically have better things to do than wait on the sound card. We will discuss ways to avoid blocking in the next section.

When the entire set of samples has been transferred to the sound card, our player closes the \filename{/dev/dsp} device and returns zero.

\subsection{Reality Check}
The OSS Web site has two main documents about OSS programming: \othertitle{Audio Programming} and \othertitle{Making Audio Complicated}. The former describes the basic method of OSS programming and is fairly simple to understand. The latter is aptly named; sound programming can quickly devolve into a messy subject. We will now cover some of the ugly details that are necessary for real-world game development. Unfortunately, the simple example we just discussed is woefully inadequate.

\index{OSS!synchronization}Perhaps the stickiest issue in game sound programming (particularly with OSS) is keeping the sound playback stream synchronized with the rest of the game. SDL provided a nice callback that would notify your program whenever the sound card was hungry for more samples, but OSS doesn't have a callback system. If you try to write too many samples before the card has a chance to play them, the \code{write} function will block (that is, it will not return until it can complete). As a result, the timing of your entire program will depend on OSS's playback speed. This is not good.

\label{osslatency}\index{OSS!latency}A closely related issue is latency. It is inevitable that a game's sound will lag slightly behind the action on the screen; all games experience this, and it is not a problem so long as the latency is only a small fraction of a second (preferably under $\frac{1}{10}$ second). Unfortunately, OSS has quite a bit of internal buffer space, and if you were to fill it to capacity by blindly calling \code{write}, your latency would go through the roof. Imagine playing \gametitle{Quake} and hearing a gunshot sound several seconds after firing the gun---you would probably laugh and switch to a different game!

OSS stores samples in an internal buffer that is divided into \index{OSS!fragments}\firstterm{fragments} of equal size. Each fragment stores approximately $\frac{1}{2}$ second of audio data (resulting in a potential latency of $\frac{1}{2}$ second). When the sound card is finished playing a fragment, it jumps to the next. If the next fragment has not been filled with samples, the sound card will play whatever happens to be there, which results in audio glitches. Ideally, you want to stay just a few fragments ahead of the sound card, to avoid skipping while keeping latency to a minimum. You can, within limits, specify the number of fragments and the size of each.

\index{OSS!fragments!adjusting|(}To set OSS's internal fragment size, send the \code{SNDCTL\_DSP\_SETFRAGMENT} \code{ioctl} with a bitfield-encoded argument:

\begin{codesegment}
\begin{verbatim}
int ioctl_frag;

/* Set the fragment parameters. See the text for an explanation. */
ioctl_frag = 10;          /* fragment size is 2^10 = 1024 bytes */
ioctl_frag += 3 * 65536;  /* fragment count is 3 */
if (ioctl(dsp,SNDCTL_DSP_SETFRAGMENT,&ioctl_frag) != 0) {
        /* handle error */
}
\end{verbatim}
\end{codesegment}

This \code{ioctl} should be called as soon as possible after opening the audio device; it will not work after the first \code{write} call has been made, and it might not work after other settings have been applied. Its unsigned integer argument is divided into two bit fields. The lower 16 bits specify the fragment size as a power of 2. For instance, a value of 10 in the lower 16 bits requests a fragment size of $2^{10}$, or 1,024 bytes. The high 16 bits of the argument specify the number of fragments to allocate. In this case we request only three fragments, meaning that we will have to be fairly attentive to the audio device if we want to avoid glitches.
\index{OSS!fragments!adjusting|)}

\index{OSS!blocking|(}Our next task is to get rid of the blocking \code{write} call. We can use OSS's buffer information \code{ioctl} to avoid blocking:

\begin{codesegment}
\begin{verbatim}
for (;;) {
    audio_buf_info info;

    /* Ask OSS if there is any free space in the buffer. */
    if (ioctl(dsp,SNDCTL_DSP_GETOSPACE,&info) != 0) {
        perror("Unable to query buffer space");
        close(dsp);
        return 1;
    }

    /* Any empty fragments? */
    if (info.fragments > 0) break;
                
    /* Not enough free space in the buffer. Waste time. */
     usleep(100);
}
\end{verbatim}
\end{codesegment}

This addition to the main player loop (right before the actual \code{write} call) simply queries OSS to check for an empty fragment. If at least one fragment is available, we can safely write a chunk of sample data without blocking. If there is no space in the outgoing sound buffer, we kill time with the \code{usleep} function. A game would probably use this time to do something productive, such as updating the video display. We use the \code{SNDCTL\_DSP\_GETOSPACE} \code{ioctl} to check for the availability of a fragment. This \code{ioctl} returns a structure (defined in \filename{soundcard.h}) with various statistics about OSS's internal buffers.
\index{OSS!blocking|)}

\newpage
\begin{warning}
Generally speaking, minor tweaks to OSS parameters (such as setting a custom fragment size) are safe; they are unlikely to break compatibility with most drivers or even ALSA's OSS emulator. I used a laptop with the ALSA sound driver to create these code examples. ALSA's OSS emulation is less than perfect, and the examples worked slightly differently under ALSA and OSS. It is important to test audio code on as many systems as possible and with both ALSA and OSS. Audio programming involves a lot of fine-tuning; in a world with thousands of different audio cards, it is far from a precise science.
\end{warning}

\subsection{Achieving Higher Performance with Direct DMA Buffer Access}
Sometimes OSS's basic mechanism of writing samples to the sound device does not provide sufficient performance. OSS provides an alternative, but it is neither pretty nor simple. It is possible to use the UNIX \index{OSS!memory mapped DMA}\codei{mmap} function to gain direct access to the sound driver's DMA buffer. The DMA buffer is a region of memory that the sound system's hardware scans for samples at regular intervals; it is the audio equivalent of the raw video framebuffer. If your program can somehow gain access to the DMA buffer, it can mix and send its own fragments directly to the sound card, without any driver intervention. Latency is still a concern, but this technique bypasses the driver's internal buffering and therefore gives you the power to push latency as close to the line as you wish (at the risk of skipping or other anomalies).

\emph{Direct DMA buffer access is absolutely not portable.} It is known to be incompatible with certain configurations (including my laptop, much to my chagrin), but it will probably work on most Linux-based systems with ``normal'' sound hardware. This particular example did work on my FreeBSD 4.1 system, but with a noticeable choppiness. So far it has worked on one of my Linux systems. We recommend avoiding this technique if possible, or at least providing a ``safe'' alternative in case DMA initialization fails. Have you ever been frustrated that an OSS application (such as \index{Quake 3}\gametitle{Quake 3}) would not work with the ALSA or ESD emulation drivers? Direct DMA access was likely the cause. However, these applications do enjoy improved audio performance with less CPU usage.

The next player back end accesses the DMA buffer directly. It uses the basic technique presented in 4Front Technologies' original \filename{mmap\_test.c} example, but with a slightly different buffering scheme in the main loop. This player back end has been tested successfully on a number of sound cards, but it is not compatible with ALSA's OSS emulation driver.

\index{OSS!memory mapped DMA!example|(}
\begin{codelisting}{mp-dma.c}{code:mpdma}
\begin{verbatim}
/* DMA sound playback with OSS. */

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include <sys/mman.h>

/* Plays a sound with OSS (/dev/dsp), using direct DMA access.
   Returns 0 on successful playback, nonzero on error. */
int PlayerDMA(u_int8_t *samples, int bits,
              int channels, int rate, int bytes)
{
    /* file handle for /dev/dsp */
    int dsp = 0;

    /* Variables for ioctl's. */
    unsigned int requested, ioctl_format, ioctl_channels,
        ioctl_rate, ioctl_caps, ioctl_enable;
    audio_buf_info ioctl_info;
        
    /* Buffer information. */
    int frag_count, frag_size;
    u_int8_t *dmabuffer = NULL;
    int dmabuffer_size = 0;
    int dmabuffer_flag = 0;

    /* Playback status variables. */
    int position = 0, done = 0;



    /* Attempt to open /dev/dsp for playback. We need to open for
       read/write in order to mmap() the device file. */
    dsp = open("/dev/dsp",O_RDWR);
        
    /* This could very easily fail, so we must handle errors. */
    if (dsp < 0) {
        perror("DMA player: error opening /dev/dsp for playback");
        goto error;
    }
        
    /* Select the appropriate sample format. */
    switch (bits) {
    case 8: ioctl_format = AFMT_U8; break;                
    case 16: ioctl_format = AFMT_S16_NE; break;                
    default: printf("DMA player: unknown sample size.\n");
        goto error;
    }

    /* We've decided on a format. We now need to pass it to OSS. */
    requested = ioctl_format;
    if (ioctl(dsp,SNDCTL_DSP_SETFMT,&ioctl_format) == -1) {
        perror("DMA player: format selection failed");
        goto error;
    }
        
    /* ioctl's usually modify their arguments. SNDCTL_DSP_SETFMT
       sets its integer argument to the sample format that OSS
       actually gave us. This could be different than what we
       requested. For simplicity, we will not handle this
       situation. */
    if (requested != ioctl_format) {
        printf("DMA player: unsupported sample format.\n");
        goto error;
    }
                
    /* We must inform OSS of the number of channels (mono or stereo)
       before we set the sample rate. This is due to limitations in
       some (older) sound cards. */
    ioctl_channels = channels;
    if (ioctl(dsp,SNDCTL_DSP_CHANNELS,&ioctl_channels) == -1) {
        perror("DMA player: unable to set the number of channels");
        goto error;
    }

    /* OSS might not have granted our request, even if the ioctl
       succeeded. */
    if (channels != ioctl_channels) {
        printf("DMA player: unable to set the number of channels.\n");
        goto error;
    }

    /* We can now set the sample rate. */
    ioctl_rate = rate;
    if (ioctl(dsp,SNDCTL_DSP_SPEED,&ioctl_rate) == -1) {
        perror("DMA player: unable to set sample rate");
        goto error;
    }
        
    /* OSS sets the SNDCTL_DSP_SPEED argument to the actual sample rate,
       which may be different from the requested rate. In this case, a
       production-quality player would upsample or downsample the sound
       data. We'll simply report an error. */
    if (rate != ioctl_rate) {
        printf("DMA player: unable to set the sample rate.\n");
        goto error;
    }

    /* Now check for DMA compatibility. It's quite possible that the
       driver won't support this. It would be a *very* good idea to
       provide a fallback in case DMA isn't supported - there are some
       sound cards that simply don't work with the DMA programming
       model at all. */
    if (ioctl(dsp,SNDCTL_DSP_GETCAPS,&ioctl_caps) != 0) {
        perror("DMA player: unable to read sound driver capabilities");
        goto error;
    }
        
    /* The MMAP and TRIGGER bits must be set for this to work.
       MMAP gives us the ability to access the DMA buffer directly,
       and TRIGGER gives us the ability to start the sound card's
       playback with a special ioctl. */
    if (!(ioctl_caps & DSP_CAP_MMAP) ||
        !(ioctl_caps & DSP_CAP_TRIGGER)) {
        printf("DMA player: this sound driver is not capable of DMA.");
        goto error;
    }

    /* Query the sound driver for the actual fragment
       configuration so that we can calculate the total size of
       the DMA buffer. Note that we haven't selected a particular
       fragment size or count. Fragment boundaries are meaningless
       in a mapped buffer; we're really just interested in the
       total size. */
    if (ioctl(dsp,SNDCTL_DSP_GETOSPACE,&ioctl_info) != 0) {
        perror("DMA player: unable to query buffer information");
        goto error;
    }

    frag_count = ioctl_info.fragstotal;
    frag_size = ioctl_info.fragsize;
    dmabuffer_size = frag_count * frag_size;
        
    /* We're good to go. Map a buffer onto the audio device. */
    dmabuffer = mmap(NULL,
                     dmabuffer_size,  /* length of region to map */
                     PROT_WRITE,      /* select the output buffer
                                         (PROT_READ alone selects
                                          input) */
                     /* NOTE: I had to add PROT_READ to
                        make this work with FreeBSD.
                        However, this causes the code to
                        fail under Linux. SNAFU. */
                     MAP_FILE | MAP_SHARED,  /* see the mmap()
                                                manual page */
                     dsp,             /* opened file to map */
                     0);              /* start at offset zero */

    /* This could fail for a number of reasons. */
    if (dmabuffer == (u_int8_t *)MAP_FAILED) {
        perror("DMA player: unable to mmap a DMA buffer");
        goto error;
    }
        
    /* Clear the buffer to avoid static at the beginning. */
    memset(dmabuffer, 0, dmabuffer_size);

    /* The DMA buffer is ready! Now we can start playback by
       toggling the device's PCM output bit. Yes, this is a
       very hacky interface. We're actually using the OSS
       "trigger" functionality here. */
    ioctl_enable = 0;
    if (ioctl(dsp, SNDCTL_DSP_SETTRIGGER, &ioctl_enable) != 0) {
        perror("DMA player: unable to disable PCM output");
        goto error;
    }
        
    ioctl_enable = PCM_ENABLE_OUTPUT;
    if (ioctl(dsp, SNDCTL_DSP_SETTRIGGER, &ioctl_enable) != 0) {
        perror("DMA player: unable to enable PCM output");
        goto error;
    }

    /* The done variable simply makes sure that the last chunk
       actually gets played. We'll play a brief period of silence
       after the last data chunk. */
    while (done < 4) {
        struct count_info status;
        int i;
                
        /* Find the location of the DMA controller within the
           buffer. This will be exact at least to the level of
           a fragment. */
        if (ioctl(dsp, SNDCTL_DSP_GETOPTR, &status) != 0) {
            perror("DMA player: unable to query playback status");
            goto error;
        }
                
        /* Our buffer is comprised of several fragments. However,
           in DMA mode, it is safe to treat the entire buffer as
           one big block.
           We will divide it into two logical chunks. While the
           first chunk is playing, we will fill the second with
           new samples, and vice versa. With a small buffer, we
           will still enjoy low latency.
                   
           status.ptr contains the offset of the DMA controller
           within the buffer. */
        if (dmabuffer_flag == 0) {
            /* Do we need to refill the first chunk? */
            if (status.ptr < dmabuffer_size/2) {
                int amount;
                
                /* Copy data into the DMA buffer. */
                if (bytes - position < dmabuffer_size/2) {
                    amount = bytes-position;
                } else amount = dmabuffer_size/2;
                
                for (i = 0; i < amount; i++) {
                    dmabuffer[i+dmabuffer_size/2] =
                        samples[position+i];
                }
                
                /* Zero the rest of this half. */
                for (; i < dmabuffer_size/2; i++) {
                    dmabuffer[i+dmabuffer_size/2] = 0;
                }
                                                                
                /* Update the buffer position. */
                position += amount;
                        
                /* Next update will be the first chunk. */
                dmabuffer_flag = 1;
                                
                /* Have we reached the end? */
                if (position >= bytes) done++;
            }
        } else if (dmabuffer_flag == 1) {
            /* Do we need to refill the first chunk? */
            if (status.ptr >= dmabuffer_size/2) {
                int amount;

                /* Copy data into the DMA buffer. */
                if (bytes - position < dmabuffer_size/2) {
                    amount = bytes-position;
                } else amount = dmabuffer_size/2;
                                
                for (i = 0; i < amount; i++) {
                    dmabuffer[i] = samples[position+i];
                }

                /* Zero the rest of this half. */
                for (; i < dmabuffer_size/2; i++) {
                    dmabuffer[i] = 0;
                }
                                
                /* Update the buffer position. */
                position += amount;
                                                        
                /* Next update will be the second chunk. */
                dmabuffer_flag = 0;

                /* Have we reached the end? */
                if (position >= bytes) done++;
            }
        }

        WritePlaybackStatus(position, bytes, channels, bits, rate); 
        printf(" (%i)\r", dmabuffer_flag);
        fflush(stdout);
                
        /* Wait a while. A game would normally do the rest of its
           processing here. */
        usleep(50);
    }

    printf("\n");

    munmap(dmabuffer,dmabuffer_size);
    close(dsp);

    return 0;
        
    /* Error handler. gotos are normally bad,
       but they make sense here. */
 error:
    if (dmabuffer != NULL)
        munmap(dmabuffer,dmabuffer_size);
    if (dsp > 0) close(dsp);
                
    return -1;
}
\end{verbatim}
\end{codelisting}
\index{OSS!memory mapped DMA!example|)}

The program begins as usual, loading a sound file and setting a few OSS parameters via \code{ioctl}. It then queries the OSS device's capabilities and checks for DMA compatibility. If the sound card is capable of this type of DMA access, the \code{SNDCTL\_DSP\_MMAP} and \code{SNDCTL\_DSP\_TRIGGER} bits will be set. (Initialization can still fail, however.) The program queries OSS for the size and number of buffer fragments, multiplying them to obtain the total size of the sound buffer in bytes. Next it uses \code{mmap} to map a buffer onto the sound device.

\label{mmap}\codei{mmap} is a strange beast. It can be used for a variety of purposes, but its most common use is to map buffers of memory onto files (so that accesses to the memory will result in accesses to the file). This provides a very convenient way to load large data structures from disk; you can simply map the file into memory and grab the data with \code{memcpy}. OSS uses it to provide a convenient way to map the sound card's DMA buffer into a program's address space. We will leave a full discussion of \code{mmap} to other sources, but its use in this case is fairly straightforward.

With a DMA buffer in place and the card properly configured, the program ``triggers'' the sound card's DMA by toggling the driver's PCM output enable bit. The trigger feature is designed to allow applications to gain precise control over playback timing, but it doubles as a way to set off DMA transfers. Once the DMA controller has been started, it cannot be stopped without shutting down OSS. This is a flaw, in my opinion, but it is only a minor issue. (To effectively stop playback, simply fill the buffer with zeros.) After this bit has been cleared and then reset, the DMA controller (either part of the sound card or a component of the motherboard's chipset) will repeatedly loop over the DMA buffer and send whatever it finds directly to the sound card. To play sound, we simply have to copy our samples into the DMA buffer.

This is the tricky part. The DMA controller is a separate piece of hardware, and we have little control over its operation once it has been started. It sweeps across the DMA buffer at a predictable rate, returning to the start of the buffer when it reaches the end. We need to make sure that we always keep a fresh set of sample data in front of the DMA controller's path. We have chosen a simple method that seems to work fairly well. The DMA buffer contains several (probably four or five) fragments of data, each 1,024 bytes. We disregard this organization and treat the buffer as one chunk. While the DMA controller is busy scanning the first half of the buffer, we (quickly) fill the second half with new samples. When the controller crosses the halfway mark, we update the first half of the buffer. We could reduce latency by dividing the buffer into more sections, but this should rarely be necessary. When the entire sound clip has been played, our program unmaps the DMA buffer and shuts down OSS.

As you've seen, direct access to the DMA buffer is a powerful tool for squeezing performance out of OSS, but you should not count on its availability or even on reliable detection. Don't be surprised if it doesn't work on a given sound card or under different operating systems. The SDL toolkit uses DMA if the user specifically asks for it (with an environment variable), but it's not a safe mechanism to use by default.

\index{OSS!ioctl list@\code{ioctl} list|(}
\begin{table}
\begin{center}
\begin{tabular}{|c|p{3in}|}
\hline
\code{ioctl} call & Purpose \\
\hline\hline
\code{SNDCTL\_DSP\_SETFMT} & Sets OSS to use a particular sample format. Takes a pointer to an integer containing an \code{AFMT\_\emph{format}} constant (defined in \filename{sys/soundcard.h}). Changes this integer to reflect the format that OSS was able to obtain. \\
\hline
\code{SNDCTL\_DSP\_CHANNELS} & Sets the number of channels. Takes a pointer to an integer containing 1 for mono, 2 for stereo. \\
\hline
\code{SNDCTL\_DSP\_SPEED} & Sets the sound device's sampling rate. Takes a pointer to an integer containing the desired sampling rate. Changes this integer to reflect the closest match that OSS could obtain. \\
\hline
\code{SNDCTL\_DSP\_SETFRAGMENT} & Sets the sound driver's fragment size. Takes a pointer to an integer. The top 16 bits define the number of fragments desired, and the lower 16 bits define the size of each fragment as a power of 2 (for instance, a value of 10 would result in a fragment size of $2^{10}$, or $1,024$ bytes). There are reasonable limits on the minimum and maximum number of fragments and the size of each, and OSS reserves the right to reject your selection. \\
\hline
\code{SNDCTL\_DSP\_GETOSPACE} & Queries the amount of buffer space available in the sound driver. Takes a pointer to an \code{audio\_buf\_info} structure (defined in \filename{sys/soundcard.h}). Fills the structure with information about the driver's buffer fragments. \\
\hline
\code{SNDCTL\_DSP\_GETCAPS} & Queries the sound driver's capabilities. Takes a pointer to an integer. Sets the integer to a bitmask of supported \code{DSP\_CAP\_\emph{func}} functions (defined in \filename{sys/soundcard.h}). \\
\hline
\end{tabular}
\textit{\caption{A few important OSS \code{ioctl} calls}}
\label{tab:ossioctl}
\end{center}
\end{table}

That's it for OSS! It all boils down to configuring a file descriptor with \code{ioctl} and sending samples with \code{write}. See Table \ref{tab:ossioctl} for a list of OSS \code{ioctl} calls.

\index{OSS!ioctl list@\code{ioctl} list|)}
\index{OSS!using|)}

\section{Playing Sound with ALSA}
\index{ALSA!using|(}
ALSA is a well-designed API, but unfortunately its design is still in progress. \index{ALSA!philosophy}The native ALSA API seems to change slightly with each major release, and so it's somewhat of a moving target. We will describe the release that is current at the time of this writing, the 0.5.x series. For those who are frustrated with ALSA's evolution, remember that OSS provides a somewhat ugly but consistent API that is ready \emph{now}, while the ALSA project is trying to decide how to do it \emph{right} (and from what I've seen, they are doing an excellent job). The finished product will be much more polished and more pleasant to use than OSS. For the time being, ALSA's OSS emulation module seems to work well enough, and the adventurous might enjoy using its API directly.

With that in mind, let's look at the Multi-Play ALSA back end.

\longpage
\index{ALSA!example|(}
\index{Multi-Play!ALSA back end|(}
\begin{codelisting}{mp-alsa.c}{code:mpalsa}
\begin{verbatim}
/* Sound playback with ALSA. */

#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/asoundlib.h>


/* Plays a sound with the Advanced Linux Sound Architecture, ALSA.
   Returns 0 on successful playback, nonzero on error. */
int PlayerALSA(u_int8_t *samples, int bits,
               int channels, int rate, int bytes)
{
    int i;

    /* ALSA is a bit verbose, and it tends to require
       lots of structures. */
    int alsa_device, alsa_card;
    char *alsa_card_name;
    snd_ctl_t *alsa_ctl;
    snd_ctl_hw_info_t alsa_hw_info;
    snd_pcm_t *alsa_pcm;
    snd_pcm_channel_params_t alsa_params;
        
    /* Playback status variables. */
    int position;

    /* Scan for ALSA cards and devices. Each card has an integer
       ID less than snd_cards(). We scan for the first available
       card in order to demonstrate ALSA's organization, but we
       could find the default card and PCM device numbers
       immediately with the snd_defaults_pcm_card() and
       snd_defaults_pcm_device() functions. */
    alsa_pcm = NULL;
    for (alsa_card = 0; alsa_card < snd_cards(); alsa_card++) {

        /* Try to open this card. */
        if (snd_ctl_open(&alsa_ctl,alsa_card) < 0)
            continue;
                
        /* Retrieve card info. */
        if (snd_ctl_hw_info(alsa_ctl,&alsa_hw_info) < 0) {
            snd_ctl_close(alsa_ctl);
            continue;
        }
                
        snd_ctl_close(alsa_ctl);
                

        /* Find a suitable device on this card. */
        alsa_pcm = NULL;
        for (alsa_device = 0; alsa_device < alsa_hw_info.pcmdevs;
             alsa_device++) {
            if (snd_pcm_open(&alsa_pcm,alsa_card,
                             alsa_device,SND_PCM_OPEN_PLAYBACK) < 0)
                continue;
            
            /* Device successfully opened. */
            break;
        }
                
        if (alsa_pcm != NULL) break;
    }
        
    /* Were we able to open a device? */
    if (alsa_card == snd_cards()) {
        printf("ALSA player: unable to find a configured device.\n");
        return -1;
    }
        
    /* Print info about the device. */
    if (snd_card_get_longname(alsa_card,&alsa_card_name) < 0)
        alsa_card_name = "(unknown)";
    printf("ALSA player: using device %i:%i (%s)\n",
           alsa_card, alsa_device, alsa_card_name);
        
    /* Configure the device for the loaded sound data. */
    memset(&alsa_params,0,sizeof (alsa_params));
    alsa_params.channel = SND_PCM_CHANNEL_PLAYBACK;

    /* Use stream mode. In this mode, we don't have to give ALSA
       complete blocks; we can send it data as we get it. Block
       mode is needed for mmap() functionality. Unlike OSS, ALSA's
       mmap() functionality is quite reliable, and easily accessible
       through library functions.
       We won't use it here, though; there's no need. */
    alsa_params.mode = SND_PCM_MODE_STREAM;
    alsa_params.format.interleave = 1;
    


    /* We'll assume little endian samples. You may wish to use
       the data in the GNU C Library's endian.h to support other
       endiannesses. We're ignoring that case for simplicity. */
    if (bits == 8)
        alsa_params.format.format = SND_PCM_SFMT_U8;
    else if (bits == 16)
        alsa_params.format.format = SND_PCM_SFMT_S16_LE;
    else {
        printf("ALSA player: invalid sample size.\n");
        return -1;
    }
    alsa_params.format.rate = rate;
    alsa_params.format.voices = channels;
    alsa_params.start_mode = SND_PCM_START_DATA;
    alsa_params.stop_mode = SND_PCM_STOP_ROLLOVER;
    alsa_params.buf.block.frag_size = 4096;
    alsa_params.buf.block.frags_min = 1;
    alsa_params.buf.block.frags_max = 2;
        
    if ((i = snd_pcm_plugin_params(alsa_pcm,&alsa_params)) < 0) {
        printf("ALSA player: unable to set parameters.\n");
        snd_pcm_close(alsa_pcm);
        return -1;
    }

    if (snd_pcm_plugin_prepare(alsa_pcm.
                               SND_PCM_CHANNEL_PLAYBACK) < 0) {
        printf("ALSA player: unable to prepare playback.\n");
        snd_pcm_close(alsa_pcm);
        return -1;
    }
        
    /* Feed the sound data to ALSA. */
    position = 0;
    while (position < bytes) {
        int written, blocksize;
        int fd;
                
        if (bytes-position < 4096)
            blocksize = bytes-position;
        else
            blocksize = 4096;

                
        /* Write to the sound device. */
        written = snd_pcm_plugin_write(alsa_pcm,
                                       &samples[position],
                                       blocksize);

        /* If ALSA can't take any more data right now, it'll
           return -EAGAIN.
           If this were sound code for a game, we'd probably
           just contine the game loop and try to write data the
           next time around. In a game, you'd probably also want
           to put the device in nonblocking mode (see the
           snd_pcm_nonblock_mode() function). */
        if (written == -EAGAIN) {
            /* Waste some time.
               This keeps us from using 100% CPU. */
            usleep(1000);

            written = 0;
        } else {
            if (written < 0) {
                perror("\nALSA player: write error");
                snd_pcm_close(alsa_pcm);
                return -1;
            }
        }

        /* Update the position. */
        position += written;

        /* Print some information. */
        WritePlaybackStatus(position, bytes,
                            channels, bits, rate);
        printf("\r");
        fflush(stdout);
    }

    printf("\n");






    /* Wait until ALSA's internal buffers are empty,
       then stop playback.
       This will make sure that the entire sound clip
       has played. */
    snd_pcm_channel_flush(alsa_pcm, SND_PCM_CHANNEL_PLAYBACK);
    snd_pcm_close(alsa_pcm);

    return 0;
}
\end{verbatim}
\end{codelisting}
\index{ALSA!example|)}
\index{Multi-Play!ALSA back end|)}

ALSA is a bit of a change from OSS. A welcome change, to be certain: it presents a library-based interface, not a system of semi-intuitive and inadequately documented \code{ioctl}s. \index{ALSA!organization}ALSA is based on \firstterm{cards} and \firstterm{devices}. A card is simply a piece of sound hardware, and a device is a logical piece of functionality provided by that card (such as PCM sampling or MIDI sequencing). A card generally encompasses multiple devices. Cards and devices are numbered, starting with zero. The first step in writing an ALSA sound player is to locate a suitable card and device. Our program loops through the possible cards, from zero to the limit returned by the \code{snd\_cards} function, attempting to find an available PCM output device on each. If this loop is successful, the \code{alsa\_pcm} variable will end up pointing to an open PCM playback device. We could save some work by jumping directly to the card and device suggested by the \code{snd\_defaults\_pcm\_card} and \code{snd\_defaults\_pcm\_device} functions, but the loop does a better job of illustrating ALSA's organization.

\index{ALSA!playback states}ALSA devices are marked with \firstterm{states}. A PCM playback device can be \firstterm{not ready} (uninitialized), \firstterm{ready} (initialized but not capable of processing sound), \firstterm{prepared} (ready to start immediately upon receiving audio data), \firstterm{running} (actually playing back samples), \firstterm{underrun} (in limbo because samples were not received quickly enough), or \firstterm{paused}. A PCM device moves to the ready state when it receives its initial configuration data, and to the prepared state when \code{snd\_pcm\_playback\_prepare} is called. You must put the ALSA device into the prepared state before you can play samples. This system of states might seem like overkill, but they make ALSA programming much more predictable than OSS programming. Given a particular state, you can always expect a certain set of responses from the sound device. The distinction between prepared and ready allows you to set up an ALSA device to start playback with almost no advance notice. This is hardly an issue for games, but it could be important for other applications.

After opening a PCM output device, our player clears a \code{snd\_pcm\_channel\_params} structure and fills in the relevant pieces of information about our sound data. It is important to zero this memory first; some fields in the structure are considered valid only if a certain bit is set, and we generally don't want to mess with these. Take note of the \code{mode}, \code{start\_mode}, and \code{stop\_mode} fields. \code{mode} should be either \code{SND\_PCM\_MODE\_STREAM} or \code{SND\_PCM\_MODE\_BLOCK}. The block mode can allow for higher performance, but it requires all of our data transfers to be even multiples of the fragment size (4,096 bytes in this case), and it disallows the use of the plugin functions. These instructions won't really be a problem; if we run out of samples, we can just send zeros (for silence), and we can handle format conversions by hand. Block mode also allows you to use \code{snd\_pcm\_mmap}, which is nice but overkill for our example. The start and stop modes tell ALSA when to start playback and how to handle underrun conditions. Our player uses \code{SND\_PCM\_START\_DATA} to instruct ALSA to begin playback as soon as we send the first chunk of data, and it uses \code{SND\_PCM\_STOP\_ROLLOVER} to ask ALSA \emph{not} to stop playback if it runs out of data. (Instead, it will loop over the data it already has until it gets more.) In theory, underruns should never happen unless the application can't keep up with the sound card (which is unlikely). When all of the required information has been set in the structure, our program sends it to ALSA with \code{snd\_pcm\_plugin\_params}.

Finally, our program shifts the sound device into the prepared state with the \code{snd\_pcm\_plugin\_prepare}\footnote{The ``plugin'' functions we've used here provide a few advantages over the normal ALSA functions. In particular, they can handle format conversions when the underlying hardware would normally be incapable of using a particular sample format. This would allow you to, say, use 16-bit samples with an 8-bit sound device. The plugin functions come at the expense of direct buffer access---you can't use ALSA's \code{mmap} functionality with them. Don't be fooled by the name; the plugin functions are part of the ALSA library, and they don't require any extra work on your part.} function and begins writing PCM data. We detect errors from \code{snd\_pcm\_plugin\_write}, and we handle the \code{EAGAIN} condition separately. This error occurs when the device is (probably) not in a state of error, but unable to respond immediately, such as when it's not ready for more data. If this happens, we waste a bit of time with \code{usleep} and try again. If you remove the \code{usleep}, the program will probably eat up a lot of CPU in a tight loop. In a game programming situation you'd probably want to use this time to do useful work (for instance, you might continue the game loop and try the audio write again on another pass).

When our player has finished writing samples to the ALSA device, it calls \code{snd\_pcm\_channel\_flush} to make sure that the output buffer has finished playing, and then it closes the device with the \code{snd\_pcm\_close} function.

ALSA is an enormously powerful audio system, and this simple player only scratches the surface of its capabilities. It offers a lot to game programmers, but unfortunately it's not supported as universally as OSS just yet. With any luck, this API will catch on and we can be free of OSS's lousy interface forever.\footnote{In an ideal world, 4Front Techonologies would throw its full support behind the ALSA project and start writing commercial ALSA drivers instead of OSS drivers. Competition is good, but there's no reason that it can't take place within a single, well-designed API. Alas, this is unlikely to happen, but it would be great for Linux.}
\index{ALSA!using|)}

\section{Sharing the Sound Card with ESD}
OSS and ALSA share a serious weakness: they allow the sound card to be used by only one application at a time. This exclusivity can be quite an annoyance, since many users like to listen to MP3 music or streaming radio while they use the computer, and these compete with other applications for the sound card. Some third-party OSS drivers (such as the Sound Blaster Live driver from Creative) allow multiple applications to share the sound device, but the 4Front Technologies drivers don't generally permit this.

\index{ESD!overview}ESD is a network-enabled real-time sound mixer. Any number of ESD-capable applications can share the computer's sound hardware seamlessly; ESD mixes their sound streams together, just as we mixed several samples together in the SDL sound demo. ESD is a server-based sound toolkit, and a running ESD server can even accept connections from remote machines. The ESD interface is a bit less flexible than the low-level OSS interface (for instance, it doesn't allow direct access to a sound card's DMA buffers), but it provides the basic functionality needed to select sample formats and send samples to the card. ESD also has the ability to cache samples, so that they can be played back with a single command. It does not directly interface with the system's sound hardware but uses whichever low-level sound API happens to be present (OSS or ALSA).

\label{esdlatency}\index{ESD!latency}Unfortunately, ESD has a serious latency problem; delays of more than half a second are common, which is entirely unacceptable for most games. There is no simple way to reduce this latency. ESD uses a fixed buffer (currently 4,096 bytes, specified by the \code{ESD\_BUF\_SIZE} constant), which cannot be changed. Even if the buffer size could be reduced, ESD would still experience high latency due to its internal mixing and network overhead. It is therefore not well suited for gaming; its real use is for playing background music without obstructing the audio device. Nonetheless, several audio toolkits such as OpenAL and the SDL audio subsystem provide optional support for ESD. We discuss a possible workaround for the latency problem after the next example.

The next player plays sound through a local ESD connection. It would be trivial to modify the code to connect to ESD servers on remote machines; the programming interface is the same. If you play around with this code a bit, you'll notice the extreme latency inherent in ESD.

\index{ESD!example|(}
\begin{codelisting}{mp-esd.c}{code:mpesd}
\begin{verbatim}
/* Sound playback with ESD. */

#include <unistd.h>
#include <stdio.h>
#include <esd.h>

/* Plays a sound with the Enlightened Sound Daemon (ESD).
   Returns 0 on successful playback, nonzero on error. */
int PlayerESD(u_int8_t *samples, int bits,
              int channels, int rate, int bytes)
{
    /* ESD data socket. */
    int esd = 0;
        
    int esd_flags;
        
    /* Playback status variables. */
    int position;
        
    /* Select the appropriate ESD flags. */
    switch (channels) {
    case 1: esd_flags = ESD_MONO; break;
    case 2: esd_flags = ESD_STEREO; break;                
    default:
        printf("ESD player: unknown number of channels.\n");
        return -1;
    }
        
    switch (bits) {
        /* ESD sometimes has problems with 8-bit sound. */
    case 8: esd_flags |= ESD_BITS8; break;
    case 16: esd_flags |= ESD_BITS16; break;
    default:
        printf("ESD player: unknown sample size.\n");
        return -1;
    }

    /* Open ESD with the desired parameters. */
    esd = esd_play_stream(esd_flags | ESD_PLAY | ESD_STREAM,
                          rate, NULL ,"PlayerESD");        
                              
    if (esd < 0) {
        printf("ESD player: unable to connect to ESD.\n");
        return -1;
    }
        
    /* Feed the sound data to ESD. */
    position = 0;
    while (position < bytes) {
        int written, blocksize;
                
        /* ESD has a fixed buffer size. */
        if (bytes-position < ESD_BUF_SIZE)
            blocksize = bytes-position;
        else
            blocksize = ESD_BUF_SIZE;
               
 
        /* Write to the sound device. */
        written = write(esd, &samples[position], blocksize);
        if (written == -1) {
            perror("\nESD player: write error");
            close(esd);
            return -1;
        }

        /* Update the position. */
        position += written;

        /* Print some information. */
        WritePlaybackStatus(position, bytes,
                            channels, bits, rate);
        printf("\r");
        fflush(stdout);
    }

    printf("\n");
    close(esd);

    return 0;
}
\end{verbatim}
\end{codelisting}
\index{ESD!example|)}

Having already battled our way through OSS's slightly testy interface, ESD is no surprise. Our modified player opens a connection to the ESD server with \codei{esd\_play\_stream} and then simply feeds samples to ESD through the returned socket. (For more about sockets, see Chapter \ref{ch:gamenetworking}; for now, think of a socket as a simple file handle.) When it is finished playing the sound clip, it closes the socket with \codei{esd\_close}. This is very similar to our first OSS programming example. You will notice that the sound continues playing up to a full second after the program exits, due to ESD's internal buffering. Remember that ESD is a server that your program communicates with through a network socket; the ESD server does not shut down when your program exits.

As we briefly mentioned earlier, ESD allows applications to cache samples in the server itself, so that they can be quickly retrieved and played without actually being written again. This is convenient, since it delegates the task of mixing to ESD and reduces latency considerably. It would be a markedly bad idea to upload hundreds of large samples into the ESD server (since they remain resident in memory until they are deleted), but there should be no problem with uploading a set of reasonably small sound clips for the duration of a game. As useful as this may seem, however, there is a problem: it is not possible to adjust the volume of a sound clip once it has been uploaded. Games frequently vary the volume of sounds to indicate their relative distance from the listener's position, and this is not possible with ESD sample caching. Nonetheless, this limitation may not be an issue for some games, and sample caching may provide a practical way to avoid extreme latency.

\index{ESD!control connection}The ESD library actually supports two types of server connections. We have already seen the first type, which is used for sound playback. Most ESD applications need only this type of connection. The second type provides a channel for controlling the server. After obtaining a control connection handle with \codei{esd\_open\_sound}, an application can lock the ESD daemon (block all other clients from connecting), temporarily shut down ESD's sound playback capabilities (and release the \filename{/dev/dsp} device), or manage the sample cache. Control connections should be closed with \code{esd\_close} when they are no longer needed.

\index{ESD!caching samples}To upload a sample to ESD's cache, establish a control connection and call \codei{esd\_sample\_cache}. This will return an integer cache ID that you can use to reference the sound clip in the future. Upload the complete sample with \code{write}, making sure to check \code{write}'s return values to verify that the entire sample has been uploaded. Finally, call \codei{esd\_confirm\_sample\_cache} with the clip's ID to check that ESD received all of the data. After the sample has been uploaded, you can play it by calling \codei{esd\_sample\_play} with the sample's ID. A program should explicitly delete all of its cached samples before it exits by calling \code{esd\_sample\_free} with each sample ID.

ESD is a useful but somewhat flawed sound interface, and you should support it if you can do so without giving yourself a headache. Many games support ESD with the understanding that its latency will be annoying, but that some users will want to use it anyway.

\begin{apiref}
\indexc{esd\_play\_stream}
\apifunction{esd\_play\_stream(flags, rate, host, progname)}
\apisynopsis{Opens a connection to a local or remote ESD server and prepares to play an audio stream.}
\apireturns{Open file descriptor connected to the ESD server. This might be a pipe or a socket. Returns $<$ 0 on failure.}
\apifirstparm{flags}{ESD playback flags. See Listing \ref{code:mpesd} for a typical set of flags.}
\apinextparm{rate}{Playback sampling rate.}
\apinextparm{host}{Hostname of a remote ESD server, or \code{NULL} to connect to a local server.}
\apinextparm{progname}{Name of this program (to appear in ESD's client list).}
\apiendparms
\end{apiref}

\begin{apiref}
\indexc{esd\_close}
\apifunction{esd\_close(fd)}
\apisynopsis{Closes an ESD connection.}
\apifirstparm{fd}{File descriptor to close.}
\apiendparms
\end{apiref}

\section{Building Multi-Play}
You've seen how Multi-Play handles its output; now it's time to flesh out the application. Multi-Play is simple. It comes with almost no frills other than the ability to play sound in five different ways. Without further ado, here is the main Multi-Play code:

\index{Multi-Play!main file|(}
\begin{codelisting}{multi-play.c}{code:multiplay}
\begin{verbatim}
/* Multi-Play's main file. */

/* Selectively enable compilation of parts of the player. */
#ifndef DISABLE_OSS
#define ENABLE_OSS
#endif

#ifndef DISABLE_ESD
#define ENABLE_ESD
#endif

#ifndef DISABLE_ALSA
#define ENABLE_ALSA
#endif

#include <stdio.h>
#include <sndfile.h>
#include <endian.h>

/* sys/types.h provides convenient typedefs, such as int8_t. */
#include <sys/types.h>

/* ESD header. */
#ifdef ENABLE_ESD
#include <esd.h>
#endif

/* ALSA header. */
#ifdef ENABLE_ALSA
#include <sys/asoundlib.h>
#endif

/* Prototypes. */
void WritePlaybackStatus(int position, int total, int channels,
                         int bits, int rate);
int LoadSoundFile(char *filename, int *rate, int *channels,
                  int *bits, u_int8_t **buf, int *buflen);                  

int PlayerOSS(u_int8_t *samples, int bits,
              int channels, int rate, int bytes);
int PlayerOSS2(u_int8_t *samples, int bits,
               int channels, int rate, int bytes);
int PlayerDMA(u_int8_t *samples, int bits,
              int channels, int rate, int bytes);
int PlayerESD(u_int8_t *samples, int bits,
              int channels, int rate, int bytes);
int PlayerALSA(u_int8_t *samples, int bits,
               int channels, int rate, int bytes);

/* The loader code is in a separate file for book organization. */
#include "mp-loadsound.c"


/* Optionally include the OSS player code. */
#ifdef ENABLE_OSS
# include "mp-oss.c"
# include "mp-oss2.c"
# include "mp-dma.c"
#endif

/* Optionally include the ESD player code. */
#ifdef ENABLE_ESD
# include "mp-esd.c"
#endif

/* Optionally include the ALSA player code. */
#ifdef ENABLE_ALSA
# include "mp-alsa.c"
#endif

/* Writes a playback status line, with no \n. This is purely for
   aesthetic value. */
void WritePlaybackStatus(int position, int total,
                         int channels, int bits, int rate)
{
    int i;
        
    printf("[");
    for (i = 0; i < (10*position/total); i++)
        printf("-");
    printf("|");
    for (; i < 10; i++)
        printf("-");
    printf("] %3i%% ", (100*position/total));
    printf("%2i-bit %s @ %i KHz",
           bits,
           (channels == 1 ? "mono  " : "stereo"),
           rate/1000);
}

/* Prints a summary of command-line usage. */
void usage(char *progname)
{
    printf("Usage: %s player filenames\n", progname);
    printf("  Available players are:\n");
#ifdef ENABLE_OSS
    printf("  --oss     Normal OSS output, somewhat latent.\n");
    printf("  --oss2    Normal OSS output, less latency.\n");
    printf("  --dma     Direct DMA access with OSS.\n");
#endif
#ifdef ENABLE_ESD
    printf("  --esd     Output to ESD.\n");
#endif
#ifdef ENABLE_ALSA
    printf("  --alsa    Normal ALSA output.\n");
#endif
}

/* The main player program. */
int main(int argc, char *argv[])
{
    char *filename;
    int arg;
    enum { OSS, OSS2, OSSDMA, ESD, ALSA } player;

    /* Variables for the loaded sound. */
    u_int8_t *samples = NULL;
    int sample_size;
    int sample_rate;
    int sample_bytes;
    int num_channels;        
        
    if (argc < 3) {
        usage(argv[0]);
        return 1;
    }

    /* Decide which player to use. */
    if (0) { }
#ifdef ENABLE_OSS
    else if (!strcmp(argv[1],"--oss"))
        player = OSS;
    else if (!strcmp(argv[1],"--oss2"))
        player = OSS2;
    else if (!strcmp(argv[1],"--dma"))
        player = OSSDMA;
#endif
#ifdef ENABLE_ESD
    else if (!strcmp(argv[1],"--esd"))
        player = ESD;
#endif
#ifdef ENABLE_ALSA
    else if (!strcmp(argv[1],"--alsa"))
        player = ALSA;
#endif                
    else {
        usage(argv[0]);
        return 1;
    }
        
    /* Treat the rest of the command line as filenames to play. */
    for (arg = 2; arg < argc; arg++) {
        filename = argv[arg];
        
        /* Load the sound data. */
        if (LoadSoundFile(filename, &sample_rate, &num_channels,
                          &sample_size, &samples,
                          &sample_bytes) != 0) {
            printf("Skipping '%s'.\n", filename);
            continue;
        }
                
        switch (player) {
#ifdef ENABLE_OSS
        case OSS:
            if (PlayerOSS(samples, sample_size, num_channels,
                          sample_rate, sample_bytes) != 0)
                printf("Sound playback with OSS failed.\n");
            break;
                        
        case OSS2:
            if (PlayerOSS2(samples, sample_size, num_channels,
                           sample_rate, sample_bytes) != 0)
                printf("Sound playback with OSS2 failed.\n");
            break;
                        
        case OSSDMA:
            if (PlayerDMA(samples, sample_size, num_channels,
                          sample_rate, sample_bytes) != 0)
                printf("Sound playback with DMA failed.\n");
            break;
#endif

#ifdef ENABLE_ESD                        
        case ESD:
            if (PlayerESD(samples, sample_size, num_channels,
                          sample_rate, sample_bytes) != 0)
                printf("Sound playback with ESD failed.\n");
            break;
#endif
#ifdef ENABLE_ALSA                
        case ALSA:
            if (PlayerALSA(samples, sample_size, num_channels,
                           sample_rate, sample_bytes) != 0)
                printf("Sound playback with ALSA failed.\n");
            break;
#endif
        default:
            printf("Bug!\n");
            return 1;
        }
                
        free(samples);
    }
        
    return 0;
        
}
\end{verbatim}
\end{codelisting}
\index{Multi-Play!main file|)}

The main file uses the fairly common technique of selective compilation to allow the various back ends to be individually disabled. This is useful, for instance, if you want to build the player on a system with OSS but not ALSA or ESD. In this case, you would compile the player with \cmdline{-DDISABLE\_ALSA -DDISABLE\_ESD}. (The \cmdline{-D} option, as you may recall, adds the equivalent of a preprocessor \code{\#define} to the top of your program.) The player back ends are simply added to the main file with \code{\#include}. This is an acceptable way to assemble a small project, but a larger project would more appropriately use separate header and object files for each module.

\index{Multi-Play!command-line options}Multi-Play requires at least two command-line options: the name of a player back end and a list of filenames to play. The player back ends are \cmdline{--oss} (the simple OSS player), \cmdline{--oss2} (the low-latency version of the OSS player), \cmdline{--dma} (the DMA-enabled player), \cmdline{--alsa} (the ALSA player), and \cmdline{--esd} (the ESD player). The rest of the command-line arguments are treated as filenames and are played in sequence.

\index{Multi-Play!compiling}Compiling Multi-Play is trivial, but it requires several libraries to link properly. In addition to the \filename{libsndfile.so} shared library, Multi-Play needs libraries for ALSA and ESD (if it is compiled with support for these players). The following command builds Multi-Play on my particular configuration (Linux, with working installations of ALSA and ESD):

\begin{shell}
\begin{verbatim}
$ gcc multi-play.c -o multi-play -lasound -lesd -lsndfile
\end{verbatim}
\end{shell}

% $

Or, to build without ALSA support,

\begin{shell}
\begin{verbatim}
$ gcc -DDISABLE_ALSA multi-play.c -o multi-play -lesd -lsndfile
\end{verbatim}
\end{shell}

% $

Since these files can be in various places on different Linux configurations, it might be worthwhile to set up a GNU Autoconf script for this program. Chapter \ref{ch:gameinstall} talks about the capabilities of Autoconf but leaves a full discussion to other sources.

Once you've compiled Multi-Play, give it a spin. It can handle just about any common (uncompressed) audio file format. Multi-Play's main limitation is that it cannot stream audio data directly from disk, so the entire sound file must be loaded into memory. Streaming might make for an interesting project. We won't discuss Multi-Play further here, since our main coding project in this book is \gametitle{Penguin Warrior}.
\index{Multi-Play|)}

\section{Environmental Audio with OpenAL}
\index{OpenAL|(}
\index{OpenAL!overview|(}
OpenAL (AL for short) is a portable environmental audio library. In addition to interacting with the sound card (most likely through another audio API, such as OSS), it provides the ability to simulate real-world physics on audio, including the \index{Doppler effect}Doppler shift (a change in a sound's apparent frequency due to relative motion) and attenuation (a loss in intensity over distance). These effects can add a great deal of depth and realism to game environments. OpenAL is designed to be hardware accelerated on multiple platforms by multiple vendors, and as such it is a completely open standard (under the control of a review board very similar to that of OpenGL). OpenAL is free to be downloaded, modified, and used in any type of application, subject to the terms of the GNU LGPL. Although OpenAL is still evolving rapidly, it is usable right now on Linux, Windows, and several other platforms.

Not every application needs environmental audio, and sometimes it's a better idea to stick with SDL's audio system, OSS, or ALSA. For instance, it would probably be silly to use OpenAL for a sound file player or a recording program. However, OpenAL is flexible enough to handle just about any environmental audio situation as well as basic items like background music, so it's well suited as a general-purpose audio library for games. Later in this chapter we'll use OpenAL to add environmental audio and music support to \gametitle{Penguin Warrior}. First, let's talk out about the basic terminology and philosophy of OpenAL.
\index{OpenAL!overview|)}

\subsection{OpenAL Basics}
\longpage
OpenAL is an audio rendering library (as opposed to a simple buffer playback system like OSS). It plays sound as it would be heard from a certain point, known as the \index{OpenAL!listener}\firstterm{listener}, in a 3D world. Sounds come from points in space called \index{OpenAL!sources}\firstterm{sources}, each of which can be stationary or moving. Each source is attached to a \index{OpenAL!buffers}\firstterm{buffer}, a chunk of raw PCM sound data that describes what the source sounds like when the listener is right on top of it. Multiple sources can share the same buffer (just as multiple brick walls can use the same texture in a game such as \gametitle{Quake}), but it's not possible to assign multiple buffers to the same source.\footnote{This makes sense---a source is supposed to represent a particular noise coming from a certain point in space. If you want multiple sounds coming from the same place, put multiple sources at that position.} Sources, buffers, and the listener are all considered \index{OpenAL!objects}\firstterm{objects}, and they're all easy to work with after you know which properties (position, velocity, and so forth) are relevant to each type.

\extfiguren{figures/openal}{An OpenAL world}

\index{OpenAL!objects!properties}Object properties are the key to getting along with OpenAL. Rather than providing separate functions to set each possible property of a given object, OpenAL defines symbolic names for each property an object can have and supplies a few generic functions for accessing them by name. Instead of functions like \code{alSetSourcePosition} and \code{alSetSourceOrientation}, for instance, OpenAL provides a single \code{alSourcefv} function for modifying the vector properties of source objects. \code{alSourcefv(obj, AL\_POSITION, pos)} would set the position of the source object \code{obj} to the vector in \code{pos}. (A vector in this case is just an array of three \code{ALfloat} values.) The OpenAL specification lists all of the possible properties each type of object can have, and you can find some of the more important ones in the \gametitle{Penguin Warrior} code later in this chapter.

\pagebreak
\begin{note}{This Looks Familiar\ldots}
\index{OpenAL!design philosophy|(}
If you think OpenAL's design is a cheap knockoff of the OpenGL 3D graphics library, you're right! OpenGL is an amazingly clean and well-designed API with a wide following, and OpenAL's designers thought they'd do well to follow its style. This makes a lot of sense, especially since OpenAL is also used to provide audio support in OpenGL applications. OpenGL-oriented data structures tend to carry over to OpenAL without much trouble.

\index{OpenAL!naming convention}In particular, OpenAL uses OpenGL's peculiar function naming scheme; for instance, a function call that sets the \code{AL\_BAR} property of a Foo-type object to the first few entries of an array of floats would look something like \code{alFoofv(foo\_id, AL\_BAR, position)}. In this case \code{foo\_id} is the ``name'' (integer identifier) of the object to be modified, \code{AL\_BAR} is the property of the object to modify, and \code{fv} signifies that the function deals with a vector of floats. OpenAL function names are always prefixed with \code{al} or \code{AL}.
\index{OpenAL!design philosophy|)}
\end{note}

Once you understand the basics, OpenAL is even simpler to use than OSS. First you need a \index{OpenAL!devices}\firstterm{device} and a \index{OpenAL!contexts}\firstterm{context}. A device represents an initialized sound card (with possible hardware acceleration features), and a context is piece of data that represents OpenAL's current state. Each context represents one listener and multiple sources, and all commands dealing with these objects affect the current context. Since nearly everything relevant to OpenAL is stored in the current context, it's possible to maintain several contexts and swap them out as you please. (This probably won't be useful in most cases, however.) Only one context can be current at a time.

\index{OpenAL!devices!opening}The \codei{alcOpenDevice} function opens an audio device and prepares it for OpenAL's use. It typically does this by looking for a supported audio interface (OSS, ALSA, or ESD) and performing whatever initialization steps the interface requires. This function takes a single argument, a device specifier. In most cases you can set this to \code{NULL}; it's useful only if you want to request a particular output device (and doing so is a quick way to kill portability). \codei{alcOpenDevice} returns \code{NULL} if it can't find a usable device and returns a pointer to a device structure if all goes well. If you can't open a device, don't bother with any more OpenAL calls; nearly everything requires a valid context, and a context requires a valid audio device.

Once you've obtained a usable device, you should create a context. \codei{alcCreateContext} creates an OpenAL context and associates it with a particular audio device. It takes two parameters: an open device and a list of attributes. The attributes let you request a particular sampling frequency or refresh rate; in most cases, \code{NULL} is a sufficient answer. Even with a valid audio device, \code{alcCreateContext} could fail, so be sure to check for errors. Once you have an OpenAL context, you're in business. It's a good idea to explicitly set your new context as current with \codei{alcMakeContextCurrent}. (It's possible, but not common, to use multiple contexts within a single application; in this case, only one will be current at any given time, and you need to switch between them manually.)

With a context in place, you can add sources and buffers, configure the listener, and start playback. Once OpenAL is in motion, you can add, remove, or modify objects at any time; whatever changes you make will affect the outgoing audio stream almost immediately. OpenAL runs continuously in the background and requires no attention unless you decide to change something in its 3D world. Sources and the listener are specific to a particular context, and changes you make to these types of objects won't affect other contexts. Buffers don't belong to any particular OpenAL context, but you need to have a context before you can create them (since OpenAL uses the current context for error reporting). It follows that you need to destroy all of your buffers before you delete the last context. You should be wary of doing anything with OpenAL without a valid context.

Now that you know a bit about the API and its capabilities, let's put OpenAL to work as a sound engine for \gametitle{Penguin Warrior}.

\begin{apiref}
\indexc{alcOpenDevice}
\apifunction{alcOpenDevice(device)}
\apisynopsis{Opens an audio device suitable for OpenAL output.}
\apireturns{Pointer to an \code{ALCdevice} structure on success, \code{NULL} on failure. On failure, you can retrieve error information with \code{alcGetError}.}
\apifirstparm{device}{Platform-dependent device specifier. This should be \code{NULL} unless you have a good reason to use something else.}
\apiendparms
\end{apiref}

\begin{apiref}
\indexc{alcCloseDevice}
\apifunction{alcCloseDevice(device)}
\apisynopsis{Closes a device opened by a previous call to \code{alcOpenDevice}. Never close a device that is currently in use; destroy any context that is using it first.}
\apifirstparm{device}{Pointer to the \code{ALCdevice} to close.}
\apiendparms
\end{apiref}

\begin{apiref}
\indexc{alcCreateContext}
\apifunction{alcCreateContext(device, params)}
\apisynopsis{Creates an OpenAL context.}
\apireturns{A valid OpenAL context (as an \code{ALvoid} pointer) on success, \code{NULL} on failure. On failure, you can retrieve error information with \code{alcGetError}.}
\apifirstparm{device}{Pointer to a valid \code{ALCdevice}.}
\apinextparm{params}{Pointer to an array of configuration flags, as described in the OpenAL specification. \code{NULL} is usually sufficient. (OpenAL will pick the best sampling rate and format it can find, so there's little need to interfere.)}
\apiendparms
\end{apiref}

\begin{apiref}
\indexc{alcMakeContextCurrent}
\apifunction{alcMakeContextCurrent(context)}
\apisynopsis{Makes a context current. Only one context can be current at a time, and you must set a current context before you make \emph{any} non-\code{alc} OpenAL calls. This is not likely to fail, but you can check for errors with \code{alcGetError() != ALC\_NO\_ERROR}.}
\apifirstparm{context}{Pointer to the context to make current.}
\apiendparms
\end{apiref}

\begin{apiref}
\indexc{alcDestroyContext}
\apifunction{alcDestroyContext(context)}
\apisynopsis{Destroys an OpenAL context. It's a good idea to do this before your program exits. Never destroy the current context; call \code{alcMakeContextCurrent(NULL)} first.}
\apifirstparm{context}{Pointer to the context to destroy.}
\apiendparms
\end{apiref}

\begin{apiref}
\indexc{alGenSources}
\index{OpenAL!sources!creating}
\apifunction{alGenSources(count, buffer)}
\apisynopsis{Creates \code{count} sources in the current OpenAL context and stores their names (integer IDs) in \code{buffer}. This is unlikely to fail, but you can test for errors with \code{alGetError() != AL\_NO\_ERROR}. It's not necessary for a program to clean up its sources before it exits (destroying the context does that), but you can do this with the \code{alDeleteSources} function (which takes identical parameters).}
\apifirstparm{count}{Number of sources to generate.}
\apinextparm{buffer}{Pointer to an \code{ALuint} buffer big enough to hold the generated source names.}
\apiendparms
\end{apiref}

\begin{apiref}
\indexc{alGenBuffers}
\index{OpenAL!buffers!creating}
\apifunction{alGenBuffers(count, buffer)}
\apisynopsis{Generates \code{count} buffers. Buffers are not tied to any particular OpenAL context, but \code{alGenBuffers} must be an active context for error-handling purposes. This is not likely to fail, but as usual you can test for errors with the \code{alGetError} function. You can delete buffers with the \code{alDeleteBuffers} function (which takes identical parameters).}
\apifirstparm{count}{Number of buffers to generate.}
\apinextparm{buffer}{Pointer to an \code{ALuint} buffer big enough to hold the generated buffer names.}
\apiendparms
\end{apiref}

\begin{apiref}
\indexc{alSourcePlay}
\index{OpenAL!sources!starting playback}
\apifunction{alSourcePlay(sourceid)}
\apisynopsis{Starts playback on a source. Uses the buffer assigned to the source with the \code{AL\_BUFFER} property. If the \code{AL\_LOOPING} attribute of the source is nonzero, playback will never stop; otherwise it will stop when the buffer has been played once.}
\apifirstparm{sourceid}{Name of the source in the current context to play.}
\apiendparms
\end{apiref}

\begin{apiref}
\indexc{alSourceStop}
\index{OpenAL!sources!stopping}
\apifunction{alSourceStop(sourceid)}
\apisynopsis{Stops playback on a source immediately.}
\apifirstparm{sourceid}{Name of the source in the current context to stop.}
\apiendparms
\end{apiref}

\begin{apiref}
\indexc{alBufferData}
\index{OpenAL!buffers!loading}
\apifunction{alBufferData(bufferid, format, data, size, freq)}
\apisynopsis{Sets a buffer's PCM sample data. This is akin to sending texture data to OpenGL.}
\apifirstparm{bufferid}{Name of the buffer to modify.}
\apinextparm{format}{Format of the sample data. Valid formats are \code{AL\_FORMAT\_MONO8}, \code{AL\_FORMAT\_MONO16}, \code{AL\_FORMAT\_STEREO8}, and \code{AL\_FORMAT\_STEREO16}. OpenAL converts between formats as necessary; there's no need to supply data to it in a particular format.}
\apinextparm{data}{\code{ALvoid} pointer to the raw sample data. OpenAL copies this data into its own buffer; you can free this pointer immediately after the \code{alBufferData} call.}
\apinextparm{size}{Size of the sample data, in bytes.}
\apiendparms
\end{apiref}

\subsection{Adding Environmental Audio to Penguin Warrior}
\shortpage\shortpage
How can \gametitle{Penguin Warrior} take advantage of environmental audio? Well, right now it's pretty hard to navigate in the game world. Locating the opponent ship can be quite an annoyance, since there's currently no radar or direction pointer. The opponent could be anywhere, and the only way to find him, her, or it is to fly around randomly until you make visual contact. We can make the game a lot more interesting by using OpenAL to simulate the opponent's engine noise and weapon sounds.\footnote{Sound doesn't travel in space, but neither do highly maneuverable ships with curved wings and laser cannons. At least not yet.} To do this, of course, we'd place the listener at the player's position and orientation in the world, place a source on top of the opponent ship, and attach this source to a buffer that sounds something like an engine. To simulate weapon sounds, we would create another source on top of the opponent and select appropriate buffers for the weapon. Simple? Yes. Effective? OpenAL does an amazing job.\footnote{OpenAL's filtering and output are reasonably solid, but some of its effects can produce strange results on low-end sound hardware. \gametitle{Penguin Warrior}'s environmental audio works quite well on my primary computer (which has an Ensoniq ES1373 card), but Doppler-shifted audio sounds awful on my laptop (which has an integrated Yamaha sound chip and tiny speakers). OpenAL can add a lot to a game, but some players might appreciate an option for disabling advanced environmental effects.}

\begin{note}{Source Files}
We add the source files \filename{audio.c}, \filename{audio.h}, \filename{music.c}, and \filename{music.h} to \gametitle{Penguin Warrior} in this chapter. In addition, we now need to link the game against libsndfile, libopenal, and libvorbis (\cmdline{-lsndfile -lopenal -lvorbis}). To compile this chapter's version of \gametitle{Penguin Warrior}, you'll need a recent copy of OpenAL from \ulink{http://www.openal.org}, as well as the Vorbis audio compression library from \ulink{http://www.vorbis.com}. We'll discuss the Vorbis code later in this chapter; for now, we'll concentrate on the OpenAL side of things.

In addition to the main work in \filename{audio.c}, we'll make a few simple modifications to \filename{main.c} and \filename{resources.c}. These should be easy to spot and simple to understand.

You can find this chapter's \gametitle{Penguin Warrior} code in the \filename{pw-ch5/} subdirectory of the source archive.
\end{note}

\newpage
\begin{codelisting}{audio.c}{code:pwaudio.c}
\begin{verbatim}
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "audio.h"
#include "resources.h"

/* Include the OpenAL headers. */
#include <AL/al.h>
#include <AL/alc.h>

/* Factor to control attenuation of audio.
   We'll divide all coordinates by this factor each time we update
   the source positions. OpenAL does provide a cleaner way to do
   this, but it changed recently. */
#define DISTANCE_FACTOR 50.0

/* We'll set this flag to 1 after audio has been
   successfully initialized. */
int audio_enabled = 0;

/* Our OpenAL context. This is just like an OpenGL context,
   if you're familiar with GL's workings. A context represents
   a combination of a particular output device, a sampling
   frequency, and so on. */
ALvoid *audio_context = NULL;

/* An output device. We'll set this to AL's default
   in InitAudio().  */
ALCdevice *audio_device = NULL;

/* Our sources. Sources are objects in 3D space that emit sound.
   We're ignoring the fact that there's no sound in space. */
static ALuint opponent_engine_source;
static ALuint opponent_phaser_source;
/* There is no player engine source; see note
   in StartAudio below. */
static ALuint player_phaser_source;

void InitAudio()
{
    int err;

    /* Create a context with whatever settings are available.
       We could replace NULL with a list of parameters.

       We use alcGetError instead of alGetError for error detection.
       This is because error conditions are stored within contexts,
       and it's pretty meaningless to retrieve an error code from
       something that does not yet exist. */
    audio_device = alcOpenDevice(NULL);
    if (audio_device == NULL)
        fprintf(stderr, "Warning: NULL device.\n");
    else
        fprintf(stderr, "Got a device.\n");
    audio_context = alcCreateContext(audio_device, NULL);

    err = alcGetError();
    if (err != ALC_NO_ERROR || audio_context == NULL) {
        fprintf(stderr, "Unable to create an OpenAL context (%s).\n",
                alGetString(err));
        return;
    }

    /* Make sure we have a chance to clean up. */
    atexit(CleanupAudio);

    /* Now make the context current. The current context is the
       subject of all OpenAL API calls. Some calls will even
       segfault if there isn't a valid current context. */
    alcMakeContextCurrent(audio_context);
    if (alcGetError() != ALC_NO_ERROR) {
        fprintf(stderr, "Unable to make OpenAL context current.\n");
        goto error_cleanup;
    }

    /* Good. Now create some sources (things that make noise).
       These will be assigned buffers later. Sources don't do
       anything until you associate them with buffers (which
       contain PCM sound data). */
    alGenSources(1, &opponent_engine_source);
    alGenSources(1, &opponent_phaser_source);
    alGenSources(1, &player_phaser_source);
    if (alGetError() != AL_NO_ERROR) {
        fprintf(stderr, "Unable to allocate audio sources.\n");
        goto error_cleanup;
    }

    /* Ready to go. */
    audio_enabled = 1;
    printf("Audio enabled. OpenAL information:\n");
    printf("  Version:    %s\n", alGetString(AL_VERSION));
    printf("  Renderer:   %s\n", alGetString(AL_RENDERER));
    printf("  Vendor:     %s\n", alGetString(AL_VENDOR));
    printf("  Extensions: %s\n", alGetString(AL_EXTENSIONS));

    return;

    /* Invoked on error. Cleans up the context. */
 error_cleanup:
    alcMakeContextCurrent(NULL);
    alcDestroyContext(audio_context);
}

void CleanupAudio()
{

    /* If OpenAL is initialized, clean up. */
    if (audio_enabled) {
        /* Never try to destroy an active context. */
        alcMakeContextCurrent(NULL);

        alcDestroyContext(audio_context);
        alcCloseDevice(audio_device);
        audio_context = NULL;

        audio_enabled = 0;
    }

}

void UpdateAudio(player_p player, player_p opponent)
{
    ALfloat position[3];
    ALfloat velocity[3];
    ALfloat orientation[6];

    /* Is audio enabled? */
    if (!audio_enabled)
        return;

    /* The player is the listener. Set the listener's position to
       the player's position. */
    position[0] = (ALfloat)player->world_x / DISTANCE_FACTOR;
    position[1] = (ALfloat)player->world_y / DISTANCE_FACTOR;
    position[2] = (ALfloat)0.0;
    alListenerfv(AL_POSITION, position);

    /* Set the player's orientation in space. The first three
       values are the "up" vector (sticking out of the ship's
       cockpit), and the next three are the "at" vector (stickout
       out of the ship's nose). */
    orientation[0] = 0;
    orientation[1] = 0;
    orientation[2] = 1.0;
    orientation[3] = cos(player->angle*PI/180.0);
    orientation[4] = -sin(player->angle*PI/180.0);
    orientation[5] = 0;
    alListenerfv(AL_ORIENTATION, orientation);

    /* To properly simulate the Doppler effect, OpenAL needs to
       know the listener's velocity (as a vector). */
    velocity[0] = (ALfloat)player->velocity *
        cos(player->angle*PI/180.0) / DISTANCE_FACTOR;
    velocity[1] = (ALfloat)player->velocity *
        -sin(player->angle*PI/180.0) / DISTANCE_FACTOR;
    velocity[2] = (ALfloat)0.0;
    alListenerfv(AL_VELOCITY, velocity);

    /* The player's weapon is obviously at the location of the
       player. This source won't do anything until we add
       weapons to the game. */
    alSourcefv(player_phaser_source, AL_POSITION, position);
    alSourcefv(player_phaser_source, AL_VELOCITY, velocity);

    /* Now for the enemy's information. */
    position[0] = (ALfloat)opponent->world_x / DISTANCE_FACTOR;
    position[1] = (ALfloat)opponent->world_y / DISTANCE_FACTOR;
    position[2] = (ALfloat)0.0;
    alSourcefv(opponent_engine_source, AL_POSITION, position);
    alSourcefv(opponent_phaser_source, AL_POSITION, position);

    velocity[0] = (ALfloat)opponent->velocity *
        cos(opponent->angle*PI/180.0) / DISTANCE_FACTOR;
    velocity[1] = (ALfloat)opponent->velocity *
        -sin(opponent->angle*PI/180.0) / DISTANCE_FACTOR;
    velocity[2] = (ALfloat)0.0;
    alSourcefv(opponent_engine_source, AL_VELOCITY, velocity);
    alSourcefv(opponent_phaser_source, AL_VELOCITY, velocity);

}

void StartAudio()
{
    /* Activate the opponent's engine noise. We won't attach an
       engine noise to the player, because quite frankly it would
       be annoying, though perhaps a bit more realistic. */
    if (audio_enabled) {
        alSourcei(opponent_engine_source, AL_BUFFER,
                  engine_sound.name); /* assign a buffer */
        alSourcei(opponent_engine_source, AL_LOOPING,
                  1);   /* enable looping */
        alSourcePlay(opponent_engine_source);
                        /* set it to playback mode */
    }
}

void StopAudio()
{
    /* Stop all sources. */
    if (audio_enabled) {
        alSourceStop(opponent_engine_source);
        alSourceStop(opponent_phaser_source);
        alSourceStop(player_phaser_source);
    }
}
\end{verbatim}
\end{codelisting}

\gametitle{Penguin Warrior}'s audio interface is straightforward. \filename{main.c} calls \code{InitAudio} during startup and \code{CleanupAudio} at exit. During each frame of animation, the game loop calls \code{UpdateAudio} with pointers to the current player data structures to update the positions and velocities of the audio sources. \code{StartAudio} actually starts playback (by setting the relevant sources to playback mode with \code{alSourcePlay}), and \code{StopAudio} stops playback on all sources. We'll need to add more to this interface in Chapter \ref{ch:finishingpw} so that we can trigger weapon and explosion sound effects, but this is sufficient for now.

\code{InitAudio} does most of the OpenAL setup work. It opens a device with \code{alcOpenDevice}, creates a context with \code{alcCreateContext}, and makes the context current with \code{alcMakeContextCurrent}. It then uses \code{alcGenSources} to create sources for all of the sound-emitting objects in the game (the opponent's engine and weapons for both players). Sources and buffers are always represented by integer handles (type \code{ALuint} or just \code{unsigned int}). The actual source, listener, and buffer data structures are of no consequence to us; they're hidden inside OpenAL, and we access them by passing their handles to the various OpenAL object functions. Finally, \code{InitAudio} sets a few environmental parameters (relative distances for the Doppler effect and distance attenuation), prints a bit of information about the OpenAL library, and returns successfully.

\code{UpdateAudio} keeps OpenAL informed about the state of the game world. It uses \code{alListenerfv} to set the listener to the position and direction of the player's ship, and it uses \code{alSourcefv} to position each source. These functions expect their information as vectors: a three-element 3D $<x,y,z>$ vector for position and velocity and a six-element $<ux,uy,uz>$ $<fx,fy,fz>$ pair of 3D vectors for orientation (indicating the ``up'' and ``forward'' vectors of the object in question). Since the game takes place in a two-dimensional plane, the $z$ coordinates of these vectors will always be constant. (See Figure \ref{fig:pw3d} for an idea of what the game looks like if we bring it into 3D space with a constant $z$ coordinate.)

\extfigure{figures/pw-3d}{fig:pw3d}{\gametitle{Penguin Warrior}, rendered in 3D space}

\longpage
\code{CleanupAudio} is charged with shutting down OpenAL safely. This isn't too difficult; it deactivates and destroys the current context, closes the audio device, and marks audio as disabled. As with any multimedia toolkit, it's a good idea to close OpenAL cleanly when you're finished with it. It's \emph{probably} safe to leave an OpenAL context hanging when your program exits, but doing so could be messy if your OpenAL library happens to be using the sound card's hardware acceleration features.

Finally, \filename{resources.c} has some new sound file loading code. The \code{LoadSoundFile} function (not shown here) is adapted from Multi-Play's libsndfile-based loader code. Instead of returning a buffer of loaded samples, \code{LoadSoundFile} creates a new OpenAL buffer and copies the sample data into it. The code should be self-explanatory; buffers work like sources for the most part, since they are also OpenAL objects. There's one important stipulation when calling \code{LoadSoundFile}, though: since it uses the OpenAL API, it's important to make sure that OpenAL is initialized first. This means that we need to call \code{InitAudio} before \code{LoadResources} at startup.

\emph{Voila!} \gametitle{Penguin Warrior} now has environmental audio. Give it a try. You should be able to locate the opponent without even looking at the screen, especially if your sound card supports surround sound. Now for some music.

\index{OpenAL|)}

\section{Implementing Game Music with Ogg Vorbis}
\index{Ogg Vorbis|(}
Unless you've been living under a rock for the past few years, you've probably heard of MP3\index{MP3}. Since high-quality PCM audio data can take up an enormous amount of space (over a megabyte every 10 seconds, in some cases), raw PCM samples are not an ideal way to store music and other long audio clips. A typical music album is likely to occupy several hundred megabytes. This is not a problem when music is distributed on physical media---ordinary compact discs can hold around 650 megabytes---but it is an impractical way to download music over the Internet. The MP3 compression system offers a solution to this problem by compressing audio data (sometimes by as much as 90\%) while preserving most of the audio's original quality.\footnote{\index{MP3!quality loss}MP3 compression does trash some audio frequency ranges, and it is certainly possible to hear a difference in a side-by-side comparison of CD and MP3 music. However, this is usually not a severe problem. MP3 compression is not all-or-nothing; music can be compressed only slightly (with very little loss in quality) or can be heavily compressed (reducing the audio to telephone quality or worse).} MP3 is an open standard (meaning that anyone can learn how it works), but it is encumbered by patents\index{MP3!patent issues} (meaning that anyone who writes MP3 encoding software without purchasing a license is likely to be sued). Needless to say, this has been the cause of great consternation among some segments of the (generally antipatent) online community. Although the free SMPEG\index{SMPEG library} library can handle MP3 audio (and has been used for that purpose in at least one commercial title), there is a better option.

The Xiphophorous Company\index{Xiphophorus}, an oddly named team of smart hackers previously known for the popular CD Paranoia\index{CD Paranoia} program, has created an alternative to MP3 called \firstterm{Ogg Vorbis}. The Vorbis codec (\emph{co}der/\emph{dec}oder)\index{codec} offers audio compression very similar to MP3 (slightly better in some cases), free of patents\index{Ogg Vorbis!patent issues!lack thereof} and available to everyone. Ogg, the streaming media infrastructure, provides support for multiple Vorbis streams within a single bitstream or file and provides robustness against many types of corruption. The Ogg Vorbis team has created a libvorbisfile\index{libvorbisfile} library to allow programmers to easily add Ogg Vorbis support to applications, and they have created a set of utilities for working with Ogg Vorbis (\filename{.ogg}) files from the command line. The Vorbis code is still in development, but the bitstream format is finalized, and future releases will be backward compatible. (That is, your Vorbis-enabled software will be able to process all future versions of the Vorbis bitstream specification.)

To my knowledge, there are no real drawbacks to using Ogg Vorbis for high-quality game music, except perhaps that the decoding\index{Ogg Vorbis!CPU usage} takes a significant amount of CPU horsepower (especially on antiquated, register-poor CPU architectures like the x86). MP3 generally provides better performance than Vorbis at low data rates (significant in the quality/size trade-off) and on low-end processors, but these are both improving in Vorbis's favor. Modern Linux boxes should have no trouble decoding high-quality Vorbis streams in the background.

More information on the Ogg Vorbis project is available on the Web at \ulink{http://www.xiph.org/ogg/vorbis/index.html} or in \#vorbis on irc.openprojects.net.

\subsection{Working with Vorbis Files}
\index{Ogg Vorbis!supporting|(}
Although SDL supports Ogg Vorbis music (through the external SDL\_mixer\index{SDL\_mixer} library), this doesn't help much if you're using OSS or OpenAL. Fortunately, the Vorbis API is straightforward, and a complete Vorbis client can be written in fewer than 50 lines of code, with a bit of help from the libvorbisfile utility library.

Decoding Ogg Vorbis streams is just as you'd imagine: the Vorbis libraries take in a stream of encoded packets and return a stream of PCM samples. There's a small amount of extra complication involving multiple logical bitstreams within a single physical stream, but you can more or less avoid this if you're interested only in implementing game music (though you could use this capability to store more than one soundtrack in the same music data file). Logical bitstreams are easy to create---just shove two \filename{.ogg} files into a single file with the \code{cat} command.

\index{Ogg Vorbis!linking against|(}
We'll get to the nuts and bolts of working with Ogg Vorbis streams in the next section, but here's a basic overview of what's required:

\longpage
\begin{enumerate}
\item Install the Ogg Vorbis development kit, available from \ulink{http://www.xiph.org/ogg/vorbis/index.html}. At the time of this writing, you need at least libao, libogg, libvorbis, and libvorbisfile. These are all rather easy to configure and install.

\item Include \filename{vorbis/vorbisfile.h} and \filename{vorbis/codec.h} (assuming a standard installation of the Ogg Vorbis libraries).

\item Link in \filename{libvorbisfile.so}, \filename{libvorbis.so}, and \filename{libogg.so} \emph{in that order} (\code{-lvorbisfile -lvorbis -logg}).

\item Create a buffer for storing the decoded PCM data. The Xiphophorous documentation recommends a 4,096-byte buffer, but games usually need larger chunks of music than that.

\item Open the \filename{.ogg} file you wish to play with the normal stdio (\code{fopen}) interface, and prepare an \code{OggVorbis\_File} structure with \code{ov\_open}. After \code{ov\_open} succeeds, don't touch the original \code{FILE} structure. Find out relevant facts about the stream with the \code{ov\_info} function.

\item Fill buffers of PCM samples with \code{ov\_read}. This function does not always return the requested amount of data;\footnote{It seems that \code{ov\_read} never processes more than 4,096 bytes, regardless of how much data you request. I'm sure there's a perfectly good reason for this, but it escapes me.} if it doesn't, call it repeatedly to build up a sufficient amount of data. Play the buffers with your audio API of choice. Repeat until \code{ov\_read} returns zero, indicating the end of the stream.

\item Close the \code{OggVorbis\_File} with \code{ov\_clear}. libvorbisfile will close the original file automatically.
\end{enumerate}

That's it! You'll need to add a bit more code if you care about properly handling logical bitstreams, but there's not much to that. Now let's put Ogg Vorbis to work with, you guessed it, \gametitle{Penguin Warrior}.
\index{Ogg Vorbis!linking against|)}

\begin{apiref}
\indexc{ov\_open}
\apifunction{ov\_open(file, ovfile, initial, initialsize)}
\apisynopsis{Prepares an \code{OggVorbis\_File} structure for decoding. If you need to use a data source other than a file, you probably want the \code{ov\_open\_callbacks} interface, not \code{ov\_open}.}
\apireturns{Zero on success, an error code on failure.}
\apifirstparm{file}{Pointer to an open \code{FILE} (from \code{fopen}). \emph{You do not need to close this file---libvorbisfile takes care of that.}}
\apinextparm{ovfile}{Pointer to an \code{OggVorbis\_File} structure.}
\apinextparm{initial}{\code{char *} to any data you've already read from \code{file}. Usually \code{NULL}.}
\apinextparm{initialsize}{Size of \code{initial} in bytes. Usually zero.}
\apiendparms
\end{apiref}

\begin{apiref}
\indexc{ov\_clear}
\apifunction{ov\_clear(ovfile)}
\apisynopsis{Closes an \code{OggVorbis\_File}.}
\apifirstparm{ovfile}{\code{OggVorbis\_File} to close.}
\apiendparms
\end{apiref}

\begin{apiref}
\indexc{ov\_info}
\apifunction{ov\_info(ovfile, stream)}
\apisynopsis{Retrieves information about an Ogg Vorbis stream.}
\apireturns{Pointer to a \code{vorbis\_info} structure that describes the given stream.}
\apifirstparm{ovfile}{Pointer to the \code{OggVorbis\_File} to query.}
\apinextparm{stream}{Logical bitstream number. $-1$ selects the current bitstream.}
\apiendparms
\end{apiref}

\begin{apiref}
\indexc{ov\_read}
\apifunction{ov\_read(ovfile, buffer, length, beflag, samplesize, signedflag, stream)}
\apisynopsis{Reads PCM data from an Ogg Vorbis stream.}
\apireturns{Number of bytes successfully decoded on success, \code{OV\_HOLE} on any type of (probably recoverable) data glitch, \code{OV\_EBADLINK} if the requested logical stream is invalid, or zero if there is no more data to decode.}
\apifirstparm{ovfile}{Pointer to the \code{OggVorbis\_File} to decode.}
\apinextparm{buffer}{Pointer to the buffer to receive the decoded samples. This is prototyped as a \code{char *}, but of course you can use any type of buffer you wish.}
\apinextparm{length}{Maximum number of bytes to decode. There is no guarantee that libvorbisfile will return this much data---it's only an upper limit. libvorbisfile seems to return at most 4,096 bytes per call to \code{ov\_read}.}
\apinextparm{beflag}{1 to request big endian samples, 0 otherwise. (Intel-based machines are little endian; many others aren't. You can find this out from the \filename{endian.h} header file.)}
\apinextparm{samplesize}{Bytes per sample. libvorbisfile is nice enough to give us a choice so we don't have to convert between sample sizes ourselves. This will obviously be 1 for 8-bit samples and 2 for 16-bit samples.}
\apinextparm{signedflag}{1 to request signed samples, 0 to request unsigned samples. In practice, 16-bit samples are almost always signed ($-32,768..32,767$) and 8-bit samples are almost always unsigned ($0..255$).}
\apinextparm{stream}{Pointer to an integer to receive the number of the logical bitstream that libvorbisfile is currently working on.}
\apiendparms
\end{apiref}

\begin{apiref}
\indexc{vorbis\_info}
\structname{vorbis\_info}
\structsynopsis{Contains basic information about an Ogg Vorbis stream.}
\structfirstmember{version}{Vorbis encoder version. This is mostly useless, since today's decoder is designed to be compatible with all future Vorbis encoder releases.}
\structnextmember{channels}{Number of channels in this stream. 1 for mono and 2 for stereo.}
\structnextmember{rate}{PCM sampling frequency of this stream. Although the bit rate (bits of encoded data per second of audio) can change throughout the stream, the sampling frequency will stay constant throughout. The sampling rate can change between logical bitstreams.}
\apiendparms
\end{apiref}

\subsection{Adding Music to Penguin Warrior}
\gametitle{Penguin Warrior} needs some music. Music can add a lot of atmosphere to a game, and it can dramatically affect the player's mood. Would the first level of \gametitle{Doom} have been quite as exciting without the fast-paced soundtrack, or the first level of \gametitle{Descent} as enthralling without the mysterious, cold, and robotic background tune? Matt Friedly of \ulink{http://phluid.acid.org} kindly gave us permission to use some of his music for this game, and we need to write some code to play it. The music was originally an \filename{.s3m} (Scream Tracker) module,\footnote{Scream Tracker is a rather old but well-written program for assembling music from pre-recorded samples.} but it's now an Ogg Vorbis stream (\filename{reflux.ogg} in this chapter's \gametitle{Penguin Warrior} directory).

We need a way to play Ogg Vorbis music through OpenAL.

This would be straightforward with just about any other audio API, but we'll have to nudge OpenAL a bit to get it to play streaming stereo music without unwanted environmental effects. Background music shouldn't really come from any particular point in space, and it should be played in stereo. There's no such thing as stereo environmental audio, since the effect we call stereo is really just a product of speaker positioning. Fortunately, OpenAL does provide a ``back door'' (via an extension) for injecting stereo sound into a buffer. We'll use this back door in a moment.

We have another problem: since music files tend to be enormous and time-consuming to decode, it would be a bad idea to load entire tracks into OpenAL buffers. It might work for small files (a few megabytes or less), but that's still a lot of wasted memory. How else can we play music? Until now we've worked only with simple OpenAL buffers that never change. It turns out that OpenAL defines a special type of buffer for streaming audio data. Streaming buffers don't take fixed-size pieces of PCM data, as most buffers do; instead, they start out empty and play data as it arrives (so long as it arrives in a timely fashion). This lets us incrementally decode music throughout the program. We just have to make sure we decode it quickly enough to stay ahead of OpenAL.

Streaming buffers are simple to work with. The \code{alGenStreamingBuffers\_LOKI} function creates one or more streaming buffers in the current OpenAL context, and the \code{alBufferAppendData\_LOKI} family of functions adds PCM data to the end of a streaming buffer.

\begin{apiref}
\indexc{alGenStreamingBuffers}
\apifunction{alGenStreamingBuffers\_LOKI(count, buffers)}
\apisynopsis{Generates \code{count} streaming buffers. Semantics are identical to \code{alGenBuffers}.}
\apifirstparm{count}{Number of buffers to generate.}
\apinextparm{buffer}{Pointer to an \code{ALuint} buffer big enough to hold the generated buffer names.}
\apiendparms
\end{apiref}

Now that we have a basic idea of how OpenAL music playback works, let's dig into the \gametitle{Penguin Warrior} music code.

\begin{codelisting}{music.c}{code:pwmusic}
\begin{verbatim}
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <AL/al.h>
#include <AL/alext.h>    /* for alBufferAppendWriteData_LOKI */
#include <vorbis/codec.h>
#include <vorbis/vorbisfile.h>

#include "audio.h"
#include "music.h"
#include "resources.h"

/* We'll set this flag to 1 after music has been
   successfully initialized. */
int music_enabled = 0;

/* We'll set this to 1 as soon as we start playback,
   and to 0 when there's no more data. */
int music_playing = 0;


/* OpenAL source and buffer for streaming music. */
static ALuint music_source = 0;
static ALuint music_buffer = 0;

/* Ogg Vorbis stream information. */
static OggVorbis_File music_file;
static vorbis_info *music_info = NULL;
static int music_section = -1;    /* Streams can have multiple
                                     sections. This lets Ogg Vorbis
                                     tell us which section
                                     we're dealing with. */
static int music_file_loaded = 0; /* 1 if a file is loaded,
                                     0 if not. */

/* Buffer for decoding music. We use an ALshort because we'll
   always request 16-bit samples from Vorbis. If you experience
   skipping or other anomalies, increase the size of this buffer. */
#define MUSIC_BUF_SIZE 65536
static ALshort buf[MUSIC_BUF_SIZE];
static int buf_count = 0;    /* Number of samples in the buffer. */
static int buf_pos = -1;     /* Playback position within buffer. */

void InitMusic()
{
    /* Check that InitAudio was successful. We'll be using
       a multichannel OpenAL streaming buffer for output,
       so OpenAL needs to be initialized. */
    if (!audio_enabled) {
        printf("Unable to initialize music.\n");
        return;
    }

    /* Generate a streaming buffer. */
    alGenStreamingBuffers_LOKI(1, &music_buffer);

    /* Create a source for the music. */
    alGenSources(1, &music_source);

    /* Set the source's position to be considered relative
           to the listener. This way we won't have to update its
           position each time the listener moves. */
    alSourcei(music_source, AL_SOURCE_RELATIVE, AL_TRUE);

    /* Assign the streaming buffer to the music source. */
    alSourcei(music_source, AL_BUFFER, music_buffer);

    /* Check for errors. */
    if (alGetError() != AL_NO_ERROR) {
        printf("Music initialization failed.\n");
        return;
    }

    printf("Music enabled.\n");
    music_enabled = 1;
}

void CleanupMusic()
{
    if (music_enabled) {
        /* Stop music playback. */
        alSourceStop(music_source);

        /* Delete the buffer and the source. */
        alDeleteBuffers(1, &music_buffer);
        alDeleteSources(1, &music_source);

        /* Close the music file, if one is open. */
        if (music_file_loaded) {
            ov_clear(&music_file);
            music_file_loaded = 0;
        }

        music_enabled = 0;
    }
}

int LoadMusic(char *filename)
{
    FILE *f;

    /* First, open the file with the normal stdio interface. */
    f = fopen(filename, "r");
    if (f == NULL) {
        printf("Unable to open music file %s.\n", filename);
        return -1;
    }

    /* Now pass it to libvorbis. */
    if (ov_open(f, &music_file, NULL, 0) < 0) {
        printf("Unable to attach libvorbis to %s.\n", filename);
        fclose(f);
        return -1;
    }


    /* Retrieve information about this stream. */
    music_info = ov_info(&music_file, -1);

    printf("Reading %li Hz, %i-channel music from %s.\n",
           music_info->rate,
           music_info->channels,
           filename);
      
    music_file_loaded = 1;
        
    return 0;
}

void StartMusic()
{
    /* If music is enabled and a file is ready to go,
       start playback. */
    if (music_enabled && music_file_loaded) {
        alSourcePlay(music_source);
        music_playing = 1;
    }
}

void StopMusic()
{
    if (music_enabled) {
        alSourceStop(music_source);
        music_playing = 0;
    }
}

void UpdateMusic()
{
    int written;
    int format;
        
    if (music_enabled && music_file_loaded) {

        /* Do we need to fetch more data? */
        if (buf_pos == -1) {

            buf_count = 0;
            buf_pos = 0;
                        
            if (music_playing) {
                /* libvorbisfile does not always return the
                   full amount of data requested, so loop
                   until we have a full block. */
                while (music_playing && buf_count <
                       MUSIC_BUF_SIZE) {
                    int amt;
                    amt = ov_read(&music_file,
                                  (char *)&buf[buf_count],
                                  (MUSIC_BUF_SIZE-buf_count)*2,
                                  0, 2, 1, &music_section) / 2;

                    buf_count += amt;
                                        
                    /* End of the stream? */
                    if (amt == 0) {
                        printf("End of music stream.\n");
                        music_playing = 0;
                        break;
                    }
                }

            } else {
                /* No more music, so fill the buffer
                   with zeroes. */
                buf_count = MUSIC_BUF_SIZE;
                memset(buf, 0, MUSIC_BUF_SIZE*2);
            }
        }

        /* Determine the correct format.
           This can change at any time.
           (it probably won't, but Vorbis allows for this) */
        if (music_info->channels == 1)
            format = AL_FORMAT_MONO16;
        else
            format = AL_FORMAT_STEREO16;

        /* If we have a buffer of data, append it to the playback
           buffer. alBufferAppendWriteData_LOKI is similar to the
           well-documented alBufferAppendData, but it allows us to
           specify the internal storage format for the data. This
           prevents OpenAL from converting stereo data to mono.
           (With this function, we should get stereo playback.) */
        if (buf_count != 0) {

            written = alBufferAppendWriteData_LOKI(music_buffer,
                                               format,
                                               &buf[buf_pos],
                                               MUSIC_BUF_SIZE-buf_pos,
                                               music_info->rate,
                                               format);
                
            /* Check for (unlikely) errors. If something went wrong,
               disable music. */
            if (written < 0 || alGetError() != AL_NO_ERROR) {
                printf("OpenAL error, disabling music.\n");
                CleanupMusic();
            }

            /* Update the buffer position based on how much data
               we wrote. If we've played the entire buffer, set
               the position to -1 so that the next call to
               UpdateMusic will refill the buffer. */
            buf_pos += written;
            if (buf_pos >= buf_count)
                buf_pos = -1;
        }
    }
}
\end{verbatim}
\end{codelisting}

The overall structure of \filename{music.c} should be pretty clear. \code{InitMusic} creates a streaming buffer and a source to go along with it. It sets the source to \firstterm{relative} mode, which means that its position will be determined as a vector relative to the listener (and hence the default position of $<0,0,0>$ will remain directly on top of the listener, no matter where the listener moves). This will keep environmental audio effects out of our music stream---you obviously can't detect attenuation or the Doppler effect from a pair of headphones.

\code{CleanupMusic} deletes the streaming buffer and the relative source. Nothing breathtaking here.

\code{LoadMusic} sets up the music system to play a new Ogg Vorbis file. It opens the file with \code{fopen}, makes it into an \code{OggVorbis\_File} structure with \code{ov\_open}, and gets information about the sampling rate and number of channels with \code{ov\_info}. Once the file is ready to go, \code{LoadMusic} sets the \code{music\_file\_loaded} flag to let the playback code know it's OK to start playing.

\code{StartMusic} calls \code{alSourcePlay} to begin playback. Nothing will actually happen until \code{UpdateMusic} gets around to decoding and writing some PCM data to the streaming buffer, but at this point it's primed and ready to go. \code{StopMusic} does just the opposite; it calls \code{alSourceStop} to end playback.

Finally, the meat of the music system: \code{UpdateMusic} is responsible for actually decoding and writing music data. It decodes chunks of data with \code{ov\_read}, looping until it gets as much data as it wants, and writes the data to the streaming buffer with \code{alBufferAppendWriteData\_LOKI}. This function is easy to understand, despite its long-winded name: it adds a buffer of PCM data to the end of a streaming buffer, preserving the requested sample format. This is the back door we mentioned earlier. The \code{\_LOKI} at the end of the function name means that this is a vendor-specific OpenAL extension and not part of the core specification. However, since Loki Software is responsible for most of the OpenAL specification, it's a safe bet that \code{\_LOKI} extensions will be part of any future OpenAL implementations.

\begin{apiref}
\indexc{alBufferAppendWriteData\_LOKI}
\apifunction{alBufferAppendWriteData\_LOKI(buffer, format, data, size, freq, iformat)}
\apisynopsis{Adds PCM data to the end of a streaming buffer, preserving the requested sample format internally. This is generally used to preserve multichannel (stereo) sound, which is normally converted to mono in OpenAL.}
\apireturns{Number of samples successfully appended to the buffer. Since streaming buffers have a limited amount of space, it's necessary to check for partial writes.}
\apifirstparm{buffer}{Name (integer id) of the buffer to append the data to.}
\apinextparm{format}{Sample format of the incoming data. One of the \code{AL\_FORMAT\_\emph{fmt}} constants.}
\apinextparm{data}{Pointer to the raw PCM data to append.}
\apinextparm{size}{Length of the data in samples (not bytes, not frames). One stereo frame is two samples.}
\apinextparm{freq}{Sampling frequency of the incoming data.}
\apinextparm{iformat}{Desired internal storage format. OpenAL will store the data in this format internally.}
\apiendparms
\end{apiref}

The game loop is supposed to call \code{UpdateMusic} very frequently. If you take a look at \filename{main.c}, you'll see that we've actually put \code{UpdateMusic} in its own thread, launched with \code{SDL\_CreateThread}. This makes sure that \code{UpdateMusic} gets called at regular intervals (timed with \code{SDL\_Delay}), regardless of the speed of the game loop. Since streaming buffers do store a few seconds of audio in advance, the timing isn't absolutely critical, but slowdowns in the game loop might cause sporadic glitches in the music. This isn't a problem when the music system runs in a separate thread. OpenAL is thread safe, so there's no harm in calling OpenAL functions from more than one thread at a time.

That's all there is to it. OpenAL's streaming buffer interface might change in the near future, but the changes should be easy to integrate into our music system. The new system is already in place, but an evening of frustration has proven that it is not production-quality yet. If the streaming buffer interface disappears, you'll be able to find updated source listings on the book's Web site.
\index{Ogg Vorbis!supporting|)}
\index{Ogg Vorbis|)}

Wow, that was a long ride. It's time for a game of \gametitle{Command and Conquer} for me, and then hopefully some fresh air. The next chapter talks about scripting systems and what they can do for a game. In it, we'll put scripting to work by adding a scripted opponent to \gametitle{Penguin Warrior}.
