padbound 0.1.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.
Files changed (39) hide show
  1. padbound-0.1.0/.claude/settings.local.json +13 -0
  2. padbound-0.1.0/.github/dependabot.yml +16 -0
  3. padbound-0.1.0/.github/workflows/conventional-commits.yml +19 -0
  4. padbound-0.1.0/.github/workflows/py-formatting.yml +23 -0
  5. padbound-0.1.0/.github/workflows/release-please.yml +64 -0
  6. padbound-0.1.0/.gitignore +167 -0
  7. padbound-0.1.0/.pre-commit-config.yaml +21 -0
  8. padbound-0.1.0/.release-please.manifest.json +3 -0
  9. padbound-0.1.0/LICENSE +21 -0
  10. padbound-0.1.0/PKG-INFO +264 -0
  11. padbound-0.1.0/README.md +211 -0
  12. padbound-0.1.0/assets/logo_dark.svg +173 -0
  13. padbound-0.1.0/assets/logo_light.svg +206 -0
  14. padbound-0.1.0/examples/demo_akai_apc_mini_mk2.py +412 -0
  15. padbound-0.1.0/examples/demo_akai_lpd8.py +249 -0
  16. padbound-0.1.0/examples/demo_presonus_atom.py +300 -0
  17. padbound-0.1.0/examples/demo_x_touch_mini.py +286 -0
  18. padbound-0.1.0/examples/demo_xjam.py +238 -0
  19. padbound-0.1.0/pyproject.toml +117 -0
  20. padbound-0.1.0/release-please.config.json +29 -0
  21. padbound-0.1.0/src/padbound/__init__.py +76 -0
  22. padbound-0.1.0/src/padbound/callbacks.py +357 -0
  23. padbound-0.1.0/src/padbound/config.py +281 -0
  24. padbound-0.1.0/src/padbound/controller.py +817 -0
  25. padbound-0.1.0/src/padbound/controls.py +408 -0
  26. padbound-0.1.0/src/padbound/logging_config.py +110 -0
  27. padbound-0.1.0/src/padbound/midi_io.py +329 -0
  28. padbound-0.1.0/src/padbound/plugin.py +657 -0
  29. padbound-0.1.0/src/padbound/plugins/__init__.py +29 -0
  30. padbound-0.1.0/src/padbound/plugins/akai_apc_mini_mk2.py +1123 -0
  31. padbound-0.1.0/src/padbound/plugins/akai_lpd8_mk2.py +1243 -0
  32. padbound-0.1.0/src/padbound/plugins/behringer_x_touch_mini.py +819 -0
  33. padbound-0.1.0/src/padbound/plugins/example_midi_controller.py +626 -0
  34. padbound-0.1.0/src/padbound/plugins/presonus_atom.py +636 -0
  35. padbound-0.1.0/src/padbound/plugins/xjam.py +1121 -0
  36. padbound-0.1.0/src/padbound/registry.py +226 -0
  37. padbound-0.1.0/src/padbound/state.py +389 -0
  38. padbound-0.1.0/src/padbound/utils.py +108 -0
  39. padbound-0.1.0/uv.lock +2813 -0
