module alsatest; import std.string, std.math, std.random, std.time; alias Sample = short; class Sound { void open() { writeln "unoverridden open()! "; _interrupt 3; } void close() { writeln "unoverridden close()! "; _interrupt 3; } Sample[] copydump; void writeCopydump(int len) { writeln "unoverridden writeCopydump()! "; _interrupt 3; } void dump(Sample delegate(int) dg, int newlen, float mult) { if (copydump.length < newlen) copydump = new Sample[newlen]; for int i <- 0..newlen { auto value = Sample:short:int:(dg(i) * mult); copydump[i] = value; } writeCopydump(newlen); } void dump(Sample[] data, float mult) { if (copydump.length < data.length) copydump = new Sample[data.length]; for int i <- 0..data.length copydump[i] = Sample:short:int:(data[i]*mult); writeCopydump(data.length); } } c_include "alsa/pcm.h"; extern(C) { int snd_pcm_open(snd_pcm_t** pcm, char* name, int stream, int mode); int snd_pcm_set_params(snd_pcm_t*, int, int, int channels, int rate, int soft_resample, int latency); int snd_pcm_writei(snd_pcm_t*, void* buffer, int size); char* snd_strerror(int); } template check(alias A) <= 0 && f < end; } } class SineNote : Note { float baseCalc(float pos) { return sin(pos * freq * 2 * PI); } } class RectNote : Note { float baseCalc(float pos) { auto p2 = pos * freq; auto val = (p2 - floorf p2); if val > 0.5 val = 1; else val = 0; return val; } } class KarplusStrongNote : Note { float[] samples; float prevInt, prevRes; float baseCalc(float pos) { int length = int:(48000 / freq); if !samples.length { samples = new float[length]; auto rng = getPRNG 23; for int i <- 0..length samples[i] = randf(rng) * 2 - 1; prevInt = 0; prevRes = 0; } int c = int:(pos * freq * length) % length, d = (c + 1) % length; samples[d] = (samples[c] + samples[d]) * 0.5 * 0.999; alias C = 0.5; auto res = samples[d]; (float prev, prevInt) = (prevInt, res); res = C * res + prev - C * prevRes; prevRes = res; return res; } } void cfgNote(Note res, float freq, float len) { res.freq = freq; using res { maxVolume = 0.2; sustain = 0.1; decayStart = len * 0.05; sustainStart = len * 0.3; releaseStart = len * 0.7; end = len; } } template qsort(alias Smaller) << EOT1 template qsort(T) << EOT2 void qsort(T array) { void qsort_recurse(int from, to) { if (to == from || to == from + 1) return; if (to == from + 2) { if (!mixin(Smaller.replace("%a", "array[from]").replace("%b", "array[to - 1]"))) array[(from, to - 1)] = array[(to - 1, from)]; return; } int pivot = (to + from) / 2; auto pival = array[pivot]; array[(pivot, to - 1)] = array[(to - 1, pivot)]; pivot = to - 1; auto store = from; // thanks wp for int i <- from..to { if (mixin(Smaller.replace("%a", "array[i]").replace("%b", "pival"))) { array[(i, store)] = array[(store, i)]; store ++; } } array[(store, pivot)] = array[(pivot, store)]; qsort_recurse(from, store); qsort_recurse(store, to); } qsort_recurse(0, array.length); } EOT2 EOT1 void main() { auto snd = new AlsaSound("default"); snd.open(); float delegate(float) dg; int length = 1024; int fadeout = 4096; float[12] scale; for int i <- 0..12 scale[i] = pow(pow(2, 1/12f), i); (Note, float)[] notelist; void addNoteAt(float start, float freq, float length) { // writeln "addNoteAt($start, $freq, $length)"; (Note, float)[auto~] res; for int i <- 0..notelist.length res ~= notelist[i]; Note n = new KarplusStrongNote; cfgNote(n, freq, length); res ~= (n, start); notelist = res[]; } void addNoteAtRef(float* pp, float freq, float len) { alias pos = *pp; addNoteAt(pos, freq, len); pos += len; } void clearList(float pos) { (Note, float)[auto~] res; for int i <- 0..notelist.length { alias n = notelist[i]; if pos < (n[1] + n[0].end) res ~= n; } notelist = res[]; } void sortList() { qsort!"eval (%a[1]) < (%b[1])" notelist; for int i <- 1..notelist.length { if (notelist[i-1][1] > notelist[i][1]) { writeln "ERROR IN QUICKSORT AT $i. "; _interrupt 3; } } } void addTrack(string notes) { float baseFreq = 220; float len = 0.3705; int octaves; // int so it doesn't get fucked up by repeated mul/div float lenf = 1; float offs = 0; alias state = (baseFreq, len, lenf, offs); type-of state[] stack; char lastChar; while notes.length { (char cur, notes) = notes[(0, 1..$)]; if cur == "."[0] { lenf = 1.5; } else { auto l = len * lenf; void handleChar(char ch) { if ch == "c" addNoteAtRef(&offs, baseFreq * scale[0], l); else if ch == "d" addNoteAtRef(&offs, baseFreq * scale[2], l); else if ch == "e" addNoteAtRef(&offs, baseFreq * scale[4], l); else if ch == "f" addNoteAtRef(&offs, baseFreq * scale[5], l); else if ch == "g" addNoteAtRef(&offs, baseFreq * scale[7], l); else if ch == "a" addNoteAtRef(&offs, baseFreq * scale[9], l); else if ch == "b" addNoteAtRef(&offs, baseFreq * scale[10], l); else if ch == "h" addNoteAtRef(&offs, baseFreq * scale[11], l); else if ch == "_" offs += l; else if ch == ">" octaves ++; else if ch == "<" octaves --; else if ch == "+" len *= 2; else if ch == "-" len /= 2; else if ch == "[" stack ~= (baseFreq, len, lenf, offs); else if ch == "]" stack = stack[0 .. $-1]; else if ch == "," (baseFreq, len, lenf, offs) = stack[$-1]; } if (cur >= "0" && cur <= "9") { int num = cur - "0"; while notes.length && notes[0] >= "0" && notes[0] <= "9" { num = num * 10 + (notes[0] - "0"); notes = notes[1 .. $]; } for 0..(num - 1) handleChar lastChar; } else handleChar cur; lastChar = cur; lenf = 1; baseFreq = 220 * pow(2, octaves); } } } // addTrack "eec-de+dg-aeh>cceec-de+dg-aeh>cc= n[1]) { if f < n[1] + n[0].end res += n[0].calcValue (f - n[1]); } else { done = true; auto doneAt = i; if (doneAt != lastDoneAt) clearList f; lastDoneAt = doneAt; } i++; } return res; }; int base; while true { snd.dump(delegate Sample(int i) { auto res = Sample:short:int:(dg((base + i) / 48000f) * 32767f); return res; }, length, 1f); base += length; } snd.close(); }