( var formatPatternData; //~~~~~~~~~~~~ALLOCATE BUSSES~~~~~~~~~~~~ ~allocBusses = {arg server; var preampBusses, accompBusses, postampBusses; preampBusses = 3.collect({Bus.audio(server, 1)}); accompBusses = 4.collect({Bus.audio(server, 1)}); postampBusses = 7.collect({Bus.audio(server, 1)}); [preampBusses, accompBusses, postampBusses]; }; //~~~~~~~~~~~~DEFINE SYNTHS~~~~~~~~~~~~ ~defineSynths = {arg server, preampBusses, accompBusses, postampBusses; var sdPlayer, sdTransport, sdClick, sdAmpCurve, sdEns, sdAccomp, sdClip, sdBass, sdDiskOut, allSds; sdPlayer = SynthDef(\masterPlayerControl_ ++ ~hash, { var sigs, sigsPanned, masterSig, imp; sigs = postampBusses.collect({arg bus, i; In.ar(bus) * NamedControl.kr(\vol_ ++ i, 1, 0.1)}); sigsPanned = sigs.collect({arg sig, i; Pan2.ar(sig * NamedControl.kr(\mute_ ++ i, 1, 0.1), NamedControl.kr(\pan_ ++ i, 0.5, 0.1))}); masterSig = Mix.ar(sigsPanned.drop(-1)) * NamedControl.kr("vol_master" ++ i, 1, 0.1) * NamedControl.kr("mute_master" ++ i, 1, 0.1); Out.ar(0, masterSig); Out.ar(2, sigsPanned.last); //change this if you want the click to go somewhere else imp = Impulse.kr(10); SendReply.kr(imp, '/masterLevels_' ++ ~hash, values: [Amplitude.kr(masterSig)]); sigs.collect({arg sig, i; SendReply.kr(imp, '/trackLevel_' ++ i ++ "_" ++ ~hash, values: [Amplitude.kr(sig)])}); }).add; sdTransport = SynthDef(\transport_ ++ ~hash, {arg measure = 0, beat = 0, section = 0, subsection = 0, gate = 1, dur = 1; SendReply.kr(Impulse.kr(0) * (measure > 0) * (beat > 0),'/measureClock_' ++ ~hash, values: [measure, beat, section, subsection]); //SendReply.kr(Impulse.kr(0),'/nextSubsection_' ++ ~hash); EnvGen.kr(Env.sine(dur), gate, doneAction: 2); }).add; sdClick = SynthDef(\click_ ++ ~hash, {arg beat = 0, gate = 1, dur = 1; Out.ar(postampBusses[6], 10 * BPF.ar(WhiteNoise.ar * EnvGen.kr(Env.perc(0.01, 0.1), gate), 440 * ((beat <= 1) + 1), 0.02)); EnvGen.kr(Env.sine(dur), gate, doneAction: 2); }).add; sdAmpCurve = SynthDef(\amp_curve_ ++ ~hash, {arg amp = 0, dur = 0.1, gate = 1, bus = 0; Out.kr(bus, amp) }).add; sdEns = SynthDef(\ens_ ++ ~hash, {arg freq = 440, amp = 1, dur = 1, gate = 1, bus = 0, ampBus = 0; Out.ar(bus, SinOsc.ar(freq, 2pi.rand, 0.1) * Latch.kr(In.kr(ampBus), Impulse.kr(0)) * EnvGen.kr(Env.asr(0.1, 1, 0.1), gate, doneAction: 2)) }).add; sdAccomp = SynthDef(\accomp_ ++ ~hash, {arg freq = 440, amp = 1, attack = 1, dur = 1, gate = 1, bout = 0, ampBus = 0; Out.ar(bout, SinOsc.ar(freq, 2pi.rand, 1) * 0.01 * amp * Latch.kr(In.kr(ampBus), Impulse.kr(0)) * EnvGen.kr(Env.asr(attack, 1, 0.01), gate, doneAction: 2)) }).add; sdClip = SynthDef(\clip_ ++ ~hash, {arg dur = 1, gate = 1, bin = 0, bus = 0; //Out.ar([0, 1], (In.ar(bin)).clip(0, 1) * pow(Line.kr(0, 1, 240), 2)) Out.ar(bus, (In.ar(bin)).clip(0, 1) * 50 * pow(Line.kr(0, 1, 10), 2)) }).add; sdBass = SynthDef(\bass_mono_ ++ ~hash, {arg freq = 440, ampBus = 0, bus = 0; Poll.kr(Changed.kr(In.kr(ampBus)), In.kr(ampBus)); Out.ar(bus, (SinOsc.ar(freq) * 0.4 * In.kr(ampBus))) }).add; sdDiskOut = SynthDef(\disk_out_ ++ ~hash, {arg bufnum, inbus; DiskOut.ar(bufnum, In.ar(inbus)); }).add; allSds = [sdPlayer, sdTransport, sdClick, sdAmpCurve, sdEns, sdAccomp, sdClip, sdBass, sdDiskOut]; allSds.do({arg sd; sd.add}); allSds }; // group data by measures for navigation formatPatternData = {arg musData, measureLen; var dataLen; dataLen = musData[0][0].size; musData.collect({arg partData; var res; res = partData.flop; res = res.add(res[1]); res[1] = (res[1].differentiate.drop(1) ++ [10]); res = res.flop ++ measureLen.collect({arg measure; dataLen.collect({0}) ++ [measure * 16]}); res = res.sort({arg a, b; a.last < b.last}).flop; res = res.insert(1, (res.last.differentiate.drop(1) ++ [10])).flop; res = res.separate({arg a, b; (a.last / 16).trunc != (b.last / 16).trunc}); res.collect({arg measureData; measureData.flop}) }).flop }; //~~~~~~~~~~~~GENERATE PATTERNS~~~~~~~~~~~~ //this generates patterns grouped by measures except for bass data and amp curve data which are much higher resolution //these are used to make playable patterns ~genPatterns = {arg ensData, accompData, bassData, ampData, sectionData, preampBusses, accompBusses, postampBusses; var measureLen, ensDataFormatted, accompDataFormatted, bassDataFormatted, ampDataFormatted, dUnit, section, subsection, patterns; measureLen = (( ensData.collect({arg partData; partData.last[1]}) ++ accompData.flatten.collect({arg partData; partData.last[1]}) ).maxItem.ceil(16) / 16).asInteger + 1; ensDataFormatted = formatPatternData.value(ensData, measureLen); accompDataFormatted = formatPatternData.value(accompData.flatten, measureLen); dUnit = 8.reciprocal; patterns = measureLen.collect({arg measure; if(sectionData[measure * 4] != nil, { section = sectionData[measure * 4][2]; subsection = sectionData[measure * 4][3]; }); Ppar( //check how amplitude is being handled ensDataFormatted[measure].collect({arg musData, m; Pbind( \instrument, \ens_ ++ ~hash, \freq, Pseq(musData[0].replace(0, Rest(0))), \dur, Pseq(musData[1] * dUnit), \sustain, Pseq(musData[2] * dUnit), \amp, [0.5, 0.3, 0.3, 0.3][m], //\amp, Pseq(musData[2].collect({arg item; [0, 0.25, 0.5, 0.75][item]}) * [1, 0.5, 0.25, 0.1][m] * 16); \ampBus, preampBusses[0].index, \bus, postampBusses[m].index ) }) ++ //check how amplitude and attack are being handled accompDataFormatted[measure].collect({arg musData, m; Pbind( \instrument, \accomp_ ++ ~hash, \freq, Pseq(musData[0].replace(0, Rest(0))), \dur, Pseq(musData[1] * dUnit), \sustain, Pseq(musData[2] * dUnit), \amp, Pseq(musData[3].collect({arg item; [0, 2, 4, 8][item]}) * 0.0125 * 1), //\attack, Pseq(musData[1] * abs(musData[3].clip(0, 0.25) * 2 - 1) * dUnit), \attack, Pseq(musData[2] * 1 * dUnit), \ampBus, preampBusses[0].index, \bout, Pseq(musData[4].collect({arg index; accompBusses[index].index})) ) }) ++ [ Pbind( \instrument, \transport_ ++ ~hash, \measure, measure + 1, \beat, Pseq([1, 2]), \section, section, \subsection, subsection, \dur, 8 * dUnit ), Pbind( \instrument, \click_ ++ ~hash, \beat, Pseq([1, 2]), \dur, 8 * dUnit ) ] ) }); [patterns, bassData, ampData] }; //this is a playable pattern based on start measure ~genPlayablePatterns = {arg startMeasure, patterns, preampBusses, accompBusses, postampBusses; Ppar( [Pseq(patterns[0][startMeasure..], 1)] ++ patterns[2].collect({arg pattern, p; Pmono(\amp_curve_ ++ ~hash, \amp, Pseg(Pseq(pattern.slice(nil, 0)[(startMeasure * 16)..], 1), 1 * ~dUnit, \cub), \dur, 1 * ~dUnit, \bus, preampBusses[p].index ) }) ++ patterns[1].collect({arg pattern, p; Pmono(\bass_mono_ ++ ~hash, \freq, Pseq(pattern.slice(nil, 0)[(startMeasure * 16)..], 1), \dur, 1 * ~dUnit, \ampBus, preampBusses[p + 1].index, \bus, postampBusses[4].index ) }) ); }; //~~~~~~~~~~~~BOUNCE AUDIO~~~~~~~~~~~~ //this bounces the audio for use in another DAW or for practice ~bounceAudio = {arg seed; var trackNames, basePath, server, buffers, recDur, preampBusses, nextNode, accompBusses, postampBusses, synths, prePatterns, playablePatterns, score; trackNames = ["part_start", "part_III", "part_II", "part_I", "accomp_I", "accomp_II", "click"]; basePath = ~dir +/+ ".." +/+ "audio" +/+ "seed_" ++ seed; basePath.mkdir; server = Server(\nrt_ ++ ~hash, options: ServerOptions.new .numOutputBusChannels_(7) .numInputBusChannels_(0) ); # preampBusses, accompBusses, postampBusses = ~allocBusses.value(s); postampBusses = 7.collect({arg index; Bus.new(rate: 'audio', index: index, numChannels: 1, server: server)}); synths = ~defineSynths.value(s, preampBusses, accompBusses, postampBusses); prePatterns = ~genPatterns.value(~musicData[0], ~musicData[1], ~musicData[2], ~musicData[3], ~sectionData, preampBusses, accompBusses, postampBusses); playablePatterns = ~genPlayablePatterns.value(0, prePatterns, preampBusses, accompBusses, postampBusses); recDur = (prePatterns[2][0].size / 8) + 10; score = playablePatterns.asScore(duration: recDur, timeOffset: 0.001); nextNode = score.score.slice(nil, 1).select({arg msg; msg[0] == 9}).slice(nil, 2).maxItem + 1; synths.do({arg synth; score.add([0.0, [\d_recv, synth.asBytes]])}); 4.collect({arg p; score.add([0.0, [\s_new, \clip_ ++ ~hash, nextNode, 1, 1, \bin, accompBusses[p].index, \bus, postampBusses[5].index]]); nextNode = nextNode + 1; }); buffers = 7.do({arg track; score.add([0.0, [\b_alloc, track, 65536, 1]]); score.add([0.0, [\b_write, track, basePath +/+ "tkam_" ++ trackNames[track] ++ ".wav".standardizePath, "WAV", "int16", 0, 0, 1]]); score.add([0.0, [\s_new, \disk_out_ ++ ~hash, nextNode, 1, 1, \bufnum, track, \inbus, track]]); score.add([recDur, [\n_free, nextNode]]); score.add([recDur, [\b_close, track]]); score.add([recDur, [\b_free, track]]); nextNode = nextNode + 1; }); score.sort; score.recordNRT( outputFilePath: basePath +/+ "tkam_all" ++ ".wav".standardizePath, sampleRate: 44100, headerFormat: "WAV", sampleFormat: "int16", options: server.options, duration: recDur ); server.remove; } )