@@ -0,0 +1,13 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(python3:*)",
5
+ "Bash(find:*)",
6
+ "Bash(grep:*)",
7
+ "Bash(python -m py_compile:*)",
8
+ "Bash(cat:*)",
9
+ "Bash(wc:*)",
10
+ "Bash(ruff check:*)"
11
+ ]
12
+ }
13
+ }
@@ -0,0 +1,16 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "uv"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ commit-message:
8
+ prefix: fix
9
+ prefix-development: chore
10
+ - package-ecosystem: "github-actions"
11
+ directory: "/"
12
+ schedule:
13
+ interval: "weekly"
14
+ commit-message:
15
+ prefix: fix
16
+ prefix-development: chore
@@ -0,0 +1,19 @@
1
+ # Validates PR title follows conventional commits
2
+ name: conventional-commits
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+ types:
7
+ - edited
8
+ - opened
9
+ - synchronize
10
+ - reopened
11
+
12
+ concurrency:
13
+ group: ${{ github.workflow }}-${{ github.ref }}
14
+ cancel-in-progress: true
15
+ jobs:
16
+ conventional_commit_title:
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: chanzuckerberg/github-actions/.github/actions/conventional-commits@v6.10.0
@@ -0,0 +1,23 @@
1
+ name: Python Linting
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches: [main]
7
+
8
+ jobs:
9
+ pre_commit_checks:
10
+ name: pre-commit checks
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v5
14
+ - uses: actions/setup-python@v6
15
+ with:
16
+ python-version: "3.12.5"
17
+ - name: Install action-validator with asdf
18
+ uses: asdf-vm/actions/install@v4
19
+ with:
20
+ tool_versions: |
21
+ action-validator 0.6.0
22
+ - name: check linting
23
+ uses: pre-commit/action@v3.0.1
@@ -0,0 +1,64 @@
1
+ name: Create Release PRs
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ push:
6
+ branches:
7
+ - main
8
+
9
+ permissions:
10
+ contents: write
11
+ issues: write
12
+ pull-requests: write
13
+
14
+ jobs:
15
+ release-please:
16
+ concurrency:
17
+ group: ${{ github.workflow }}-${{ github.ref }}
18
+ cancel-in-progress: true
19
+
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - name: release please
23
+ uses: googleapis/release-please-action@v4
24
+ id: release
25
+ with:
26
+ manifest-file: ".release-please.manifest.json"
27
+ config-file: "release-please.config.json"
28
+ target-branch: "main"
29
+ token: ${{ secrets.GITHUB_TOKEN }}
30
+
31
+ outputs:
32
+ paths_released: ${{ steps.release.outputs.paths_released }}
33
+
34
+ publish-pypi-package:
35
+ name: Build and publish Python package to PyPI
36
+ runs-on: ubuntu-latest
37
+ needs: release-please
38
+ if: contains(needs.release-please.outputs.paths_released, '.')
39
+ environment:
40
+ name: pypi
41
+ url: https://pypi.org/p/padbound
42
+ permissions:
43
+ id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
44
+ steps:
45
+ - name: Checkout ref branch
46
+ uses: actions/checkout@v5
47
+ with:
48
+ ref: ${{ github.ref }}
49
+ fetch-depth: 0
50
+
51
+ - name: Install uv
52
+ uses: astral-sh/setup-uv@v7
53
+ with:
54
+ version: "0.7.13"
55
+ python-version: "3.12"
56
+
57
+ - name: build
58
+ run: |
59
+ uv build
60
+
61
+ - name: Publish distribution 📦 to PyPI
62
+ uses: pypa/gh-action-pypi-publish@release/v1
63
+ with:
64
+ packages-dir: dist
@@ -0,0 +1,167 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # poetry
98
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102
+ #poetry.lock
103
+
104
+ # pdm
105
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106
+ #pdm.lock
107
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108
+ # in version control.
109
+ # https://pdm.fming.dev/#use-with-ide
110
+ .pdm.toml
111
+
112
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113
+ __pypackages__/
114
+
115
+ # Celery stuff
116
+ celerybeat-schedule
117
+ celerybeat.pid
118
+
119
+ # SageMath parsed files
120
+ *.sage.py
121
+
122
+ # Environments
123
+ .env
124
+ .venv
125
+ env/
126
+ venv/
127
+ ENV/
128
+ env.bak/
129
+ venv.bak/
130
+
131
+ # Spyder project settings
132
+ .spyderproject
133
+ .spyproject
134
+
135
+ # Rope project settings
136
+ .ropeproject
137
+
138
+ # mkdocs documentation
139
+ /site
140
+
141
+ # mypy
142
+ .mypy_cache/
143
+ .dmypy.json
144
+ dmypy.json
145
+
146
+ # Pyre type checker
147
+ .pyre/
148
+
149
+ # pytype static type analyzer
150
+ .pytype/
151
+
152
+ # Cython debug symbols
153
+ cython_debug/
154
+
155
+ # PyCharm
156
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
159
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160
+ .idea/
161
+ .DS_Store/
162
+ .test/
163
+ .notebook/
164
+ .workspace/
165
+ .CLAUDE.md
166
+ CLAUDE.md
167
+ docs/
@@ -0,0 +1,21 @@
1
+ repos:
2
+ - repo: https://github.com/psf/black-pre-commit-mirror
3
+ rev: 23.9.1
4
+ hooks:
5
+ - id: black
6
+ - repo: https://github.com/astral-sh/ruff-pre-commit
7
+ rev: v0.0.292
8
+ hooks:
9
+ - id: ruff
10
+ args:
11
+ - --fix
12
+ - repo: https://github.com/pre-commit/pre-commit-hooks
13
+ rev: v4.5.0
14
+ hooks:
15
+ - id: check-toml
16
+ - id: check-yaml
17
+ exclude: 'mkdocs.yml'
18
+ - id: check-json
19
+ - id: check-merge-conflict
20
+ - id: end-of-file-fixer
21
+ - id: trailing-whitespace
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.1.0"
3
+ }
padbound-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Utz Ermel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,264 @@
1
+ Metadata-Version: 2.4
2
+ Name: padbound
3
+ Version: 0.1.0
4
+ Summary: A stateful python interface for USB MIDI controllers.
5
+ Project-URL: Repository, https://github.com/uermel/padbound.git
6
+ Project-URL: Issues, https://github.com/uermel/padbound/issues
7
+ Author-email: "Utz H. Ermel" <utz@ermel.me>
8
+ License: MIT License
9
+
10
+ Copyright (c) 2024 Utz Ermel
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in all
20
+ copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ SOFTWARE.
29
+ License-File: LICENSE
30
+ Keywords: MIDI,control surface,controller,interface,stateful
31
+ Classifier: Development Status :: 3 - Alpha
32
+ Classifier: License :: OSI Approved :: MIT License
33
+ Requires-Python: >=3.12
34
+ Requires-Dist: mido>=1.3.3
35
+ Requires-Dist: pydantic>=2.12.5
36
+ Requires-Dist: rich>=13.0.0
37
+ Provides-Extra: dev
38
+ Requires-Dist: black>=25.1.0; extra == 'dev'
39
+ Requires-Dist: ipython>=8.18.1; extra == 'dev'
40
+ Requires-Dist: notebook>=7.4.3; extra == 'dev'
41
+ Requires-Dist: pre-commit>=4.2.0; extra == 'dev'
42
+ Requires-Dist: ruff>=0.12.0; extra == 'dev'
43
+ Requires-Dist: textual-dev>=1.7.0; extra == 'dev'
44
+ Provides-Extra: docs
45
+ Requires-Dist: mkdocs-autorefs>=1.4.2; extra == 'docs'
46
+ Requires-Dist: mkdocs-material>=9.6.14; extra == 'docs'
47
+ Requires-Dist: mkdocs>=1.6.1; extra == 'docs'
48
+ Requires-Dist: mkdocstrings[python]>=0.29.1; extra == 'docs'
49
+ Provides-Extra: test
50
+ Requires-Dist: pytest-cov>=6.2.1; extra == 'test'
51
+ Requires-Dist: pytest>=8.4.1; extra == 'test'
52
+ Description-Content-Type: text/markdown
53
+
54
+
55
+
56
+ <p align="center">
57
+ <picture>
58
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/uermel/padbound/refs/heads/main/assets/logo_dark.svg">
59
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/uermel/padbound/refs/heads/main/assets/logo_light.svg">
60
+ <img alt="Padbound" src="https://raw.githubusercontent.com/uermel/padbound/refs/heads/main/assets/logo_light.svg">
61
+ </picture>
62
+ </p>
63
+
64
+
65
+ A general, stateful python interface for MIDI controllers that abstracts hardware differences behind a simple API.
66
+
67
+ ## Overview
68
+
69
+ Padbound provides a high-level abstraction over MIDI controllers, allowing applications to work with three fundamental
70
+ control types (toggles, momentary triggers, continuous controls) and RGB colors without dealing with raw MIDI messages.
71
+
72
+ ## Features
73
+
74
+ - **Three Control Types**: Toggle, Momentary, and Continuous controls with unified API
75
+ - **Progressive State Discovery**: Honest representation of hardware limitations (knobs/faders start in "unknown" state)
76
+ - **Capability-Based API**: Validates hardware support before attempting operations
77
+ - **Thread-Safe**: Safe concurrent access from callbacks and main thread
78
+ - **Plugin Architecture**: Extensible system for supporting different controllers
79
+ - **Callback System**: Global, per-control, and type-based callbacks with error isolation
80
+ - **Bank Support**: Handles controllers with bank switching (when supported)
81
+ - **Strict/Permissive Modes**: Choose between errors or warnings for unsupported operations
82
+
83
+ ## Installation
84
+
85
+ ```bash
86
+ pip install -e .
87
+ ```
88
+
89
+ ## Quick Start
90
+
91
+ ```python
92
+ from padbound import Controller, ControlType
93
+
94
+ # Auto-detect and connect to controller
95
+ with Controller(plugin='auto', auto_connect=True) as controller:
96
+ # Register callback for a specific pad
97
+ controller.on_control('pad_1', lambda state: print(f"Pad 1: {state.is_on}"))
98
+
99
+ # Register callback for all continuous controls (knobs/faders)
100
+ def on_continuous(control_id, state):
101
+ if state.is_discovered:
102
+ print(f"{control_id}: {state.normalized_value:.2f}")
103
+ controller.on_type(ControlType.CONTINUOUS, on_continuous)
104
+
105
+ # Main loop
106
+ while True:
107
+ controller.process_events()
108
+ ```
109
+
110
+ ## Examples
111
+
112
+ See the `examples/` directory for controller-specific demos:
113
+ - `demo_akai_lpd8.py` - AKAI LPD8 MK2
114
+ - `demo_akai_apc_mini_mk2.py` - AKAI APC mini MK2
115
+ - `demo_presonus_atom.py` - PreSonus ATOM
116
+ - `demo_xjam.py` - Xjam
117
+ - `demo_x_touch_mini.py` - Behringer X-Touch Mini
118
+
119
+ ### Callback Registration
120
+
121
+ ```python
122
+ from padbound import Controller, ControlType
123
+
124
+ with Controller(plugin='auto', auto_connect=True) as controller:
125
+ # Per-control callback
126
+ controller.on_control('pad_1', lambda state: print(f"Pad 1: {state.is_on}"))
127
+
128
+ # Per-type callback (all toggles, all continuous, etc.)
129
+ controller.on_type(ControlType.TOGGLE, lambda cid, state: print(f"{cid} toggled"))
130
+
131
+ # Per-category callback (e.g., all transport buttons)
132
+ controller.on_category('transport', lambda cid, state: print(f"Transport: {cid}"))
133
+
134
+ # Global callback (all controls)
135
+ controller.on_global(lambda cid, state: print(f"Any control: {cid}"))
136
+
137
+ while True:
138
+ controller.process_events()
139
+ ```
140
+
141
+ ### Setting Control State
142
+
143
+ ```python
144
+ from padbound import Controller
145
+
146
+ with Controller(plugin='auto', auto_connect=True) as controller:
147
+ # Set pad LED color and state
148
+ if controller.can_set_state('pad_1', is_on=True, color='red'):
149
+ controller.set_state('pad_1', is_on=True, color='red')
150
+
151
+ # Query control state
152
+ state = controller.get_state('pad_1')
153
+ if state:
154
+ print(f"Pad 1 is {'on' if state.is_on else 'off'}")
155
+ ```
156
+
157
+ ### Using Configuration
158
+
159
+ ```python
160
+ from padbound import Controller, ControllerConfig, BankConfig, ControlConfig, ControlType
161
+
162
+ # Configure pad colors and types
163
+ config = ControllerConfig(banks={
164
+ 'bank_1': BankConfig(controls={
165
+ 'pad_1': ControlConfig(type=ControlType.TOGGLE, color='red', off_color='dim_red'),
166
+ 'pad_2': ControlConfig(type=ControlType.MOMENTARY, color='green'),
167
+ })
168
+ })
169
+
170
+ with Controller(plugin='auto', config=config, auto_connect=True) as controller:
171
+ while True:
172
+ controller.process_events()
173
+ ```
174
+
175
+ ## Supported Controllers
176
+
177
+ ### Capability Comparison
178
+
179
+ | Controller | Pads | Knobs/Encoders | Faders | Buttons | RGB LEDs | LED Modes | Banks | Persistent Config | Special Features |
180
+ |------------|----|---------------|--------|---------|----------|-----------|-------|-------------------|------------------|
181
+ | **AKAI LPD8 MK2** | 8 | 8 knobs | — | — | ✓ Full | Solid | 4 (HW) | ✓ SysEx | Multi-signal pads (NOTE/CC/PC) |
182
+ | **AKAI APC mini MK2** | 64 | — | 9 | 17 | ✓ Full | Solid/Pulse/Blink | 1 | — | Fader position discovery |
183
+ | **PreSonus ATOM** | 16 | 4 | — | 20 | ✓ Full | Solid/Pulse/Blink | 8 (HW) | — | Native Control mode, encoder acceleration |
184
+ | **Xjam** | 16 | 6 | — | — | — | — | 3 (HW) | ✓ SysEx | Multi-signal pads, multiple encoder modes |
185
+ | **X-Touch Mini** | 16 | 8 + buttons | 1 | — | Single | Solid | 2 (HW) | — | Deferred LED feedback, auto-reflecting encoder rings |
186
+
187
+ **Legend:**
188
+ - **HW** = Hardware-managed bank switching
189
+ - **RGB LEDs**: Full = True RGB color support, Single = On/off only
190
+ - **LED Modes**: Animation/behavior modes supported
191
+ - **Persistent Config**: Device stores configuration in non-volatile memory
192
+
193
+ ### Detailed Controller Information
194
+
195
+ #### AKAI LPD8 MK2
196
+ **Control Surface**: 8 RGB pads + 8 knobs\
197
+ **Banks**: 4 banks with hardware-based switching\
198
+ **Capabilities**:
199
+ - **Pad LED Feedback**: Full RGB via SysEx
200
+ - **Pad LED Modes**: Solid
201
+ - **Pad Modes**: Toggle or momentary (global per bank)
202
+ - **Knob Feedback**: None (read-only)
203
+ - **Configuration**: Persistent (SysEx)
204
+
205
+ #### AKAI APC mini MK2
206
+ **Control Surface**: 8×8 RGB pad grid + 9 faders + 17 buttons\
207
+ **Banks**: Single layer\
208
+ **Capabilities**:
209
+ - **Pad LED Feedback**: Full RGB via SysEx
210
+ - **Pad LED Modes**: Solid, pulse, blink
211
+ - **Pad Modes**: Toggle or momentary (per pad)
212
+ - **Fader Feedback**: None (read-only, initial position discovered)
213
+ - **Button LED Feedback**: Single-color (red for track, green for scene)
214
+ - **Configuration**: Volatile
215
+
216
+ #### PreSonus ATOM
217
+ **Control Surface**: 16 RGB pads (4×4) + 4 encoders + 20 buttons\
218
+ **Banks**: 8 hardware-managed banks (not software-accessible)\
219
+ **Capabilities**:
220
+ - **Pad LED Feedback**: Full RGB via Native Control mode
221
+ - **Pad LED Modes**: Solid, pulse, breathe
222
+ - **Pad Modes**: Toggle or momentary (per pad)
223
+ - **Encoder Type**: Relative with acceleration
224
+ - **Encoder Feedback**: None (read-only)
225
+ - **Button LED Feedback**: Single-color
226
+ - **Configuration**: Volatile
227
+
228
+ #### Xjam (ESI/Artesia Pro)
229
+ **Control Surface**: 16 pads + 6 knobs per bank\
230
+ **Banks**: 3 banks (Green, Yellow, Red) with synchronized pad/knob switching\
231
+ **Capabilities**:
232
+ - **Pad LED Feedback**: None (hardware-managed)
233
+ - **Pad Modes**: Toggle or momentary (global)
234
+ - **Knob Type**: Configurable (absolute or 3 relative modes)
235
+ - **Knob Feedback**: None (read-only)
236
+ - **Configuration**: Persistent (SysEx)
237
+
238
+ #### Behringer X-Touch Mini
239
+ **Control Surface**: 8 encoders with buttons + 16 pads + 1 fader\
240
+ **Banks**: 2 layers (A, B) with hardware switching\
241
+ **Capabilities**:
242
+ - **Pad LED Feedback**: Single-color
243
+ - **Pad LED Modes**: Solid
244
+ - **Pad Modes**: Toggle or momentary (per pad)
245
+ - **Encoder Type**: Absolute
246
+ - **Encoder Feedback**: LED ring auto-reflects value
247
+ - **Encoder Button Feedback**: Single-color
248
+ - **Fader Feedback**: None (read-only)
249
+ - **Configuration**: Volatile
250
+
251
+ ## Documentation
252
+
253
+ TBD
254
+
255
+ ## Acknowledgements
256
+
257
+ Some protocol information for supported controllers was gathered from:
258
+ - **AKAI LPD8 MK2**: [stephensrmmartin/lpd8mk2](https://github.com/stephensrmmartin/lpd8mk2)
259
+ - **PreSonus ATOM**: [EMATech/AtomCtrl](https://github.com/EMATech/AtomCtrl)
260
+ - **Behringer X-Touch Mini**: [AndreasPantle/X-Touch-Mini-HandsOn](https://github.com/AndreasPantle/X-Touch-Mini-HandsOn)
261
+
262
+ ## License
263
+
264
+ MIT License