#include #include "SDL.h" #include "SDL_audio.h" #include "SDL_error.h" #include "SDL_events.h" #include "SDL_mouse.h" #define KB(x) (1024 * (x)) static SDL_Window *window = NULL; static SDL_Renderer *renderer = NULL; static SDL_AudioDeviceID audio_device = 0; static SDL_AudioStream *stream = NULL; static float volume_slider_value = 1.0f; #if defined(__clang__) || defined(__GNUC__) static void panic_and_abort(const char *title, const char *text) __attribute__((noreturn)); #endif static void panic_and_abort(const char *title, const char *text) { fprintf(stderr, "PANIC: %s ... %s\n", title, text); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, text, window); SDL_Quit(); exit(1); } // TODO: Refactor this nonsense into a struct static Uint8 *wavbuf = NULL; static Uint32 wavlen = 0; static Uint32 wavpos = 0; static SDL_AudioSpec wavspec; static SDL_bool open_audio_file(const char *fname) { SDL_FreeAudioStream(stream); stream = NULL; SDL_FreeWAV(wavbuf); wavbuf = NULL; wavlen = 0; wavpos = 0; if (SDL_LoadWAV(fname, &wavspec, &wavbuf, &wavlen) == NULL) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't load wav file!", SDL_GetError(), window); return SDL_FALSE; } stream = SDL_NewAudioStream(wavspec.format, wavspec.channels, wavspec.freq, AUDIO_F32, 2, 48000); if (!stream) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create audiostream!", SDL_GetError(), window); SDL_FreeWAV(wavbuf); wavbuf = NULL; wavlen = 0; wavpos = 0; return SDL_FALSE; } if (SDL_AudioStreamPut(stream, wavbuf, wavlen) == -1) { // FIXME: graceful handling panic_and_abort("Audio stream put failed", SDL_GetError()); } SDL_AudioStreamFlush(stream); // FIXME: error handling return SDL_TRUE; } int main(int argc, char **argv) { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) != 0) { panic_and_abort("SDL_Init failed", SDL_GetError()); } window = SDL_CreateWindow("sdlamp", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1024, 768, 0); if (!window) { panic_and_abort("SDL_CreateWindow failed", SDL_GetError()); } renderer = SDL_CreateRenderer(window, 0, SDL_RENDERER_PRESENTVSYNC); if (!renderer) { panic_and_abort("SDL_CreateRenderer failed", SDL_GetError()); } SDL_AudioSpec desired; SDL_zero(desired); desired.freq = 48000; desired.format = AUDIO_F32; desired.channels = 2; desired.samples = 4096; desired.callback = NULL; audio_device = SDL_OpenAudioDevice(NULL, 0, &desired, NULL, 0); if (audio_device == 0) { panic_and_abort("Couldn't load audio device!", SDL_GetError()); } // Enable events SDL_EventState(SDL_DROPFILE, SDL_ENABLE); SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE); // TODO: Remove temp load open_audio_file("CantinaBand60.wav"); const SDL_Rect rewind_rect = {(1024 - 100) / 3, 100, 100, 100}; const SDL_Rect pause_rect = {((1024 - 100) / 3) * 2 , 100, 100, 100}; const SDL_Rect volume_rect = {(1024 - 500) / 2, 400, 500, 20}; SDL_Rect volume_knob = { (volume_rect.x + volume_rect.w) - volume_knob.w, volume_rect.y, 20, volume_rect.h }; SDL_bool paused = SDL_TRUE; SDL_bool quit = SDL_FALSE; while (!quit) { SDL_Event e; while (SDL_PollEvent(&e)) { switch (e.type) { case SDL_QUIT: quit = SDL_TRUE; break; case SDL_MOUSEBUTTONDOWN: { SDL_Point pt = { e.button.x, e.button.y }; if (SDL_PointInRect(&pt, &rewind_rect)) { // Pressed the "rewind" button SDL_ClearQueuedAudio(audio_device); SDL_AudioStreamClear(stream); if (SDL_AudioStreamPut(stream, wavbuf, wavlen) == -1) { // FIXME: graceful handling panic_and_abort("Audio stream put failed", SDL_GetError()); } SDL_AudioStreamFlush(stream); } else if (SDL_PointInRect(&pt, &pause_rect)) { // Pressed the "pause" button paused = paused ? SDL_FALSE : SDL_TRUE; SDL_PauseAudioDevice(audio_device, paused); } break; } case SDL_MOUSEMOTION: { SDL_Point pt = { e.motion.x, e.motion.y }; if (SDL_PointInRect(&pt, &volume_rect) && (e.motion.state & SDL_BUTTON_LMASK)) { // left mouse pressed inside the "volume" slider const float fx = (float)(pt.x - volume_rect.x); volume_slider_value = (fx / (float)volume_rect.w); volume_knob.x = pt.x - (volume_knob.w / 2); volume_knob.x = SDL_max(volume_knob.x, volume_rect.x); volume_knob.x = SDL_min(volume_knob.x, (volume_rect.x + volume_rect.w) - volume_knob.w); } break; } case SDL_DROPFILE: { open_audio_file(e.drop.file); SDL_free(e.drop.file); break; } } } if (SDL_GetQueuedAudioSize(audio_device) < KB(8)) { const int bytes_remaining = SDL_AudioStreamAvailable(stream); if (bytes_remaining > 0) { const int new_bytes = SDL_min(bytes_remaining, KB(32)); static Uint8 converted_buffer[KB(32)]; SDL_AudioStreamGet(stream, converted_buffer, new_bytes); // FIXME: Error checking const int num_samples = (new_bytes / sizeof(float)); float *samples = (float*)converted_buffer; for (size_t i = 0; i < num_samples; ++i) { samples[i] *= volume_slider_value; } SDL_QueueAudio(audio_device, converted_buffer, new_bytes); } } SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); SDL_RenderFillRect(renderer, &rewind_rect); SDL_RenderFillRect(renderer, &pause_rect); SDL_RenderFillRect(renderer, &volume_rect); SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255); SDL_RenderFillRect(renderer, &volume_knob); SDL_RenderPresent(renderer); } SDL_FreeWAV(wavbuf); SDL_CloseAudioDevice(audio_device); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); return 0; }