summaryrefslogtreecommitdiff
path: root/src/SDL2.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/SDL2.c')
-rw-r--r--src/SDL2.c221
1 files changed, 221 insertions, 0 deletions
diff --git a/src/SDL2.c b/src/SDL2.c
new file mode 100644
index 0000000..4e51269
--- /dev/null
+++ b/src/SDL2.c
@@ -0,0 +1,221 @@
+#define SDL_MAIN_HANDLED
+#include <SDL2/SDL.h>
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "include.h"
+
+int module_openmpt(struct blob *file, struct userdata *userdata);
+int module_fluidsynth(struct blob *file, struct userdata *userdata);
+
+#define eprintf(...) fprintf(stderr, __VA_ARGS__)
+
+static const int WINDOW_WIDTH = 160, WINDOW_HEIGHT = 90;
+
+SDL_Window *window = NULL;
+
+int paused = 0;
+
+struct blob load_file(char const *const name);
+
+struct userdata userdata = {
+ .callback = NULL,
+ .user = NULL,
+};
+
+void audio_callback(void *userdata_void, unsigned char *stream_void, int const length) {
+ struct userdata *user = userdata_void;
+ float *stream = (float *) stream_void;
+ if (user->callback == NULL) {
+ for (int i = 0; i < length / sizeof (float); i++) {
+ stream[i] = 0.0;
+ }
+ } else {
+ user->callback(user->user, stream_void, length);
+ }
+}
+
+int (*file_ext(char *file))(struct blob *, struct userdata *);
+
+int main(int argc, char **argv) {
+ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) {
+ eprintf("failed to init SDL: %s\n");
+ return EXIT_FAILURE;
+ }
+
+ SDL_StopTextInput();
+
+ if ((window = SDL_CreateWindow("mu-sdl", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN)) == NULL) {
+ eprintf("failed to create a window: %s\n");
+ SDL_Quit();
+ return EXIT_FAILURE;
+ }
+
+ SDL_Renderer *renderer;
+ if ((renderer = SDL_CreateRenderer(window, -1, 0)) == NULL) {
+ eprintf("failed to create a renderer: %s\n");
+ SDL_DestroyWindow(window);
+ SDL_Quit();
+ return EXIT_FAILURE;
+ }
+
+ SDL_ShowWindow(window);
+ SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
+ SDL_RenderClear(renderer);
+ SDL_RenderPresent(renderer);
+
+ SDL_AudioSpec desired = {
+ .freq = SAMPLE_RATE,
+ .format = AUDIO_F32SYS,
+ .channels = OUT_CHANNELS,
+ .samples = 8096,
+ .callback = audio_callback,
+ .userdata = &userdata,
+ };
+ SDL_AudioDeviceID audio = SDL_OpenAudioDevice(NULL, 0, &desired, NULL, 0);
+
+ if (argc == 2) {
+ int (*module_func)(struct blob *, struct userdata *) = file_ext(argv[1]);
+ if (module_func == NULL) {
+ eprintf("%s: unrecognized file extension\n", argv[1]);
+ goto arg_load_done;
+ }
+ struct blob file = load_file(argv[1]);
+ if (file.data == NULL) {
+ perror(argv[1]);
+ goto arg_load_done;
+ }
+ module_func(&file, &userdata);
+ }
+ arg_load_done:
+
+ SDL_PauseAudioDevice(audio, 0);
+ while (true) {
+ SDL_Event evt;
+ while (SDL_PollEvent(&evt)) {
+ switch (evt.type) {
+ case SDL_QUIT:
+ goto done;
+
+ case SDL_WINDOWEVENT:
+ SDL_RenderClear(renderer);
+ SDL_RenderPresent(renderer);
+ break;
+
+ case SDL_KEYDOWN:
+ // ^Q
+ if (evt.key.keysym.sym == SDLK_q && evt.key.keysym.mod & KMOD_CTRL) {
+ goto done;
+ }
+ // ESC
+ if (evt.key.keysym.sym == SDLK_ESCAPE) {
+ goto done;
+ }
+
+ if (evt.key.keysym.sym == SDLK_SPACE) {
+ paused = !paused;
+ SDL_PauseAudioDevice(audio, paused);
+ }
+ break;
+
+ case SDL_DROPFILE:
+ puts(evt.drop.file);
+ struct blob file = load_file(evt.drop.file);
+ if (file.data == NULL) {
+ perror(evt.drop.file);
+ SDL_free(evt.drop.file);
+ break;
+ }
+ struct userdata newuser = {.callback = NULL, .freefunc = NULL};
+ int (*module_func)(struct blob *, struct userdata *) = file_ext(evt.drop.file);
+ if (module_func == NULL) {
+ eprintf("%s: unrecognized file extension\n", evt.drop.file);
+ free(file.data);
+ break;
+ }
+ SDL_free(evt.drop.file);
+ if (module_func(&file, &newuser)) {
+ // error
+ } else {
+ SDL_LockAudioDevice(audio);
+ if (userdata.freefunc != NULL) {
+ userdata.freefunc(userdata.user);
+ }
+ userdata = newuser;
+ SDL_UnlockAudioDevice(audio);
+ }
+ free(file.data);
+ break;
+
+ default:
+ }
+ }
+ }
+ done:
+ if (userdata.freefunc != NULL) {
+ SDL_LockAudioDevice(audio);
+ userdata.freefunc(userdata.user);
+ SDL_PauseAudioDevice(audio, 1);
+ SDL_UnlockAudioDevice(audio);
+ }
+
+ SDL_DestroyRenderer(renderer);
+
+ SDL_DestroyWindow(window);
+
+ SDL_CloseAudioDevice(audio);
+
+ SDL_Quit();
+
+ return EXIT_SUCCESS;
+}
+
+int (*file_ext(char *file))(struct blob *, struct userdata *) {
+ size_t len = strlen(file);
+ #define ext(extension) memcmp(file + len - sizeof (extension) + 1, extension, sizeof (extension))
+ if ((ext(".mptm") && ext(".mod") && ext(".xm") && ext(".s3m") && ext(".it")) == 0) {
+ return module_openmpt;
+ } else if ((ext(".mid") && ext(".midi")) == 0) {
+ return module_fluidsynth;
+ }
+ #undef ext
+ return NULL;
+}
+
+struct blob load_file(char const *const name) {
+ const size_t START_SIZE = 1;
+ FILE *file = fopen(name, "rb");
+ if (file == NULL) {
+ return (struct blob) {.data = NULL};
+ }
+ void *data = malloc(START_SIZE);
+ size_t allocated = START_SIZE;
+ size_t used = 0;
+ while (1) {
+ size_t read = fread(data + used, 1, allocated - used, file);
+ if (read != allocated - used) {
+ used += read;
+ break;
+ }
+ used += read;
+ allocated *= 2;
+ void *const newdata = realloc(data, allocated);
+ if (newdata == NULL) {
+ goto realloc_error;
+ }
+ data = newdata;
+ }
+ void *const newdata = realloc(data, used);
+ if (newdata == NULL && used != 0) {
+ goto realloc_error;
+ }
+ fclose(file);
+ return (struct blob) {.data = newdata, .size = used};
+
+ realloc_error:
+ free(data);
+ fclose(file);
+ return (struct blob) {.data = NULL};
+}