circuitpython-synthtools 0.6.0__py3-none-any.whl → 0.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {circuitpython_synthtools-0.6.0.dist-info → circuitpython_synthtools-0.8.0.dist-info}/METADATA +16 -15
- {circuitpython_synthtools-0.6.0.dist-info → circuitpython_synthtools-0.8.0.dist-info}/RECORD +12 -11
- synthtools/__init__.py +7 -1
- synthtools/audio_fx.py +47 -10
- synthtools/bassline_synth.py +534 -71
- synthtools/harmony.py +198 -0
- synthtools/trig_sequencer.py +7 -6
- synthtools/wavetable.py +47 -19
- synthtools/wavetable_synth.py +114 -202
- {circuitpython_synthtools-0.6.0.dist-info → circuitpython_synthtools-0.8.0.dist-info}/WHEEL +0 -0
- {circuitpython_synthtools-0.6.0.dist-info → circuitpython_synthtools-0.8.0.dist-info}/licenses/LICENSE +0 -0
- {circuitpython_synthtools-0.6.0.dist-info → circuitpython_synthtools-0.8.0.dist-info}/top_level.txt +0 -0
{circuitpython_synthtools-0.6.0.dist-info → circuitpython_synthtools-0.8.0.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: circuitpython-synthtools
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: CircuitPython helper library to do help doing synthio
|
|
5
5
|
Author-email: Tod Kurt <tod@todbot.com>
|
|
6
6
|
License: MIT
|
|
@@ -125,16 +125,17 @@ Usage Example
|
|
|
125
125
|
What's Included
|
|
126
126
|
===============
|
|
127
127
|
|
|
128
|
+
* ``Patch`` -- inert, JSON-able patch data; save/load with
|
|
129
|
+
``save_patches()`` / ``load_patches()``
|
|
128
130
|
* ``Synth`` -- synth engine base: shared voice, patch, and modulation handling,
|
|
129
|
-
|
|
131
|
+
complex filter & pitch envelopes, and a ``mono`` mode for a single-voice
|
|
132
|
+
synth with ``glide_time`` portamento
|
|
130
133
|
* ``SubtractiveSynth`` -- subtractive two-oscillator synth w/ detune
|
|
134
|
+
* ``WavetableSynth`` -- wavetable-playback with adjustable LFO for wave_pos
|
|
131
135
|
* ``FMSynth`` -- two-operator phase-modulation voice: a carrier waveform
|
|
132
136
|
pre-rendered from ``sin(theta + fm_index*sin(fm_ratio*theta))``.
|
|
133
137
|
``fm_ratio`` must be an integer; ``fm_index`` is PM depth in radians,
|
|
134
|
-
0 = plain single-oscillator. (Not a
|
|
135
|
-
synthio's Math/LFO blocks only update every 256 samples, too slow for
|
|
136
|
-
that; see the module docstring for why.)
|
|
137
|
-
* ``WavetableSynth`` -- wavetable-playback with adjustable wave_pos
|
|
138
|
+
0 = plain single-oscillator. (Not a full FM synth, see module for details)
|
|
138
139
|
* ``BasslineSynth`` -- TB-303-style acid bassline: monophonic, one
|
|
139
140
|
oscillator, a decay-only filter sweep, per-step slide and accent. Can
|
|
140
141
|
own its own filter/distortion/echo effects chain via ``fx_*`` patch
|
|
@@ -144,23 +145,23 @@ What's Included
|
|
|
144
145
|
``tracking_filter()`` builds extra filter stages that follow the synth's
|
|
145
146
|
own cutoff and resonance for a steeper slope (needs ``audiofilters`` in
|
|
146
147
|
the build)
|
|
147
|
-
* ``
|
|
148
|
-
|
|
148
|
+
* ``Waves`` -- waveform factory (saw, square, sine, triangle, noise, and
|
|
149
|
+
"analog" variants)
|
|
149
150
|
* ``Wavetable`` -- loads a wavetable WAV file and lerps between frames
|
|
150
151
|
* ``AHREnvelope`` -- shared-block attack/release envelope, used for both
|
|
151
152
|
the filter and pitch envelopes
|
|
152
|
-
* ``Waves`` -- waveform factory (saw, square, sine, triangle, noise, and
|
|
153
|
-
"analog" variants)
|
|
154
153
|
* ``Arpeggiator`` / ``StepSequencer`` / ``TrigSequencer`` -- poll-based
|
|
155
154
|
sequencers with on/off callbacks
|
|
156
|
-
* ``Param`` / ``ParamSet`` -- knob-pickup and scaling for UIs with fewer
|
|
157
|
-
knobs than parameters
|
|
158
|
-
* ``ParamScaler`` -- proportional ("scale") knob takeover for a single
|
|
159
|
-
control, when you are not using ``ParamSet``
|
|
160
|
-
* ``GaugeCluster`` -- a bar-graph display of a parameter page
|
|
161
155
|
* ``Glider`` -- a standalone pitch-slide block for hand-built
|
|
162
156
|
``synthio.Note`` graphs (the engines above have their own portamento,
|
|
163
157
|
via ``mono`` + ``glide_time``)
|
|
158
|
+
* ``Scale`` / ``chord`` -- scales and diatonic/chromatic chords for
|
|
159
|
+
mapping pads and MIDI onto pitch
|
|
160
|
+
* ``Param`` / ``ParamSet`` -- (UI) knob-pickup and scaling for UIs with fewer
|
|
161
|
+
knobs than parameters
|
|
162
|
+
* ``ParamScaler`` -- (UI) proportional ("scale") knob takeover for a single
|
|
163
|
+
control, when you are not using ``ParamSet``
|
|
164
|
+
* ``GaugeCluster`` -- (UI) a bar-graph display of a parameter page
|
|
164
165
|
* ``RollingAverage`` -- moving-average smoothing for noisy knob reads
|
|
165
166
|
|
|
166
167
|
Documentation
|
{circuitpython_synthtools-0.6.0.dist-info → circuitpython_synthtools-0.8.0.dist-info}/RECORD
RENAMED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
circuitpython_synthtools-0.
|
|
2
|
-
synthtools/__init__.py,sha256=
|
|
1
|
+
circuitpython_synthtools-0.8.0.dist-info/licenses/LICENSE,sha256=jhhRyxpqyQxyF_D6SzY2-jCA5v8Z8JXfezCtxBS85CM,1075
|
|
2
|
+
synthtools/__init__.py,sha256=dB2eJTgbLs_sfEMCFcm8F_FNoblgNcZd6toMpHFQThw,2752
|
|
3
3
|
synthtools/ahr_envelope.py,sha256=fWhjp7LahNGjL8qtTgyiAnX5hnviAULuyc9dEJ6t74I,9233
|
|
4
4
|
synthtools/arpeggiator.py,sha256=n2sPnpZcu9soXsFJCh6OP65_0DuPnvQGUP0ao7V8SXM,3901
|
|
5
|
-
synthtools/audio_fx.py,sha256=
|
|
6
|
-
synthtools/bassline_synth.py,sha256=
|
|
5
|
+
synthtools/audio_fx.py,sha256=PjnETf5kKIlG_tLKwM49PCvSkM2txfZf2o7MuEnU2W4,8778
|
|
6
|
+
synthtools/bassline_synth.py,sha256=X8efKBgemBP2qAX0hL3PgWk3c_jkBWo0aX4KrgccHKE,51547
|
|
7
7
|
synthtools/blocks.py,sha256=EsGcDEzIJX_dvBN8CVlDrewtl16x5leU03tum9jrhr0,1995
|
|
8
8
|
synthtools/fm_synth.py,sha256=-r4NcsESjEechkbmV0tZx4pXayHIz4qUfIznKtQoKgQ,6073
|
|
9
9
|
synthtools/gauge_cluster.py,sha256=lcMkRK-db6KV6pu0PUJRWAOJT8IdjgpE_4bAi0lQwxc,2910
|
|
10
|
+
synthtools/harmony.py,sha256=V_btMIqO-Ep2x64XwM1A1agSeQqwPcbLqpvGjxlnpUE,6206
|
|
10
11
|
synthtools/param_scaler.py,sha256=dmaFkzRhrfZn54DAgPwZs7P4B4BFd90ZUvMHpWIf2i4,6202
|
|
11
12
|
synthtools/paramset.py,sha256=--V89zHR4pUhkSey1CX7iMLNsF5l1hKmKWrAr3j_cRA,9938
|
|
12
13
|
synthtools/patch.py,sha256=vMr-kdVD66uxY8BrrnB0J6uDLREjuu16I8HzfhAkqHs,6033
|
|
@@ -15,12 +16,12 @@ synthtools/step_sequencer.py,sha256=SkQuDsRtpAdKOXU3OsFmngeDPVQ0OXt3Gj5wqFCkEPM,
|
|
|
15
16
|
synthtools/subtractive_synth.py,sha256=WIpmhbiiLtyQRFV9mHWWM-duST5RfY9HUN4SRf5wTOw,3382
|
|
16
17
|
synthtools/swarm_synth.py,sha256=kCYsdVuaafTKkD06_m22iFaPUMCzeBKX67mkrsEq3pk,10945
|
|
17
18
|
synthtools/synth.py,sha256=ZdmAKWBVzJqPyzU5W1hxnJ6j09D4rnCWm77s5jRW_Ww,36528
|
|
18
|
-
synthtools/trig_sequencer.py,sha256=
|
|
19
|
+
synthtools/trig_sequencer.py,sha256=J3_PdbNmYEoFkz7Hj58qNzMRZQuUm-sf2dKZIrBTV8U,3052
|
|
19
20
|
synthtools/utils.py,sha256=deKQbkGXIBdGUOXBOpRCc-UHdPiL8YmybIJ3BBo9aA4,648
|
|
20
21
|
synthtools/waves.py,sha256=lwDka71TsoHGJcyVfmG4t-TEf6TjBwftJEXSS4Qxyx0,22407
|
|
21
|
-
synthtools/wavetable.py,sha256=
|
|
22
|
-
synthtools/wavetable_synth.py,sha256=
|
|
23
|
-
circuitpython_synthtools-0.
|
|
24
|
-
circuitpython_synthtools-0.
|
|
25
|
-
circuitpython_synthtools-0.
|
|
26
|
-
circuitpython_synthtools-0.
|
|
22
|
+
synthtools/wavetable.py,sha256=7zQdAvHyiSp7wiVjl-lIfeifpbdsp7M5cCVbMT7eku0,3725
|
|
23
|
+
synthtools/wavetable_synth.py,sha256=nggGXEr3tlADvTI38gCDRSw8XEaB5kHkzvQGSFfw16E,7969
|
|
24
|
+
circuitpython_synthtools-0.8.0.dist-info/METADATA,sha256=K6l8EWT1soJUgzphRNq1V1Qhi3mdcCYXt0VAHjE5MFw,7139
|
|
25
|
+
circuitpython_synthtools-0.8.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
26
|
+
circuitpython_synthtools-0.8.0.dist-info/top_level.txt,sha256=1xt53cH064i7vvFVDFyuJugc1NogReXvzsXbYGlgnKE,11
|
|
27
|
+
circuitpython_synthtools-0.8.0.dist-info/RECORD,,
|
synthtools/__init__.py
CHANGED
|
@@ -36,7 +36,7 @@ Anything not listed below is still importable by its module path::
|
|
|
36
36
|
|
|
37
37
|
import sys
|
|
38
38
|
|
|
39
|
-
__version__ = "0.
|
|
39
|
+
__version__ = "0.8.0"
|
|
40
40
|
__repo__ = "https://github.com/todbot/CircuitPython_SynthTools.git"
|
|
41
41
|
|
|
42
42
|
#: Public name -> the submodule that defines it. Every entry is loaded on
|
|
@@ -49,6 +49,12 @@ _LAZY = {
|
|
|
49
49
|
"BasslineSynth": "bassline_synth",
|
|
50
50
|
"FMSynth": "fm_synth",
|
|
51
51
|
"GaugeCluster": "gauge_cluster",
|
|
52
|
+
"Scale": "harmony",
|
|
53
|
+
"chord": "harmony",
|
|
54
|
+
"note_name": "harmony",
|
|
55
|
+
"SCALES": "harmony",
|
|
56
|
+
"CHORDS": "harmony",
|
|
57
|
+
"DIATONIC_SHAPES": "harmony",
|
|
52
58
|
"ParamScaler": "param_scaler",
|
|
53
59
|
"Patch": "patch",
|
|
54
60
|
"load_patches": "patch",
|
synthtools/audio_fx.py
CHANGED
|
@@ -26,10 +26,14 @@
|
|
|
26
26
|
# Identical resonant sections stacked this way do pile up gain at the cutoff,
|
|
27
27
|
# each stage adding its own peak on top of the last. At a squelchy filt_q
|
|
28
28
|
# that is real headroom to watch, and also most of why a cascaded resonant
|
|
29
|
-
# filter reads as more aggressive than a single one.
|
|
29
|
+
# filter reads as more aggressive than a single one. tracking_filter()'s
|
|
30
|
+
# `spread` is the lever: offsetting the stages spreads those peaks out the
|
|
31
|
+
# way a real ladder's mismatched poles do.
|
|
30
32
|
|
|
31
33
|
import synthio
|
|
32
34
|
|
|
35
|
+
from .blocks import product
|
|
36
|
+
|
|
33
37
|
try:
|
|
34
38
|
import audiofilters
|
|
35
39
|
except ImportError: # not in every CircuitPython build
|
|
@@ -103,33 +107,66 @@ class EffectsChain:
|
|
|
103
107
|
src = stage
|
|
104
108
|
|
|
105
109
|
|
|
106
|
-
def tracking_filter(synth, stages=1, buffer_size=1024, mix=1.0):
|
|
110
|
+
def tracking_filter(synth, stages=1, buffer_size=1024, mix=1.0, hpf_f=None, spread=1.0):
|
|
107
111
|
"""Build one ``audiofilters.Filter`` holding ``stages`` extra Biquads
|
|
108
112
|
that track ``synth.filter``'s cutoff AND resonance; see the module
|
|
109
113
|
comment for why that's automatic once they share the live blocks.
|
|
110
114
|
``stages`` extra 12 dB/octave sections plus the synth's own give
|
|
111
115
|
``12*(stages+1)`` overall.
|
|
112
116
|
|
|
117
|
+
``spread`` offsets each stage's cutoff above the last by that ratio,
|
|
118
|
+
approximating a real ladder's wide pole spread instead of stacking
|
|
119
|
+
identical resonant peaks on one frequency; 1.0 shares one node and
|
|
120
|
+
allocates nothing.
|
|
121
|
+
|
|
122
|
+
``hpf_f`` adds a fixed high-pass section at that frequency in Hz, in
|
|
123
|
+
the SAME biquad tuple, so it costs no extra buffer and no extra pass.
|
|
124
|
+
It is a plain number rather than a shared block on purpose: it must
|
|
125
|
+
not follow the cutoff. A TB-303's thin bass comes from small coupling
|
|
126
|
+
capacitors between the filter and the VCA, which sit at one corner
|
|
127
|
+
whatever the filter is doing. ``stages=0`` is legal with it, for a
|
|
128
|
+
high-pass alone.
|
|
129
|
+
|
|
113
130
|
Raises ``ImportError`` if this build has no ``audiofilters``,
|
|
114
|
-
``ValueError`` if ``stages < 1``
|
|
115
|
-
|
|
116
|
-
built with
|
|
131
|
+
``ValueError`` if ``stages < 1`` without an ``hpf_f``, or if tracking
|
|
132
|
+
stages were asked for and ``synth`` has no filter to track (either a
|
|
133
|
+
poly synth, which has no ``.filter`` at all, or a mono one built with
|
|
134
|
+
``filt_type=None``).
|
|
117
135
|
"""
|
|
118
136
|
if audiofilters is None:
|
|
119
137
|
raise ImportError("audiofilters is not in this CircuitPython build")
|
|
120
|
-
if stages < 1:
|
|
138
|
+
if stages < 1 and not hpf_f:
|
|
121
139
|
raise ValueError("stages must be >= 1")
|
|
122
140
|
src = getattr(synth, "filter", None)
|
|
123
|
-
if src is None:
|
|
141
|
+
if stages >= 1 and src is None:
|
|
124
142
|
raise ValueError("synth has no filter to track (mono-only, and filt_type must be set)")
|
|
125
143
|
synthesizer = synth.synthio
|
|
126
144
|
# Copies sharing src's frequency AND Q blocks, both live, so both track
|
|
127
145
|
# the synth (sweep, accent, a filt_q turn) with nothing to keep in sync
|
|
128
146
|
# by hand. One Filter holding a tuple, not a Filter each: `filter` runs
|
|
129
147
|
# the sample through them in order, saving a buffer and a pass per stage.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
148
|
+
# `spread` offsets each stage above the last by that ratio. Identical
|
|
149
|
+
# cascaded sections pile their resonant peaks on one frequency; real
|
|
150
|
+
# ladder filters do not, and the TB-303's least of all, its bottom
|
|
151
|
+
# ladder capacitor being half the value of the other three. Mismatched
|
|
152
|
+
# stages approximate that wider pole spread and take the edge off the
|
|
153
|
+
# ringing. 1.0, the default, shares one node and allocates nothing.
|
|
154
|
+
#
|
|
155
|
+
# An offset node needs NO rooting: measured on rp2040, a Math reachable
|
|
156
|
+
# only through a Biquad inside an audiofilters.Filter tracks the sweep
|
|
157
|
+
# exactly, while an otherwise identical one reachable from nothing at
|
|
158
|
+
# all reads 0.0 forever. Nesting counts across the audiofilters
|
|
159
|
+
# boundary, so `synthesizer.blocks` stays out of this.
|
|
160
|
+
sections = []
|
|
161
|
+
ratio = 1.0
|
|
162
|
+
for _ in range(stages):
|
|
163
|
+
freq = src.frequency if ratio == 1.0 else product(src.frequency, ratio)
|
|
164
|
+
sections.append(synthio.Biquad(src.mode, frequency=freq, Q=src.Q))
|
|
165
|
+
ratio *= spread
|
|
166
|
+
biquads = tuple(sections)
|
|
167
|
+
if hpf_f:
|
|
168
|
+
# last, so it sheds low end from everything ahead of it
|
|
169
|
+
biquads += (synthio.Biquad(synthio.FilterMode.HIGH_PASS, frequency=hpf_f, Q=0.707),)
|
|
133
170
|
return audiofilters.Filter(
|
|
134
171
|
filter=biquads,
|
|
135
172
|
mix=mix,
|