nebconfigbegin
ksmps,64
-B,2048
-b,512
sr,48000
freeze,latching,rising
reset,triggered,rising
source,latching,falling
file,incremental,falling
record,latching,rising
nebconfigend

;previous settings:
;ksmps,128
;-B,2048
;-b,128
;- Granular File/Buffer Looping Granular Instrument
; Qu-bit Electronix
; Author: Stephen Hensley
; San Clemente, CA
; 2017
;
; Global Data from External Software
; All of the following globals are set from external software.
; Controls are named after their hardware control name.
; gilen[] - Array containing all file lengths
; gkpitch - percentage of original pitch. (Negative values allowed)
; gkspeed - percentage of original speed. (Negative values allowed)
; gkloopstart - percentage of file to start at.
; gkloopsize - percentage of post-loopstart size file to play
; gkdensity - 
; gkoverlap - 
; gkblend - percentage of granular sound (inverse percentage of dry audio file)
; gkwindow - percentage of degradation of audio output signal
; gkfilesel - index of table containing audio file data.
; gkfreeze - binary freeze state value (0 or 1)
; gknext - trigger input for advancing files
; gkreset - trigger input for restarting phase to loopstart point.
; gksource - toggles between live input source/and usb file sources
; gkrecord - toggles record behavior
; gkfilestate - state of the record button, independent of latching state and gate input.
; gksourcegate - separate state for the source gate input. used to repurpose separately from source control.
; gkeol - end of sample 1 or 0 essentially high while the sample is resetting to 0.
; gkbufferlength - size of buffer 

;;;;;;;;;;;;;;;;;
;;;; UDOs ;;;;;;;
;;;;;;;;;;;;;;;;;
; The Following UDOs are based on those in the Csound FLOSS Manual on recording and playing buffers

giMaxRecordBuffer = 300; Max record time in seconds
;giftEmpty   ftgen   0, 0, giMaxRecordBuffer * sr, 7, 0
giftEmpty   ftgen   0, 0, 16, 7, 0
giftLA      ftgen   0, 0, giMaxRecordBuffer * sr, 7, 0
giftRA      ftgen   0, 0, giMaxRecordBuffer * sr, 7, 0
giftLB       ftgen   200, 0, giMaxRecordBuffer * sr, 7, 0
giftRB       ftgen   201, 0, giMaxRecordBuffer * sr, 7, 0

opcode CopyPartialBuffer, k, kkk
krecstart, kbuffsize, ktrig xin
kchannel init 0
knew changed ktrig
if knew == 1 then
    if ktrig == 1 then
        kchannel = 0
        kcopying = 1
    endif
    if kcopying == 1 then
        if kchannel == 0 then
            ;tablemix giftLB, krecstart, kbuffsize, giftLA, krecstart, 1.0, giftEmpty, 0, 0.0
            ;tablecopy giftLB, giftLA
            kchannel += 1
        else
            ;tablemix giftRB, krecstart, kbuffsize, giftRA, krecstart, 1.0, giftEmpty, 0, 0.0
            ;tablecopy giftRB, giftRA
            kcopying = 0
        endif
    endif
endif

xout kcopying
endop

opcode CplxBufRec1, kkk, aaiikkkkkk
ain1, ain2, ifta, iftb, krec, kstart, kend, kpos, kwrap, kmode xin
setksmps 1
kdisablecount init 0
kcopying init 0
ktmp init 0
kndx init 0
kcopying init 0
koverlapping init 0
kactivebuffer init 0
kmaxdelay = 12000; maybe a bit excessive.
kmaxfade = 1200
knew changed krec ; 1 if record just started
if knew == 1 then
    if krec == 1 then
        krec_delayed = 1
        kstartsmps = kstart * sr - 1
        kwrapsmps = kpos * sr - 1
        kendsmps = kend * sr
        kfinished = 0
        ktmp = 0
        kcoyping = 0
        koverlap = 0
    else 
        kfinished = 1
        kreclength = kndx
        kdisablecount = kmaxdelay
        koverlapping = 1
    endif
endif
if kwrap == 1 then
    kwrapsmps = kpos * sr - 1
    kendsmps = kend * sr 
    kstartsmps = kstart * sr - 1
endif
kendsmps = (kendsmps == 0 || kendsmps > ftlen(ifta) ? ftlen(ifta) : kendsmps)
if krec == 1 then
	if knew == 1 && krec == 1 then
		kndx = kstartsmps
        kfade = 0
        kcpndx = -1
	endif
	if kndx >= kendsmps - 1 && kwrap == 1 then
		kndx = kwrapsmps
        kreclength = kndx
        event "i", 2, 0, -1, kwrapsmps + 1, kendsmps
        koverlap = 1
        ;kstate CopyPartialBuffer, kwrapsmps + 1, kendsmps, 1
        ;tablemix giftLB, kwrapsmps + 1, kendsmps, giftLA, kwrapsmps + 1, 1.0, giftEmpty, 0, 0.0
        ;tablemix giftRB, kwrapsmps + 1, kendsmps, giftRA, kwrapsmps + 1, 1.0, giftEmpty, 0, 0.0
    endif
    if kndx < kendsmps-1 then
        if kndx > kwrapsmps + (ksmps * 4) then
            koverlap = 0
        endif
		kndx = kndx + 1
		andx = kndx
        asig = ain1 + ain2
        tabw asig, andx, ifta
        ;aprevidx = kndx > 0 ? kndx - 1 : kendsmps
        ;tabw asig, andx, iftb
        ;aprev tab aprevidx, ifta
        ;tabw asig, aprevidx, iftb
    elseif kndx >= kendsmps -1 then
        kfinished = 1
        koverlapping = 1
        kreclength = kndx
    endif
