matchpatch 0.4.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 (130) hide show
  1. matchpatch-0.4.0/.gitattributes +18 -0
  2. matchpatch-0.4.0/.github/dependabot.yml +17 -0
  3. matchpatch-0.4.0/.github/workflows/quality.yml +134 -0
  4. matchpatch-0.4.0/.github/workflows/release.yml +128 -0
  5. matchpatch-0.4.0/.gitignore +20 -0
  6. matchpatch-0.4.0/.pre-commit-config.yaml +66 -0
  7. matchpatch-0.4.0/.python-version +1 -0
  8. matchpatch-0.4.0/AGENTS.md +35 -0
  9. matchpatch-0.4.0/LICENSE +21 -0
  10. matchpatch-0.4.0/PKG-INFO +134 -0
  11. matchpatch-0.4.0/Python/adjust_gain.py +20 -0
  12. matchpatch-0.4.0/Python/decrypt_hls.py +76 -0
  13. matchpatch-0.4.0/Python/encrypt_hls.py +85 -0
  14. matchpatch-0.4.0/Python/list_cab_presets.py +114 -0
  15. matchpatch-0.4.0/Python/preset_handling.py +1853 -0
  16. matchpatch-0.4.0/Python/remove_inactive_blocks.py +278 -0
  17. matchpatch-0.4.0/Python/replace_amp.py +127 -0
  18. matchpatch-0.4.0/Python/reset_output_levels.py +189 -0
  19. matchpatch-0.4.0/Python/stereofy.py +254 -0
  20. matchpatch-0.4.0/README.md +121 -0
  21. matchpatch-0.4.0/audio/reference-di/DI_Strandberg_Boden_Fusion_Bridge_Humbucker.wav +0 -0
  22. matchpatch-0.4.0/docs/_static/matchpatch-docs.css +8 -0
  23. matchpatch-0.4.0/docs/assets/matchmatch-icon-512.png +0 -0
  24. matchpatch-0.4.0/docs/assets/matchmatch-icon.png +0 -0
  25. matchpatch-0.4.0/docs/assets/matchmatch-logo.png +0 -0
  26. matchpatch-0.4.0/docs/assets/screenshots/backend-selector.png +0 -0
  27. matchpatch-0.4.0/docs/assets/screenshots/completed-results-table.png +0 -0
  28. matchpatch-0.4.0/docs/assets/screenshots/failed-measurement-row.png +0 -0
  29. matchpatch-0.4.0/docs/assets/screenshots/hardware-routing.png +0 -0
  30. matchpatch-0.4.0/docs/assets/screenshots/loaded-setlist.png +0 -0
  31. matchpatch-0.4.0/docs/assets/screenshots/normalization-ongoing.png +0 -0
  32. matchpatch-0.4.0/docs/assets/screenshots/optimization-dialog.png +0 -0
  33. matchpatch-0.4.0/docs/assets/screenshots/parameter-study-setup.png +0 -0
  34. matchpatch-0.4.0/docs/assets/screenshots/timing-tab.png +0 -0
  35. matchpatch-0.4.0/docs/concepts/backends.md +73 -0
  36. matchpatch-0.4.0/docs/concepts/crest-factor.md +36 -0
  37. matchpatch-0.4.0/docs/concepts/lufs-and-loudness.md +54 -0
  38. matchpatch-0.4.0/docs/concepts/measurement-and-adjusted-files.md +75 -0
  39. matchpatch-0.4.0/docs/concepts/reading-results.md +102 -0
  40. matchpatch-0.4.0/docs/concepts/reference-di.md +113 -0
  41. matchpatch-0.4.0/docs/concepts/routing-and-levels.md +59 -0
  42. matchpatch-0.4.0/docs/concepts/snapshots-solos-and-ignored.md +80 -0
  43. matchpatch-0.4.0/docs/concepts/timing.md +81 -0
  44. matchpatch-0.4.0/docs/conf.py +28 -0
  45. matchpatch-0.4.0/docs/dev/architecture.md +275 -0
  46. matchpatch-0.4.0/docs/dev/commands.md +364 -0
  47. matchpatch-0.4.0/docs/dev/file-formats.md +226 -0
  48. matchpatch-0.4.0/docs/dev/release.md +202 -0
  49. matchpatch-0.4.0/docs/developer-notes.md +235 -0
  50. matchpatch-0.4.0/docs/faq.md +192 -0
  51. matchpatch-0.4.0/docs/glossary.md +205 -0
  52. matchpatch-0.4.0/docs/index.md +112 -0
  53. matchpatch-0.4.0/docs/musician-guide.md +271 -0
  54. matchpatch-0.4.0/docs/quick-start.md +102 -0
  55. matchpatch-0.4.0/docs/troubleshooting.md +431 -0
  56. matchpatch-0.4.0/docs/workflows/custom-adjustments.md +86 -0
  57. matchpatch-0.4.0/docs/workflows/hardware-measurement.md +116 -0
  58. matchpatch-0.4.0/docs/workflows/manual-editing-and-csv.md +106 -0
  59. matchpatch-0.4.0/docs/workflows/normalize-setlist.md +115 -0
  60. matchpatch-0.4.0/docs/workflows/normalize-single-preset.md +84 -0
  61. matchpatch-0.4.0/docs/workflows/optimize-timing.md +97 -0
  62. matchpatch-0.4.0/docs/workflows/save-and-import.md +97 -0
  63. matchpatch-0.4.0/docs/workflows/select-changed-presets.md +73 -0
  64. matchpatch-0.4.0/docs/workflows/test-without-hardware.md +75 -0
  65. matchpatch-0.4.0/installer/README.md +91 -0
  66. matchpatch-0.4.0/installer/matchpatch.iss +53 -0
  67. matchpatch-0.4.0/installer/pyinstaller/build_support.py +125 -0
  68. matchpatch-0.4.0/installer/pyinstaller/matchpatch-gui.spec +69 -0
  69. matchpatch-0.4.0/installer/smoke/smoke_installed.ps1 +121 -0
  70. matchpatch-0.4.0/installer/smoke/smoke_payload.ps1 +71 -0
  71. matchpatch-0.4.0/pyproject.toml +97 -0
  72. matchpatch-0.4.0/scripts/build-docs.sh +15 -0
  73. matchpatch-0.4.0/scripts/build-windows-installer-from-wsl.sh +43 -0
  74. matchpatch-0.4.0/scripts/build-windows-installer.cmd +101 -0
  75. matchpatch-0.4.0/scripts/build-windows-payload.cmd +66 -0
  76. matchpatch-0.4.0/scripts/check_commit_msg.py +88 -0
  77. matchpatch-0.4.0/scripts/measure-windows-from-wsl.sh +15 -0
  78. matchpatch-0.4.0/scripts/release.py +666 -0
  79. matchpatch-0.4.0/scripts/run_hook_with_hint.py +35 -0
  80. matchpatch-0.4.0/scripts/stage-installer-docs.sh +36 -0
  81. matchpatch-0.4.0/scripts/sync-windows-from-wsl.sh +30 -0
  82. matchpatch-0.4.0/scripts/sync-windows.cmd +20 -0
  83. matchpatch-0.4.0/scripts/sync-wsl.sh +8 -0
  84. matchpatch-0.4.0/scripts/test-gui.sh +16 -0
  85. matchpatch-0.4.0/scripts/test-windows-installer-from-wsl.sh +63 -0
  86. matchpatch-0.4.0/scripts/test-windows-installer.cmd +95 -0
  87. matchpatch-0.4.0/src/matchpatch/__init__.py +20 -0
  88. matchpatch-0.4.0/src/matchpatch/analysis.py +97 -0
  89. matchpatch-0.4.0/src/matchpatch/app.py +71 -0
  90. matchpatch-0.4.0/src/matchpatch/audio.py +148 -0
  91. matchpatch-0.4.0/src/matchpatch/cli.py +70 -0
  92. matchpatch-0.4.0/src/matchpatch/config.py +181 -0
  93. matchpatch-0.4.0/src/matchpatch/custom_adjustments.py +60 -0
  94. matchpatch-0.4.0/src/matchpatch/devices/__init__.py +5 -0
  95. matchpatch-0.4.0/src/matchpatch/devices/base.py +204 -0
  96. matchpatch-0.4.0/src/matchpatch/devices/helix.py +447 -0
  97. matchpatch-0.4.0/src/matchpatch/devices/registry.py +22 -0
  98. matchpatch-0.4.0/src/matchpatch/gui/__init__.py +1 -0
  99. matchpatch-0.4.0/src/matchpatch/gui/app.py +193 -0
  100. matchpatch-0.4.0/src/matchpatch/gui/device_panels.py +142 -0
  101. matchpatch-0.4.0/src/matchpatch/gui/dialogs.py +150 -0
  102. matchpatch-0.4.0/src/matchpatch/gui/help.py +213 -0
  103. matchpatch-0.4.0/src/matchpatch/gui/main_window.py +7745 -0
  104. matchpatch-0.4.0/src/matchpatch/gui/snapshot_header.py +48 -0
  105. matchpatch-0.4.0/src/matchpatch/gui/worker.py +135 -0
  106. matchpatch-0.4.0/src/matchpatch/measure.py +1191 -0
  107. matchpatch-0.4.0/src/matchpatch/measurement_optimizer.py +646 -0
  108. matchpatch-0.4.0/src/matchpatch/normalize.py +874 -0
  109. matchpatch-0.4.0/src/matchpatch/progress.py +39 -0
  110. matchpatch-0.4.0/src/matchpatch/workflow.py +361 -0
  111. matchpatch-0.4.0/tests/README.md +117 -0
  112. matchpatch-0.4.0/tests/test_analysis.py +71 -0
  113. matchpatch-0.4.0/tests/test_app.py +81 -0
  114. matchpatch-0.4.0/tests/test_audio.py +162 -0
  115. matchpatch-0.4.0/tests/test_check_commit_msg.py +41 -0
  116. matchpatch-0.4.0/tests/test_cli.py +58 -0
  117. matchpatch-0.4.0/tests/test_config.py +77 -0
  118. matchpatch-0.4.0/tests/test_devices.py +51 -0
  119. matchpatch-0.4.0/tests/test_gui.py +5343 -0
  120. matchpatch-0.4.0/tests/test_gui_app.py +309 -0
  121. matchpatch-0.4.0/tests/test_gui_dialogs.py +95 -0
  122. matchpatch-0.4.0/tests/test_gui_help.py +334 -0
  123. matchpatch-0.4.0/tests/test_helix.py +557 -0
  124. matchpatch-0.4.0/tests/test_installer_metadata.py +184 -0
  125. matchpatch-0.4.0/tests/test_measure.py +942 -0
  126. matchpatch-0.4.0/tests/test_measurement_optimizer.py +585 -0
  127. matchpatch-0.4.0/tests/test_normalize.py +1126 -0
  128. matchpatch-0.4.0/tests/test_preset_handling.py +316 -0
  129. matchpatch-0.4.0/tests/test_progress.py +27 -0
  130. matchpatch-0.4.0/uv.lock +1383 -0
