rendered paste bodymodule 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) <<EOT void check(ParamTypes type-of &A a) { auto res = A a; if res == 0 return; else raise-error new Error "While calling $(string-of A): $res: $(CToString snd_strerror res)"; }EOTcontext Stream { alias Playback = 0;}context SndFormat { alias Unknown = -1; alias S8 = 0; alias U8 = 1; alias S16_LE = 2; alias S16_BE = 3; alias U16_LE = 4; alias U16_BE = 5;}context Access { alias MMap_Interleaved = 0; alias MMap_NonInterleaved = 1; alias MMap_Complex = 2; alias RW_Interleaved = 3; alias RW_NonInterleaved = 4;}class AlsaSound : Sound { snd_pcm_t* hdl; string name; void init(string n) { name = n; } void open() { auto foo = Stream.Playback; check!snd_pcm_open(&hdl, toStringz name, Stream.Playback, 0); check!snd_pcm_set_params(hdl, SndFormat.S16_LE, Access.RW_Interleaved, 1, 48000, 1, 400000); check!snd_pcm_nonblock(hdl, false); } void close() { check!snd_pcm_close(hdl); hdl = null; } void writeCopydump(int len) { bool closeAfter; if !hdl { open(); closeAfter = true; } onExit if (closeAfter) close(); auto cd = copydump[0 .. len]; while true { // writeln "< $(cd.length)"; // writeln "$cd"; auto frames = snd_pcm_writei(hdl, cd.ptr, cd.length); if (frames < 0) { writeln "Attempt to recover: $frames"; frames = snd_pcm_recover(hdl, frames, 0); } if (frames < 0) { writeln "Write failure! "; return; } if (frames < cd.length) { writeln "Short write: expected $(cd.length), got $frames"; cd = cd[frames .. $]; } return; } }}class Note { float maxVolume, sustain; float decayStart, sustainStart, releaseStart; float end; float freq; float calcVolume(float pos) { if pos < decayStart return pos * maxVolume / decayStart; if pos < sustainStart return maxVolume - (pos - decayStart) * (maxVolume - sustain) / (sustainStart - decayStart); if pos < releaseStart return sustain; if pos < end return sustain - (pos - releaseStart) * sustain / (end - releaseStart); return 0; } float baseCalc(float pos) { writeln "baseCalc in note: need override"; _interrupt 3; } float calcValue(float pos) { // auto val = sin(pos * freq * 2 * PI); auto val = baseCalc pos; val = val * 2 - 1; return val * calcVolume(pos); } bool contains(float f) { return eval f >= 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); } EOT2EOT1void 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 "<a>e<h>ec-de+dg-aeh>c<h->c<h+agegde+.c<-h+ a>e<h>ec-de+dg-aeh>c<h->c<h+ag++a"; // addTrack "<+fgaefgaefgaefg+a"; string str = "-efef+ga-efef+ga-cdcd+ef-cdcd+fe", st2; for 0..8 st2 ~= str; st2 ~= "-c8d8<b8f4e4>c8d8<b8++fg--b8>c8<a8>d4c4<b8>c8<a8>d4c4<b14__+>"; for 0..2 st2 ~= str; st2 ~= "-_>[f16,c16] [d16,<a16>][f16,c16] [d14__,<a14__>]<+"; st2 ~= "- <[f16,>c16<][d16,a16] [f16,>c16<][d15,a15]+>"; for 0..2 st2 ~= str; st2 ~= "-<b8>c8<a8>d4c4<b8>c8<a8>d4c4<b14__+>"; addTrack st2; sortList(); writeln "added $(notelist.length) notes"; int lastDoneAt; dg = delegate float(float f) { float res = 0f; bool done; int i; while !done && i < notelist.length { alias n = notelist[i]; if (f >= 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();}