thecodingidiot.com

Who Wants to Be a Game Developer? AcousticStarting Music

Starting Music

start_music creates a child process and replaces it with aplay. The parent returns the child's PID so the caller can stop it later.

fork()

fork() duplicates the calling process. After fork() returns, two processes are running: the parent and the child. They are identical — same code, same memory, same file descriptors — up to one difference: the return value.

Return valueWho receives it
0the child
child's PID (a positive integer)the parent
-1the caller, on error (no child was created)
pid_t pid = fork();
if (pid == -1)
    /* error — no child */
if (pid == 0)
    /* this is the child */
/* this is the parent — pid holds the child's PID */

The two processes diverge at the if chain. The child takes the pid == 0 branch; the parent falls through.

exec()

Once the child is running, it holds a copy of the game's code and memory. We do not want it to run the game — we want it to run aplay.

execlp replaces the current process image with a new program:

execlp("aplay", "aplay", "-q", path, NULL);

The arguments:

  • "aplay" — the program name, found by searching PATH
  • "aplay"argv[0] for the new process (by convention, the program's own name)
  • "-q" — quiet mode: suppresses aplay's "Playing WAVE..." line
  • path — the WAV file to play
  • NULL — terminates the argument list

If execlp succeeds, it does not return. The child process image is gone; aplay is now running in its place. The game loop code in the child ceases to exist.

If execlp fails (file not found, not executable), it returns -1 and the child is still alive. _exit(1) terminates it immediately. _exit rather than exit because _exit does not flush stdio buffers — flushing shared buffers in the child would corrupt the parent's output.

start_music

#include "music.h"
 
pid_t  start_music(const char *path)
{
    pid_t  pid;
 
    pid = fork();
    if (pid == -1)
        return (-1);
    if (pid == 0)
    {
        execlp("aplay", "aplay", "-q", path, NULL);
        _exit(1);
    }
    return (pid);
}

Replace the stub in music.c with this implementation.

What happens when you call it

pid_t music_pid = start_music("music/tier1.wav");
  1. fork() creates a child. Two processes now exist.
  2. The child enters the pid == 0 branch and calls execlp.
  3. The child's process image is replaced by aplay. aplay begins reading music/tier1.wav and writing decoded audio to ALSA.
  4. The parent receives the child's PID and returns it. The game loop continues on the next line.

From the game's perspective, start_music is a non-blocking call that kicks off audio in the background. The game does not wait for aplay to finish — aplay will keep playing the WAV file, looping if you pass -l, until something stops it.

Build check

make re
./game questions.txt

Tier 1 music should now play as soon as the game starts. It will continue until the binary exits (because stop_music still does nothing). The next page implements stop_music.