#include <math.h>
#include <time.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
//#define MUSICINDEX 0
#define RATE 48000
#define NOISERATE 2000
#define NOISETIME 10
#define HALFCHUNK 60 // must be multiple of NOISETIME
#define CHUNKTIME (HALFCHUNK*2)
#define EARDIST 0.2
#define MAXDIST 16.0
#define MINDIST 1.0
#define BASEDIST 4.0
#define DROPBOOP 1.0
#define DROPT0 0.001
#define DROPT1 0.004
#define DROPT2 0.004//*10
#define DPS 100
#define NOISEVOL 0.25
#define PI 3.141592653589793
#define PRECISION 1e-6
typedef struct {
unsigned seed;
double dist;
double freq;
double voll;
double volr;
double boop;
double len0;
double len1;
double len2;
} Drop;
typedef struct {
const char *notes;
int step;
int dtone;
int steps;
} Music;
typedef struct {
unsigned seed;
unsigned musicidx[CHUNKTIME*RATE];
double samples[CHUNKTIME*RATE*2];
double noisel[CHUNKTIME*NOISERATE];
double noiser[CHUNKTIME*NOISERATE];
double noisev[CHUNKTIME/NOISETIME];
double noiseb[CHUNKTIME/NOISETIME];
} Chunk;
// --E4--
// D3
// --C3--
// B3
// --A3-- --c--
// g2 b
// ----f2-----------a-------------
// e2 G
// ----d2-----------F-------------
// c2 E
// ----b2-----------D--------------
// a2 C
// ----G------------B-------------
// F A
// ----E------------g-------------
// D f
// --C-- --e--
// B d
// --A-- --c--
// g0
// --f0-
Music musics[] = {
{ "----" // hotyd
" E:-B:-E-F#:-B:-F#-"
" G:-B:-G-A2:-G:-F#-"
" G----F#----G:F#:E-- ----"
" E:-B:-E-F#:-B:-F#-"
" G:-B:-G-A2:-G:-D2-"
" B2----A2:--G:F#:G:F#:E-- ---- B"
"----", RATE*60/192/4, 12 },
{ "----" // cot
" D#-D#-D#--D--C--A#--D----"
" D#-D#-D#--D--C--A#--C----"
" D#-D#-D#--D--C--D--F----"
" G#-G-G--D#-D--------"
" D#-D#-D#--D--C--A#--D----"
" D#-D#-D#--D--C--A#--C----"
" D#-D#-D#--D--C--D--F----"
" G#-G-G--D#-D"
"----", RATE*60/120/8, 12 },
{ "----" // cot
" D#-D#-D#--D--C--A#--D----"
" D#-D#-D#--D--C--A#--C----"
" D#-D#-D#--D--C--D--F----"
" G#-G-G--D#-D--------"
" D#-D#-D#--D--C--A#--D----"
" D#-D#-D#--D--C--A#--C----"
" D#-D#-D#--D--C--D--F----"
" G#-G-G--D#-D"
"----", RATE*60/120/8, 12 },
{ "----" // hal
":G0#:G0#C#:G0#:G0#D#:G0#:G0#C#:G0#:"
":G0#:G0#C#:G0#:G0#D#:G0#:G0#C#:G0#:"
":G0#:G0#C#:G0#:G0#D#:G0#:G0#E#:G0#: G0#F#:G0#:G0#E#:G0#:G0#D#:G0#:G0#C#:G0#:"
":G0#:G0#C#:G0#:G0#D#:G0#:G0#C#:G0#:"
":G0#:G0#C#:G0#:G0#D#:G0#:G0#C#:G0#:"
":G0#:G0#C#:G0#:G0#D#:G0#:G0#E#:G0#: G0#F#:G0#:G0#E#:G0#:G0#D#:G0#:G0#C#:G0#:"
"----", RATE*60/188/4, 12 }, // #DEFG
{ "----" // sun
" D-A-D-A-E-A-F-A-"
" D-A-D-A-E-G0-C-E-"
" D-A-D-A-E-A-F-A-"
" D-A-D-A-E-G0-C-E-"
" ---F-F--F-G--G-F-E-F----F-F-G--G-F-E-F--F-F-F-G-G-F--E-F--F-F-F-A2--A2-G-F-D--DF-DF-DF-EG-EG-DF-CE-DF--DF-DF-DF-FA2--FA2-EG-DF"
"----", RATE*60/56/16, 24 },
{ "----" // sun
" D-A-D-A-E-A-F-A-"
" D-A-D-A-E-G0-C-E-"
" D-A-D-A-E-A-F-A-"
" D-A-D-A-E-G0-C-E-"
" ---F-F--F-G--G-F-E-F----F-F-G--G-F-E-F--F-F-F-G-G-F--E-F--F-F-F-A2--A2-G-F-D--DF-DF-DF-EG-EG-DF-CE-DF--DF-DF-DF-FA2--FA2-EG-DF"
"----", RATE*60/56/16, 24 },
};
unsigned rnd(unsigned seed) { return seed*1103515245u + 12345u; }
unsigned rndu(unsigned *seed) { return *seed = rnd(*seed); }
double rndf(unsigned *seed) { return rndu(seed)/(double)UINT_MAX; }
double rndf2(unsigned *seed) { return rndf(seed)*2 - 1; }
double rndnorm(unsigned *seed) { return sqrt(-2*log(rndf(seed)))*sin(2*PI*rndf(seed)); }
double rndlognorm(unsigned *seed, double d) { return exp(d*rndnorm(seed)); }
double cubic(double *v, double t) {
double tt = t*t, ttt = tt*t;
return v[0]*( -0.5*ttt + tt - 0.5*t )
+ v[1]*( 1.5*ttt - 2.5*tt + 1.0 )
+ v[2]*( -1.5*ttt + 2.0*tt + 0.5*t )
+ v[3]*( 0.5*ttt - 0.5*tt );
}
void droplog(Drop *d, const char *prefix) {
fprintf(stderr, "%s: drop: d%g f%g v%g/%g/%g l%g/%g/%g #%x\n",
prefix, d->dist, d->freq, d->voll, d->volr, d->boop, d->len0, d->len1, d->len2, d->seed);
}
void dropgen(Drop *d, unsigned seed, double dist, double freq, double boop, double len0, double len1, double len2) {
double d0 = dist*dist + EARDIST*EARDIST;
double de = 2*EARDIST*dist*cos(rndf(&seed)*2*PI);
d->seed = seed;
d->dist = dist;
d->freq = freq;
d->boop = boop;
d->len0 = len0;
d->len1 = len1;
d->len2 = len2;
d->voll = 1/(1 + d0 - de);
d->volr = 1/(1 + d0 + de);
}
void dropgenfull(Drop *d, unsigned seed) {
double da = rndf(&seed);
double db = rndf(&seed);
double dist = MINDIST + (da > db ? da : db)*MAXDIST;
double freq = 3000*pow(2, rndf2(&seed));
double boop = DROPBOOP;
double len0 = DROPT0;//pow(2, rndf2(&seed));
double len1 = DROPT1;//pow(2, rndf2(&seed));
double len2 = DROPT2;//pow(2, rndf2(&seed));
dropgen(d, seed, dist, freq, boop, len0, len1, len2);
}
void dropbakeboop(Drop *d, double *samples, double vol, double len) {
//fprintf(stderr, "boop: v%g l%g\n", vol, len);
double dt = 1.0/RATE;
for(double t = 0; t < len; t += dt) {
double x = 2*t/len - 1;
x *= x;
if (x < PRECISION) continue;
double v = vol*sqrt(1 - x);
*samples++ += v*d->voll;
*samples++ += v*d->volr;
}
}
void dropbakewave(Drop *d, double *samples, double freq, double vol, double len) {
//fprintf(stderr, "wave: f%g v%g l%g\n", freq, vol, len);
double dt = 1.0/RATE;
double t1 = len*10;
double kv = pow(0.5, dt/len);
double ka = freq*2*PI;
for(double t = 0; t < t1; t += dt, vol *= kv) {
double v = sin(t*ka)*vol*pow(0.5, t/len);
*samples++ += v*d->voll;
*samples++ += v*d->volr;
}
}
void dropbake(Drop *d, double *samples) {
unsigned seed = d->seed;
double vb = rndu(&seed)%2*2.0 - 1;
double vw = rndu(&seed)%2*2.0 - 1;
dropbakeboop(d, samples, vb*d->boop, d->len0); // boop
samples += (int)(d->len1*RATE + 0.5)*2; // silence
dropbakewave(d, samples, d->freq, vw, d->len2); // wave
}
int readnote(const char **s, int tone0, double *freq, int *steps) {
static const int tones[] = { 0, 2, 3, 5, 7, 8, 10 };
char c = **s;
if (!c) return 0;
double f = 0;
if (c >= 'A' && c <= 'G') {
++*s;
int tone = tone0 + tones[c - 'A'];
while(1) {
c = **s;
if (c == '#') ++tone; else
if (c == 'b') --tone; else
if (c >= '0' && c <= '9') tone += (c - '1')*12; else
break;
++*s;
}
f = 440*pow(2, tone/12.0);
}
int st = 0;
while(1) {
c = **s;
if (!c) break;
if (c >= 'A' && c <= 'G') break;
st += c == '.' ? 1
: c == ':' ? 2
: c == '-' ? 4 : 0;
++*s;
}
if (freq) *freq = f;
if (steps) *steps = st;
return 1;
}
int musicsteps(const char *music) {
int steps = 0, s = 0;
while(readnote(&music, 0, NULL, &s)) steps += s;
return steps;
}
void musicbake(double *samples, unsigned seed, const char *music, int step, int dtone, int maxsteps) {
int steps = 0;
double freq = 0;
int sumsteps = 0;
while(readnote(&music, dtone, &freq, &steps)) {
if (freq) {
Drop d = {};
double dk = sumsteps/(double)maxsteps;
double dist = BASEDIST*(1 + 0.5*dk*dk);
dropgen(&d, rndu(&seed)^8, dist, freq, DROPBOOP*0.5, DROPT0, DROPT1, DROPT2*4);
dropbake(&d, samples);
//droplog(&d, "music");
}
//if (steps) fprintf(stderr, "steps: %d\n", steps);
samples += steps*step*2;
sumsteps += steps;
}
}
void initmusics() {
int mcnt = sizeof(musics)/sizeof(*musics);
for(int i = 0; i < mcnt; ++i) {
Music *m = musics + i;
m->steps = musicsteps(m->notes);
fprintf(stderr, "%s: %d\n", m->notes, m->steps);
if (m->steps*m->step >= HALFCHUNK*RATE) fprintf(stderr, "bad music steps: %s: %d*%d\n", m->notes, m->steps, m->step);
}
}
void genmusic(Chunk *c, int half) {
unsigned seed = rndu(&c->seed)^9;
int mcnt = sizeof(musics)/sizeof(*musics);
for(int i = 0; i < 5; ++i) {
#ifdef MUSICINDEX
Music *m = musics + MUSICINDEX;
int mlen = m->steps*m->step;
int sidx = 0;
#else
Music *m = musics + rndu(&seed)%mcnt;
int mlen = m->steps*m->step;
int sidx = rndu(&seed)%(HALFCHUNK*RATE - half*(mlen + 2*RATE));
#endif
unsigned *mp = c->musicidx + sidx;
if (*mp%2 || mp[mlen] != *mp)
continue;
fprintf(stderr, "%s: %g\n", m->notes, sidx/(double)RATE);
if (sidx + mlen >= CHUNKTIME*RATE) fprintf(stderr, "overrun: %s: %d + %d\n", m->notes, sidx, mlen);
for(unsigned *me = mp + mlen; mp < me; ++mp) ++*mp;
for(unsigned *me = c->musicidx + CHUNKTIME*RATE; mp < me; ++mp) *mp += 2;
fprintf(stderr, "%d\n", *(mp-1));
musicbake(c->samples + sidx*2, rndu(&seed)^8, m->notes, m->step, m->dtone, m->steps);
}
}
void genrain(Chunk *c) {
int count = DPS*HALFCHUNK;
unsigned seed = rndu(&c->seed)^2;
while(count--) {
if (count%DPS == 0) { fprintf(stderr, "."); fflush(stderr); }
Drop d = {};
dropgenfull(&d, rndu(&seed)^3);
int sidx = rndu(&seed)%(HALFCHUNK*RATE);
if (d.dist < MINDIST && c->musicidx[sidx]%2) continue;
if (d.dist < MINDIST) continue;
//droplog(&d, "rain");
dropbake(&d, c->samples + sidx*2);
}
fprintf(stderr, "\n");
}
void gennoise(Chunk *c) {
unsigned seed = c->seed^17;
size_t cntvb = CHUNKTIME/NOISETIME/2;
size_t cntlr = CHUNKTIME*NOISERATE/2;
for(double *n = c->noisev + cntvb + 2, *e = n + cntvb - 2; n < e; ) *n++ = rndf2(&seed);
for(double *n = c->noiseb + cntvb + 2, *e = n + cntvb - 2; n < e; ) *n++ = rndf2(&seed);
for(double *n = c->noisel + cntlr + 2, *e = n + cntlr - 2; n < e; ) *n++ = rndf2(&seed);
for(double *n = c->noiser + cntlr + 2, *e = n + cntlr - 2; n < e; ) *n++ = rndf2(&seed);
double *nv = c->noisev;
double *nb = c->noiseb;
double *nl = c->noisel;
double *nr = c->noiser;
double tvb = 0, tlr = 0;
double dtvb = 1.0/RATE/NOISETIME;
double dtlr = NOISERATE/(double)RATE;
for(double *s = c->samples, *e = s + HALFCHUNK*RATE*2; s < e; tvb += dtvb, tlr += dtlr) {
while(tvb > 1) { tvb -= 1; ++nv; ++nb; }
while(tlr > 1) { tlr -= 1; ++nl; ++nr; }
double v = (1 + cubic(nv, tvb)*0.7)/2 * NOISEVOL;
double b = (1 + cubic(nb, tvb)*0.5)/2;
*s++ += v*cubic(nl, tlr)*b;
*s++ += v*cubic(nr, tlr)*(1-b);
}
}
void movehalf(void* p, size_t size) {
size /= 2;
memcpy(p, (char*)p + size, size);
memset((char*)p + size, 0, size);
}
void rotate(Chunk *c) {
c->seed = rnd(c->seed^1);
movehalf(c->samples, sizeof(c->samples));
movehalf(c->musicidx, sizeof(c->musicidx));
movehalf(c->noisev, sizeof(c->noisev));
movehalf(c->noiseb, sizeof(c->noiseb));
movehalf(c->noisel, sizeof(c->noisel));
movehalf(c->noiser, sizeof(c->noiser));
unsigned *m = c->musicidx + HALFCHUNK*RATE, v = *(m-1);
if (v%2) { fprintf(stderr, "bad musicidx: %d\n", v); ++v; }
for(unsigned *e = m + HALFCHUNK*RATE; m < e; ++m) *m = v;
}
void out(Chunk *c, double k) {
//fprintf(stderr, "out: %g\n", k);
for(double *s = c->samples, *e = s + HALFCHUNK*RATE*2; s < e; ++s) {
//fprintf(stderr, "s: %g\n", *s);
double d = *s*k;
d = 1/(1+exp(-d));
//fprintf(stderr, "s*k: %g * %g = %g\n", *s, k, d);
int i = d*65536;
if (i < 0) i = 0;
if (i > 65535) i = 65535;
i -= 32768;
fputc(i & 0xff, stdout);
fputc((i >> 8) & 0xff, stdout);
}
}
Chunk chunk;
int main(int argc, char **argv) {
int count = 0;
unsigned seed = time(NULL);
if (argc > 1) count = atoi(argv[1]);
if (argc > 2) seed = atoll(argv[2]);
fprintf(stderr, "aseed: %u\n", seed);
fprintf(stderr, "arepeats: %u\n", count);
initmusics();
if (count <= 0) count = INT_MAX;
chunk.seed = seed;
gennoise(&chunk);
rotate(&chunk);
unsigned seed0 = chunk.seed;
#ifndef MUSICINDEX
genmusic(&chunk, 1);
genrain(&chunk);
gennoise(&chunk);
#endif
rotate(&chunk);
unsigned seed1 = chunk.seed;
#ifndef MUSICINDEX
genmusic(&chunk, 0);
genrain(&chunk);
gennoise(&chunk);
#endif
fprintf(stderr, "begin: %u\n", count);
while(count--) {
rotate(&chunk);
if (count == 1) chunk.seed = seed0;
if (count == 0) chunk.seed = seed1;
genmusic(&chunk, count == 0);
#ifndef MUSICINDEX
genrain(&chunk);
gennoise(&chunk);
#endif
out(&chunk, 1/2.0);
fprintf(stderr, "remain: %u\n", count);
}
return 0;
}