<CsoundSynthesizer>
<CsInstruments>
sr = 44100
kr = 4410
ksmps = 10
nchnls = 2
0dbfs = 1.0

; Instruments
# define GEN_Saw      # 1 #
# define GEN_Square   # 2 #
# define GEN_Triangle # 3 #
# define Simple       # 4 #
# define String       # 5 #

; Single cycle waves
# define Sine # 1 #
# define Tri  # 2 #

; RAP
# define RAP_Triangle_8 # 100 #
# define RAP_Square_16  # 101 #
# define RAP_Saw_32     # 102 #
# define RAP_Buzz_32    # 103 #

; Curves
# define Lin    # 200 #
# define Exp    # 201 # 
# define Custom # 202 #

; Body
# define Flat         # 300 #
# define SawDown      # 301 #
# define TriNoise_512 # 302 #
# define Comb_64      # 303 #
# define TriNoise_8   # 304 #

; Tune
# define Slight  # 400 #
# define Extreme # 401 #

; Shapes
# define Rise # 500 #
# define Fall # 501 #
# define Peak # 502 #
# define Dip  # 503 #

opcode GEN_Saw, 0, ii
    ; Generates data for a band-limited saw wave in Add_Synth format:
    ;     frequency ratio, amplitude, phase
    
    ifn,         \  ; Table number
    in_harmonics \  ; Number of harmonics
    xin
    
    isize = abs(in_harmonics * 3) * -1
    itemp ftgen ifn, 0, isize, 10, 0
        
    i_index = 0
    loop_start:
        ifreq = i_index + 1
        iamp = 1 / ifreq
        iphase = 0
        
        tabw_i ifreq, i_index * 3, ifn
        tabw_i iamp, i_index * 3 + 1, ifn
        tabw_i iphase, i_index * 3 + 2, ifn
        
        prints "GEN_Saw %d: %f %f %f\n", i_index, ifreq, iamp, iphase
        
    loop_lt i_index, 1, in_harmonics, loop_start
endop

opcode GEN_Square, 0, ii
    ; Generates data for a band-limited square wave in Add_Synth format:
    ;     frequency ratio, amplitude, phase
    
    ifn,          \  ; Table number
    imax_harmonic \  ; Highest Harmonic
    xin
    
    in_harmonics = int((imax_harmonic + 1) / 2)
    isize = abs(in_harmonics * 3) * -1
    itemp ftgen ifn, 0, isize, 10, 0
        
    i_index = 0
    loop_start:
        ifreq = i_index * 2 + 1
        iamp = 1 / ifreq
        iphase = 0
        
        tabw_i ifreq, i_index * 3, ifn
        tabw_i iamp, i_index * 3 + 1, ifn
        tabw_i iphase, i_index * 3 + 2, ifn
        
        prints "GEN_Square %d: %f %f %f\n", i_index, ifreq, iamp, iphase
        
    loop_lt i_index, 1, in_harmonics, loop_start
endop

opcode GEN_Triangle, 0, ii
    ; Generates data for a band-limited triangle wave in Add_Synth format:
    ;     frequency ratio, amplitude, phase
    
    ifn,          \  ; Table number
    imax_harmonic \  ; Highest Harmonic
    xin
    
    in_harmonics = int((imax_harmonic + 1) / 2)
    isize = abs(in_harmonics * 3) * -1
    itemp ftgen ifn, 0, isize, 10, 0
        
    i_index = 0
    loop_start:
        ifreq = i_index * 2 + 1
        iamp = 1 / ((i_index * 2 + 1) ^ 2) * (-1 ^ i_index)
        iphase = 0
        
        tabw_i ifreq, i_index * 3, ifn
        tabw_i iamp, i_index * 3 + 1, ifn
        tabw_i iphase, i_index * 3 + 2, ifn
        
        prints "GEN_Triangle %d: %f %f %f\n", i_index, ifreq, iamp, iphase
        
    loop_lt i_index, 1, in_harmonics, loop_start
endop

opcode Get_RAP, iii, ii
    ; Returns Add_Synth data
    
    ifn,     \  ; Table number
    i_index, \  ; Index of data
    xin

    isize = ftlen(ifn) / 3
    
    iratio tab_i i_index * 3, ifn
    iamp tab_i i_index * 3 + 1, ifn
    iphase tab_i i_index * 3 + 2, ifn

    ; Returns frequency ratio, amplitude and phase
    xout iratio, iamp, iphase
endop