endif
if koverlapping == 1 then
    if kreclength + ktmp < (giMaxRecordBuffer * sr) - 1 then
        if ktmp < kmaxdelay then
            ktmp = ktmp + 1
            atmp = a(ktmp)
            tabw ain1 + ain2, kreclength + atmp, iftb
            tabw ain1 + ain2, kreclength + atmp, ifta
        endif
        if ktmp >= kmaxdelay then
            koverlap = 1
            koverlapping = 0
            ktmp = 0
            kcopying = 0
            kcpoff = 0
        endif
    else
        ;koverlap = 1
        koverlapping = 0
        ktmp = 0
    endif
endif

xout kfinished, kreclength, koverlap 
endop

opcode CplxBufRec2, kkk, aaaaiiiikkkkkk
kcp init 0
ain1L, ain1R, ain2L, ain2R, iftLA, iftRA, iftLB, iftRB, krec, kstart, kend, kpos, kwrap, kmode xin
kfin, kreclen, koverlap CplxBufRec1 ain1L, ain2L, iftLA, iftLB, krec, kstart, kend, kpos, kwrap, kmode
kfin, kreclen, koverlap CplxBufRec1 ain1R, ain2R, iftRA, iftRB, krec, kstart, kend, kpos, kwrap, kmode
xout kfin, kreclen, koverlap 
endop

;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Global Tables ;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;
;giwindowtablesize = 1024
giwindowtablesize = 512
giHamming   ftgen   0, 0, giwindowtablesize, 20, 9, 1  
giBartlett  ftgen   0, 0, giwindowtablesize, 20, 3 
giBlkHarris ftgen   0, 0, giwindowtablesize, 20, 5
giGaussian  ftgen   0, 0, giwindowtablesize, 20, 6
;giRectangle ftgen   0, 0, giwindowtablesize, 7, 0, 886, 0, 138, 1, 6144, 1, 138, 0
;giRectangle ftgen   0, 0, giwindowtablesize, 7, 0, 1024, 0, 8, 1, 4095, 1, 8, 0, 1024, 0
;giRectangle ftgen   0, 0, giwindowtablesize, 7, 0, 1024, 0, 1, 1, 6143, 1, 1, 0, 1024, 0
;giRectangle ftgen   0, 0, giwindowtablesize, 16, 1, giwindowtablesize, 0, 1
giRectangle ftgen   0, 0, giwindowtablesize, 20, 8, 1
;giRampUp    ftgen   0, 0, giwindowtablesize, 7, 0, 7992, 1, 200, 0
;giRampDown  ftgen   0, 0, giwindowtablesize, 7, 0, 200, 1, 7992, 0
giRampUp    ftgen   0, 0, giwindowtablesize, 7, 0, 15.0 * (giwindowtablesize / 16.0), 1, (giwindowtablesize / 16.0), 0
giRampDown  ftgen   0, 0, giwindowtablesize, 7, 0,(giwindowtablesize / 16.0),1 ,  15.0 * (giwindowtablesize / 16.0), 0
giWin		ftgen	0, 0, giwindowtablesize, 20, 9, 1 ; Hamming Window 
giWinB		ftgen	0, 0, giwindowtablesize, 20, 9, 1 ; Hamming Window 
giWinMix	ftgen	0, 0, giwindowtablesize, 20, 9, 1 ; Hamming Window 
giCosine	ftgen	0, 0, 8193, 9, 1, 1, 90 ; Cosine Table
giSquare    ftgen   0, 0, 2048, 7, 1.0, 1023, 1.0, 1, 0.0
giLine10    ftgen   0, 0, 2048, 7, 0, 2048, 10.0
giLine1     ftgen   0, 0, 2048, 7, 0, 2048, 1
giPanL      ftgen   0, 0, 256, -21, 1
giPanR      ftgen   0, 0, 256, -21, 1
giPanNone   ftgen   0, 0, 256, -24, giPanL, 0.0, 0.0 ; Edited from 0.5 for non-panned partikkel
giPanAllL   ftgen   0, 0, 256, -24, giPanL, 0.0, 1.0
giPanAllR   ftgen   0, 0, 256, -24, giPanR, 0.0, 1.0
giPanMixL   ftgen   0, 0, 256, -24, giPanL, 0.0, 0.0
giPanMixR   ftgen   0, 0, 256, -24, giPanR, 0.0, 0.0

instr 1

