#include "SDL.h"
#include "common.h"
#define NUM_CHANNELS 8
#define NUM_DRUMS 4
static struct
{
SDL_Rect rect;
SDL_Color upColor;
SDL_Color downColor;
int isPressed;
int touchIndex;
} buttons[NUM_DRUMS];
struct sound
{
Uint8 *buffer;
Uint32 length;
};
static struct sound drums[NUM_DRUMS];
void handleMouseButtonDown(SDL_Event * event);
void handleMouseButtonUp(SDL_Event * event);
int playSound(struct sound *);
void initializeButtons(SDL_Renderer *);
void audioCallback(void *userdata, Uint8 * stream, int len);
void loadSound(const char *file, struct sound *s);
struct
{
struct
{
Uint8 *position;
Uint32 remaining;
Uint32 timestamp;
} channels[NUM_CHANNELS];
SDL_AudioSpec outputSpec;
int numSoundsPlaying;
} mixer;
void
initializeButtons(SDL_Renderer *renderer)
{
int i;
int spacing = 10;
SDL_Rect buttonRect;
SDL_Color upColor = { 86, 86, 140, 255 };
SDL_Color downColor = { 191, 191, 221, 255 };
int renderW, renderH;
SDL_RenderGetLogicalSize(renderer, &renderW, &renderH);
buttonRect.x = spacing;
buttonRect.y = spacing;
buttonRect.w = renderW - 2 * spacing;
buttonRect.h = (renderH - (NUM_DRUMS + 1) * spacing) / NUM_DRUMS;
for (i = 0; i < NUM_DRUMS; i++) {
buttons[i].rect = buttonRect;
buttons[i].isPressed = 0;
buttons[i].upColor = upColor;
buttons[i].downColor = downColor;
buttonRect.y += spacing + buttonRect.h;
}
}
void
loadSound(const char *file, struct sound *s)
{
SDL_AudioSpec spec;
SDL_AudioCVT cvt;
int result;
if (SDL_LoadWAV(file, &spec, &s->buffer, &s->length) == NULL) {
fatalError("could not load .wav");
}
result = SDL_BuildAudioCVT(&cvt, spec.format, spec.channels, spec.freq,
mixer.outputSpec.format,
mixer.outputSpec.channels,
mixer.outputSpec.freq);
if (result == -1) {
fatalError("could not build audio CVT");
} else if (result != 0) {
cvt.buf = (Uint8 *) SDL_malloc(s->length * cvt.len_mult);
cvt.len = s->length;
SDL_memcpy(cvt.buf, s->buffer, s->length);
if (SDL_ConvertAudio(&cvt) == -1) {
fatalError("could not convert .wav");
}
SDL_free(s->buffer);
s->buffer = cvt.buf;
s->length = cvt.len_cvt;
}
}
void
handleMouseButtonDown(SDL_Event * event)
{
int x, y, mouseIndex, i, drumIndex;
mouseIndex = 0;
drumIndex = -1;
SDL_GetMouseState(&x, &y);
for (i = 0; i < NUM_DRUMS; i++) {
if (x >= buttons[i].rect.x
&& x < buttons[i].rect.x + buttons[i].rect.w
&& y >= buttons[i].rect.y
&& y < buttons[i].rect.y + buttons[i].rect.h) {
drumIndex = i;
break;
}
}
if (drumIndex != -1) {
buttons[drumIndex].touchIndex = mouseIndex;
buttons[drumIndex].isPressed = 1;
playSound(&drums[drumIndex]);
}
}
void
handleMouseButtonUp(SDL_Event * event)
{
int i;
int mouseIndex = 0;
for (i = 0; i < NUM_DRUMS; i++) {
if (buttons[i].touchIndex == mouseIndex) {
buttons[i].isPressed = 0;
}
}
}
void
render(SDL_Renderer *renderer)
{
int i;
SDL_SetRenderDrawColor(renderer, 50, 50, 50, 255);
SDL_RenderClear(renderer);
for (i = 0; i < NUM_DRUMS; i++) {
SDL_Color color =
buttons[i].isPressed ? buttons[i].downColor : buttons[i].upColor;
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
SDL_RenderFillRect(renderer, &buttons[i].rect);
}
SDL_RenderPresent(renderer);
}
int
playSound(struct sound *s)
{
int i;
int selected_channel = -1;
int oldest_channel = 0;
if (mixer.numSoundsPlaying == 0) {
SDL_PauseAudio(0);
}
for (i = 0; i < NUM_CHANNELS; i++) {
if (mixer.channels[i].position == NULL) {
selected_channel = i;
break;
}
if (mixer.channels[i].timestamp <
mixer.channels[oldest_channel].timestamp)
oldest_channel = i;
}
if (selected_channel == -1)
selected_channel = oldest_channel;
else
mixer.numSoundsPlaying++;
mixer.channels[selected_channel].position = s->buffer;
mixer.channels[selected_channel].remaining = s->length;
mixer.channels[selected_channel].timestamp = SDL_GetTicks();
return selected_channel;
}
void
audioCallback(void *userdata, Uint8 * stream, int len)
{
int i;
int copy_amt;
SDL_memset(stream, mixer.outputSpec.silence, len);
for (i = 0; i < NUM_CHANNELS; i++) {
if (mixer.channels[i].position == NULL) {
continue;
}
copy_amt =
mixer.channels[i].remaining <
len ? mixer.channels[i].remaining : len;
SDL_MixAudioFormat(stream, mixer.channels[i].position,
mixer.outputSpec.format, copy_amt, SDL_MIX_MAXVOLUME);
mixer.channels[i].position += copy_amt;
mixer.channels[i].remaining -= copy_amt;
if (mixer.channels[i].remaining == 0) {
mixer.channels[i].position = NULL;
mixer.numSoundsPlaying--;
if (mixer.numSoundsPlaying == 0) {
SDL_PauseAudio(1);
}
}
}
}
int
main(int argc, char *argv[])
{
int done;
SDL_Window *window;
SDL_Renderer *renderer;
SDL_Event event;
int i;
int width;
int height;
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
fatalError("could not initialize SDL");
}
window = SDL_CreateWindow(NULL, 0, 0, 320, 480, SDL_WINDOW_BORDERLESS | SDL_WINDOW_ALLOW_HIGHDPI);
renderer = SDL_CreateRenderer(window, 0, 0);
SDL_GetWindowSize(window, &width, &height);
SDL_RenderSetLogicalSize(renderer, width, height);
SDL_memset(&mixer, 0, sizeof(mixer));
mixer.outputSpec.freq = 44100;
mixer.outputSpec.format = AUDIO_S16LSB;
mixer.outputSpec.channels = 2;
mixer.outputSpec.samples = 256;
mixer.outputSpec.callback = audioCallback;
mixer.outputSpec.userdata = NULL;
if (SDL_OpenAudio(&mixer.outputSpec, NULL) != 0) {
fatalError("Opening audio failed");
}
loadSound("ds_kick_big_amb.wav", &drums[3]);
loadSound("ds_brush_snare.wav", &drums[2]);
loadSound("ds_loose_skin_mute.wav", &drums[1]);
loadSound("ds_china.wav", &drums[0]);
initializeButtons(renderer);
done = 0;
while (!done) {
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_MOUSEBUTTONDOWN:
handleMouseButtonDown(&event);
break;
case SDL_MOUSEBUTTONUP:
handleMouseButtonUp(&event);
break;
case SDL_QUIT:
done = 1;
break;
}
}
render(renderer);
SDL_Delay(1);
}
for (i = 0; i < NUM_DRUMS; i++) {
SDL_free(drums[i].buffer);
}
SDL_Quit();
return 0;
}