summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--GNUmakefile2
-rw-r--r--src/portaudio.c162
2 files changed, 163 insertions, 1 deletions
diff --git a/GNUmakefile b/GNUmakefile
index a9e2537..f7eb64a 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -13,7 +13,7 @@ deps ::= $(addprefix out/,$(notdir ${srcs:.c=.d}))
 
 .PHONY: all run clean
 
-all: out/mu-SDL2
+all: out/mu-SDL2 out/mu-portaudio
 
 run: out/mu-SDL2
 	./$<
diff --git a/src/portaudio.c b/src/portaudio.c
new file mode 100644
index 0000000..f4ca4b7
--- /dev/null
+++ b/src/portaudio.c
@@ -0,0 +1,162 @@
+#define SDL_MAIN_HANDLED
+#include <portaudio.h>
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.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;
+
+volatile sig_atomic_t keep_going = 1;
+
+void sigint_handler(int sig_num) {
+	signal(SIGINT, SIG_IGN);
+	keep_going = 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) {
+int audio_callback(void const *inBuf, void *outBuf, unsigned long const frameCount, PaStreamCallbackTimeInfo const* timeInfo, PaStreamCallbackFlags statusFlags, void *userdata_void) {
+	(void) inBuf;
+	(void) timeInfo;
+	(void) statusFlags;
+	struct userdata *user = userdata_void;
+	user->callback(user->user, outBuf, frameCount * sizeof (float) * 2);
+	return 0;
+}
+
+int (*file_ext(char *file))(struct blob *, struct userdata *);
+
+int main(int argc, char **argv) {
+	if (argc != 2) {
+		eprintf("usage: %s audio-file.{mid,mod,xm,it,s3m}\n", argv[0]);
+		return EXIT_FAILURE;
+	} else {
+		int (*module_func)(struct blob *, struct userdata *) = file_ext(argv[1]);
+		if (module_func == NULL) {
+			eprintf("%s: unrecognized file extension\n", argv[1]);
+			return EXIT_FAILURE;
+		}
+		struct blob file = load_file(argv[1]);
+		if (file.data == NULL) {
+			perror(argv[1]);
+			return EXIT_FAILURE;
+		}
+		module_func(&file, &userdata);
+	}
+	
+	signal(SIGINT, sigint_handler); // signal(3) claims this method is deprecated, but..
+	
+	PaError paErr;
+	if ((paErr = Pa_Initialize()) != paNoError) {
+		printf("error: Pa_Initialize: %s\n", Pa_GetErrorText(paErr));
+		goto error;
+	}
+	
+	PaStream *stream;
+	if ((paErr = Pa_OpenDefaultStream(
+		&stream,
+		IN_CHANNELS,
+		OUT_CHANNELS,
+		paFloat32,
+		SAMPLE_RATE,
+		paFramesPerBufferUnspecified,
+		audio_callback,
+		&userdata
+	)) != paNoError) {
+		printf("error: Pa_OpenDefaultStream: %s\n", Pa_GetErrorText(paErr));
+		goto error;
+	}
+	
+	if ((paErr = Pa_StartStream(stream)) != paNoError) {
+		printf("error: Pa_StartStream: %s\n", Pa_GetErrorText(paErr));
+		goto error;
+	}
+	
+	while (keep_going) {
+		Pa_Sleep(1000);
+	}
+	
+	if ((paErr = Pa_StopStream(stream)) != paNoError) {
+		printf("error: Pa_StopStream: %s\n", Pa_GetErrorText(paErr));
+		goto error;
+	}
+	
+	if ((paErr = Pa_CloseStream(stream)) != paNoError) {
+		printf("error: Pa_CloseStream: %s\n", Pa_GetErrorText(paErr));
+		goto error;
+	}
+	
+	if ((paErr = Pa_Terminate()) != paNoError) {
+		printf("error: Pa_Terminate: %s\n", Pa_GetErrorText(paErr));
+		goto error;
+	}
+	
+	return EXIT_SUCCESS;
+	
+	error:
+	return EXIT_FAILURE;
+}
+
+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};
+}