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.
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.
- Find a sound effect you like (a short `.wav` file is perfect).
- 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".
- 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->RPort, (state == 1) ? 1 : 2);
Move(win->RPort, 140, 50);
Text(win->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 << win->UserPort->mp_SigBit);
while(msg = (struct IntuiMessage *)GetMsg(win->UserPort)) {
if (msg->Class == IDCMP_CLOSEWINDOW) {
running = FALSE;
}
if (msg->Class == IDCMP_MOUSEBUTTONS) {
if (msg->Code == SELECTDOWN) {
// Check if click is inside our button area
if (msg->MouseX >= 100 && msg->MouseX <= 220 &&
msg->MouseY >= 40 && msg->MouseY <= 60)
{
draw_button(1); // Animate: pressed state
DoDTMethod(sound_obj, NULL, NULL, DTM_PLAY, NULL, SNDA_DEST_AUDION, 0, TAG_END);
}
}
if (msg->Code == SELECTUP) {
draw_button(0); // Animate: normal state
}
}
ReplyMsg((struct Message *)msg);
}
}
cleanup(0);
return 0;
}
How to Compile and Run on macOS
- Save the Code: Save the C code above into a file named `button_sound.c`.
- Get your Sound: Make sure you have your `sound.8svx` file in the same folder.
- 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 -lamigaThis command tells vbcc to link the necessary Amiga libraries (`-lamiga` and `-lauto`) for OS functions and create an executable named `button_sound`.
- 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`.
- See the Result: A window will open with a button. Click it to hear your sound play and see the button animate!