#include #include #include #include #define SAMPLERATE 44100 #define MIDDLE_C 261.6255653005986346778499935233 /* based on A-440 */ #define SEMITONE 1.0594630943592952645618252949463417 /* 12th root of 2 */ #define STARTFUZZ (SAMPLERATE/100) typedef struct thread THREAD; typedef struct noteparams NOTEPARAMS; typedef struct playing PLAYING; struct noteparams { int note; int octave; int attack; int decay; } ; struct thread { unsigned int beatsshift; unsigned int beatsamples; unsigned int beats; NOTEPARAMS **pattern; NOTEPARAMS defparams; unsigned int beatat; unsigned int nextbeat; } ; struct playing { int age; NOTEPARAMS params; double pitch; double vol; double phase; int end; } ; static const int notepowers[5] = { 1, 3, 6, 8, 10 }; static int nthreads; static int athreads; static THREAD *threads; static int nplaying; static int aplaying; static PLAYING *playing; static unsigned int age; static void init_playing(void) { nplaying = 0; aplaying = 8; playing = malloc(aplaying*sizeof(*playing)); } static void init_threads(void) { nthreads = 0; athreads = 0; threads = 0; } static void start_note(NOTEPARAMS *def, NOTEPARAMS *np) { PLAYING *p; if (nplaying >= aplaying) playing = realloc(playing,(aplaying=nplaying+8)*sizeof(*playing)); p = &playing[nplaying]; p->age = - (random() % STARTFUZZ); p->params = *def; if (np->note >= 0) p->params.note = np->note; if (np->octave >= 0) p->params.octave = np->octave; if (np->attack >= 0) p->params.attack = np->attack; if (np->decay >= 0) p->params.decay = np->decay; p->pitch = MIDDLE_C * pow(SEMITONE,(p->params.octave*12)+notepowers[p->params.note]); p->vol = log(MIDDLE_C) / log(p->pitch); p->vol *= p->vol; p->phase = 0; p->end = p->params.attack + p->params.decay; nplaying ++; } static void start_notes(void) { int i; THREAD *t; NOTEPARAMS *p; for (i=nthreads-1;i>=0;i--) { t = &threads[i]; if (age >= t->beatat) { p = t->pattern[t->nextbeat]; if (p) start_note(&t->defparams,p); t->beatat += t->beatsamples; t->nextbeat ++; if (t->nextbeat >= t->beats) t->nextbeat = 0; } } } static void gen_sample(void) { double s; double v; int is; int i; PLAYING *p; s = 0; for (i=nplaying-1;i>=0;i--) { p = &playing[i]; if (p->age >= 0) { if (p->age < p->params.attack) { v = 1 - cos((p->age*M_PI)/p->params.attack); } else if (p->age < p->end) { v = 1 + cos(((p->age-p->params.attack)*M_PI)/p->params.decay); } else { nplaying --; if (i != nplaying) playing[i] = playing[nplaying]; continue; } s += v * p->vol * sin(p->phase); p->phase += (2 * M_PI * p->pitch) / SAMPLERATE; if (p->phase > M_PI) p->phase -= 2 * M_PI; } p->age ++; } is = (s * 3000) + 32768; if (is < 0) is = 0; else if (is > 65535) is = 65535; putchar(is>>8); putchar(is&0xff); putchar(is>>8); putchar(is&0xff); } static void mutate_start_or_kill(void) { int n; int i; THREAD *t; n = random() & 15; if (n > nthreads) { if (nthreads >= athreads) threads = realloc(threads,(athreads=nthreads+8)*sizeof(*threads)); t = &threads[nthreads]; t->beatsshift = 12 + (random() & 3); t->beatsamples = 1 << t->beatsshift; t->beats = (2 + (random() & 3)) << (random() & 3); t->pattern = malloc(t->beats*sizeof(*t->pattern)); for (i=t->beats-1;i>=0;i--) t->pattern[i] = 0; t->defparams.note = random() % 5; switch (random() % 10) { case 0: t->defparams.octave = -2; break; case 1: case 2: t->defparams.octave = -1; break; case 3: case 4: case 5: case 6: t->defparams.octave = 0; break; case 7: case 8: t->defparams.octave = 1; break; case 9: t->defparams.octave = 2; break; } t->defparams.attack = SAMPLERATE / 100; t->defparams.decay = SAMPLERATE / 4; t->pattern[0] = malloc(sizeof(*t->pattern[0])); *t->pattern[0] = t->defparams; t->beatat = ((age / t->beatsamples) + 1) * t->beatsamples; t->nextbeat = 0; fprintf(stderr,"start beatsshift=%d beats=%d note=%d octave=%d\n",t->beatsshift,t->beats,t->defparams.note,t->defparams.octave); nthreads ++; } else if (n < nthreads) { nthreads --; fprintf(stderr,"kill %d\n",n); t = &threads[n]; for (i=t->beats-1;i>=0;i--) free(t->pattern[i]); free(t->pattern); if (n < nthreads) threads[n] = threads[nthreads]; } } static void mutate_voice_or_silent(void) { int silentbeats; int voicedbeats; int tx; THREAD *t; int px; int n; silentbeats = 0; voicedbeats = 0; for (tx=nthreads-1;tx>=0;tx--) { t = &threads[tx]; for (px=t->beats-1;px>=0;px--) { if (t->pattern[px]) voicedbeats ++; else silentbeats ++; } } n = random() % (silentbeats + voicedbeats + 1); if (n < voicedbeats) { for (tx=nthreads-1;tx>=0;tx--) { t = &threads[tx]; for (px=t->beats-1;px>=0;px--) { if (t->pattern[px]) { if (n < 1) { free(t->pattern[px]); t->pattern[px] = 0; fprintf(stderr,"silence %d %d\n",tx,px); return; } n --; } } } } else if (n > voicedbeats) { n -= voicedbeats; for (tx=nthreads-1;tx>=0;tx--) { t = &threads[tx]; for (px=t->beats-1;px>=0;px--) { if (! t->pattern[px]) { if (n < 1) { t->pattern[px] = malloc(sizeof(*t->pattern[px])); *t->pattern[px] = t->defparams; fprintf(stderr,"voice %d %d\n",tx,px); return; } n --; } } } } } /* * Mutations possible: * * - Start new thread * - Kill existing thread * - Change an existing note parameter * - Change an existing thread default parameter * - Silence a thread beat * - Voice a silent thread beat * - Double/triple thread pattern length * - Introduce half beats * - Drop half beats * * Probabilities change depending on existing state. */ static void mutate(void) { if (!nthreads || !(random() & 63)) { mutate_start_or_kill(); return; } if (! (random() & 15)) { mutate_voice_or_silent(); return; } } int main(void); int main(void) { srandom(time(0)); init_playing(); init_threads(); age = 0; while (1) { start_notes(); gen_sample(); if (! (random() & 32767)) mutate(); age ++; } }