@@ -0,0 +1,18 @@
1
+ # Text files
2
+ * text=auto eol=lf
3
+
4
+ # Windows scripts
5
+ *.cmd eol=crlf
6
+ *.bat eol=crlf
7
+
8
+ # Shell scripts
9
+ *.sh eol=lf
10
+
11
+ # Binary files
12
+ *.png binary
13
+ *.jpg binary
14
+ *.jpeg binary
15
+ *.gif binary
16
+ *.wav binary
17
+ *.hlx binary
18
+ *.hls binary
@@ -0,0 +1,17 @@
1
+ version: 2
2
+
3
+ updates:
4
+ - package-ecosystem: "uv"
5
+ directory: "/"
6
+ schedule:
7
+ interval: "weekly"
8
+
9
+ - package-ecosystem: "github-actions"
10
+ directory: "/"
11
+ schedule:
12
+ interval: "weekly"
13
+
14
+ - package-ecosystem: "pre-commit"
15
+ directory: "/"
16
+ schedule:
17
+ interval: "weekly"
@@ -0,0 +1,134 @@
1
+ name: Quality
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ docs:
12
+ name: Docs / Python 3.12
13
+ runs-on: ubuntu-latest
14
+
15
+ steps:
16
+ - name: Check out repository
17
+ uses: actions/checkout@v6
18
+
19
+ - name: Install uv and Python
20
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
21
+ with:
22
+ enable-cache: true
23
+ python-version: "3.12"
24
+
25
+ - name: Synchronize docs environment
26
+ run: uv sync --locked --no-default-groups --group docs
27
+
28
+ - name: Build docs
29
+ run: uv run --frozen --no-default-groups --group docs sphinx-build -W --keep-going -b html docs docs_html
30
+
31
+ native:
32
+ name: ${{ matrix.os }} / Python ${{ matrix.python-version }}
33
+ runs-on: ${{ matrix.os }}
34
+ strategy:
35
+ fail-fast: false
36
+ matrix:
37
+ os:
38
+ - ubuntu-latest
39
+ - windows-latest
40
+ python-version:
41
+ - "3.12"
42
+ - "3.13"
43
+ - "3.14"
44
+
45
+ steps:
46
+ - name: Check out repository
47
+ uses: actions/checkout@v6
48
+
49
+ - name: Install uv and Python
50
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
51
+ with:
52
+ enable-cache: ${{ runner.os != 'Windows' }}
53
+ python-version: ${{ matrix.python-version }}
54
+
55
+ - name: Synchronize environment
56
+ run: uv sync --locked --no-default-groups --group wsl --extra gui
57
+
58
+ - name: Install Qt runtime libraries
59
+ if: runner.os == 'Linux'
60
+ run: sudo apt-get update && sudo apt-get install -y libegl1
61
+
62
+ - name: Ruff lint
63
+ run: uv run --frozen --no-default-groups --group wsl --extra gui ruff check .
64
+
65
+ - name: Ruff format check
66
+ run: uv run --frozen --no-default-groups --group wsl --extra gui ruff format --check .
67
+
68
+ - name: ty
69
+ run: uv run --frozen --no-default-groups --group wsl --extra gui ty check
70
+
71
+ - name: pytest
72
+ run: uv run --frozen --no-default-groups --group wsl --extra gui pytest
73
+
74
+ wsl:
75
+ name: WSL / Python ${{ matrix.python-version }}
76
+ runs-on: windows-latest
77
+ strategy:
78
+ fail-fast: false
79
+ matrix:
80
+ python-version:
81
+ - "3.12"
82
+ - "3.13"
83
+ - "3.14"
84
+
85
+ steps:
86
+ - name: Check out repository
87
+ uses: actions/checkout@v6
88
+
89
+ - name: Install Ubuntu on WSL
90
+ uses: Vampire/setup-wsl@v7
91
+ with:
92
+ distribution: Ubuntu-24.04
93
+ additional-packages: curl ca-certificates
94
+
95
+ - name: Run quality checks in WSL
96
+ shell: wsl-bash {0}
97
+ run: |
98
+ cd "$(wslpath '${{ github.workspace }}')"
99
+ curl -LsSf https://astral.sh/uv/install.sh | sh
100
+ export PATH="$HOME/.local/bin:$PATH"
101
+ export UV_PYTHON="${{ matrix.python-version }}"
102
+ uv python install "$UV_PYTHON"
103
+ uv sync --locked --no-default-groups --group wsl --extra gui
104
+ uv run --frozen --no-default-groups --group wsl --extra gui ruff check .
105
+ uv run --frozen --no-default-groups --group wsl --extra gui ruff format --check .
106
+ uv run --frozen --no-default-groups --group wsl --extra gui ty check
107
+ uv run --frozen --no-default-groups --group wsl --extra gui pytest
108
+
109
+ installer:
110
+ name: Windows installer smoke
111
+ runs-on: windows-latest
112
+
113
+ steps:
114
+ - name: Check out repository
115
+ uses: actions/checkout@v6
116
+
117
+ - name: Install uv and Python
118
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
119
+ with:
120
+ python-version: "3.12"
121
+
122
+ - name: Install Inno Setup
123
+ run: choco install innosetup --no-progress -y
124
+
125
+ - name: Build and test installer
126
+ shell: cmd
127
+ run: scripts\test-windows-installer.cmd
128
+
129
+ - name: Upload installer artifact
130
+ uses: actions/upload-artifact@v4
131
+ with:
132
+ name: matchpatch-installer-smoke
133
+ path: dist/installer/*.exe
134
+ if-no-files-found: error
@@ -0,0 +1,128 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ publish:
13
+ name: Publish to PyPI
14
+ runs-on: ubuntu-latest
15
+ environment:
16
+ name: pypi
17
+ url: https://pypi.org/p/matchpatch
18
+ permissions:
19
+ contents: read
20
+ id-token: write
21
+
22
+ steps:
23
+ - name: Check out repository
24
+ uses: actions/checkout@v6
25
+
26
+ - name: Install uv and Python
27
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
28
+ with:
29
+ python-version: "3.14"
30
+
31
+ - name: Verify tag matches package version
32
+ shell: bash
33
+ run: |
34
+ package_version="$(python -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')"
35
+ test "${GITHUB_REF_NAME}" = "v${package_version}"
36
+
37
+ - name: Build distributions
38
+ run: uv build --no-sources
39
+
40
+ - name: Smoke test wheel
41
+ shell: bash
42
+ run: uv run --isolated --no-project --with dist/*.whl python -c "import matchpatch"
43
+
44
+ - name: Smoke test source distribution
45
+ shell: bash
46
+ run: uv run --isolated --no-project --with dist/*.tar.gz python -c "import matchpatch"
47
+
48
+ - name: Synchronize docs environment
49
+ run: uv sync --locked --no-default-groups --group docs
50
+
51
+ - name: Build offline docs
52
+ run: uv run --frozen --no-default-groups --group docs sphinx-build -W --keep-going -b html docs docs_html
53
+
54
+ - name: Stage installer docs payload
55
+ shell: bash
56
+ run: |
57
+ mkdir -p installer-payload/docs_html
58
+ cp -a docs_html/. installer-payload/docs_html/
59
+ test -f installer-payload/docs_html/index.html
60
+
61
+ - name: Upload installer docs payload
62
+ uses: actions/upload-artifact@v4
63
+ with:
64
+ name: matchpatch-docs-html-${{ github.ref_name }}
65
+ path: installer-payload/
66
+ if-no-files-found: error
67
+
68
+ - name: Publish distributions
69
+ run: uv publish
70
+
71
+ windows-installer:
72
+ name: Windows installer
73
+ runs-on: windows-latest
74
+ permissions:
75
+ contents: write
76
+
77
+ steps:
78
+ - name: Check out repository
79
+ uses: actions/checkout@v6
80
+
81
+ - name: Install uv and Python
82
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
83
+ with:
84
+ python-version: "3.12"
85
+
86
+ - name: Resolve package version
87
+ id: version
88
+ shell: pwsh
89
+ run: |
90
+ $version = python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])"
91
+ if ($env:GITHUB_REF_NAME -ne "v$version") {
92
+ throw "Release tag '$env:GITHUB_REF_NAME' does not match package version 'v$version'."
93
+ }
94
+ "version=$version" >> $env:GITHUB_OUTPUT
95
+ "installer=dist/installer/MatchPatch-Setup-$version.exe" >> $env:GITHUB_OUTPUT
96
+
97
+ - name: Install Inno Setup
98
+ run: choco install innosetup --no-progress -y
99
+
100
+ - name: Build and test installer
101
+ shell: cmd
102
+ run: scripts\test-windows-installer.cmd
103
+
104
+ - name: Upload installer artifact
105
+ uses: actions/upload-artifact@v4
106
+ with:
107
+ name: matchpatch-installer-${{ github.ref_name }}
108
+ path: ${{ steps.version.outputs.installer }}
109
+ if-no-files-found: error
110
+
111
+ - name: Attach installer to GitHub Release
112
+ shell: pwsh
113
+ env:
114
+ GH_TOKEN: ${{ github.token }}
115
+ run: |
116
+ $tag = $env:GITHUB_REF_NAME
117
+ $installer = "${{ steps.version.outputs.installer }}"
118
+ if (-not (Test-Path -LiteralPath $installer)) {
119
+ throw "Installer artifact was not found: $installer"
120
+ }
121
+ gh release view $tag 2>$null
122
+ if ($LASTEXITCODE -ne 0) {
123
+ gh release create $tag --title $tag --notes "MatchPatch $tag"
124
+ if ($LASTEXITCODE -ne 0) {
125
+ throw "Failed to create GitHub Release for $tag."
126
+ }
127
+ }
128
+ gh release upload $tag $installer --clobber
@@ -0,0 +1,20 @@
1
+ Attic
2
+ Backups
3
+ Media
4
+ Unused
5
+ __pycache__
6
+ .venv
7
+ .venv-wsl
8
+ .venv-windows
9
+ .coverage
10
+ htmlcov
11
+ /docs_html/
12
+ /dist/
13
+ /build/
14
+ /plans/
15
+ /installer-payload/
16
+ *.spec.bak
17
+ *.json
18
+ *.hls
19
+ *.hlx
20
+ matchpatch_normalization_*
@@ -0,0 +1,66 @@
1
+ default_install_hook_types:
2
+ - pre-commit
3
+ - commit-msg
4
+ - pre-push
5
+
6
+ repos:
7
+ - repo: https://github.com/pre-commit/pre-commit-hooks
8
+ rev: v6.0.0
9
+ hooks:
10
+ - id: trailing-whitespace
11
+ - id: end-of-file-fixer
12
+ - id: check-yaml
13
+ - id: check-toml
14
+ - id: check-added-large-files
15
+ exclude: ^(audio/reference-di/|doc/assets/matchmatch-logo\.png$)
16
+
17
+ - repo: local
18
+ hooks:
19
+ - id: conventional-commit
20
+ name: Conventional commit message
21
+ entry: python scripts/check_commit_msg.py
22
+ language: python
23
+ stages:
24
+ - commit-msg
25
+
26
+ - id: ruff-lint
27
+ name: Ruff lint
28
+ entry: >-
29
+ bash -c 'export UV_PROJECT_ENVIRONMENT="${XDG_DATA_HOME:-$HOME/.local/share}/matchpatch/.venv-wsl";
30
+ python3 scripts/run_hook_with_hint.py
31
+ --hint "UV_PROJECT_ENVIRONMENT=\"\${XDG_DATA_HOME:-\$HOME/.local/share}/matchpatch/.venv-wsl\" uv run --frozen --no-default-groups --group wsl ruff check --fix ."
32
+ -- uv run --frozen --no-default-groups --group wsl ruff check .'
33
+ language: system
34
+ pass_filenames: false
35
+
36
+ - id: ruff-format
37
+ name: Ruff format check
38
+ entry: >-
39
+ bash -c 'export UV_PROJECT_ENVIRONMENT="${XDG_DATA_HOME:-$HOME/.local/share}/matchpatch/.venv-wsl";
40
+ python3 scripts/run_hook_with_hint.py
41
+ --hint "UV_PROJECT_ENVIRONMENT=\"\${XDG_DATA_HOME:-\$HOME/.local/share}/matchpatch/.venv-wsl\" uv run --frozen --no-default-groups --group wsl ruff format ."
42
+ -- uv run --frozen --no-default-groups --group wsl ruff format --check .'
43
+ language: system
44
+ pass_filenames: false
45
+
46
+ - id: ty
47
+ name: ty
48
+ entry: >-
49
+ bash -c 'export UV_PROJECT_ENVIRONMENT="${XDG_DATA_HOME:-$HOME/.local/share}/matchpatch/.venv-wsl";
50
+ python3 scripts/run_hook_with_hint.py
51
+ --hint "UV_PROJECT_ENVIRONMENT=\"\${XDG_DATA_HOME:-\$HOME/.local/share}/matchpatch/.venv-wsl\" uv run --frozen --no-default-groups --group wsl ty check"
52
+ -- uv run --frozen --no-default-groups --group wsl ty check'
53
+ language: system
54
+ pass_filenames: false
55
+
56
+ - id: pytest
57
+ name: pytest
58
+ entry: >-
59
+ bash -c 'export UV_PROJECT_ENVIRONMENT="${XDG_DATA_HOME:-$HOME/.local/share}/matchpatch/.venv-wsl";
60
+ python3 scripts/run_hook_with_hint.py
61
+ --hint "UV_PROJECT_ENVIRONMENT=\"\${XDG_DATA_HOME:-\$HOME/.local/share}/matchpatch/.venv-wsl\" uv run --frozen --no-default-groups --group wsl pytest"
62
+ -- uv run --frozen --no-default-groups --group wsl pytest'
63
+ language: system
64
+ pass_filenames: false
65
+ stages:
66
+ - pre-push
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,35 @@
1
+ # MatchPatch Agent Notes
2
+
3
+ Python package in `src/matchpatch`; legacy Helix JSON/HLS utilities live in `Python/`.
4
+ Use existing WSL env, not bare `pytest` or a stale project `.venv`:
5
+
6
+ ```bash
7
+ scripts/sync-wsl.sh
8
+ $HOME/.local/share/matchpatch/.venv-wsl/bin/pytest
9
+ ```
10
+
11
+ For GUI work use:
12
+
13
+ ```bash
14
+ scripts/test-gui.sh [pytest args]
15
+ ```
16
+
17
+ Quality checks:
18
+
19
+ ```bash
20
+ ruff check .
21
+ ruff format --check .
22
+ ty check
23
+ ```
24
+
25
+ Run them from the synced WSL env, or via the pre-push hook. GUI tests use PySide6
26
+ with offscreen Qt. Prefer focused tests in `tests/test_gui.py` for window/widget
27
+ changes, and broaden only when behavior crosses workflows.
28
+
29
+ MatchPatch normalizes Helix `.hls` setlists and `.hlx` presets. Core flow:
30
+ `workflow.py` creates/uses measurement CSVs, `normalize.py` bridges WSL to the
31
+ native Windows worker, `measure.py` records/analyzes audio, device profiles adapt
32
+ processor files and steering. Keep new device-specific behavior behind
33
+ `DeviceProfile`/`PatchFileHandler`/`DeviceController`. Preserve the user’s dirty
34
+ worktree; do not revert unrelated edits. Prefer `rg`, focused patches, and tests
35
+ that mock hardware unless the user explicitly asks for real-device integration.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MatchPatch contributors
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,134 @@
1
+ Metadata-Version: 2.4
2
+ Name: matchpatch
3
+ Version: 0.4.0
4
+ Summary: Audio processor preset gain normalization and measurement tools
5
+ Project-URL: Homepage, https://github.com/noseglasses/MatchPatch
6
+ Project-URL: Issues, https://github.com/noseglasses/MatchPatch/issues
7
+ Project-URL: Repository, https://github.com/noseglasses/MatchPatch.git
8
+ License-File: LICENSE
9
+ Requires-Python: <3.15,>=3.12
10
+ Provides-Extra: gui
11
+ Requires-Dist: pyside6>=6.8; extra == 'gui'
12
+ Description-Content-Type: text/markdown
13
+
14
+ # MatchPatch
15
+
16
+ <p align="center">
17
+ <img src="docs/assets/matchmatch-logo.png" alt="MatchPatch: Normalize presets. Match volume." width="260">
18
+ </p>
19
+
20
+ [![Quality](https://github.com/noseglasses/MatchPatch/actions/workflows/quality.yml/badge.svg)](https://github.com/noseglasses/MatchPatch/actions/workflows/quality.yml)
21
+ [![Release](https://github.com/noseglasses/MatchPatch/actions/workflows/release.yml/badge.svg)](https://github.com/noseglasses/MatchPatch/actions/workflows/release.yml)
22
+ [![PyPI](https://img.shields.io/pypi/v/matchpatch.svg)](https://pypi.org/project/matchpatch/)
23
+ [![Python](https://img.shields.io/pypi/pyversions/matchpatch.svg)](https://pypi.org/project/matchpatch/)
24
+
25
+ <p align="center">
26
+ <strong><a href="https://youtu.be/Dw1Kez0AnCk">Watch the demo video</a></strong>
27
+ ·
28
+ <strong><a href="https://noseglasses.github.io/MatchPatch/">Read the documentation</a></strong>
29
+ ·
30
+ <strong><a href="https://github.com/noseglasses/MatchPatch/releases/latest">Download MatchPatch</a></strong>
31
+ </p>
32
+
33
+ ![MatchPatch GUI showing a loaded Helix setlist](docs/assets/screenshots/normalization-ongoing.png)
34
+
35
+ **No more unexpected volume jumps when switching sounds.**
36
+
37
+ MatchPatch automatically equalizes the loudness of presets and snapshots in
38
+ guitar processors such as the Line 6 Helix.
39
+
40
+ ## Why MatchPatch?
41
+
42
+ You create a great clean sound. You create a great lead sound. Then you switch
43
+ between them and one is much louder than the other.
44
+
45
+ MatchPatch measures your presets and calculates the gain adjustments needed to
46
+ make them consistent, so your setlist feels balanced before rehearsal or stage
47
+ use.
48
+
49
+ ## Features
50
+
51
+ - Measure preset loudness automatically.
52
+ - Analyze snapshots.
53
+ - Calculate required gain corrections.
54
+ - Modify Helix setlists and presets.
55
+ - Test the workflow without hardware.
56
+ - Configure normal runs from the GUI.
57
+ - Use CLI and worker commands for advanced scripting.
58
+ - Open source.
59
+
60
+ ## Current Support
61
+
62
+ Current normal workflows support:
63
+
64
+ - Line 6 Helix
65
+ - `.hls` Helix setlists
66
+ - `.hlx` Helix presets
67
+ - GUI-first workflows
68
+
69
+ Loopback and simulated modes are available for no-hardware tests. Hardware mode
70
+ is for real Helix measurement.
71
+
72
+ ## Documentation
73
+
74
+ - Online manual: [noseglasses.github.io/MatchPatch](https://noseglasses.github.io/MatchPatch/)
75
+ - Start here: [docs/index.md](docs/index.md)
76
+ - 10-minute guide: [docs/quick-start.md](docs/quick-start.md)
77
+ - Main manual: [docs/musician-guide.md](docs/musician-guide.md)
78
+ - Test without hardware: [docs/workflows/test-without-hardware.md](docs/workflows/test-without-hardware.md)
79
+ - Hardware measurement: [docs/workflows/hardware-measurement.md](docs/workflows/hardware-measurement.md)
80
+ - Reference DI: [docs/concepts/reference-di.md](docs/concepts/reference-di.md)
81
+ - Troubleshooting: [docs/troubleshooting.md](docs/troubleshooting.md)
82
+ - FAQ: [docs/faq.md](docs/faq.md)
83
+ - Glossary: [docs/glossary.md](docs/glossary.md)
84
+
85
+ ## Safety Notes
86
+
87
+ > Warning:
88
+ > Keep backups of your original Helix files.
89
+
90
+ > Warning:
91
+ > Measurement files are for measuring, not for live playing.
92
+
93
+ ## Install And Launch
94
+
95
+ On Windows, download the latest installer from
96
+ [GitHub Releases](https://github.com/noseglasses/MatchPatch/releases/latest),
97
+ run `MatchPatch-Setup-<version>.exe`, then launch MatchPatch from the Start
98
+ Menu. The installed app bundles offline Help, available from the GUI.
99
+
100
+ For source checkouts, use the verified local setup commands below and see
101
+ [Developer Notes](docs/developer-notes.md) and
102
+ [developer commands](docs/dev/commands.md) for fuller setup details.
103
+
104
+ Install the optional GUI support and launch MatchPatch:
105
+
106
+ ```bash
107
+ # Linux or WSL
108
+ scripts/sync-wsl.sh --extra gui
109
+ matchpatch-gui
110
+ ```
111
+
112
+ ```powershell
113
+ # Windows PowerShell
114
+ cd C:\src\MatchPatch-windows
115
+ .\scripts\sync-windows.cmd --extra gui
116
+ .\.venv-windows\Scripts\matchpatch-gui.exe
117
+ ```
118
+
119
+ Hardware measurement from WSL needs a synced native Windows runtime. See
120
+ [developer commands](docs/dev/commands.md) before using real Helix hardware from
121
+ WSL.
122
+
123
+ ## Advanced And Developer Information
124
+
125
+ Technical details live in the developer docs:
126
+
127
+ - [Developer Notes](docs/developer-notes.md)
128
+ - [Architecture](docs/dev/architecture.md)
129
+ - [Commands](docs/dev/commands.md)
130
+ - [File Formats](docs/dev/file-formats.md)
131
+
132
+ ## License
133
+
134
+ MatchPatch is open source software released under the [MIT License](LICENSE).
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env python3
2
+ """Compatibility wrapper for the historical Helix adjustment command."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ PROJECT_DIR = Path(__file__).resolve().parent.parent
10
+ sys.path.insert(0, str(PROJECT_DIR / "src"))
11
+
12
+ from matchpatch.normalize import main # noqa: E402
13
+
14
+ if __name__ == "__main__":
15
+ arguments = sys.argv[1:]
16
+
17
+ if "--device" not in arguments:
18
+ arguments = ["--device", "helix", *arguments]
19
+
20
+ main(arguments)
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import argparse
4
+ import base64
5
+ import json
6
+ import os
7
+ import sys
8
+ import zlib
9
+
10
+
11
+ def require_extension(path, expected_ext, label):
12
+ ext = os.path.splitext(path)[1].lower()
13
+
14
+ if ext != expected_ext:
15
+ raise ValueError(f"{label} must have extension {expected_ext}, got {ext or '<none>'}")
16
+
17
+
18
+ def get_extension(path):
19
+ return os.path.splitext(path)[1].lower()
20
+
21
+
22
+ def parse_args():
23
+ parser = argparse.ArgumentParser(
24
+ description=(
25
+ "Decrypt/unpack a Helix .hls file to JSON, or validate/copy a .hlx preset to .hlx"
26
+ )
27
+ )
28
+
29
+ parser.add_argument("-i", "--input", required=True, help="Input .hls or .hlx file")
30
+
31
+ parser.add_argument(
32
+ "-o",
33
+ "--output",
34
+ required=True,
35
+ help="Output .json file for .hls input, or .hlx for .hlx input",
36
+ )
37
+
38
+ return parser.parse_args()
39
+
40
+
41
+ def main():
42
+ args = parse_args()
43
+
44
+ input_ext = get_extension(args.input)
45
+
46
+ if input_ext == ".hlx":
47
+ require_extension(args.output, ".hlx", "Output")
48
+
49
+ with open(args.input, "r", encoding="utf-8") as f:
50
+ preset = json.load(f)
51
+
52
+ with open(args.output, "w", encoding="utf-8") as f:
53
+ json.dump(preset, f, indent=1)
54
+
55
+ return
56
+
57
+ require_extension(args.input, ".hls", "Input")
58
+ require_extension(args.output, ".json", "Output")
59
+
60
+ with open(args.input, "r", encoding="utf-8") as f:
61
+ wrapper = json.load(f)
62
+
63
+ compressed = base64.b64decode(wrapper["encoded_data"])
64
+ raw = zlib.decompress(compressed)
65
+ text = raw.decode("utf-8")
66
+
67
+ with open(args.output, "w", encoding="utf-8") as f:
68
+ f.write(text)
69
+
70
+
71
+ if __name__ == "__main__":
72
+ try:
73
+ main()
74
+ except Exception as exc:
75
+ print(f"ERROR: {exc}", file=sys.stderr)
76
+ sys.exit(1)