22
22
* EG: A decay-only envelope generator controls both filter cutoff and amplitude.
23
23
The EG is retriggered on every step and has no sustain or release
24
24
25
- Common post effects added to TB-303 are:
26
-
27
- * Overdrive / Distortion
28
- * Delay, usually tempo-sync'd
29
-
30
25
The 16-step sequencer of the TB-303 is different than most, having 3 attributes
31
26
per step:
32
27
36
31
37
32
This code does not implement the sequencer, but does implement accent and slide.
38
33
34
+ Common post effects added to TB-303 are:
35
+
36
+ * Overdrive / Distortion
37
+ * Delay, usually tempo-sync'd
38
+
39
+ Both of these effects are added to this synth.
39
40
40
41
"""
41
42
# Sorta like a TB303
56
57
np .linspace (wave_vol , - wave_vol , num = wave_size , dtype = np .int16 ), # saw
57
58
np .concatenate ((np .ones (wave_size // 2 , dtype = np .int16 ) * wave_vol , # square
58
59
np .ones (wave_size // 2 , dtype = np .int16 ) * - wave_vol )),
59
-
60
60
)
61
61
62
62
class TBishSynth :
63
63
def __init__ (self , sample_rate , channel_count ):
64
64
self .synth = synthio .Synthesizer (sample_rate = sample_rate ,
65
65
channel_count = channel_count )
66
66
self .note = None
67
- self .secs_per_step = 0.125
67
+ self .secs_per_step = 0.125 # fixme
68
+ self .transpose = 0
68
69
self .wavenum = 1 # which waveform to use: saw=0, square=1
69
70
self .cutoff = 8000 # aka 'filter frequency'
70
71
self .envmod = 0.5 # aka 'filter depth'
71
- self .envdecay = 0.01
72
- self ._accent = 0.5
72
+ self .decay = 0.01
73
+ self .accent = 0.5
73
74
self .autoslide = True # FIXME unused yet
74
75
self .filt_env = synthio .LFO (rate = 1 , scale = self .cutoff , once = True ,
75
76
waveform = np .array ((32767 ,0 ),dtype = np .int16 ))
@@ -119,6 +120,7 @@ def add_audioeffects(self):
119
120
return self .fx_delay # the "output" of this synth
120
121
121
122
def note_on_step (self , midi_note , slide = False , accent = False ):
123
+ """Trigger a note, with slide and accent"""
122
124
print ("note_on_step: %3d %1d %1d" % (midi_note , slide , accent ))
123
125
self .note_off (midi_note ) # must do when in step mode
124
126
attack_level = 0.8
@@ -134,7 +136,7 @@ def note_on_step(self, midi_note, slide=False, accent=False):
134
136
if slide :
135
137
glide_time = 0.1
136
138
# FIXME also do appropriate other actions for slide
137
- envdecay = max (0.05 , self .secs_per_step * self .envdecay * 1.5 ) # 1.5 fudge
139
+ envdecay = max (0.05 , self .secs_per_step * self .decay * 1.5 ) # FIXME: 1.5 fudge
138
140
frate = 1 / envdecay
139
141
self .filt_env .offset = ((1 - envmod ) * cutoff )
140
142
self .filt_env .scale = cutoff - self .filt_env .offset
@@ -147,11 +149,11 @@ def note_on_step(self, midi_note, slide=False, accent=False):
147
149
decay_time = envdecay ,
148
150
sustain_level = 0 ,
149
151
release_time = 0.01 )
150
- self .note = synthio .Note (synthio .midi_to_hz (midi_note ),
152
+ self .note = synthio .Note (synthio .midi_to_hz (midi_note + self . transpose ),
151
153
bend = self .glider .lerp ,
152
154
filter = self .filter ,
153
155
envelope = ampenv ,
154
- waveform = waves [self .wavenum ])
156
+ waveform = waves [int ( self .wavenum ) ])
155
157
self .synth .press (self .note )
156
158
157
159
@@ -160,27 +162,11 @@ def note_on(self, midi_note, vel=100):
160
162
self .note_on_step (midi_note , slide = (vel == 1 ), accent = (vel == 127 ))
161
163
162
164
def note_off (self , midi_note , vel = 0 ):
163
- """Used for both MIDI and sequencer mode"""
165
+ """Used for both MIDI and sequencer mode, passed in args are not used. """
164
166
if self .note :
165
167
self .synth .release (self .note )
166
168
self .note = None
167
169
168
- @property
169
- def decay (self ):
170
- return self .envdecay
171
-
172
- @decay .setter
173
- def decay (self ,t ):
174
- self .envdecay = t
175
-
176
- @property
177
- def accent (self ):
178
- return self .accent
179
-
180
- @accent .setter
181
- def accent (self , v ):
182
- self .accent = v
183
-
184
170
@property
185
171
def resonance (self ):
186
172
return self .filter .Q
@@ -201,6 +187,14 @@ def drive(self,d):
201
187
self .fx_distortion .post_gain = - d / 2
202
188
self .fx_distortion .pre_gain = d
203
189
190
+ @property
191
+ def drive_mix (self ):
192
+ return self .fx_distortion .mix
193
+
194
+ @drive_mix .setter
195
+ def drive_mix (self , m ):
196
+ self .fx_distortion .mix = m
197
+
204
198
@property
205
199
def delay_mix (self ):
206
200
return self .fx_delay .mix
@@ -211,8 +205,8 @@ def delay_mix(self, m):
211
205
212
206
@property
213
207
def delay_time (self ):
214
- return self .fx_delay .delay_ms / (self .secs_per_step * 100 * 2 )
208
+ return self .fx_delay .delay_ms / (self .secs_per_step * 100 * 4 )
215
209
216
210
@delay_time .setter
217
211
def delay_time (self , m ):
218
- self .fx_delay .delay_ms = self .secs_per_step * 1000 * 2 * m
212
+ self .fx_delay .delay_ms = self .secs_per_step * 1000 * 4 * m
0 commit comments