Creating Signals - Quefumas/gensound GitHub Wiki
Aliasing an existing signal
Mostly useful to try out different oscillators which are reused in many places.
MySignal = Triangle
MySignal("A4", 1e3).play()
Using a function
This method is simple and flexible, and does not require understanding how Gensound is built - any user can do it. See the following examples:
def MySignal(frequency, duration):
# Square wave with ADSR envelope and filter
return Square(frequency, duration)*ADSR(...)*Filter(...)
def MySignal2(frequency, duration):
# mix between sine, octave square and noise
return 0.5*Sine(frequency, duration) + 0.3*Square(frequency*2, duration) + 0.2*WhiteNoise(duration)
s = MySignal("A G F# E", 1e3) + MySignal2("F3 E3 Ab3 G3", 1e3)
s.play()
TODO consider how this interacts with future feature shorthand melodic notation
Subclassing Signal
When you need to create the signal "by hand", it is easy to subclass Signal.
This involves overriding __init__
, which mostly just saves instance variables, and generate
, which returns the actual signal as numpy.ndarray
.
While the former is called on object construction, generate
is only called when calling play
or export
on some Signal containing the new signal.
class MySignal(Signal):
def __init__(self, ...):
super().__init__()
# do something with arguments if needed
...
def generate(self, sample_rate):
# user code here, should return np.ndarray object with shape of length two (?): (num channels, num samples)
# data type should be np.float64
...
Oscillator
Subclassing If the new signal behaves like an oscillator - that is, it is identified by a single main frequency - it is recommended to subclass Oscillator
instead.
This is both shorter and will likely carry other benefits (like phase inference). All that is needed is defining a single class member, and the rest is taken care of:
class MyOsc(Oscillator):
wave = lambda phase: ... # a function receiving phases (np.ndarray) and returning amplitudes
TODO change from lambda to class function