;;;;;;;;;;;;;;;;;;
;;;;; inits ;;;;;;
;;;;;;;;;;;;;;;;;;
tableiw 254, 1, giPanNone
tableiw 0, 0, giPanAllL
tableiw 254, 1, giPanAllL
tableiw 0, 0, giPanAllR
tableiw 254, 1, giPanAllR
tableiw 0, 0, giPanMixL
tableiw 254, 1, giPanMixL
tableiw 0, 0, giPanMixR
tableiw 254, 1, giPanMixR
krecordedbuff init giMaxRecordBuffer
kBufferEmpty init 1
kcopyingbuffer init 0
kprevsize init 0
aoutl init 0
aoutr init 0
arecL init 0
arecR init 0
amixl init 0
amixr init 0
aprevmixl init 0
aprevmixr init 0
arecordsync init 0
kActiveBuffer init 0
; generates right channel ftables for stereo files
gifile_right_offset = 599
itempidx = 0
loop:
    if gichn[itempidx] == 2 then
        giwoffset = itempidx + gifile_right_offset
        giwoffset ftgen (itempidx+gifile_right_offset), 0, 0, 1, gSname[itempidx], 0, 0, 2
    endif
    itempidx += 1
    if (itempidx < 100) igoto loop
gkfilesel_offset = 399

kfirsttime init 1

if kfirsttime == 1 then ; Reset!
    giftLA ftgen   0, 0, giMaxRecordBuffer * sr, 7, 0
    giftRA ftgen   0, 0, giMaxRecordBuffer * sr, 7, 0
    giftLB ftgen   200, 0, giMaxRecordBuffer * sr, 7, 0
    giftRB ftgen   201, 0, giMaxRecordBuffer * sr, 7, 0
    klen = giMaxRecordBuffer
    kBufferEmpty = 1
    kActiveBuffer = 0
    ;printf "bam! Zeroed Buffer is empty: %d\n", kfirsttime, kBufferEmpty
    kmaintainsize = 0
endif

;;;;;;;;;;;;;;;;;;;;;;
;; control clipping ;;
;;;;;;;;;;;;;;;;;;;;;;
if gkloopstart < 0.0015 then
gkloopstart = 0
endif
if gkloopstart > 0.995 then
gkloopstart = 1
endif
if gkloopsize < 0.0015 then
gkloopsize = 0
endif
if gkloopsize > 0.995 then
gkloopsize = 1
endif
if gkblend < 0.01 then
gkblend = 0.0
elseif gkblend > 0.99 then
gkblend = 1.0
endif
;;;;;;;;;;;;;;;;;;;
;; phasor config ;;
;;;;;;;;;;;;;;;;;;;
; Set Start and Size
if gksource == 1 then
    kfilesr = sr
	klen = krecordedbuff 
    kfilelen = klen
	kglen = giMaxRecordBuffer
    kpeakamp = 1.0
    if kBufferEmpty == 1 then
        gkbufferlength = 0
    else
        gkbufferlength = klen * sr
    endif
else
    if kBufferEmpty == 1 then
        gkbufferlength = 0
    else
        gkbufferlength = krecordedbuff * sr
    endif
    kfilesr = gisr[gkfilesel]
    kfilelen = gilen[gkfilesel]
    kfilesamps = (kfilelen * kfilesr) * (sr / kfilesr)
    kpeakamp = gipeak[gkfilesel]
    ;klen = kfilesamps / sr
	klen = kfilelen; * (kfilesr / sr); Length of Current File in seconds
	kglen = kfilesamps / sr
endif
if klen == 0 then
    klen = giMaxRecordBuffer
endif
kconvertedlen = (klen * (kfilesr / sr))
kloopstart = (gkloopstart * kconvertedlen)
kloopscalar = gkloopsize
kloopsize = ((kloopscalar * kloopscalar) * (kconvertedlen-kloopstart))
if (kloopsize <= 0.000035) then 
	kloopsize = 0.000035
endif

; Set Speed
if gkrecord == 0 || gkrecord_alt == 1 then
    kspeed = (gkspeed * 8.0) - 4.0
endif
if abs(kspeed) <= 1.025 && abs(kspeed) >= 0.975 then
    if kspeed > 0 then
        kspeed = 1.0
    else
        kspeed = -1.0
    endif
endif

; Set Freeze
if (gkfreeze == 1) then
	kspeed = 0.0
endif


ainl, ainr inch 1, 2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; mincer/partikkel primary controls ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; pitch setting
ilogmax = log(32.00)
ilogmin = log(1.0)
kfactor = exp(gkpitch * (ilogmax - ilogmin) + ilogmin)
kpitch = 0.125 * kfactor
if kpitch <= 1.005 && kpitch >= 0.995 then
kpitch = 1.0
endif

; Window Shape
kwindow portk gkwindow, 0.1
kwindowsel = int(kwindow * 6)
kwindowblend = kwindow * 6
kwindowchanged changed kwindowsel
if kwindowchanged == 1 || kfirsttime == 1 then
    if kwindowsel < 1  then
        tablecopy giWin, giGaussian
        tablecopy giWinB, giBlkHarris
    elseif kwindowsel < 2 then
        tablecopy giWin, giBlkHarris
        tablecopy giWinB, giRampDown
    elseif kwindowsel < 3 then
        tablecopy giWin, giRampDown
        tablecopy giWinB, giBartlett
    elseif kwindowsel < 4 then
        tablecopy giWin, giBartlett
        tablecopy giWinB, giRampUp
    elseif kwindowsel < 5 then
        tablecopy giWin, giRampUp
        tablecopy giWinB, giHamming
    elseif kwindowsel < 6 then
        tablecopy giWin, giHamming
        tablecopy giWinB, giRectangle
    elseif kwindowsel < 7 then
        tablecopy giWin, giRectangle
    endif
    kfirsttime = 0
