circuitpython-synthtools 0.8.0__py3-none-any.whl → 0.9.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.

Potentially problematic release.


This version of circuitpython-synthtools might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: circuitpython-synthtools
3
- Version: 0.8.0
3
+ Version: 0.9.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
@@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3
15
15
  Description-Content-Type: text/x-rst
16
16
  License-File: LICENSE
17
17
  Requires-Dist: Adafruit-Blinka
18
+ Requires-Dist: adafruit-circuitpython-ticks
18
19
  Provides-Extra: optional
19
20
  Dynamic: license-file
20
21
 
@@ -60,6 +61,8 @@ Dependencies
60
61
  This driver depends on:
61
62
 
62
63
  * `Adafruit CircuitPython <https://github.com/adafruit/circuitpython>`_
64
+ * `Adafruit CircuitPython Ticks <https://github.com/adafruit/Adafruit_CircuitPython_Ticks>`_
65
+ (``StepSequencer``)
63
66
 
64
67
  Please ensure all dependencies are available on the CircuitPython filesystem.
65
68
  This is easily achieved by downloading
@@ -1,9 +1,9 @@
1
- circuitpython_synthtools-0.8.0.dist-info/licenses/LICENSE,sha256=jhhRyxpqyQxyF_D6SzY2-jCA5v8Z8JXfezCtxBS85CM,1075
2
- synthtools/__init__.py,sha256=dB2eJTgbLs_sfEMCFcm8F_FNoblgNcZd6toMpHFQThw,2752
1
+ circuitpython_synthtools-0.9.0.dist-info/licenses/LICENSE,sha256=jhhRyxpqyQxyF_D6SzY2-jCA5v8Z8JXfezCtxBS85CM,1075
2
+ synthtools/__init__.py,sha256=gJ90pjs_sYcEZsznlkNIZkOc8CBf983wybRAl7iL-R0,2752
3
3
  synthtools/ahr_envelope.py,sha256=fWhjp7LahNGjL8qtTgyiAnX5hnviAULuyc9dEJ6t74I,9233
4
- synthtools/arpeggiator.py,sha256=n2sPnpZcu9soXsFJCh6OP65_0DuPnvQGUP0ao7V8SXM,3901
4
+ synthtools/arpeggiator.py,sha256=s9tV68pw_GhKlxuNKrN0f8l7V2azhp5SyO-4lOPijQA,4185
5
5
  synthtools/audio_fx.py,sha256=PjnETf5kKIlG_tLKwM49PCvSkM2txfZf2o7MuEnU2W4,8778
6
- synthtools/bassline_synth.py,sha256=X8efKBgemBP2qAX0hL3PgWk3c_jkBWo0aX4KrgccHKE,51547
6
+ synthtools/bassline_synth.py,sha256=1QM7r2l29Nscbgn_sxmD_f-XhZt80xaOFsevOM0dpbQ,51553
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
@@ -12,16 +12,16 @@ synthtools/param_scaler.py,sha256=dmaFkzRhrfZn54DAgPwZs7P4B4BFd90ZUvMHpWIf2i4,62
12
12
  synthtools/paramset.py,sha256=--V89zHR4pUhkSey1CX7iMLNsF5l1hKmKWrAr3j_cRA,9938
13
13
  synthtools/patch.py,sha256=vMr-kdVD66uxY8BrrnB0J6uDLREjuu16I8HzfhAkqHs,6033
14
14
  synthtools/pitch_glider.py,sha256=R5h0Lbyq_slo7SKORRM7a0oumVugE15L8hOZJCov0AY,1927
15
- synthtools/step_sequencer.py,sha256=SkQuDsRtpAdKOXU3OsFmngeDPVQ0OXt3Gj5wqFCkEPM,4202
15
+ synthtools/step_sequencer.py,sha256=lCFsFmysXOAo_aXFLiBHqK4pStW2Fr5QmnxQ-uA_Kzw,4831
16
16
  synthtools/subtractive_synth.py,sha256=WIpmhbiiLtyQRFV9mHWWM-duST5RfY9HUN4SRf5wTOw,3382
17
17
  synthtools/swarm_synth.py,sha256=kCYsdVuaafTKkD06_m22iFaPUMCzeBKX67mkrsEq3pk,10945
18
18
  synthtools/synth.py,sha256=ZdmAKWBVzJqPyzU5W1hxnJ6j09D4rnCWm77s5jRW_Ww,36528
