packages feed

csound-expression-typed-0.2.3.1: data/opcodes/LiveRow.udo

; 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