From 3d1eae53e8efd24f9331bfc973c0951981512d7c Mon Sep 17 00:00:00 2001 From: James McKenzie Date: Mon, 16 Jan 2023 08:14:09 +0000 Subject: first cut --- .gitignore | 4 + Makefile | 16 ++++ fugue21.mid | Bin 0 -> 8542 bytes midi.c | 247 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 267 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 fugue21.mid create mode 100644 midi.c 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 Binary files /dev/null and b/fugue21.mid 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 +#include +#include +#include +#include +#include +#include + + +#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; +} -- cgit v1.2.3