endif

; Window Blend
kwblend = kwindowblend - kwindowsel
tablemix giWinMix, 0, giwindowtablesize, giWin, 0, 1.0-kwblend, giWinB, 0, kwblend

; Density and Overlap
ilogmaxgfreq = log(2500.0)
;ilogmingfreq = log(0.1667)
ilogmingfreq = log(0.12)
kdensityscalartan = ((tanh((gkdensity * 2.0) - 1.0)) + 0.8) * 0.4
kdensityscalarcube = ((((gkdensity * 2.0) - 1.0) ^ 3) + 1.0) * 0.5
kdensityscalar = (kdensityscalartan * 0.25) + (kdensityscalarcube * 0.75)
if gkdensity > 0.05 || gkfreeze_alt == 0 then
    kgrainfreq = exp((kdensityscalar) * (ilogmaxgfreq - ilogmingfreq) + ilogmingfreq)
else
    kgrainfreq = 0
endif

if (kgrainfreq < 4) then
    kmaxgrainpw = 4.0
else
    kmaxgrainpw = 6.0
endif
if (gkoverlap_alt < 0.01) then
    krandsizel = 0.0
    krandsizer = 0.0
else
    krandsizel = (birnd(gkoverlap_alt * gkoverlap_alt)) 
    krandsizer = (birnd(gkoverlap_alt * gkoverlap_alt))
endif
kgsizescalarL = krandsizel + (gkoverlap * gkoverlap)
kgsizescalarR = krandsizer + (gkoverlap * gkoverlap)
if kgsizescalarL > 1.0 then
    kgsizescalarL = 1.0 
elseif kgsizescalarL < 0.0 then
    kgsizescalarL = 0.0
endif
if kgsizescalarR > 1.0 then
    kgsizescalarR = 1.0 
elseif kgsizescalarR < 0.0 then
    kgsizescalarR = 0.0
endif

kgrainsync init 1

if gkfreeze_alt == 1 then
    kgrainsync trigger gksourcegate, 0.5, 0
else
    kgrainsync = 0
endif

;* If we decide we need to handle grain size based on incoming grain rate.
ktickssincelastclock init 0
if kgrainsync == 1 then
    kincomingclockrate = ktickssincelastclock * (1 / kr)
    ktickssincelastclock = 0
endif
ksmoothclockrate portk kincomingclockrate, 0.5
ktickssincelastclock += 1
if kgrainfreq > 0 then
    kgrainsizel = (kgsizescalarL) * (((1 / kgrainfreq) * kmaxgrainpw) * 1000) + 1
    kgrainsizer = (kgsizescalarR) * (((1 / kgrainfreq) * kmaxgrainpw) * 1000) + 1
else
    ;kgrainsizel = (kgsizescalarL * kgsizescalarL * 2000) + 0.5
    ;kgrainsizer = (kgsizescalarR * kgsizescalarR * 2000) + 0.5
    kgrainsizel = (kgsizescalarL * kgsizescalarL * 1000) + 0.5
    kgrainsizer = (kgsizescalarR * kgsizescalarR * 1000) + 0.5
    ;if ksmoothclockrate < 0.03  && gkfreeze_alt == 1 then
    if kgrainsizel >  6 *ksmoothclockrate * 1000.0 then
        kgrainsizel = (6 * ksmoothclockrate * 1000.0)
    endif
    if kgrainsizer > 6 * ksmoothclockrate * 1000.0 then
        kgrainsizer = (6 * ksmoothclockrate * 1000.0)
    endif
    if kgrainsizel == 0 then
        kgrainsizel = 0.5
    endif
    if kgrainsizer == 0 then
        kgrainsizer = 0.5
    endif
endif
if kgrainsizel > 8000.0 then
    kgrainsizel = 8000.0
endif
if kgrainsizer > 8000.0 then
    kgrainsizer = 8000.0
endif
;printks "clock rate: %f\t grainsize: %f\n", 0.25, ksmoothclockrate, kgrainsizel

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; mincer/partikkel secondary controls ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



; Prep Pan table
kpanamount = gkloopsize_alt 
kpanchanged changed kpanamount
if kpanchanged == 1 then
    tablemix giPanMixL, 0, 256, giPanNone, 0, 1.0-kpanamount, giPanAllL, 0, kpanamount
    tablemix giPanMixR, 0, 256, giPanNone, 0, 1.0-kpanamount, giPanAllR, 0, kpanamount
    tablew 0, 0, giPanMixL
    tablew 254, 1, giPanMixL
    tablew 0, 0, giPanMixR
    tablew 254, 1, giPanMixR
endif

; Random Pitch
;krandpitchamt = gkloopstart_alt
krandpitchamt = gkpitch_alt
krndpitchmod rand (krandpitchamt * 1200)

