<?xml version="1.0" encoding="UTF-8"?>
<plant>
<namespace>SimpleADSR</namespace>
<name>SimpleADSR</name>
<cabbagecode>
image bounds(0,0,225,256), colour(0,0,0,0) {
  image pos(0,0), size(223,105), colour(0,0,0,255)
  gentable pos(5, 5), size(213, 100), tablenumber(1), amprange(0, 1), identchannel("graph"), visible(1)
  label pos(10, 93), size(215, 11), text("-- uninitialized --"), align("left"), identchannel("text")
    rslider pos(175, 105), size(50, 50), channel("Scale"), range(.1, 10, 1, 1, .1), text("Scale"), popuppostfix(" Sec")

    rslider pos(0, 160), size(50, 50), channel("Att"), range(.01, 100, .01, .5, 0.01), text("Attack"), popuppostfix(" % of Scale")
    rslider pos(50, 160), size(50, 50), channel("Dec"), range(.01, 100, 35, .5, 0.01), text("Decay"), popuppostfix(" % of Scale")
    rslider pos(125, 160), size(50, 50), channel("Sus"), range(0, 100, 50, .5, 0.01), text("Sustain"), popuppostfix(" %")
    rslider pos(175, 160), size(50, 50), channel("Rel"), range(.01, 100, 10, .5, 0.01), text("Release"), popuppostfix(" % of Scale")

    rslider pos(0, 210), size(50, 50), channel("AttShape"), range(-10, 10, 0, 1, 0.01), text("Shape"), popupprefix("Attack Shape: ")
    rslider pos(50, 210), size(50, 50), channel("DecShape"), range(-10, 10, 0, 1, 0.01), text("Shape"), popupprefix("Decay Shape: ")
    rslider pos(175, 210), size(50, 50), channel("RelShape"), range(-10, 10, 0, 1, 0.01), text("Shape"), popupprefix("Release Shape: ")
}
</cabbagecode>
<csoundcode>

; Name of instrument to trigger for drawing shapes
; Has to be a unique string, not numeric
#ifndef ADSR_INSTR
  #define ADSR_INSTR #SimpleADSRInstr#
#endif

; Set lowest number possible in exponential segments
#define EXPN_MIN #.00001#

; Set how large should graph ftable be
#define SHAPE_SIZE #8193#
#define SHAPE_HALF_SIZE #$SHAPE_SIZE*.5#
#define SHAPE_QUARTER_SIZE #$SHAPE_SIZE*.25#

; UDO listens for val changes and forces a redraw using INSTR_ADSR
; THIS SHOULD BE CALLED FROM AN ALWAYS ON INSTRUMENT, not from the midi instr
; iTable needs to exist ahead of time, and be the same length as $SHAPE_SIZE
; scale is irrelevant to drawing the table, so not included here!
opcode SimpleADSR_Mon,0,Si
  SChanPrefix, iTable xin

  if metro(20)==1 then
    kAtt chnget strcat(SChanPrefix,"Att")
    kDec chnget strcat(SChanPrefix,"Dec")
    kSus chnget strcat(SChanPrefix,"Sus")
    kHold chnget strcat(SChanPrefix,"Hold")
    kRel chnget strcat(SChanPrefix,"Rel")
    kAttShape chnget strcat(SChanPrefix,"AttShape")
    kDecShape chnget strcat(SChanPrefix,"DecShape")
    kRelShape chnget strcat(SChanPrefix,"RelShape")

    kScale = chnget:k(strcat(SChanPrefix,"Scale"))*.01

    if(changed(kAtt,kDec,kSus,kHold,kRel,kAttShape,kDecShape,kRelShape)==1) then
      ; Trigger instrument that draws a new waveform and send an update to the text widget
      scoreline sprintfk("i\"%s\" 0 .01 \"%s\" %d", "$ADSR_INSTR", SChanPrefix,iTable), k(1)
      chnset sprintfk("text(A: %2.3fs  D: %2.3fs  S: %4.2f%%  R: %2.3fs)",kAtt*kScale,kDec*kScale,kSus,kRel*kScale), strcat(SChanPrefix,"text")
    elseif(changed(kScale)==1) then
      ; Send an update to the text widget *ONLY* when scale changes, since the table stays same
      chnset sprintfk("text(A: %2.3fs  D: %2.3fs  S: %4.2f%%  R: %2.3fs)",kAtt*kScale,kDec*kScale,kSus,kRel*kScale), strcat(SChanPrefix,"text")
    endif
  endif