19
- synthtools/trig_sequencer.py,sha256=J3_PdbNmYEoFkz7Hj58qNzMRZQuUm-sf2dKZIrBTV8U,3052
19
+ synthtools/trig_sequencer.py,sha256=kqMFgr7q_lNn0KV-tt-52SEP4oz2PpS7kSq18svvFlM,3300
20
20
  synthtools/utils.py,sha256=deKQbkGXIBdGUOXBOpRCc-UHdPiL8YmybIJ3BBo9aA4,648
21
21
  synthtools/waves.py,sha256=lwDka71TsoHGJcyVfmG4t-TEf6TjBwftJEXSS4Qxyx0,22407
22
22
  synthtools/wavetable.py,sha256=7zQdAvHyiSp7wiVjl-lIfeifpbdsp7M5cCVbMT7eku0,3725
23
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,,
24
+ circuitpython_synthtools-0.9.0.dist-info/METADATA,sha256=mhnlzRg78n-2sNVNECEcjAdwgrIKAlMco4fNI-PnNcM,7298
25
+ circuitpython_synthtools-0.9.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
26
+ circuitpython_synthtools-0.9.0.dist-info/top_level.txt,sha256=1xt53cH064i7vvFVDFyuJugc1NogReXvzsXbYGlgnKE,11
27
+ circuitpython_synthtools-0.9.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (84.0.0)
2
+ Generator: setuptools (84.2.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
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.8.0"
39
+ __version__ = "0.9.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
synthtools/arpeggiator.py CHANGED
@@ -11,16 +11,7 @@ Part of synthtools.
11
11
 
12
12
  """
13
13
 
14
- import time
15
-
16
- try:
17
- from supervisor import ticks_ms
18
- except ImportError:
19
-
20
- def ticks_ms():
21
- """stand-in for supervisor.ticks_ms"""
22
- return time.monotonic_ns() // 1_000_000
23
-
14
+ from adafruit_ticks import ticks_add, ticks_diff, ticks_ms
24
15
 
25
16
  patterns = (
26
17
  (0, 4, 7, 12),
@@ -68,6 +59,7 @@ class Arpeggiator:
68
59
  self.held_note = None
69
60
  self.next_millis = 0
70
61
  self.held_millis = 0
62
+ self._frac_millis = 0.0 # step_millis's fraction, carried between steps
71
63
 
72
64
  def set_bpm(self, bpm, rate=None):
73
65
  """Set BPM and optionally rate"""
@@ -90,6 +82,7 @@ class Arpeggiator:
90
82
  def start(self):
91
83
  """Start the arpeggiator running"""
92
84
  self.next_millis = ticks_ms()
85
+ self._frac_millis = 0.0
93
86
  self.on = True
94
87
 
95
88
  def stop(self):
@@ -105,12 +98,12 @@ class Arpeggiator:
105
98
  now = ticks_ms()
106
99
 
107
100
  # trigger note-off after gate time
108
- if self.held_note and now - self.held_millis > 0:
101
+ if self.held_note and ticks_diff(now, self.held_millis) > 0:
109
102
  self.off_func(self.held_note)
110
103
  self.held_note = None
111
104
 
112
105
  # trigger note-on on if time to do so
113
- delta_millis = now - self.next_millis
106
+ delta_millis = ticks_diff(now, self.next_millis)
114
107
  if delta_millis >= 0 and len(self.notes) > 0: # time for new note
115
108
  note = self.notes[self.i] + self.oct_distance * self.octave
116
109
  # print("\t\t\t\t\t", "delta:",delta_millis)
@@ -120,8 +113,13 @@ class Arpeggiator:
120
113
  self.held_note = note # save for note-off
121
114
 
122
115
  # set up when note off and next note happens
123
- self.held_millis = now + (self.step_millis * self.gate)
124
- self.next_millis = now + self.step_millis - delta_millis // 2
116
+ # int ticks: they wrap at 2**29, first ~65 s after power-on, and a
117
+ # 30-bit float can't hold one that big to the ms
118
+ self._frac_millis += self.step_millis
119
+ step = int(self._frac_millis)
120
+ self._frac_millis -= step
121
+ self.held_millis = ticks_add(now, int(self.step_millis * self.gate))
122
+ self.next_millis = ticks_add(now, step - delta_millis // 2)
125
123
 
126
124
  # go to next note
127
125
  self.i = (self.i + 1) % len(self.notes)
@@ -306,8 +306,8 @@ class BasslineSynth(Synth):
306
306
  self._accent_sweep_decay = getattr(p, "accent_sweep_decay", 0.55)
307
307
  self._accent_sweep_max = getattr(p, "accent_sweep_max", 1.6)
308
308
  self._slide_time = getattr(p, "slide_time", 0.06)
309
- self._decay = p.fenv_attack # super() already wrote it into the block
310
- self._fall = p.fenv_attack # ...and that is what the block holds
309
+ self._decay = getattr(p, "decay", p.fenv_attack)
310
+ self._fall = p.fenv_attack # what super() wrote into the block
311
311
  get_wave(self._wave_name) # warm the cache; note-on only reads it
312
312
  self._accent_on = False
313
313
  self._accent_sweep = 0.0
@@ -368,6 +368,7 @@ class BasslineSynth(Synth):
368
368
  peak, rest = self._env_endpoints() # un-accented: no sweep widening
369
369
  p.fenv_amount = rest - peak
370
370
  p.fenv_attack = self._decay
371
+ p.decay = self._decay
371
372
  p.fx_filter_stages = self._fx_filter_stages
372
373
  p.fx_filter_mix = self._fx_filter_mix
373
374
  p.fx_hpf_f = self._fx_hpf_f
@@ -11,15 +11,7 @@ Part of synthtools.
11
11
 
12
12
  """
13
13
 
14
- import time
15
-
16
- try:
17
- from supervisor import ticks_ms
18
- except ImportError:
19
-
20
- def ticks_ms():
21
- """stand-in for supervisor.ticks_ms"""
22
- return time.monotonic_ns() // 1_000_000
14
+ from adafruit_ticks import ticks_add, ticks_diff, ticks_ms
23
15
 
24
16
 
25
17
  class StepSequencer:
@@ -46,8 +38,10 @@ class StepSequencer:
46
38
  self.held_note = None # the current note playing
47
39
  self.transpose = 0
48
40
  self.playing = False # is sequence running or not (but use .start()/.stop())
41
+ self.external_clock = False # owner calls trigger(), update() only releases gates
42
+ self.catch_up_steps = 0 # how far behind to still play every step, late, not resync
49
43
  self.next_millis = 0
50
- self.error_millis = 0
44
+ self._frac_millis = 0.0 # step_millis's fraction, carried between steps
51
45
 
52
46
  @property
53
47
  def bpm(self):
@@ -68,46 +62,61 @@ class StepSequencer:
68
62
  def start(self):
69
63
  """Start sequencer going"""
70
64
  self.next_millis = ticks_ms()
65
+ self._frac_millis = 0.0
71
66
  self.playing = True
72
- self.error_millis = 0
73
67
 
74
- def stop(self):
75
- """Stop sequencer, turning off any currently-sounding note"""
68
+ def pause(self):
69
+ """Pause sequencer where it is, turning off any currently-sounding note"""
76
70
  if self.held_note:
77
71
  self.off_func(*self.held_note)
72
+ self.held_note = None
78
73
  self.playing = False
74
+
75
+ def stop(self):
76
+ """Stop sequencer and rewind, turning off any currently-sounding note"""
77
+ self.pause()
79
78
  self.i = 0
80
79
 
80
+ def trigger(self, now=None):
81
+ """Play the current step and advance, for an owner keeping its own clock"""
82
+ if now is None:
83
+ now = ticks_ms()
84
+ if self.i >= self.step_count: # step_count shrank under us
85
+ self.i = 0
86
+ (note, vel, gate, on) = self.steps[self.i] # get new note to play
87
+ note += self.transpose # adjust for transpose
88
+ self.held_note = (note, vel, gate, on) # save it for when we note_off it
89
+
90
+ # trigger new note
91
+ self.on_func(*self.held_note) # held_note = (note,vel,gate,on)
92
+
93
+ # prep for next step in sequence
94
+ self.i = (self.i + 1) % self.step_count
95
+
96
+ # int ticks: they wrap at 2**29, first ~65 s after power-on, and a
97
+ # 30-bit float can't hold one that big to the ms
98
+ self._frac_millis += self.step_millis
99
+ step = int(self._frac_millis)
100
+ self._frac_millis -= step
101
+ self.next_millis = ticks_add(self.next_millis, step)
102
+ # a stall put us too far behind
103
+ if ticks_diff(self.next_millis, now) < -self.catch_up_steps * step:
104
+ self.next_millis = ticks_add(now, step)
105
+ # next note off is some percentage smaller
106
+ gate = int(self.step_millis * self.held_note[2])
107
+ self.gate_off_millis = ticks_add(now, gate)
108
+
81
109
  def update(self):
82
110
  """Update the sequencer. Call as frequently as possible"""
83
111
  if not self.playing:
84
112
  return
85
113
 
86
114
  now = ticks_ms()
87
- delta_millis = now - self.next_millis
88
115
 
89
116
  # trigger note-off after gate time
90
- if now - self.gate_off_millis >= 0 and self.held_note:
117
+ if self.held_note and ticks_diff(now, self.gate_off_millis) >= 0:
91
118
  self.off_func(*self.held_note)
92
119
  self.held_note = None
93
120
 
94
- if delta_millis >= 0: # if zero or great, time for next step
95
- # print(" delta_millis:", delta_millis, self.error_millis)
96
- self.error_millis += delta_millis
97
- (note, vel, gate, on) = self.steps[self.i] # get new note to play
98
- note += self.transpose # adjust for transpose
99
- self.held_note = (note, vel, gate, on) # save it for when we note_off it
100
-
101
- # trigger new note
102
- self.on_func(*self.held_note) # held_note = (note,vel,gate,on)
103
-
104
- # prep for next step in sequence
105
- self.i = (self.i + 1) % self.step_count
106
-
107
- # set up when next note on is, with some error correction
108
- self.next_millis = now + self.step_millis
109
- if self.error_millis > 1:
110
- self.next_millis -= self.error_millis
111
- self.error_millis = 0
112
- # next note off is some percentage smaller
113
- self.gate_off_millis = now + self.step_millis * self.held_note[2]
121
+ if not self.external_clock and ticks_diff(now, self.next_millis) >= 0:
122
+ self.trigger(now)
@@ -11,15 +11,7 @@ Part of synthtools.
11
11
 
12
12
  """
13
13
 
14
- import time
15
-
16
- try:
17
- from supervisor import ticks_ms
18
- except ImportError:
19
-
20
- def ticks_ms():
21
- """stand-in for supervisor.ticks_ms"""
22
- return time.monotonic_ns() // 1_000_000
14
+ from adafruit_ticks import ticks_add, ticks_diff, ticks_ms
23
15
 
24
16
 
25
17
  class TrigSequencer:
@@ -45,6 +37,7 @@ class TrigSequencer:
45
37
  self.drum_map = [0] * trig_count
46
38
  self.step_millis = 0 # real value once bpm is set; update()/start() need it to exist
47
39
  self.next_millis = 0
40
+ self._frac_millis = 0.0 # step_millis's fraction, carried between steps
48
41
 
49
42
  @property
50
43
  def bpm(self):
@@ -58,6 +51,7 @@ class TrigSequencer:
58
51
  def start(self):
59
52
  """Start sequencer going"""
60
53
  self.next_millis = ticks_ms()
54
+ self._frac_millis = 0.0
61
55
  self.playing = True
62
56
 
63
57
  def stop(self):
@@ -78,15 +72,19 @@ class TrigSequencer:
78
72
  return
79
73
 
80
74
  now = ticks_ms()
81
- delta_millis = now - self.next_millis
82
75
 
83
- if delta_millis >= 0: # time to play
76
+ if ticks_diff(now, self.next_millis) >= 0: # time to play
84
77
  for t in range(self.trig_count):
85
78
  if self.trigs[t][self.i] == 1:
86
79
  self.on_func(t, self.drum_map[t])
87
80
 
88
81
  # prep for next step in sequence
89
82
  self.i = (self.i + 1) % self.step_count
90
- self.next_millis += self.step_millis
91
- if self.next_millis < now: # a stall put us a whole step behind: resync
92
- self.next_millis = now + self.step_millis
83
+ # int ticks: they wrap at 2**29, first ~65 s after power-on, and a
84
+ # 30-bit float can't hold one that big to the ms
85
+ self._frac_millis += self.step_millis
86
+ step = int(self._frac_millis)
87
+ self._frac_millis -= step
88
+ self.next_millis = ticks_add(self.next_millis, step)
89
+ if ticks_diff(self.next_millis, now) < 0: # a stall put us a whole step behind
90
+ self.next_millis = ticks_add(now, step)