; Set a lot of parameters for Partikkel
kdistribution	= gkdensity_alt * gkdensity_alt * 100; periodic grain distribution
idisttab	= giLine10 ; (default) flat distribution used for grain distribution
;async		= 0				; no sync input
async upsamp kgrainsync
async2 init 0
kenv2amt	= 1				; entirely secondary enveloping
ienv2tab	= giWinMix		; default secondary envelope (flat)
ienv_attack	= -1 ;			; default attack envelope (flat)
ienv_decay	= -1 ;			; default decay envelope (flat)
ksustain_amount	= .0 ; time (in fraction of grain dur) at sustain level for each grain
ka_d_ratio	= 0.5 			; balance between attack and decay time
iamp		= 0.45		; amp
igainmasks	= -1			; (default) no gain masking
ksweepshape	= 0				; shape of frequency sweep (0=no sweep)
iwavfreqstarttab = -1		; frequency sweep start table
iwavfreqendtab	= -1		; frequency sweep end table
ifmamptab	= -1			; default FM scaling (=1)
kfmenv		= giWin		; default FM envelope (flat)
icosine		= giCosine		; cosine ftable
kTrainCps	= 1				; grain rate for single-cycle trainlet in each grain
knumpartials	= 1			; number of partials in trainlet
kchroma		= 1				; balance of partials in trainlet
krandommask	= 0				; no random grain masking
if gkwindow_alt < 0.01 then
	krandommask = 0
else
	krandommask = gkwindow_alt * 0.99
endif
iwaveamptab	= -1			; mix of all 4 sourcve waveforms and no amp for trainlets
krandposscalar = (gkloopstart_alt * gkloopstart_alt)
krandpos = birnd(krandposscalar)

kwavekey1	= 1				; original key for source waveform
kwavekey2	= 1 
kwavekey3	= 1 
kwavekey4	= 1 
;imax_grains	= 25			; max grains per k period
imax_grains = 10 ; this doesn't underrun on my unit

; Set a few parameters for mincer
kphaselock = 1
;ifftlivesize = 1024
ifftlivesize = 2048
ifftfilesize = 2048
idecim = 4
idecimlive = 4

;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Recording Setup ;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;


; Detect Reset
ksynctrig trigger gkreset, 0.5, 0
kfreezetrig trigger gkfreeze, 0.5, 0
krecordtrig trigger gkrecord, 0.5, 1
atime init 0

if gkfilestate == 0 then
    asynctrig = a(ksynctrig)
endif
if kBufferEmpty == 1 then
    kdetectrecordend trigger k(arecordsync), 0.5, 0
    ;printf "BAM Record Sync Detected! %d \n", kdetectrecordend, kdetectrecordend
endif
; Alt Recording Features (Record + Control)
kmaintainsize init 0
if gkfilestate == 1 then
    ; clear buffer 
    if ksynctrig == 1 then ; Reset!
        ;tablecopy giftLA, giftEmpty
        ;tablecopy giftRA, giftEmpty
        ;tablecopy giftLB, giftEmpty
        ;tablecopy giftRB, giftEmpty
        giftLA ftgen   0, 0, giMaxRecordBuffer * sr, 7, 0
        giftRA ftgen   0, 0, giMaxRecordBuffer * sr, 7, 0
        giftLB ftgen   200, 0, giMaxRecordBuffer * sr, 7, 0
        giftRB ftgen   201, 0, giMaxRecordBuffer * sr, 7, 0
        klen = giMaxRecordBuffer
        kBufferEmpty = 1
        kActiveBuffer = 0
        ;printf "bam! Zeroed Buffer is empty: %d\n", ksynctrig, kBufferEmpty
        kmaintainsize = 0
    endif
endif


;printks "BufferEmpty: %d\tklen: %f\tPlayback phase: %f\trecstart: %f\trecend: %f\trecwrap: %f\n", 0.1, kBufferEmpty, klen, k(aphs), krecstart, krecend, krecwrap


;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Phasor Operation ;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;

; Run Phasor
arecordtrig = a(krecordtrig)
if kBufferEmpty == 1 && gksource == 1 then
    asynctrig += arecordtrig
endif 
if gksource == 1 then
    asynctrig += arecordsync
endif
;asynctrig += arecordtrig

kphasorfreq = ((1 / kloopsize) * kspeed) * (kfilesr / sr)
if kphasorfreq > sr then
    kphasorfreq = sr
endif
aphs, aphssync syncphasor kphasorfreq, asynctrig, 0.0000
atime = kloopstart + (kloopsize * aphs)
;if gksource == 1 then
    ;atime *= 0.99
;endif
agphs = atime / (kglen * (kfilesr / sr)) ; Compensate for SR difference

;printks "startctrl: %f\tscalarctrl: %f\tstarttime: %f\tsize: %f\tconvertedlen: %f\tlength: %f\tphase: %f\ttime: %f\tbuff size: %f\n", 0.25, gkloopstart, gkloopsize, kloopstart, kloopsize, kconvertedlen, klen, k(aphs), k(atime), kbuffsize
;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Recording Process ;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;


if kBufferEmpty == 1 then
    krecstart = 0
    krecend = giMaxRecordBuffer
    krecwrap = 0
else
    if gkrecord == 0  || gkrecord_alt == 1 then
        if kmaintainsize == 1 then
            krecwrap = kloopstart
            if kspeed >= 0 then
                krecstart = k(atime) - (ksmps * 2)
            else
                krecstart = klen - k(atime) + (ksmps * 2)
            endif
            if krecstart < 0 then
                krecstart = 0
            elseif krecstart > klen then
                krecstart = 0
            endif
            
            if kspeed != 0 || gkrecord_alt == 1 then
                if kspeed == 0 then
                    krecend = giMaxRecordBuffer
                else
                    krecend = (kloopsize + kloopstart) / abs(kspeed)
                endif
            else
                krecend = giMaxRecordBuffer
            endif
        else
            krecwrap = kloopstart
            krecstart = 0
            krecend = giMaxRecordBuffer
        endif
    endif