opcode Add_Synth, a, kkiiiiiiikki
    kfreq_base,  \  ; Base frequency
    kfreq_mod,   \  ; Frequency modulation
    ifreq_min,   \  ; Minimum frequency
    ifreq_max,   \  ; Maximum frequency
    irap,        \  ; RAP table (ratio, amplitude, phase)
    ibody_curve, \  ; Body frequency curve
    ibody_tune,  \  ; Body tuning table
    ibody_amp,   \  ; Body amplitude table
    iflt,        \  ; Filter table
    kflt_freq,   \  ; Frequency of filter (index 0 of filter table)
    kflt_width,  \  ; Width of filter
    i_index      \  ; Index of current harmonic
    xin

    ; This harmonic
    kfreq init 0  ; Frequency for this partial
    kamp init 0   ; Amplitude for this partial
    
    ; Additive band-limited waveform
    iratio, iamp, iphase Get_RAP irap, i_index  ; Extract RAP from table    
    kfreq = kfreq_base * iratio                 ; Frequency of partial
    kfreq = kfreq + kfreq * kfreq_mod           ; Modulate frequency
    kamp = iamp
    
    ; Body
    kcurve tablei kfreq / 22050, ibody_curve, 1, 0, 0  ; Frequency curve
    ktune tablei kcurve, ibody_tune, 1, 0, 0           ; Warp frequency
    kfreq = kfreq * ktune
    kbody_amp tablei kcurve, ibody_amp, 1, 0, 0        ; Amplitude
    kamp = kamp * kbody_amp
    
    ; Additive synth filter
    kflt init 0
    iflt_size = ftlen(iflt)    
    kflt_transfer = (kfreq - kflt_freq) / kflt_width

    if (kflt_transfer < 0 || kflt_width <= 0) then  ; Transfer function
        kflt tab 0, iflt
    elseif (kflt_transfer >= 1) then
        kflt tab iflt_size - 1, iflt
    else
        kflt tablei kflt_transfer, iflt, 1, 0, 0
    endif
        
    kamp = kamp * kflt
    
    ; Generate this voice
    a1 init 0
    
    if (kfreq >= ifreq_min && kfreq < ifreq_max) then
        a1 oscil kamp, kfreq, $Sine, iphase
    else
        a1 = 0
    endif
    
    ; Recursive oscillator
    a2 init 0

    if (i_index < (ftlen(irap) / 3) - 1 && i(kfreq) < ifreq_max) then
        a2 Add_Synth kfreq_base, kfreq_mod, ifreq_min, ifreq_max, irap,   \
                     ibody_curve, ibody_tune, ibody_amp, iflt, kflt_freq, \
                     kflt_width, i_index + 1
    endif
    
    ; Return audio
    xout a1 + a2
endop

instr $GEN_Saw
    ; Wrapper for UDO of the same name

    ifn = p4           ; Function number of table to create
    in_harmonics = p5  ; Number of harmonics
   
    GEN_Saw ifn, in_harmonics

    turnoff
endin

instr $GEN_Square
    ; Wrapper for UDO of the same name

    ifn = p4           ; Function number of table to create
    in_harmonics = p5  ; Number of harmonics
   
    GEN_Square ifn, in_harmonics

    turnoff
endin

instr $GEN_Triangle
    ; Wrapper for UDO of the same name

    ifn = p4           ; Function number of table to create
    in_harmonics = p5  ; Number of harmonics
   
    GEN_Triangle ifn, in_harmonics

    turnoff
endin

instr $Simple
    idur = p3          ; Duration
    iamp = p4          ; Amplitude
    ipch = cpspch(p5)  ; Pitch

    ; Fixed Add_Synth Attributes
    irap        = $RAP_Square_16
    ibody_curve = $Lin
    ibody_tune  = $Flat
    ibody_amp   = $Comb_64
    iflt        = $Rise

    ; Frequency Modulation
    kfreq_mod oscil 0.01, 5, $Tri
    
    ; Filter
    kflt_freq expon ipch * 8, idur, ipch
    kflt_width = kflt_freq * 4
    
    ; Generate Audio
    a1 Add_Synth ipch, kfreq_mod, 20, 22050, irap, ibody_curve, ibody_tune, \
                 ibody_amp, iflt, kflt_freq, kflt_width, 0
    
    ; Amp
    aenv linseg 0, 0.05, iamp, idur - 0.1, iamp, 0.05, 0
    a1 = a1 * aenv
    a1 limit a1, -1, 1  ; Prevent audio explosions
    
    ;Output
    outs a1, a1
endin

instr $String
    idur = p3          ; Duration
    iamp = p4          ; Amplitude
    ipch = cpspch(p5)  ; Pitch in octave point pitch-class
    ipan = p6          ; Pan position
    
    irap = $RAP_Saw_32
    ibody_curve = $Custom
    ibody_amp = $TriNoise_512
    ibody_tune = $Flat
    iflt = $Flat
    
    ; Pitch vibrato
    k2 linsegr 0, 0.4, 0, 0.7, 1, 1, 1, 1, 0.3, 0.01, 1
    klfo oscil k2, 4.8 + rnd(0.4), $Tri
    krand randh rnd(0.9) + 0.1, 0.125 + rnd(0.25)
    kvibrato = (klfo + krand) * (0.003 + rnd(0.007)) * 2
    
    ; Pitch
    kpch expseg 2 ^ (-1 / 12), 0.05 + rnd(0.05), 2 ^ (0 / 12), 0.001, \
                2 ^ (0 / 12)
    
    ; Generate audio
    a1 Add_Synth ipch, kvibrato + kpch, 20, 22050, irap, ibody_curve, \
                 ibody_tune, ibody_amp, iflt, 0, 22050, 0

    ; Amp
    aenv linsegr 0, 0.1 + rnd(0.105), 0.2 + rnd(0.3), 0.1, 0.5, 2, 0.333, \
                 0.2 + rnd(0.1), 0             
    asig = a1 * aenv * iamp * (0.9 + rnd(0.1))
    aleft limit asig * sqrt(1 - ipan), -1, 1
    aright limit asig * sqrt(ipan), -1, 1

    outs aleft, aright 
