summary refs log tree commit diff
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};
+}