I'm still working my way through your book, so maybe I missed it, but looking through the source code for ThinkDSP, I couldn't find any way to apply an ADSR envelope to a 'Wave' object. So I wrote this as a starting point. It could probably be improved (there isn't much validation being done), but if you think it might be useful for musical applications, feel free to include it.
Two classes are defined, Envelope, and a sub-class, ADSR. The envelope class could be used for evelopes that don't fit the classic ADSR pattern. Instances of either class can then be multiplied with a Wave object, returning another Wave object.
"""@author: David Kettle.
Copyright 2024 David Kettle
License: MIT License (https://opensource.org/licenses/MIT)
"""
from thinkdsp import Wave
import numpy as np
class Envelope(Wave):
"""Represents an amplitude envelope."""
def __init__(self, ys):
"""Initialize the envelope.
ys: array of amplitudes
"""
Wave.__init__(self,ys)
def __mul__(self, other):
"""Multiply an envelope with a wave elementwise.
other: Wave
returns: new Wave
"""
# the envelope and the wave have to have the same duration
assert len(self) == len(other)
ys = self.ys * other.ys
return Wave(ys, other.ts, other.framerate)
__rmul__ = __mul__
class ADSR(Envelope):
"""Represents an attack, decay, sustain and release envelope.
adsr: array or tuple representing attack (# of frames), decay (# of frames),
sustain (amplitude level) and release (# of frames)
duration: total duration of envelope (# of frames)
returns: new Wave
"""
def __init__(self, adsr, duration):
"""Initialize the envelope."""
assert len(adsr) == 4
(att_dur,dec_dur,sus_lvl,rel_dur) = adsr
assert sus_lvl >= 0 and sus_lvl <= 1
sus_dur = duration - (att_dur + dec_dur + rel_dur)
if sus_dur < 0:
sus_dur = 0
attack = np.linspace(0,1,int(att_dur))
decay = np.linspace(1,sus_lvl,int(dec_dur))
sustain = np.linspace(sus_lvl,sus_lvl,int(sus_dur))
release = np.linspace(sus_lvl,0,int(rel_dur))
ys = np.concatenate((attack,decay,sustain,release))[0:int(duration)]
Envelope.__init__(self,ys)
I'm still working my way through your book, so maybe I missed it, but looking through the source code for ThinkDSP, I couldn't find any way to apply an ADSR envelope to a 'Wave' object. So I wrote this as a starting point. It could probably be improved (there isn't much validation being done), but if you think it might be useful for musical applications, feel free to include it.
Two classes are defined, Envelope, and a sub-class, ADSR. The envelope class could be used for evelopes that don't fit the classic ADSR pattern. Instances of either class can then be multiplied with a Wave object, returning another Wave object.
For example:
Here's the source code: