From 3d1eae53e8efd24f9331bfc973c0951981512d7c Mon Sep 17 00:00:00 2001 From: James McKenzie Date: Mon, 16 Jan 2023 08:14:09 +0000 Subject: first cut --- midi.c | 247 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 midi.c (limited to 'midi.c') 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