circuitpython-synthtools 0.8.1__tar.gz → 0.9.0__tar.gz
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.
- {circuitpython_synthtools-0.8.1/circuitpython_synthtools.egg-info → circuitpython_synthtools-0.9.0}/PKG-INFO +4 -1
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/README.rst +2 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0/circuitpython_synthtools.egg-info}/PKG-INFO +4 -1
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/circuitpython_synthtools.egg-info/SOURCES.txt +2 -0
- circuitpython_synthtools-0.9.0/circuitpython_synthtools.egg-info/requires.txt +4 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/docs/conf.py +1 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/pyproject.toml +1 -1
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/requirements.txt +1 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/__init__.py +1 -1
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/arpeggiator.py +12 -14
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/step_sequencer.py +44 -35
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/trig_sequencer.py +12 -14
- circuitpython_synthtools-0.9.0/tests/lib/adafruit_ticks.py +141 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/run_tests.sh +2 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/test_arpeggiator.py +25 -3
- circuitpython_synthtools-0.9.0/tests/test_step_sequencer.py +154 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/test_trig_sequencer.py +36 -4
- circuitpython_synthtools-0.8.1/circuitpython_synthtools.egg-info/requires.txt +0 -3
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/.gitattributes +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/.github/workflows/build.yml +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/.github/workflows/failure-help-text.yml +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/.github/workflows/release_gh.yml +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/.github/workflows/release_pypi.yml +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/.gitignore +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/.pre-commit-config.yaml +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/.pylintrc +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/.readthedocs.yaml +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/CODE_OF_CONDUCT.md +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/LICENSE +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/LICENSES/CC-BY-4.0.txt +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/LICENSES/MIT.txt +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/LICENSES/Unlicense.txt +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/README.rst.license +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/circuitpython_synthtools.egg-info/dependency_links.txt +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/circuitpython_synthtools.egg-info/top_level.txt +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/conftest.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/docs/_static/custom.css +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/docs/_static/favicon.ico +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/docs/_static/favicon.ico.license +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/docs/api.rst +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/docs/api.rst.license +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/docs/examples.rst +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/docs/examples.rst.license +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/docs/index.rst +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/docs/index.rst.license +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/docs/requirements.txt +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/REUSE.toml +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/arpeggiator_demo.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/synth_setup.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/synthinst_code1.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/synthinst_fenv_demo.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/synthinst_pad_demo.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/synthinst_pitch_demo.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/synthinst_wave_demo.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/synthtools_ahr_envelope_bend.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/synthtools_bassline_accent_demo.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/synthtools_bassline_filter_demo.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/synthtools_bassline_fx_demo.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/synthtools_fm_demo.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/synthtools_harmony_demo.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/synthtools_midi_demo.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/synthtools_simpletest.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/synthtools_stepsequencer_demo.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/synthtools_swarm_demo.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/synthtools_wavetable_chords.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/synthtools_wavetable_simple.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/trig_sequencer_demo.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/ui/synthtools_gaugecluster_test.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/ui/synthtools_paramscaler_demo.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/wavetables/BRAIDS02.WAV +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/wavetables/PLAITS02.WAV +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/wavs/kit0_909/00_909kick4.wav +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/wavs/kit0_909/01_909snare2.wav +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/wavs/kit0_909/02_909hatclosed2a.wav +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/wavs/kit0_909/03_909hatopen5.wav +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/wavs/kit0_909/04_909clap1.wav +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/wavs/kit0_909/05_909tommed.wav +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/wavs/kit0_909/06_909blip.wav +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/wavs/kit0_909/07_909cym2.wav +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/examples/wavs/kit0_909/readme.txt +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/optional_requirements.txt +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/ruff.toml +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/setup.cfg +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/ahr_envelope.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/audio_fx.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/bassline_synth.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/blocks.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/fm_synth.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/gauge_cluster.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/harmony.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/param_scaler.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/paramset.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/patch.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/pitch_glider.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/subtractive_synth.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/swarm_synth.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/synth.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/utils.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/waves.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/wavetable.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/wavetable_synth.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/README.md +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/hw/run_on_device.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/hw/test_device.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/hw/test_display_cost.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/hw/test_trig_sequencer_timing.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/hw/test_wavetable_cost.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/plot_env.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/render/FINDINGS.md +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/render/README.md +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/render/analyze_renders.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/render/check_wavetable.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/render/check_wavetable_synth.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/render/render_chords.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/render/run.sh +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/render/wavhdr.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/stubs/synthio.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/stubs/ulab/__init__.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/stubs/ulab/numpy.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/stubs_fx/audiodelays.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/stubs_fx/audiofilters.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/test_audio_fx.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/test_env_shapes.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/test_fm_synth.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/test_harmony.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/test_mono.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/test_param_scaler.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/test_paramset.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/test_patch.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/test_pytest_smoke.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/test_swarm_synth.py +0 -0
- {circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/tests/test_wiring.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: circuitpython-synthtools
|
|
3
|
-
Version: 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
|
|
@@ -40,6 +40,8 @@ Dependencies
|
|
|
40
40
|
This driver depends on:
|
|
41
41
|
|
|
42
42
|
* `Adafruit CircuitPython <https://github.com/adafruit/circuitpython>`_
|
|
43
|
+
* `Adafruit CircuitPython Ticks <https://github.com/adafruit/Adafruit_CircuitPython_Ticks>`_
|
|
44
|
+
(``StepSequencer``)
|
|
43
45
|
|
|
44
46
|
Please ensure all dependencies are available on the CircuitPython filesystem.
|
|
45
47
|
This is easily achieved by downloading
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: circuitpython-synthtools
|
|
3
|
-
Version: 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
|
|
@@ -105,6 +105,7 @@ tests/test_param_scaler.py
|
|
|
105
105
|
tests/test_paramset.py
|
|
106
106
|
tests/test_patch.py
|
|
107
107
|
tests/test_pytest_smoke.py
|
|
108
|
+
tests/test_step_sequencer.py
|
|
108
109
|
tests/test_swarm_synth.py
|
|
109
110
|
tests/test_trig_sequencer.py
|
|
110
111
|
tests/test_wiring.py
|
|
@@ -113,6 +114,7 @@ tests/hw/test_device.py
|
|
|
113
114
|
tests/hw/test_display_cost.py
|
|
114
115
|
tests/hw/test_trig_sequencer_timing.py
|
|
115
116
|
tests/hw/test_wavetable_cost.py
|
|
117
|
+
tests/lib/adafruit_ticks.py
|
|
116
118
|
tests/render/FINDINGS.md
|
|
117
119
|
tests/render/README.md
|
|
118
120
|
tests/render/analyze_renders.py
|
|
@@ -13,7 +13,7 @@ requires = [
|
|
|
13
13
|
[project]
|
|
14
14
|
name = "circuitpython-synthtools"
|
|
15
15
|
description = "CircuitPython helper library to do help doing synthio"
|
|
16
|
-
version = "0.
|
|
16
|
+
version = "0.9.0"
|
|
17
17
|
readme = "README.rst"
|
|
18
18
|
authors = [
|
|
19
19
|
{name = "Tod Kurt", email = "tod@todbot.com"}
|
|
@@ -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.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
|
|
@@ -11,16 +11,7 @@ Part of synthtools.
|
|
|
11
11
|
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
import
|
|
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
|
|
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
|
|
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
|
-
|
|
124
|
-
|
|
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)
|
{circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/step_sequencer.py
RENAMED
|
@@ -11,15 +11,7 @@ Part of synthtools.
|
|
|
11
11
|
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
import
|
|
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.
|
|
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
|
|
75
|
-
"""
|
|
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
|
|
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
|
|
95
|
-
|
|
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)
|
{circuitpython_synthtools-0.8.1 → circuitpython_synthtools-0.9.0}/synthtools/trig_sequencer.py
RENAMED
|
@@ -11,15 +11,7 @@ Part of synthtools.
|
|
|
11
11
|
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
import
|
|
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
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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)
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
|
|
2
|
+
# SPDX-FileCopyrightText: Copyright (c) 2021 Jeff Epler for Adafruit Industries
|
|
3
|
+
#
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
"""
|
|
6
|
+
`adafruit_ticks`
|
|
7
|
+
================================================================================
|
|
8
|
+
|
|
9
|
+
Work with intervals and deadlines in milliseconds
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
* Author(s): Jeff Epler
|
|
13
|
+
|
|
14
|
+
Implementation Notes
|
|
15
|
+
--------------------
|
|
16
|
+
|
|
17
|
+
**Software and Dependencies:**
|
|
18
|
+
|
|
19
|
+
* Adafruit CircuitPython firmware for the supported boards:
|
|
20
|
+
https://github.com/adafruit/circuitpython/releases
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
# imports
|
|
25
|
+
from micropython import const
|
|
26
|
+
|
|
27
|
+
__version__ = "1.1.7"
|
|
28
|
+
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ticks.git"
|
|
29
|
+
|
|
30
|
+
_TICKS_PERIOD = const(1 << 29)
|
|
31
|
+
_TICKS_MAX = const(_TICKS_PERIOD - 1)
|
|
32
|
+
_TICKS_HALFPERIOD = const(_TICKS_PERIOD // 2)
|
|
33
|
+
|
|
34
|
+
# Get the correct implementation of ticks_ms. There are three possibilities:
|
|
35
|
+
#
|
|
36
|
+
# - supervisor.ticks_ms is present. This will be the case starting in CP7.0
|
|
37
|
+
#
|
|
38
|
+
# - time.ticks_ms is present. This is the case for MicroPython & for the "unix
|
|
39
|
+
# port" of CircuitPython, used for some automated testing.
|
|
40
|
+
#
|
|
41
|
+
# - time.monotonic_ns is present, and works. This is the case on most
|
|
42
|
+
# Express boards in CP6.x, and most host computer versions of Python.
|
|
43
|
+
#
|
|
44
|
+
# - Otherwise, time.monotonic is assumed to be present. This is the case
|
|
45
|
+
# on most non-express boards in CP6.x, and some old host computer versions
|
|
46
|
+
# of Python.
|
|
47
|
+
#
|
|
48
|
+
# Note that on microcontrollers, this time source becomes increasingly
|
|
49
|
+
# inaccurate when the board has not been reset in a long time, losing the
|
|
50
|
+
# ability to measure 1ms intervals after about 1 hour, and losing the
|
|
51
|
+
# ability to meausre 128ms intervals after 6 days. The only solution is to
|
|
52
|
+
# either upgrade to a version with supervisor.ticks_ms, or to switch to a
|
|
53
|
+
# board with time.monotonic_ns.
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
from supervisor import ticks_ms
|
|
57
|
+
except (ImportError, NameError):
|
|
58
|
+
import time
|
|
59
|
+
|
|
60
|
+
if _ticks_ms := getattr(time, "ticks_ms", None):
|
|
61
|
+
|
|
62
|
+
def ticks_ms() -> int:
|
|
63
|
+
"""Return the time in milliseconds since an unspecified moment,
|
|
64
|
+
wrapping after 2**29ms.
|
|
65
|
+
|
|
66
|
+
The wrap value was chosen so that it is always possible to add or
|
|
67
|
+
subtract two `ticks_ms` values without overflow on a board without
|
|
68
|
+
long ints (or without allocating any long integer objects, on
|
|
69
|
+
boards with long ints).
|
|
70
|
+
|
|
71
|
+
This ticks value comes from a low-accuracy clock internal to the
|
|
72
|
+
microcontroller, just like `time.monotonic`. Due to its low
|
|
73
|
+
accuracy and the fact that it "wraps around" every few days, it is
|
|
74
|
+
intended for working with short term events like advancing an LED
|
|
75
|
+
animation, not for long term events like counting down the time
|
|
76
|
+
until a holiday."""
|
|
77
|
+
return _ticks_ms() & _TICKS_MAX
|
|
78
|
+
|
|
79
|
+
else:
|
|
80
|
+
try:
|
|
81
|
+
from time import monotonic_ns as _monotonic_ns
|
|
82
|
+
|
|
83
|
+
_monotonic_ns() # Check that monotonic_ns is usable
|
|
84
|
+
|
|
85
|
+
def ticks_ms() -> int:
|
|
86
|
+
"""Return the time in milliseconds since an unspecified moment,
|
|
87
|
+
wrapping after 2**29ms.
|
|
88
|
+
|
|
89
|
+
The wrap value was chosen so that it is always possible to add or
|
|
90
|
+
subtract two `ticks_ms` values without overflow on a board without
|
|
91
|
+
long ints (or without allocating any long integer objects, on
|
|
92
|
+
boards with long ints).
|
|
93
|
+
|
|
94
|
+
This ticks value comes from a low-accuracy clock internal to the
|
|
95
|
+
microcontroller, just like `time.monotonic`. Due to its low
|
|
96
|
+
accuracy and the fact that it "wraps around" every few days, it is
|
|
97
|
+
intended for working with short term events like advancing an LED
|
|
98
|
+
animation, not for long term events like counting down the time
|
|
99
|
+
until a holiday."""
|
|
100
|
+
return (_monotonic_ns() // 1_000_000) & _TICKS_MAX
|
|
101
|
+
|
|
102
|
+
except (ImportError, NameError, NotImplementedError):
|
|
103
|
+
from time import monotonic as _monotonic
|
|
104
|
+
|
|
105
|
+
def ticks_ms() -> int:
|
|
106
|
+
"""Return the time in milliseconds since an unspecified moment,
|
|
107
|
+
wrapping after 2**29ms.
|
|
108
|
+
|
|
109
|
+
The wrap value was chosen so that it is always possible to add or
|
|
110
|
+
subtract two `ticks_ms` values without overflow on a board without
|
|
111
|
+
long ints (or without allocating any long integer objects, on
|
|
112
|
+
boards with long ints).
|
|
113
|
+
|
|
114
|
+
This ticks value comes from a low-accuracy clock internal to the
|
|
115
|
+
microcontroller, just like `time.monotonic`. Due to its low
|
|
116
|
+
accuracy and the fact that it "wraps around" every few days, it is
|
|
117
|
+
intended for working with short term events like advancing an LED
|
|
118
|
+
animation, not for long term events like counting down the time
|
|
119
|
+
until a holiday."""
|
|
120
|
+
return int(_monotonic() * 1000) & _TICKS_MAX
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def ticks_add(ticks: int, delta: int) -> int:
|
|
124
|
+
"Add a delta to a base number of ticks, performing wraparound at 2**29ms."
|
|
125
|
+
if -_TICKS_HALFPERIOD < delta < _TICKS_HALFPERIOD:
|
|
126
|
+
return (ticks + delta) % _TICKS_PERIOD
|
|
127
|
+
raise OverflowError("ticks interval overflow")
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def ticks_diff(ticks1: int, ticks2: int) -> int:
|
|
131
|
+
"""Compute the signed difference between two ticks values,
|
|
132
|
+
assuming that they are within 2**28 ticks"""
|
|
133
|
+
diff = (ticks1 - ticks2) & _TICKS_MAX
|
|
134
|
+
diff = ((diff + _TICKS_HALFPERIOD) & _TICKS_MAX) - _TICKS_HALFPERIOD
|
|
135
|
+
return diff
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def ticks_less(ticks1: int, ticks2: int) -> bool:
|
|
139
|
+
"""Return true if ticks1 is before ticks2 and false otherwise,
|
|
140
|
+
assuming that they are within 2**28 ticks"""
|
|
141
|
+
return ticks_diff(ticks1, ticks2) < 0
|
|
@@ -40,6 +40,7 @@ if command -v python3 >/dev/null 2>&1; then
|
|
|
40
40
|
run python3 tests/test_audio_fx.py
|
|
41
41
|
run python3 tests/test_arpeggiator.py
|
|
42
42
|
run python3 tests/test_trig_sequencer.py
|
|
43
|
+
run python3 tests/test_step_sequencer.py
|
|
43
44
|
run python3 tests/test_paramset.py
|
|
44
45
|
run python3 tests/test_param_scaler.py
|
|
45
46
|
run python3 tests/test_harmony.py
|
|
@@ -58,6 +59,7 @@ if command -v micropython >/dev/null 2>&1; then
|
|
|
58
59
|
run micropython tests/test_audio_fx.py
|
|
59
60
|
run micropython tests/test_arpeggiator.py
|
|
60
61
|
run micropython tests/test_trig_sequencer.py
|
|
62
|
+
run micropython tests/test_step_sequencer.py
|
|
61
63
|
run micropython tests/test_paramset.py
|
|
62
64
|
run micropython tests/test_param_scaler.py
|
|
63
65
|
run micropython tests/test_harmony.py
|
|
@@ -5,8 +5,8 @@ for the constructor bug fixed alongside this file: Arpeggiator.__init__
|
|
|
5
5
|
called `self.set_bpm(120)` with no rate, which overwrote self.rate with
|
|
6
6
|
set_bpm's own default (None) and raised TypeError on every construction.
|
|
7
7
|
|
|
8
|
-
arpeggiator.py
|
|
9
|
-
like test_patch.py this runs on a bare interpreter: no stubs needed.
|
|
8
|
+
arpeggiator.py needs only adafruit_ticks, vendored unchanged in tests/lib/,
|
|
9
|
+
so like test_patch.py this runs on a bare interpreter: no stubs needed.
|
|
10
10
|
ticks_ms is monkeypatched to a fake, settable clock so the timing tests are
|
|
11
11
|
exact and instant instead of racing real wall-clock sleeps.
|
|
12
12
|
|
|
@@ -18,6 +18,7 @@ import sys
|
|
|
18
18
|
|
|
19
19
|
_D = __file__.rsplit("/", 1)[0] if "/" in __file__ else "."
|
|
20
20
|
sys.path.insert(0, _D + "/../synthtools") # direct: skips synthtools/__init__
|
|
21
|
+
sys.path.insert(0, _D + "/lib") # the real adafruit_ticks, vendored
|
|
21
22
|
|
|
22
23
|
import arpeggiator # noqa: E402
|
|
23
24
|
from arpeggiator import Arpeggiator # noqa: E402
|
|
@@ -33,8 +34,11 @@ def ck(cond, msg):
|
|
|
33
34
|
_now = [0]
|
|
34
35
|
|
|
35
36
|
|
|
37
|
+
WRAP = 1 << 29
|
|
38
|
+
|
|
39
|
+
|
|
36
40
|
def fake_ticks_ms():
|
|
37
|
-
return _now[0]
|
|
41
|
+
return _now[0] % WRAP # wraps like supervisor.ticks_ms()
|
|
38
42
|
|
|
39
43
|
|
|
40
44
|
def set_time(ms):
|
|
@@ -162,6 +166,24 @@ ck(advance(375) == [("on", 60 + 12)],
|
|
|
162
166
|
"next pass must transpose by oct_distance * octave")
|
|
163
167
|
ck(arp4.i == 1, "i must advance normally within the transposed pass")
|
|
164
168
|
|
|
169
|
+
# --- across the ticks_ms() wrap: no stop, no hiccup -----------------------
|
|
170
|
+
# supervisor.ticks_ms() first wraps ~65 s after power-on
|
|
171
|
+
ons, offs = [], []
|
|
172
|
+
arp5 = Arpeggiator(4, lambda n: ons.append(_now[0]), lambda n: offs.append(_now[0]))
|
|
173
|
+
arp5.set_bpm(120, 4)
|
|
174
|
+
arp5.notes = [60]
|
|
175
|
+
set_time(WRAP - 600)
|
|
176
|
+
arp5.start()
|
|
177
|
+
while _now[0] < WRAP + 1000:
|
|
178
|
+
arp5.update()
|
|
179
|
+
_now[0] += 1
|
|
180
|
+
gaps = [b - a for a, b in zip(ons, ons[1:])]
|
|
181
|
+
ck(len(ons) == 13 and min(gaps) == max(gaps) == 125,
|
|
182
|
+
"notes must keep coming every 125 ms across the wrap, got gaps %r" % gaps)
|
|
183
|
+
ck(len(offs) == len(ons), "gates must keep releasing across the wrap, got %d" % len(offs))
|
|
184
|
+
ck(isinstance(arp5.next_millis, int) and isinstance(arp5.held_millis, int),
|
|
185
|
+
"tick math must stay int; a 30-bit float can't hold a tick near 2**29")
|
|
186
|
+
|
|
165
187
|
if fails:
|
|
166
188
|
print("FAILURES (%d):" % len(fails))
|
|
167
189
|
for f in fails:
|