; Anton Kholomiov, 2019
;
; UDO to play row of toggle clip like in Ableton Live
;
; Mono version. Toggle of audio clips syncronised with BPM.
;
; > aOut liveRow iTabSize, iTabs, iBpm, iBeatDur, kUserIndex, iAuxParams
;
; * iTabSize - how many audio clips in the row
; * iTabs - table that contains indices to tables with monophonic audio clips (loaded with GEN01)
; * iBpm - BPM of the track
; * iBeatDur - duration of the bar relative to quaters. So 4 - means 4/4, 3 means 3/4, 3.5 means 7/8 etc.
; * kUserIndex - index of the played clip, if it's out of bounds silence is played back.
;
; The clip switch happens only in even iBeatDur times. So that it's always in sync.
; It's supposed that audio clips are monophonic and aligned with specified tempo.
; So no automatic rescaling is done.
;
; Basic functionality is to switch the clips. But there are also aux-params that
; can control the length and spacing of playback.
;
; The aux-params are packed into single table. It's last parameter and we can omit it.
; The params follow by packets of 6 digits per audio-clip in the row.
;
; We can create the aux-param table with GEN02 like this
;
; > ; Aux params Size Del Tail AutoSwitch NeedRetrig Volume
; > f201 0 10 -2 1 0 1 -1 1 1
; 1 0 0 -1 0 1
;
; in the example above we can see two 5-packets sets for two audio clips.
;
; There are params:
; * Size (Int) - the size of the audio in bars
; * Delay (Int) - how many bars to skip on clip start
; * Tail delay (Int) - how many empty bara to play at the end of the clip
; * AutoSwitch (Int) - what audio-clip to switch when the current is over
; if it equals to -1 than we continue to play the current clip.
; * NeedRetrig - if we switch the clips we can continue to play from where we left out (default behaviour)
; or we can start from beginning on switch (if NeedRetrig equals 1).
; * Volume scale per audio-clip
;
opcode liveRow, a, iiiikjj
iTabSize, iTabs, iBpm, iBeatDur, kUserIndex, iAuxParams, isDebugPrint xin
kUserIndex = floor(kUserIndex)
iDur = iBeatDur / (iBpm / 60)
ii = 0
; iTabSize = ftlen(iTabs)
itTotalSteps[] init iTabSize
itTotalDurs[] init iTabSize
itTrans[] init iTabSize
itDels[] init iTabSize
itTailDels[] init iTabSize
itRetrig[] init iTabSize
itClipVolume[] init iTabSize
while ii < iTabSize do
iTab tab_i ii, iTabs
itCurrentFileSteps = floor((ftlen(iTab) / ftsr(iTab)) / iDur)
if (iAuxParams < 0) then
itTotalSteps[ii] = itCurrentFileSteps
else
itUserTotalSteps tab_i (ii * 6), iAuxParams
if (itUserTotalSteps > 0) then
itTotalSteps[ii] = itUserTotalSteps
else
itTotalSteps[ii] = itCurrentFileSteps
endif
endif
itTotalDurs[ii] = itTotalSteps[ii] * iDur * itCurrentFileSteps / itTotalSteps[ii]
if (iAuxParams < 0) then
itDels[ii] = 0
else
itDels[ii] tab_i (ii * 6 + 1), iAuxParams
if (itDels[ii] <= 0) then
itDels[ii] = 0
endif
endif
if (iAuxParams < 0) then
itTailDels[ii] = 0
else
itTailDels[ii] tab_i (ii * 6 + 2), iAuxParams
if (itTailDels[ii] <= 0) then
itTailDels[ii] = 0
endif
endif
if (iAuxParams < 0) then
itTrans[ii] = ii
else
itTrans[ii] tab_i (ii * 6 + 3), iAuxParams
if (itTrans[ii] < 0) then
itTrans[ii] = ii
endif
endif
if (iAuxParams < 0) then
itRetrig[ii] = ii
else
itRetrig[ii] tab_i (ii * 6 + 4), iAuxParams
if (itRetrig[ii] < 0) then
itRetrig[ii] = 0
endif
endif
if (iAuxParams < 0) then
itClipVolume[ii] = 1
else
itClipVolume[ii] tab_i (ii * 6 + 5), iAuxParams
if (itClipVolume[ii] < 0) then
itClipVolume[ii] = 1
endif
endif
ii = ii + 1
od
ki init 0
iInitTab tab_i 0, iTabs
iInitDur = itTotalDurs[0]
kTab init iInitTab
kTotalDur init iInitDur
kStart init 0
kDel init 0
kLim init 0
kClipVolume init 1
kStarts[] init iTabSize
kTrans[] init iTabSize
kStop init 0
ktTotalSteps[] init iTabSize
ktTotalDurs[] init iTabSize
ktDels[] init iTabSize
ktTailDels[] init iTabSize
ktRetrig[] init iTabSize
ktClipVolume[] init iTabSize
while ki < iTabSize do
kStarts[ki] = 0
kTrans[ki] = itTrans[ki]
ktRetrig[ki] = itRetrig[ki]
ktDels[ki] = itDels[ki]
ktTailDels[ki] = itTailDels[ki]
ktTotalSteps[ki] = itTotalSteps[ki] + ktDels[ki] + ktTailDels[ki]
ktTotalDurs[ki] = itTotalDurs[ki]
ktClipVolume[ki] = itClipVolume[ki]
ki = ki + 1
od
aNdx phasor (1 / iDur)
aNdx = iDur * aNdx
kIndex init 0
kTrigIndex changed kUserIndex
if (kTrigIndex == 1) then
if (kUserIndex != kIndex) then
kIndex = kUserIndex
; should we retriger audio clip from the start or continue (default: continue)
if ktRetrig[kIndex] == 1 then
kStarts[kIndex] = 0
endif
endif
endif
kTrig metro (1 / iDur)
if (kTrig == 1) then
if kIndex >= 0 && kIndex <= (iTabSize - 1) then
kStop = 0
kTabNext tab kIndex, iTabs
kStart = kStarts[kIndex]
kDel = ktDels[kIndex]
kLim = ktTotalSteps[kIndex] - ktTailDels[kIndex]
kTotalDur = ktTotalDurs[kIndex]
kClipVolume = ktClipVolume[kIndex]
if (isDebugPrint > 0) then
printks "kTab %d\n", 0, kTab
printks "kTabNext %d\n", 0, kTabNext
printks "kStart %d\n", 0, kStart
printks "kLim %d\n", 0, (ktTotalSteps[kIndex] - 1)
printks "kTotalDur %f\n", 0, kTotalDur
printks "kDel %f\n", 0, kDel
endif
if (kStart == ktTotalSteps[kIndex] - 1) then
kStarts[kIndex] = 0
kIndex = kTrans[kIndex]
else
kStarts[kIndex] = (kStarts[kIndex] + 1) % ktTotalSteps[kIndex]
endif
kTab = kTabNext
else
kStop = 1
endif
endif
kFadeEnv loopseg (1 / iDur), kTrig, 0, 1, 0.99, 1, 0.01, 0
if (kStop == 0 && kStart >= kDel && kStart < kLim) then
aRes tableikt ((kStart - kDel) * iDur + aNdx) / kTotalDur, kTab, 1
aRes = aRes * kClipVolume * kFadeEnv
else
aRes = 0
endif
xout aRes
endop