#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; }