endif

koverlapFlag init 0
krecordA init 0
krecordB init 0
if krecordtrig == 1 then
    krecordB = 0
endif
kbuffsize init giMaxRecordBuffer
krecordrise trigger gkrecord, 0.5, 0
if krecordrise == 1 then
    krecordB = 1
endif
if krecordA == 1 then
    krecordA = 0
    krecordB = 1
    gkrecordstatus = 1
endif
if gksource == 0 then
    ablenddry = 0
endif
if krecstart > klen - 0.25  && krecordB == 1 then
    krecstart = kloopstart
    krecordA = 1
endif
;arecL = aprevmixl
;arecR = aprevmixr
if gksource == 0 then
    arecL = amixl
    arecR = amixr
else
    arecL = ainl
    arecR = ainr
endif
kfin, kbuffsize, koverlapFlagA CplxBufRec2 arecL, arecR, ainl * ablenddry, ainr * ablenddry, giftLA, giftRA, giftLB, giftRB, krecordB, krecstart, krecend, krecwrap, gkrecord_alt, kActiveBuffer
kfinchanged trigger kfin, 0.5, 0 ; Done Recording
kfinstarted trigger kfin, 0.5, 1 ; Started Recording
koverlaptrig trigger koverlapFlagA, 0.5, 0 ; overwrite of buffer complete
;kstate CopyPartialBuffer, krecstart , kbuffsize, kfinchanged
if kfinchanged == 1 then
    if kActiveBuffer == 1 then
        kActiveBuffer = 0
    else
        kActiveBuffer = 1
    endif
    if kBufferEmpty == 1 then
        arecordsync = 1
        kActiveBuffer = 1
    endif
    ;tablemix giftLB, krecstart, kbuffsize, giftLA, krecstart, 1.0, giftEmpty, 0, 0.0
    ;tablemix giftRB, krecstart, kbuffsize, giftRA, krecstart, 1.0, giftEmpty, 0, 0.0
    event "i", 2, 0, -1, krecstart, kbuffsize
    ;kcopyingbuffer = 1
    if gkrecord_alt == 0 then
        krecordB = 0
    endif
    if kmaintainsize == 1 then
        kprevsize = krecordedbuff
        knewsize = kbuffsize/ (sr * 1.0)
        if knewsize < kprevsize || gkrecord_alt == 1 then
            krecordedbuff = kprevsize
        else
            krecordedbuff = knewsize
        endif
    else
        kprevsize = (kbuffsize) / (sr * 1.0)
        krecordedbuff = (kbuffsize) / (sr * 1.0)
    endif 
    kmaintainsize = 1
    kBufferEmpty = 0
else
    if kfinstarted == 1 && kBufferEmpty == 1 then
        arecordsync = 1
    else
        arecordsync = 0
    endif
endif
if koverlaptrig == 1 then
    ;tablemix giftLB, krecstart, kbuffsize + (sr/4), giftLA, krecstart, 1.0, giftEmpty, 0, 0.0
    ;tablemix giftRB, krecstart, kbuffsize + (sr/4), giftRA, krecstart, 1.0, giftEmpty, 0, 0.0
    ;event "i", 2, 0, 2, krecstart, kbuffsize
    ;kcopyingbuffer = 1
endif

;printks "kbuffsize: %f\t krecordedbuff: %f\tkglen: %f\tklen: %f\tkloopscalar: %f\tkloopstart: %f\n",0.1, kbuffsize, krecordedbuff, kglen, klen, kloopscalar, kloopstart
gkrecordstatus = krecordB
;;;;;;;;;;;;;;;;;;;;;;
;;;;; processing ;;;;;
;;;;;;;;;;;;;;;;;;;;;;

if gkloopstart_alt < 0.01 then
	asamplepos1 = agphs
	asamplepos2 = agphs
	asamplepos3 = agphs
	asamplepos4 = agphs ;random klow, khigh
else
    if gksource == 1 then
        krandposlivescalar = ((krecordedbuff) / giMaxRecordBuffer) 
        krandpos *= krandposlivescalar
	    asamplepos1 = abs(agphs + krandpos)
        kgrainscalar = kloopscalar * krandposlivescalar
        if k(asamplepos1) > (krandposlivescalar) then
            asamplepos1 -= krandposlivescalar
        endif
    else
	    asamplepos1 = abs(agphs + krandpos)
        if k(asamplepos1) > (gkloopstart + kloopscalar) then
            asamplepos1 -= kloopscalar
        endif
    endif
	asamplepos2 = asamplepos1
	asamplepos3 = asamplepos1
	asamplepos4 = asamplepos1
endif

