/* This file is part of "Interface Fractures III - Silicon". Copyright (c) 2015 Luka Prinčič, All rights reserved. This program is free software distributed under GNU General Public Licence. See COPYING for more info. - - - - - - - - - - - - - - - - - - - - - - - - - - - - snd_lines.scd - deals with converting spectral lines of silicon into sound in time... ********************************************************** */ // function procLines: process lines and store/return List postln("~~~ function ~procLinesFunc ..."); ~procLinesFunc = { // process lines, return na array arg location = PathName(thisProcess.nowExecutingPath).pathOnly +/+ "../dev/silicon_lines.txt"; var siLines = FileReader.read(path: location, skipBlanks:true, skipEmptyLines:true); var intensity, minutes, seconds, freq1, freq2, midi1, midi2, red, green, blue; var threshold = 0; var linewidth = 3; var spectrumheight = 750; var colorpenlen = 40; var siLinesData, processItem; var colors = [ // these are taken from http://astro.u-strasbg.fr/~koppen/discharge/discharge.html [ 3800.0, 0, 0, 0], [ 4000.0, 150, 0, 150], [ 4400.0, 120, 0, 255], // violet [ 4500.0, 0, 0, 255], // blue [ 4800.0, 0, 255, 255], // cyan [ 5200.0, 0, 255, 0], // green [ 5800.0, 255, 255, 0], // yellow [ 6500.0, 255, 0, 0], // red [ 7000.0, 80, 0, 0], [ 7300.0, 0, 0, 0] ]; siLinesData = List.newClear(0); processItem = { |item, i, reverse=false, layer=0, tscale=1, toffset=0| if((item[1].asInt > threshold) && (item[0].asInt < 6741) && (item[0].asInt > 3950), { var color; var time, time_end = 60*37+5; var alpha = item[1].asFloat.linlin(0, 1000, 0, 255); intensity = item[1].asInt; // time if(reverse == false, // then { time = (item[0].asFloat.linlin(3950, 6741, 0, time_end)); }, // else: { time = (item[0].asFloat.linlin(3950, 6741, time_end, 0)); } ); time = time * tscale ; time = time + (toffset * time_end); minutes = (time / 60).round(1); seconds = (time % 60).round(1); freq1 = item[0].asFloat.linexp(3900, 7200, 16000, 40 ).asInt; freq2 = item[0].asFloat.linexp(3900, 7200, 40, 16000 ).asInt; midi1 = item[0].asFloat.linlin(3900, 7200, 0, 12 ).round(0.01); midi2 = item[0].asFloat.linlin(3900, 7200, 12, 0 ).round(0.01); colors.do({ |colordb, i| var thisboundary = colors[i][0]; if (i+1 < colors.size, { var nextboundary = colors[i+1][0]; if ((item[0].asFloat > thisboundary) && (item[0].asFloat < nextboundary), { var red = colors[i][1] + ( ( (colors[i+1][1] - colors[i][1]) / (colors[i+1][0] - colors[i][0]) ) * (item[0].asFloat - colors[i][0]) ); var green = colors[i][2] + ( ( (colors[i+1][2] - colors[i][2]) / (colors[i+1][0] - colors[i][0]) ) * (item[0].asFloat - colors[i][0]) ); var blue = colors[i][3] + ( ( (colors[i+1][3] - colors[i][3]) / (colors[i+1][0] - colors[i][0]) ) * (item[0].asFloat - colors[i][0]) ); var color = Color.new255(red.round(1),green.round(1),blue.round(1), alpha); var colorHSV = color.asHSV; // add line data as a List to the siLinesData List siLinesData.add(List[ time, // [0] layer, // [1] item[0], // [2] intensity, // [3] freq1, freq2, // [4], [5] midi1, midi2, // [6], [7] // R, G, B, [8] [9] [10] red.round(1), green.round(1), blue.round(1), // H, S, V, Alpha [11][12][13][14] (colorHSV[0] * 360).round(1), // hue (colorHSV[1] * 100).round(1), // saturation (colorHSV[2] * 100).round(1), // value (colorHSV[3] * 100).round(1) // ALPHA ]); // end of siLinesData.add }); // endif wavelength inside this boundary }); // endif below last item (prevents error going beyond List's boundary) }); // end colors.do }); // endif threshold and inside min/max in the spectrum }; // end of function processItem // process lines four times with offsets and scales with golden ratios siLines.do({ |item, i| processItem.value(item, i, layer:0); }); siLines.do({ | item, i| processItem.value(item, i, layer:1, reverse:true, tscale:0.618); }); siLines.do({ | item, i| processItem.value(item, i, layer:1, reverse:true, tscale: 0.618.squared, toffset:0.618); }); siLines.do({ | item, i| processItem.value(item, i, layer:2, reverse:false, tscale: 0.618.cubed, toffset:(0.618.squared-0.618.cubed)+0.618); }); // return ? siLinesData; }; // process lines, store to a List postln("~~~ process lines, store to a list: ~siLinesData, sort ..."); ~siLinesData = ~procLinesFunc.value; // sort 2D ~siLinesData = ~siLinesData.sortBy(0); // FUNCTION: display linesData list in a view/window postln("~~~ function ~viewLinesFunc ..."); ~viewLinesFunc = { arg siLinesData = ~siLinesData; // all we need is a List of data var intensity, layer, alpha, peny, penx, color, time, swin; var threshold = 0; var linewidth = 3; var spectrumheight = 750; var colorpenlen = 40; var alphamin = 10; var time_end = 60*37+5; // View/Window starts here swin = Window("TempSpectre", Rect(600, 5, width:560, height:spectrumheight)); swin.view.background_(Color.black); swin.drawFunc = { Pen.smoothing_(false); Pen.translate(0,0); Pen.fillColor = Color.white; Pen.fillRect(Rect(colorpenlen * 3, 0, w.bounds.width - (colorpenlen * 3), spectrumheight)); siLinesData.do({ |ldata, i| alpha = ldata[14].asFloat.linlin(0, 100, alphamin, 255); layer = ldata[1].asInt; spectrumheight = w.bounds.height; intensity = ldata[3].asInt; time = ldata[0]; color = Color.new255(ldata[8], ldata[9], ldata[10], alpha); peny = time.linlin(0, time_end, 0, spectrumheight); penx = 2 - layer * colorpenlen; // colored lines on black Pen.fillColor = color; Pen.fillRect(Rect(penx, peny, colorpenlen, alpha.linlin(0,255,1,linewidth))); // black/gray lines on white Pen.fillColor = Color.black.alpha_(alpha.linlin(0,255,0,1)); Pen.fillRect(Rect( colorpenlen*3, peny, w.bounds.width - (colorpenlen * 3) * intensity.linlin(0,1000,0,1), alpha.linlin(0,255,1,linewidth))); Pen.stroke; // draw the path }); // end of siLinesData.do }; // end of drawFunc{} swin.refresh; swin.front; }; // end of viewLines function /* ( // DEBUG/test: // display table of all data to post window ~siLinesData.do({ |item, i| post(i + ""); item.do({ |value, i| post(i); post(":"); post(value); post(" "); }); post("\n"); }); ) */ // this could be bound to a button, // or ideally expanded to an animated timeline // and included in main window...: // show a window/view with lines - score // ~viewLinesFunc.value; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // move to sdefs.scd at one point? -------------------------------------------- SynthDef(\siliconSynth, { |freq = 440, amp = 0.2, attack = 1, release = 1| var sig, env, frequency, amply; frequency = [freq * 1.005, freq]; amply = amp * 0.3; env = EnvGen.kr(Env.perc(attack, release, 1, [10, -10]), doneAction: 2); // ENVELOPE sig // ----------------------------------------------------------------------------------------- = SinOsc.ar( freq: frequency, mul: amply * 0.9) + SinOsc.ar( freq: frequency * 0.5 * LFNoise1.kr(0.1).linlin(-1, 1, 0.998, 1.002), mul: amply) + SinOsc.ar( freq: frequency * 0.25 * LFNoise1.kr(0.1).linlin(-1, 1, 0.998, 1.002), mul: amply) + Saw.ar( freq: frequency * LFNoise1.kr(0.1).linlin(-1, 1, 0.998, 1.002), mul: amply * 0.5) + Saw.ar( freq: frequency * 0.5 * LFNoise1.kr(0.1).linlin(-1, 1, 0.998, 1.002), mul: amply * 0.5) + Saw.ar( freq: frequency * 0.25 * LFNoise1.kr(0.1).linlin(-1, 1, 0.998, 1.002), mul: amply * 0.5) + Pulse.ar( freq: frequency * LFNoise1.kr(0.1).linlin(-1, 1, 0.998, 1.002), mul: amply * 0.5) + Pulse.ar( freq: frequency * 0.5 * LFNoise1.kr(0.1).linlin(-1, 1, 0.998, 1.002), mul: amply * 0.5) + Pulse.ar( freq: frequency * 0.24 * LFNoise1.kr(0.1).linlin(-1, 1, 0.998, 1.002), mul: amply * 0.5) + BPF.ar(BrownNoise.ar(), freq: frequency * LFNoise1.kr(0.01).linlin(-1, 1, 0.8, 1.2), rq: 0.1) + BPF.ar(WhiteNoise.ar(), freq: frequency * 0.5 * LFNoise1.kr(0.01).linlin(-1, 1, 0.8, 1.2), rq: 0.1) + BPF.ar(Crackle.ar(SinOsc.kr(0.1).linlin(-1, 1, 0.8, 2)), frequency * LFNoise1.kr(0.01).linlin(-1, 1, 0.8, 1.2), rq:0.1, mul:2) + BPF.ar(Crackle.ar(SinOsc.kr(0.1).linlin(-1, 1, 0.8, 2)), frequency * 0.5 * LFNoise1.kr(0.01).linlin(-1, 1, 0.8, 1.2), rq:0.1, mul:2) ; // ------------------------------------------------------------------------------------------- sig = sig * env; // envlope the final sound Out.ar(0, sig); // output postln("\n~~~ adding SynthDef: siliconSynth ..."); }).add; // ----------------------------------------------------------------------------- postln("~~~ loading mainTimeline routine"); post(" "); ~mainTimeline = Routine({ var delta, playhead, frequency, amp, step=0, nextdelta, nextplayhead, nextfrequency, nextamp, frequency_alt, nextfrequency_alt, siLines; siLines = ~siLinesData; postln(siLines); postln("debug"); while { step < siLines.size; } { // exposition //frequency = siLines[step][0].asFloat.linexp(3900, 7200, 16000, 40); frequency = siLines[step][4].asFloat; // frequency_alt = siLines[step][0].asFloat.linexp(3900, 7200, 40, 16000); frequency_alt = siLines[step][5].asFloat; playhead = siLines[step][0].asFloat; nextfrequency = siLines[step+1][4].asFloat; nextfrequency_alt = siLines[step+1][5].asFloat; nextplayhead = siLines[step+1][0].asFloat; delta = nextplayhead - playhead; amp = siLines[step][3].asInt * 0.001; nextamp = siLines[step+1][3].asInt * 0.001; nextdelta = siLines[step+2][0].asFloat - nextplayhead; //action::documentation post("\n~" + step.asString.padLeft(3)); post("|"); post(siLines[step][1]); post("| "); post((playhead/60).round(1).asString.padLeft(2)); post(":"); post((playhead%60).round(1).asString.padLeft(2); post(" | "); // post(siLines[step][2]); // post( "nm | " ); post(frequency.round(1).asString.padLeft(5)); post("Hz | "); post(frequency_alt.round(1).asString.padLeft(5)); post("Hz | "); post(amp.ampdb.round(1)); post("dB \\ "); post(delta.round(1)); postln("s"); post(" |"); post(siLines[step+1][1]); post("| "); post(delta.round(1)); post("s / "); post(nextfrequency.round(1).asString.padLeft(5)); post("Hz | "); post(nextfrequency_alt.round(1).asString.padLeft(5)); post("Hz | "); post(nextamp.ampdb.round(1)); post("dB \\ "); post(nextdelta.round(1)); postln("s"); Synth(\siliconSynth, [ freq: nextfrequency, amp: nextamp, attack: delta, release: nextdelta ]); Synth(\siliconSynth, [ freq: nextfrequency_alt, amp: nextamp, attack: delta, release: nextdelta ]); // epilogue step = step + 1; delta.yield; } }); TempoClock.default.tempo = 1; // beats/sec /BPS ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // DEBUG //~siLines = FileReader.read(path:"../dev/silicon_lines.txt", skipBlanks:true, skipEmptyLines:true); //~siLines[124][0].postln; //siLines.size.postln; //r.next; //r.stop; // if (siLines[step-1].notNil, { // playhead = siLines[step-1][0].asFloat.linlin(3900, 7200, 0, (60*37+5)); // at the end these are seconds! // }, { playhead = 0 }); // nextdelta = siLines[step][0].asFloat.linlin(3900, 7200, 0, (60*37+5)) - playhead; // delta = playhead - prevplayhead; // prevplayhead = playhead; /* post("t:" + item[0].asFloat.linlin(3900, 7200, 0, 12 ).asStringPrec(5) + Char.tab ); // tone post("t:" + item[0].asFloat.linlin(3900, 7200, 12, 0 ).asStringPrec(5) ); // tone reverse //{SinOsc.ar((item[0].asFloat.linlin(3900, 7200, 0, 12)+60).midicps, mul:item[1].asInt * 0.000005)}.play; /*{LFPar.ar(( item[0].asFloat.linlin(3900, 7200, 0, 12) + ( ( (i+1) % 1) * 12) + 60).midicps, mul:(item[1].asInt.linlin(0, 1000, 0.0, 0.5) + item[1].asInt.linexp(0, 1000, 0.01, 0.5)) * [i%2,abs(i%2-1)] * 0.05)}.play; */ //{SinOsc.ar(item[0].asFloat.linexp(3900, 7200, 20000, 40 ).asInt, mul:item[1].asInt * 0.000005)}.play; {SinOsc.ar(item[0].asFloat.linexp(3900, 7200, 20000, 40).asInt, mul:item[1].asInt * 0.00005)}.play; */