Interactive Sound: The 'Push My Button' Tutorial

So far, we've made things move on screen. Now, let's make some noise! The Amiga's four 8-bit sound channels, collectively named "Paula," gave it an audio capability that was light-years ahead of its time. This tutorial will guide you through loading a sound sample, drawing a button on the screen, and playing the sound when the user clicks the button.

The Concept: Events and Audio

Unlike our previous examples that took over the whole system, this program will run as a proper AmigaDOS process. We'll open a window, draw our button, and then enter a loop to wait for user input (events). When we detect a mouse click inside our button's area, we'll trigger the sound and provide some visual feedback by changing the button's color.

Illustration of an Amiga playing sound

Preparing Your Sound Sample

The Amiga hardware plays raw 8-bit signed audio samples. You can't just use an MP3 or WAV file directly. You'll need to convert your sound into the Amiga's native `8SVX` IFF format.

  1. Find a sound effect you like (a short `.wav` file is perfect).
  2. Use a modern audio editor like Audacity to convert it. Open your sound, change the format to "Other uncompressed files", and under "Header," select "SDS (Amiga 8SVX)". Set the encoding to "Signed 8-bit PCM".
  3. Save this new file as `sound.8svx`.

The Code (button_sound.c):

/*
 * button_sound.c - A simple program to demonstrate
 * interactive sound playback on the Amiga.
 * - Opens a window
 * - Draws a button
 * - Plays a sound on mouse click
 */

#include <proto/exec.h> #include <proto/dos.h> #include <proto/graphics.h> #include <proto/intuition.h> #include <proto/datatypes.h>

#include <datatypes/soundclass.h> #include <stdio.h>

/* Globals */ struct Library *ExecBase = NULL; struct Library *DOSBase = NULL; struct Library *GfxBase = NULL; struct Library *IntuitionBase = NULL; struct Library *DataTypesBase = NULL;

struct Window *win = NULL; Object *sound_obj = NULL;

/* Cleanup function */ void cleanup(int code) { if (sound_obj) DisposeDTObject(sound_obj); if (DOSBase) CloseLibrary(DOSBase); if (DataTypesBase) CloseLibrary(DataTypesBase); if (win) CloseWindow(win); if (IntuitionBase) CloseLibrary(IntuitionBase); if (GfxBase) CloseLibrary(GfxBase); exit(code); }

/* Draws the button in a specific state */ void draw_button(int state) { SetAPen(win->RPort, (state == 1) ? 2 : 1); // state 1 = pressed (color 2), 0 = normal (color 1) RectFill(win->RPort, 100, 40, 220, 60);

SetAPen(win-&gt;RPort, (state == 1) ? 1 : 2);
Move(win-&gt;RPort, 140, 50);
Text(win-&gt;RPort, "Play Sound", 10);

}

int main(void) { ExecBase = (*(struct ExecBase **)4);

/* Open necessary libraries */
GfxBase = OpenLibrary("graphics.library", 37);
IntuitionBase = OpenLibrary("intuition.library", 37);
DOSBase = OpenLibrary("dos.library", 37);
DataTypesBase = OpenLibrary("datatypes.library", 37);
if (!GfxBase || !IntuitionBase || !DOSBase || !DataTypesBase) {
    puts("Failed to open a required library.");
    cleanup(20);
}

/* Load the sound file using DataTypes */
sound_obj = NewDTObject("sound.8svx", DTA_SourceType, DTST_FILE, DTA_GroupID, GID_SOUND, TAG_END);
if (!sound_obj) {
    puts("Could not load sound.8svx. Make sure it's in the same directory.");
    cleanup(20);
}

/* Open a simple window */
win = OpenWindowTags(NULL,
    WA_Width, 320, WA_Height, 100,
    WA_Title, "Sound Player",
    WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_MOUSEBUTTONS,
    WA_Activate, TRUE,
    WA_DragBar, TRUE,
    WA_CloseGadget, TRUE,
    TAG_END);
if (!win) {
    puts("Could not open window.");
    cleanup(20);
}

draw_button(0); // Draw initial button state

/* --- Main event loop --- */
BOOL running = TRUE;
while(running) {
    struct IntuiMessage *msg;
    Wait(1L &lt;&lt; win-&gt;UserPort-&gt;mp_SigBit);
    while(msg = (struct IntuiMessage *)GetMsg(win-&gt;UserPort)) {
        if (msg-&gt;Class == IDCMP_CLOSEWINDOW) {
            running = FALSE;
        }
        if (msg-&gt;Class == IDCMP_MOUSEBUTTONS) {
            if (msg-&gt;Code == SELECTDOWN) {
                // Check if click is inside our button area
                if (msg-&gt;MouseX &gt;= 100 && msg-&gt;MouseX &lt;= 220 &&
                    msg-&gt;MouseY &gt;= 40 && msg-&gt;MouseY &lt;= 60)
                {
                    draw_button(1); // Animate: pressed state
                    DoDTMethod(sound_obj, NULL, NULL, DTM_PLAY, NULL, SNDA_DEST_AUDION, 0, TAG_END);
                }
            }
            if (msg-&gt;Code == SELECTUP) {
                draw_button(0); // Animate: normal state
            }
        }
        ReplyMsg((struct Message *)msg);
    }
}

cleanup(0);
return 0;

}

How to Compile and Run on macOS

  1. Save the Code: Save the C code above into a file named `button_sound.c`.
  2. Get your Sound: Make sure you have your `sound.8svx` file in the same folder.
  3. Compile: Open your Terminal. If you've set up the vbcc environment variables from the previous tutorial, you can use this simple command:
    vc +kick13 -o button_sound button_sound.c -lauto -lamiga

    This command tells vbcc to link the necessary Amiga libraries (`-lamiga` and `-lauto`) for OS functions and create an executable named `button_sound`.

  4. Run in Emulator: In vAmiga or FS-UAE, mount the folder containing your new `button_sound` executable and `sound.8svx`. Boot into Workbench, open the Shell, and run your program by typing `button_sound`.
  5. See the Result: A window will open with a button. Click it to hear your sound play and see the button animate!