james-trayford / strauss

Sonification Tools and Resources for Analysis Using Sound Synthesis
Apache License 2.0
33 stars 4 forks source link

ADSR Envelope #2

Open JosephHinz opened 2 years ago

JosephHinz commented 2 years ago

Hi James,

While working with the ADSR envelope code, I was getting no release at the end. Plotting the envelope generated the plot: image (For a sound length of 1 second)

I've applied a fix and extension to the envelope code in mine to overcome this problem, but thought you would like to know the fix applied:

`nlen=params['note_length'] edict=params[f'{etype}_envelope']

    # read envelope params from dictionary
    a = edict['A']
    d = edict['D']
    s = edict['S'] #s now defines the time length of the sustain
    r = edict['R']
    a_k = edict['Ac']
    d_k = edict['Dc']
    s_k = edict['Sc'] #s_k defines the value the envelope sustains at
    r_k = edict['Rc']
    lvl = edict['level']

    # effective input sample times, clipped to ensure always defined
    sampt = samp/self.samprate

    # handy time values
    t1 = a 
    t2 = a+d
    t3 = a+d+s
    t4 = a+d+s+r #added this to separate the envelope length from the sound length (allows for a time period of silence at the end)

    # determine segments and envelope value when note turns off
    #fixed envelope segments to accommodate new variables
    a_seg = lambda t: 1-env_segment_curve(t, a, 1, -a_k)
    d_seg = lambda t: s_k+env_segment_curve(t-t1, d, 1-s, d_k)
    s_seg = lambda t: s_k
    o_seg = lambda t: 0.

    if nlen < t1:
        env_off = a_seg(nlen)
    elif nlen < t2:
        env_off = d_seg(nlen)
    else:
        env_off = s_k

    r_seg = lambda t: env_segment_curve(t-t4+r, r, env_off, r_k) #fixed the release section to accommodate new variables

    # conditionals to determine which segment a sample is in
    # fixed conditionals to accommodate new variables
    a_cond = sampt < t1
    d_cond = np.logical_and(sampt<min(t2,nlen), sampt>=t1)
    s_cond = np.logical_and(sampt<min(t3,nlen), sampt>=min(t2,nlen))
    r_cond = np.logical_and(sampt<t4, sampt>=min(t3,nlen))
    o_cond = sampt >= t4

    # compute envelope for each sample 
    env =  np.piecewise(sampt,
                        [a_cond, d_cond, s_cond, r_cond, o_cond],
                        [a_seg, d_seg, s_seg, r_seg, o_seg])
    return lvl*env`

This new code gives the following plot: image

james-trayford commented 2 years ago

@JosephHinz great catch on a relatively untested part of the code, I will look into this. If possible, you can make a merge request on this file and we can incorporate your changes directly into the main code to give you proper attribution on any fix. Let me know if you have any questions about this.

james-trayford commented 2 years ago

NB it seems like in your example with the old version of the code the envelope was shooting past zero at 1.3s, so you get a signal with an inverted phase, but approaching an absolute volume level of ~ 0.1, explaining why the sound was not dying off