#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