if gksource == 1 then
    kwavfreq	= ((1/kglen)*kpitch) * cent(krndpitchmod); fundamental frequency of source waveform
    awavfm = 0
    asigL mincer atime, 1.0, kpitch, giftLB, kphaselock, ifftlivesize, idecimlive
    asigR mincer atime, 1.0, kpitch, giftRB, kphaselock, ifftlivesize, idecimlive
    agrainLL, agrainLR partikkel kgrainfreq, kdistribution, idisttab, async, kenv2amt, ienv2tab, \
            ienv_attack, ienv_decay, ksustain_amount, ka_d_ratio, kgrainsizel, iamp, igainmasks, \
            kwavfreq, ksweepshape, iwavfreqstarttab, iwavfreqendtab, awavfm, \
            ifmamptab, kfmenv, icosine, kTrainCps, knumpartials, \
            kchroma, giPanMixL, krandommask, giftLB, giftLB, giftLB, giftLB, \
            iwaveamptab, asamplepos1, asamplepos2, asamplepos3, asamplepos4, \
            kwavekey1, kwavekey2, kwavekey3, kwavekey4, imax_grains,1

    async2 partikkelsync, 1

    agrainRL, agrainRR partikkel kgrainfreq, kdistribution, idisttab, async2, kenv2amt, ienv2tab, \
            ienv_attack, ienv_decay, ksustain_amount, ka_d_ratio, kgrainsizer, iamp, igainmasks, \
            kwavfreq, ksweepshape, iwavfreqstarttab, iwavfreqendtab, awavfm, \
            ifmamptab, kfmenv, icosine, kTrainCps, knumpartials, \
            kchroma, giPanMixR, krandommask, giftRB, giftRB, giftRB, giftRB, \
            iwaveamptab, asamplepos1, asamplepos2, asamplepos3, asamplepos4, \
            kwavekey1, kwavekey2, kwavekey3, kwavekey4, imax_grains 
    agrainL = agrainLL + agrainRR
    agrainR = agrainRL + agrainLR
    if kBufferEmpty == 1 then
        agrainL = 0
        agrainR = 0
        asigL = 0
        asigR = 0
    endif
else
    kwavfreq    = ((1/klen)*kpitch) * cent(krndpitchmod);
    knumchn = gichn[gkfilesel]
    kwaveformL = gkfilesel_offset + gkfilesel + 1
    asigL mincer atime, 0.8, kpitch * (kfilesr / sr), kwaveformL, kphaselock, ifftfilesize, idecim
    knumchn = gichn[gkfilesel]
    if knumchn == 2 then 
        kwaveformR = gifile_right_offset + gkfilesel
        asigR mincer atime, 0.8, kpitch * (kfilesr / sr), kwaveformR, kphaselock, ifftfilesize, idecim
    else
        kwaveformR = kwaveformL
        asigR = asigL
    endif 
		if gkblend_alt < 0.01 then
			awavfml = 0
			awavfmr = 0
		else
			awavfml = gkblend_alt * ainl
			awavfmr = gkblend_alt * ainr
		endif
    agrainLL, agrainLR partikkel kgrainfreq, kdistribution, idisttab, async, kenv2amt, ienv2tab, \
            ienv_attack, ienv_decay, ksustain_amount, ka_d_ratio, kgrainsizel, iamp, igainmasks, \
            kwavfreq, ksweepshape, iwavfreqstarttab, iwavfreqendtab, awavfml, \
            ifmamptab, kfmenv, icosine, kTrainCps, knumpartials, \
            kchroma, giPanMixL, krandommask, kwaveformL, kwaveformL, kwaveformL, kwaveformL, \
            iwaveamptab, asamplepos1, asamplepos2, asamplepos3, asamplepos4, \
            kwavekey1, kwavekey2, kwavekey3, kwavekey4, imax_grains, 1

    async2 partikkelsync, 1

    agrainRL, agrainRR partikkel kgrainfreq, kdistribution, idisttab, async2, kenv2amt, ienv2tab, \
            ienv_attack, ienv_decay, ksustain_amount, ka_d_ratio, kgrainsizer, iamp, igainmasks, \
            kwavfreq, ksweepshape, iwavfreqstarttab, iwavfreqendtab, awavfmr, \
            ifmamptab, kfmenv, icosine, kTrainCps, knumpartials, \
            kchroma, giPanMixR, krandommask, kwaveformR, kwaveformR, kwaveformR, kwaveformR, \
            iwaveamptab, asamplepos1, asamplepos2, asamplepos3, asamplepos4, \
            kwavekey1, kwavekey2, kwavekey3, kwavekey4, imax_grains 
    agrainL = agrainLL + agrainRR
    agrainR= agrainRL + agrainLR
endif

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Auto Leveling for Grains ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

kgrainrmsL rms agrainL 
kgrainrmsR rms agrainR 
;acompensationL follow2 agrainL, 0.05, 0.05
;acompensationR follow2 agrainR, 0.05, 0.05
;acompensationL follow agrainL, (1 / 2500)
;acompensationR follow agrainR, (1 / 2500)
;acompL max acompensationL, a(0.5)
;acompR max acompensationR, a(0.5)

if kgrainrmsL > 0.20 then
    kgrainrmsL = 0.20
endif
if kgrainrmsR > 0.20 then
    kgrainrmsR = 0.20
endif