endin

</CsInstruments>
<CsScore>

; Instruments
# define GEN_Saw      # 1 #
# define GEN_Square   # 2 #
# define GEN_Triangle # 3 #
# define Simple       # 4 #
# define String       # 5 #

; Single cycle waves
# define Sine # 1 #
# define Tri  # 2 #

; RAP
# define RAP_Triangle_8 # 100 #
# define RAP_Square_16  # 101 #
# define RAP_Saw_32     # 102 #
# define RAP_Buzz_32    # 103 #

; Curves
# define Lin    # 200 #
# define Exp    # 201 # 
# define Custom # 202 #

; Body
# define Flat         # 300 #
# define SawDown      # 301 #
# define TriNoise_512 # 302 #
# define Comb_64      # 303 #
# define TriNoise_8   # 304 #

; Tune
# define Slight  # 400 #
# define Extreme # 401 #

; Shapes
# define Rise # 500 #
# define Fall # 501 #
# define Peak # 502 #
# define Dip  # 503 #


; Single cycle waves
f $Sine 0 [2 ^ 16] 10 1
f $Tri 0 [2 ^ 16] -7 -1 [2 ^ 15] 1 [2 ^ 15] -1

; Curves
f $Lin 0 [2 ^ 16] 7 0 [2 ^ 16] 1
f $Exp 0 [2 ^ 16] 5 1 [2 ^ 16] 22050
f $Custom 0 [2 ^ 16] 7 0 [2 ^ 15] 0.9 [2 ^ 15] 1

; Body
f $Flat 0 2 -2 1 1
f $SawDown 0 8192 -7 1 8192 0
f $TriNoise_512 0 512 21 3 1
f $Comb_64 0 64 -2 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 \
                   0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 \
                   1 0 1 0 1 0
f $TriNoise_8 0 8 21 3 1

; Tune
f $Slight 0 8 -2 1 0.95 1 1.05 1 0.95 1 1.05
f $Extreme 0 16 -2 0.824826039083 0.77021899829 1.08563667392 0.821626515869  \
                   0.973588554698 0.990091084488 1.24412414666 0.984434054098 \
                   0.778162401054 1.06861329021 1.15621028917 0.890593537536  \
                   0.983407895849 0.984163978657 0.945023580455 1.03768076874
; Shapes
f $Rise 0 8192 -7 1 8192 0
f $Fall 0 8192 -7 0 8192 1
f $Peak 0 8192 -7 0 4096 1 4096 0
f $Dip 0 8192 -7 1 4096 0 4096 1

; RAP
i $GEN_Triangle 0 1 $RAP_Triangle_8 8
i $GEN_Square   0 1 $RAP_Square_16  16
i $GEN_Saw      0 1 $RAP_Saw_32     32
f $RAP_Buzz_32 0 -96 -2 1 1 0 2 1 0 3 1 0 4 1 0 5 1 0 6 1 0 7 1 0 8 1 0 9 1 0 \
                        10 1 0 11 1 0 12 1 0 13 1 0 14 1 0 15 1 0 16 1 0 17 1 \
                        0 18 1 0 19 1 0 20 1 0 21 1 0 22 1 0 23 1 0 24 1 0 25 \
                        1 0 26 1 0 27 1 0 28 1 0 29 1 0 30 1 0 31 1 0 32 1 0
t 0 60

i $Simple 0 2   0.2 7.00
i $Simple + 2   .   8.00
i $Simple + 0.5 .   7.11
i $Simple + 0.5 .   8.00
i $Simple + 4   .   8.03

s

i $String 0 1.33 1.5 5.00 0.5
i $String + .    .   5.03 .
i $String + .    .   5.07 .
i $String + .    .   5.09 .
i $String + .    .   6.00 .
i $String + .    .   6.03 .
i $String + .    .   6.07 .
i $String + .    .   6.09 .
i $String + .    .   7.00 .
i $String + .    .   7.03 .
i $String + .    .   7.07 .
i $String + .    .   7.09 .
i $String + .    .   8.00 .
i $String + .    .   8.03 .
i $String + .    .   8.07 .
i $String + .    .   8.09 .
i $String + .    .   9.00 .
i $String + .    .   9.03 .
i $String + .    .   9.07 .
i $String + .    .   9.09 .

e

</CsScore>
</CsoundSynthesizer>

