summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--Makefile16
-rw-r--r--fugue21.midbin0 -> 8542 bytes
-rw-r--r--midi.c247
4 files changed, 267 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..19edf2a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+midi
+midi.o
+fugue21.raw
+fugue21.wav
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..a96b685
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,16 @@
+LIBS=-lm
+
+default: fugue21.wav
+
+fugue21.wav: fugue21.raw Makefile
+ sox -v 1.0 -t raw -r 44100 -c 1 -b 8 -e signed-integer $< -t wav -b 16 $@
+
+
+fugue21.raw: fugue21.mid midi
+ ./midi
+
+
+OBJS=midi.o
+
+midi:${OBJS}
+ ${CC} ${CFLAGS} ${LDFLAGS} -o $@ ${OBJS} ${LIBS}
diff --git a/fugue21.mid b/fugue21.mid
new file mode 100644
index 0000000..46d6dff
--- /dev/null
+++ b/fugue21.mid
Binary files differ
diff --git a/midi.c b/midi.c
new file mode 100644
index 0000000..72c2161
--- /dev/null
+++ b/midi.c
@@ -0,0 +1,247 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <byteswap.h>
+#include <string.h>
+#include <math.h>
+
+
+#define MAX_CH 4
+#define MAX_TICK 100000
+#define SAMPLE_RATE 44100
+#define SAMPLES_PER_TICK 50
+
+#define MAX_SAMPLE (SAMPLES_PER_TICK*MAX_TICK)
+
+uint8_t active_note[MAX_CH][MAX_TICK];
+
+double periods[128];
+
+
+double
+freq (int n)
+{
+ return 440. * pow (2., ((double) (n - 69)) / 12.);
+}
+
+
+void
+calc_freqs (void)
+{
+ unsigned i;
+
+ for (i = 0; i < 128; ++i)
+ periods[i] = ((double) SAMPLE_RATE) / freq (i);
+}
+
+
+
+
+void
+on (unsigned t, unsigned ch, unsigned n)
+{
+ memset (&active_note[ch][t], n, MAX_TICK - t);
+}
+
+void
+off (unsigned t, unsigned ch, unsigned n)
+{
+ memset (&active_note[ch][t], 0, MAX_TICK - t);
+}
+
+
+void
+mtrk (FILE *f, long pos, long end)
+{
+
+ uint32_t t = 0, v, m, dt;
+ uint8_t k;
+
+ end += pos;
+
+
+ while (pos < end) {
+
+ fseek (f, pos, SEEK_SET);
+
+ dt = 0;
+
+ do {
+ fread (&v, 1, 1, f);
+ pos++;
+ dt <<= 7;
+ dt |= v & 0x7f;
+ } while (v & 0x80);
+
+
+ t += dt;
+
+ fread (&v, 1, 1, f);
+
+ if (v & 0x80) {
+ m = v;
+ pos++;
+ } else
+ fseek (f, pos, SEEK_SET);
+
+
+ switch (m & 0xf0) {
+ case 0x80:
+ fread (&k, 1, 1, f);
+ fread (&v, 1, 1, f);
+ pos += 2;
+ off (t, m & 0xf, k);
+ //printf ("%d: OFF %d %d %d\n", t,m &0xf, k, v);
+ break;
+
+ case 0x90:
+ fread (&k, 1, 1, f);
+ fread (&v, 1, 1, f);
+ pos += 2;
+ //printf ("%d: ON %d %d %d\n", t, m&0xf,k, v);
+ on (t, m & 0xf, k);
+ break;
+
+ case 0xf0:
+ switch (m) {
+ case 0xff:
+ fread (&v, 1, 1, f);
+ pos++;
+
+ switch (v) {
+ case 0x3:
+ fread (&k, 1, 1, f);
+ pos++;
+ pos += k;
+ break;
+
+ case 0x54:
+ pos += 6;
+ break;
+
+ case 0x51:
+ pos += 4;
+ break;
+
+ case 0x58:
+ pos += 5;
+ break;
+
+ case 0x2f:
+ pos++;
+ break;
+
+ default:
+ printf ("?2 FF %x\n", v);
+ exit (0);
+ break;
+ }
+
+ break;
+
+ default:
+ printf ("?1 %x\n", m);
+ exit (0);
+ }
+
+ break;
+
+ default:
+ printf ("?0 %x\n", m);
+ exit (0);
+
+
+ }
+ }
+}
+
+
+
+
+
+int
+parse_chunk (FILE *f)
+{
+ char fcc[5] = { 0 };
+ uint32_t len;
+ long o;
+
+ if (fread (fcc, 4, 1, f) != 1)
+ return -1;
+
+ if (fread (&len, 4, 1, f) != 1)
+ return -1;
+
+ len = bswap_32 (len);
+
+ o = ftell (f);
+
+ printf ("%s for %d bytes at %d\n", fcc, (int) len, (int) o);
+
+ if (!strcmp (fcc, "MTrk"))
+ mtrk (f, o, len);
+
+ fseek (f, o + len, SEEK_SET);
+
+ return 0;
+}
+
+
+void
+play (FILE *f)
+{
+ unsigned s, c, t;
+ int8_t v;
+ double d;
+
+
+ calc_freqs();
+
+
+ for (s = 0; s < MAX_SAMPLE; ++s) {
+ t = s / SAMPLES_PER_TICK;
+ v = 0;
+
+ for (c = 0; c < MAX_CH; ++c) {
+ if (active_note[c][t]) {
+ d = ((double) s) / periods[active_note[c][t]];
+ d = fmod (d, 1.0);
+
+ if (d < 0.5)
+ v--;
+ else
+ v++;
+ }
+ }
+
+
+ v += (s % 3) * 2;
+ v -= 2;
+
+ if (v < 0) v = -120;
+ else v = 120;
+
+ fwrite (&v, 1, 1, f);
+ }
+}
+
+int
+main (int argc, char *argv[])
+{
+ FILE *f;
+
+ f = fopen ("fugue21.mid", "rb");
+
+ while (!parse_chunk (f));
+
+ fclose (f);
+
+ f = fopen ("fugue21.raw", "wb");
+ play (f);
+ fclose (f);
+
+
+
+ return 0;
+}