From 9fbf91db06a6d4f4b5cd8bb45389a731bb86bf22 Mon Sep 17 00:00:00 2001 From: Richard Date: Sun, 13 Apr 2025 18:48:02 +0100 Subject: initial --- site/udo/sequencing.udo | 329 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 329 insertions(+) create mode 100755 site/udo/sequencing.udo (limited to 'site/udo/sequencing.udo') diff --git a/site/udo/sequencing.udo b/site/udo/sequencing.udo new file mode 100755 index 0000000..ac4fd40 --- /dev/null +++ b/site/udo/sequencing.udo @@ -0,0 +1,329 @@ +#ifndef UDO_SEQUENCING +#define UDO_SEQUENCING ## +/* + Sequencing base + + This file is part of the SONICS UDO collection by Richard Knight 2021 + License: GPL-2.0-or-later + http://1bpm.net +*/ + +gkseq_tempo init 120 ; tempo BPM +gkseq_beat init 0 ; trigger fired on each beat +gkseq_beattime init 0 ; time in seconds of one beat (read only; set by BPM) +gkseq_quartertime init 0 ; time in seconds of one quarter beat (read only; set by BPM) +gkseq_beathz init 0 ; Hz of one beat (read only; set by BPM) +gkseq_swing init 0.2 ; swing amount +gkseq_on init 1 + +/* + Instrument to control the main beat metronome and beat time globals +*/ +instr _seq_manager + kseq_beat metro gkseq_tempo / 60 + if (gkseq_on == 1) then + gkseq_beat = kseq_beat + endif + gkseq_beattime = 60 / gkseq_tempo + gkseq_quartertime = gkseq_beattime / 4 + gkseq_beathz = (1 / 60) * gkseq_tempo +endin +;alwayson "_seq_manager" ; not available in web api +schedule("_seq_manager", 0, -1) + + + +/* + Get the swung time for a given time, if appropriate. If the index given is a second 16th, time will be swung + + kresult seq_swingtime ktime, kindex, kswing + + kresult resulting time + ktime the time to consider + kindex beat index, beginning with 0 + kswing the swing amount (0 to 1) +*/ +opcode seq_swingtime, k, kkJ + ktime, kindex, kswing xin + kswing = (kswing == -1) ? gkseq_swing : kswing + if ((kindex+1) % 2 == 0) then + ktime = ktime + (gkseq_quartertime*kswing) + endif + xout ktime +endop + + +/* + Get the swung time for a given time, if appropriate. If the index given is a second 16th, time will be swung + + iresult seq_swingtime itime, iindex, iswing + + iresult resulting time + itime the time to consider + iindex beat index, beginning with 0 + iswing the swing amount (0 to 1) +*/ +opcode seq_swingtime, i, iij + itime, index, iswing xin + iswing = (iswing == -1) ? i(gkseq_swing) : iswing + if ((index+1) % 2 == 0) then + itime = itime + (i(gkseq_quartertime)*iswing) + endif + xout itime +endop + + +/* + Set the tempo in BPM + DEPRECATED: just use init or direct assignment to gkseq_tempo + + seq_settempo ktempo + + ktempo the tempo in BPM +*/ +opcode seq_settempo, 0, k + ktempo xin + gkseq_tempo = ktempo +endop + + +/* + Set the tempo in BPM; typically for host control + + p4 the tempo in BPM +*/ +instr seq_settempo + itempo = p4 + gkseq_tempo = itempo + turnoff +endin + + + + +/* + Basic sequencer: trigger an instrument on each beat + + seq_basic SdespatchI + + SdespatchI name of the instrument to trigger +*/ +opcode seq_basic, 0, S + SdespatchI xin + schedkwhen gkseq_beat, 0, 0, SdespatchI, 0, 1 +endop + + + +/* + Basic swung sequencer: trigger an instrument on each 16th beat with + p4 as the cyclical index between 0 and 3 + p5 as the total number of beats beginning at one (eg so modulus can be used to determine position etc) + + seq_swing SdespatchI, kswing + + SdespatchI name of the instrument to trigger + kswing swing amount (0 to 1) +*/ +opcode seq_swing, 0, SJ + SdespatchI, kswing xin + kswing = (kswing == -1) ? gkseq_swing : kswing + kbeatnum init 1 + if (gkseq_beat == 1) then + kswingamount = gkseq_quartertime*kswing + event "i", SdespatchI, 0, 1, 0, kbeatnum + event "i", SdespatchI, gkseq_quartertime+kswingamount, 1, 1, kbeatnum + event "i", SdespatchI, gkseq_quartertime*2, 1, 2, kbeatnum + event "i", SdespatchI, (gkseq_quartertime*3)+kswingamount, 1, 3, kbeatnum + kbeatnum += 1 + endif +endop + + + +/* + Basic array sequencer. Treat each index as a 16th, and if it is 1, trigger the despatch instrument + + seq_array SdespatchI, karray[], kswing + + SdespatchI name of the instrument to trigger + karray[] array of sequence information + kswing swing amount (0 to 1) +*/ +opcode seq_array, 0, Sk[]k + SdespatchI, karray[], kswing xin + kindex init 0 + if (gkseq_beat == 1) then + ktime = 0 + kcount = 0 + while (kcount < 4) do + if (karray[kindex] == 1) then + event "i", SdespatchI, seq_swingtime(ktime, kcount, kswing), 0.1 + endif + + if (kindex + 1 >= lenarray(karray)) then + kindex = 0 + else + kindex += 1 + endif + kcount += 1 + ktime += gkseq_quartertime + od + endif +endop + + +/* + Parametric array sequencer. Treat each index of the first dimension as a 16th. The second dimension is as follows: + 0 trigger (1 is active) + 1 duration in seconds + 2 p4 onwards (can be as many p-fields as required to be passed to the despatch instrument) + + seq_array2d SdespatchI, karray[][], kswing + + SdespatchI name of the instrument to trigger + karray[][] 2D array of sequence information + kswing swing amount (0 to 1) +*/ +opcode seq_array2d, 0, Sk[][]k + SdespatchI, karray[][], kswing xin + kindex init 0 + if (gkseq_beat == 1) then + ktime = 0 + kcount = 0 + while (kcount < 4) do + if (karray[kindex][0] == 1) then + krow[] getrow karray, kindex + Scoreline sprintfk "i\"%s\" %f %f ", SdespatchI, seq_swingtime(ktime, kcount, kswing), krow[1] + if (lenarray(krow) > 2) then + kdx = 2 + while (kdx < lenarray(krow)) do + Scoreline strcatk Scoreline, sprintfk("%f ", krow[kdx]) + kdx += 1 + od + endif + scoreline Scoreline, 1 + endif + + if (kindex + 1 >= lenarray(karray)) then + kindex = 0 + else + kindex += 1 + endif + kcount += 1 + ktime += gkseq_quartertime + od + endif +endop + + + +/* + Freak sequencer. Three iterations of generative triggers sent to despatch instruments + + seq_freak SdespatchI1, SdespatchI2, SdespatchI3, kbeatdensity[], kbeatstrength[] + + SdespatchI1 name of the primary instrument to trigger + SdespatchI2 name of the secondary instrument to trigger + SdespatchI3 name of the tertiary instrument to trigger +*/ +opcode seq_freak, 0, SSSk[]k[] + SdespatchI1, SdespatchI2, SdespatchI3, kbeatdensity[], kbeatstrength[] xin + + kbeat init 0 + ktrig init 0 + if (gkseq_beat == 1) then ; only on every 1st of 4 beats + if (kbeat == 0) then + ktrig = 1 + kbeat += 1 + elseif (kbeat == 3) then + kbeat = 0 + else + kbeat += 1 + endif + + endif + + if (ktrig == 1 && random:k(0, 1) < kbeatdensity[0]) then + ktrig = 0 + ktime = 0 + event "i", SdespatchI1, ktime, 1, random:k(kbeatstrength[0]*0.2, kbeatstrength[0]) + kindex1 = 0 + kitems1 random 0, 8 + while (kindex1 < kitems1) do + if (random:k(0, 1) < kbeatdensity[1]) then + event "i", SdespatchI2, ktime, 1, random:k(kbeatstrength[1]*0.2, kbeatstrength[1]) + endif + ktime2 = ktime + (gkseq_beattime/8/8) + kindex2 = 0 + kitems2 random 0, 32 + while (kindex2 < kitems2) do + if (random:k(0, 1) < kbeatdensity[2]) then + event "i", SdespatchI3, ktime2, 1, random:k(kbeatstrength[2]*0.2, kbeatstrength[2]) + endif + ktime2 += gkseq_beattime/8/8 + kindex2 += 1 + od + + ktime += gkseq_beattime/8 + kindex1 += 1 + od + endif +endop + + +/* + Bar and bargroup management + + Each beat is counted upon a gkseq_beat trigger. + When the number of beats implies a bar has completed, the trigger gkseq_bar_trig is fired. + The position in the bar (beat number (0 to giseq_barlength - 1)) is available in gkseq_barbeat. + + When the number of bars implies a bargroup has completed, the trigger gkseq_bargroup_trig is fired. + The position in the bargroup (bar number (0 to giseq_bargrouplength - 1)) is available in gkseq_bargroup. +*/ +gkseq_bar_trig init 0 ; trigger +gkseq_barbeat init -1 ; number +giseq_barlength = 4 + +gkseq_bargroup_trig init 0 ; trigger +gkseq_bargroup init -1 ; number +giseq_bargrouplength = 4 + +instr _barmanager + kbarbeat init 0 + gkseq_bar_trig = 0 + gkseq_bargroup_trig = 0 + + if (gkseq_beat == 1) then + gkseq_barbeat = (gkseq_barbeat < (giseq_barlength-1) ? gkseq_barbeat + 1 : 0) + if (gkseq_barbeat == 0) then + gkseq_bar_trig = 1 + gkseq_bargroup = (gkseq_bargroup < (giseq_bargrouplength-1) ? gkseq_bargroup + 1 : 0) + if (gkseq_bargroup == 0) then + gkseq_bargroup_trig = 1 + endif + endif + endif +endin +;alwayson "_barmanager" ; not available in web api +schedule("_barmanager", 0, -1) + + +/* + metronome; first beat has a higher pitch + + aout metronome [iamp=1] + + aout metronome output + iamp optional amplitude +*/ +opcode metronome, a, p + iamp xin + kamp loopseg gkseq_tempo/60, gkseq_beat, 0, 1, 0.2, 0, 0.8, 0, 0 + aout oscil 0.1, (gkseq_barbeat == 0 ? 2000 : 1000) + xout aout * kamp * iamp +endop + + +#end -- cgit v1.2.3