endop

; UDO returns an a-rate env multiplier
; This gets called from a midi instrument
opcode SimpleADSR,a,S
  SChanPrefix xin

  ; percentages
  iAtt = chnget:i(strcat(SChanPrefix,"Att"))*.01
  iDec = chnget:i(strcat(SChanPrefix,"Dec"))*.01
  iSus = chnget:i(strcat(SChanPrefix,"Sus"))*.01
  iRel = chnget:i(strcat(SChanPrefix,"Rel"))*.01

  ; scaling by seconds
  iScale chnget strcat(SChanPrefix,"Scale")

  ; segment modes
  iAttShape chnget strcat(SChanPrefix,"AttShape")
  iDecShape chnget strcat(SChanPrefix,"DecShape")
  iRelShape chnget strcat(SChanPrefix,"RelShape")

  iAtt *= iScale
  iDec *= iScale
  iRel *= iScale

  iSus limit iSus, $EXPN_MIN, 1
  
  aEnv transegr $EXPN_MIN, iAtt, iAttShape, 1, iDec, iDecShape, iSus, iRel, iRelShape, $EXPN_MIN 

  xout aEnv
endop

; instr $ADSR_INSTR redraws waveforms based on pfields & channels
; YOU NEVER NEED TO USE THIS MANUALLY, it is auto triggered by the monitor UDO
; this instrument is triggered when SimpleADSR_Mon sees changes in important values!
; Remember, this shape table being drawn is only for visual purposes
; actual envelopes and other UI changes are handled elsewhere!
instr $ADSR_INSTR
  SChanPrefix = p4
  iTable = p5

  iAtt = chnget:i(strcat(SChanPrefix,"Att"))*.01
  iDec = chnget:i(strcat(SChanPrefix,"Dec"))*.01
  iSus = chnget:i(strcat(SChanPrefix,"Sus"))*.01
  iRel = chnget:i(strcat(SChanPrefix,"Rel"))*.01

  iAttShape chnget strcat(SChanPrefix,"AttShape")
  iDecShape chnget strcat(SChanPrefix,"DecShape")
  iRelShape chnget strcat(SChanPrefix,"RelShape")

  ; round down, then limit to 1-QUARTER_SIZE
  iSeg1 limit floor($SHAPE_QUARTER_SIZE*iAtt), 1, $SHAPE_QUARTER_SIZE
  iSeg2 limit floor($SHAPE_QUARTER_SIZE*iDec), 1, $SHAPE_QUARTER_SIZE
  iSeg4 limit floor($SHAPE_QUARTER_SIZE*iRel), 1, $SHAPE_QUARTER_SIZE
  iSeg3=$SHAPE_SIZE-(iSeg1+iSeg2+iSeg4)
  ; iSus = iSus==0 ? $EXPN_MIN : iSus
  iSus limit iSus, $EXPN_MIN, 1
  iFileTable ftgentmp iTable, 0, $SHAPE_SIZE, 16, $EXPN_MIN, iSeg1, iAttShape, 1, iSeg2, iDecShape, iSus, iSeg3, 0, iSus, iSeg4, iRelShape, $EXPN_MIN

  ; this bumps the graph, forcing an update
  chnset sprintfk("tablenumber(%d)",iTable), strcat(SChanPrefix,"graph")
endin

</csoundcode>
<help>
	This is temporary text... where does this get used?
</help>
</plant>