agrainCompL gain agrainL, kgrainrmsL, 10
agrainCompR gain agrainR, kgrainrmsR, 10
/*
 * Work in progress for scaling amplitude more effectively.
if kgrainsizel < 1 / kgrainfreq then
    aCompL balance agrainL, asigL, 10
    agrainCompL gain aCompL, kgrainrmsL, 10
else
    agrainCompL = agrainL
endif
if kgrainsizer < 1 / kgrainfreq then
    aCompR balance agrainR, asigR, 10
    agrainCompR gain aCompR, kgrainrmsR, 10
else
    agrainCompR = agrainR
endif
*/
;printks "kgrainfreq: %f\tkgrainsizel: %f\tkgrainsizer: %f\n", 0.25, kgrainfreq, kgrainsizel, kgrainsizer
;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;; mixer ;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;
;kblend portk gkblend, 0.01; reduced a bit
kblend = gkblend
if kblend > 1.0 then
    kblend = 1.0
endif
if kblend < 0.0 then 
    kblend = 0.0
endif
if gksource == 1 then
    if kblend > 0.48 && kblend < 0.52 then
        kblend = 0.5
    endif
endif
ablend interp kblend
if gksource == 1 then
    kblend_alt = gkblend_alt
    if kblend_alt > 1.0 then            
        kblend_alt = 1.0
    endif
    if kblend_alt < 0.0 then
        kblend_alt = 0.0
    endif
    ablend_alt interp kblend_alt
    ablenddryfactor min ((ablend_alt * 2.0) - 1.0), a(1.0) 
    ablendwetfactor max ((ablend_alt * 2.0) - 1.0), a(-1.0)
    ablenddry = sqrt(0.5 * (1 - ablenddryfactor))
    ablendwet = sqrt(0.5 * (1 + ablendwetfactor))
    ablenddry min ablenddry, a(1.0)
    ablendwet min ablendwet, a(1.0)
    ablenddry max ablenddry, a(0.0)
    ablendwet max ablendwet, a(0.0)
    ablendvocfactor min ((ablend * 2.0) - 1.0), a(1.0)
    ablendgrainfactor max ((ablend * 2.0) - 1.0), a(-1.0)
    ablendvocoder = sqrt(0.5 * (1 - ablendvocfactor))
    ablendgranular = sqrt(0.5 * (1 + ablendgrainfactor))
    ablendvocoder min ablendvocoder, a(1.0)
    ablendgranular min ablendgranular, a(1.0)
    ablendvocoder max ablendvocoder, a(0.0)
    ablendgranular max ablendgranular, a(0.0)
    amixl = (asigL * ablendvocoder) + (agrainCompL * ablendgranular)
    amixr = (asigR * ablendvocoder) + (agrainCompR * ablendgranular)
    aoutl = (amixl * ablendwet) + (ainl * ablenddry)
    aoutr = (amixr * ablendwet) + (ainr * ablenddry)     
else
    kampscalar = 1.0 / kpeakamp
    aprevmixl = amixl
    aprevmixr = amixr
    ;; Linear
    ;amixl = ((agrainCompL * ablend) + ((asigL * (1.0 - ablend))) * kampscalar)
    ;amixr = ((agrainCompR * ablend) + ((asigR * (1.0 - ablend))) * kampscalar)
    ; Constant Power
    ablend *= 2.0
    ablend -= 1.0
    ablend max ablend, a(-1.0)
    ablend min ablend, a(1.0)
    ablendgranular = sqrt(0.5 * (1 + ablend))
    ablendvocoder = sqrt(0.5 * (1 - ablend))
    amixl = (agrainCompL * ablendgranular) + (asigL * ablendvocoder)
    amixr = (agrainCompR * ablendgranular) + (asigR * ablendvocoder)

    aoutl = amixl
    aoutr = amixr
endif

;printks "dry: %f\tvocoder: %f\tgranular: %f\n", 0.25, k(ablenddry), k(ablendvocoder), k(ablendgranular)

outs aoutl, aoutr ; output



;;;;;;;;;;;;;;;;;;;;;;;;
;;; UI related comm. ;;;
;;;;;;;;;;;;;;;;;;;;;;;;

; Set EOL Pulse Output
ktrig metro kr
kphssync max_k aphssync, ktrig, 2
if kphssync == 1 then
    if krecordA == 1 then
        krecordA = 0
        krecordB = 1
    endif
    if gksource != 1 || kBufferEmpty == 0 then
        gkeol = 20 ;(6ms per tick each ish)
    endif
else
    if gkeol > 0 then   
        gkeol -= 1    
    endif
    ;gkeol = 0
endif

; Set Low Size indicator
if kphasorfreq > 25 then
    gksizestatus = 1
else
    gksizestatus = 0
endif
endin

instr 2
    kresting init 0
    kcopychannel init 0
    kcopysection init 0
    ibuffstart = p4
    ibufftotalsize = p5
    isectionsize = int(4 * sr)
    inumsections = int(ibufftotalsize/isectionsize) + 1

    if kresting == 0 then
        kresting = ksmps / 4
        kcopystart = ibuffstart + (kcopysection * isectionsize)
        if kcopychannel == 0 then
            tablemix giftLB, kcopystart, isectionsize, giftLA, kcopystart, 1.0, giftEmpty, 0, 0.0
            kcopychannel += 1
        else
            tablemix giftRB, kcopystart, isectionsize, giftRA, kcopystart, 1.0, giftEmpty, 0, 0.0
            kcopychannel = 0
            kcopysection += 1
            if kcopysection > inumsections - 1 then
                turnoff
            endif
        endif
    else 
        kresting -= 1
    endif
    
endin
