serum-render 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.
- serum_render-0.1.0/.github/workflows/publish.yml +39 -0
- serum_render-0.1.0/.github/workflows/tests.yml +50 -0
- serum_render-0.1.0/.gitignore +7 -0
- serum_render-0.1.0/KNOWN_ISSUES.md +82 -0
- serum_render-0.1.0/LICENSE +674 -0
- serum_render-0.1.0/PKG-INFO +159 -0
- serum_render-0.1.0/README.md +131 -0
- serum_render-0.1.0/docs/decisions.md +52 -0
- serum_render-0.1.0/docs/determinism-probe-2026-07-16.json +98 -0
- serum_render-0.1.0/docs/implementation.md +146 -0
- serum_render-0.1.0/pyproject.toml +43 -0
- serum_render-0.1.0/scripts/probe_determinism.py +270 -0
- serum_render-0.1.0/scripts/verify_dawdreamer.py +229 -0
- serum_render-0.1.0/scripts/verify_dawdreamer_serum2.py +277 -0
- serum_render-0.1.0/serum_render/__init__.py +35 -0
- serum_render-0.1.0/serum_render/api.py +203 -0
- serum_render-0.1.0/serum_render/cli.py +274 -0
- serum_render-0.1.0/serum_render/config.py +96 -0
- serum_render-0.1.0/serum_render/discover.py +155 -0
- serum_render-0.1.0/serum_render/engine.py +220 -0
- serum_render-0.1.0/serum_render/formats.py +38 -0
- serum_render-0.1.0/serum_render/isolated.py +93 -0
- serum_render-0.1.0/serum_render/jobs.py +36 -0
- serum_render-0.1.0/serum_render/output.py +33 -0
- serum_render-0.1.0/serum_render/pool.py +149 -0
- serum_render-0.1.0/tests/conftest.py +92 -0
- serum_render-0.1.0/tests/test_api.py +167 -0
- serum_render-0.1.0/tests/test_cli.py +428 -0
- serum_render-0.1.0/tests/test_config.py +165 -0
- serum_render-0.1.0/tests/test_engine.py +307 -0
- serum_render-0.1.0/tests/test_filename.py +141 -0
- serum_render-0.1.0/tests/test_isolated.py +68 -0
- serum_render-0.1.0/tests/test_jobs.py +57 -0
- serum_render-0.1.0/tests/test_midi.py +60 -0
- serum_render-0.1.0/tests/test_output.py +51 -0
- serum_render-0.1.0/tests/test_presets.py +114 -0
- serum_render-0.1.0/tests/test_sanitize.py +52 -0
- serum_render-0.1.0/tests/test_serum1_smoke.py +78 -0
- serum_render-0.1.0/tests/test_serum2_smoke.py +168 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
build:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v6
|
|
12
|
+
|
|
13
|
+
- uses: actions/setup-python@v6
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.12"
|
|
16
|
+
|
|
17
|
+
- name: Build sdist and wheel
|
|
18
|
+
run: |
|
|
19
|
+
python -m pip install --upgrade pip build
|
|
20
|
+
python -m build
|
|
21
|
+
|
|
22
|
+
- uses: actions/upload-artifact@v4
|
|
23
|
+
with:
|
|
24
|
+
name: dist
|
|
25
|
+
path: dist/
|
|
26
|
+
|
|
27
|
+
publish:
|
|
28
|
+
needs: build
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
environment: pypi
|
|
31
|
+
permissions:
|
|
32
|
+
id-token: write
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/download-artifact@v4
|
|
35
|
+
with:
|
|
36
|
+
name: dist
|
|
37
|
+
path: dist/
|
|
38
|
+
|
|
39
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
unit:
|
|
10
|
+
strategy:
|
|
11
|
+
fail-fast: false
|
|
12
|
+
matrix:
|
|
13
|
+
os: [macos-latest, windows-latest]
|
|
14
|
+
python-version: ["3.11", "3.12"]
|
|
15
|
+
runs-on: ${{ matrix.os }}
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v6
|
|
18
|
+
|
|
19
|
+
- uses: actions/setup-python@v6
|
|
20
|
+
with:
|
|
21
|
+
python-version: ${{ matrix.python-version }}
|
|
22
|
+
cache: pip
|
|
23
|
+
cache-dependency-path: pyproject.toml
|
|
24
|
+
|
|
25
|
+
- name: Install package + dev deps
|
|
26
|
+
run: |
|
|
27
|
+
python -m pip install --upgrade pip
|
|
28
|
+
pip install -e ".[dev]"
|
|
29
|
+
|
|
30
|
+
- name: Run unit tests
|
|
31
|
+
run: pytest tests/ --ignore=tests/test_serum1_smoke.py --ignore=tests/test_serum2_smoke.py -q
|
|
32
|
+
|
|
33
|
+
git-install:
|
|
34
|
+
# Smokes a non-editable install end-to-end. Catches packaging
|
|
35
|
+
# regressions that the editable install above can hide.
|
|
36
|
+
# Push-only: PR head SHAs aren't reachable at the canonical URL.
|
|
37
|
+
if: github.event_name == 'push'
|
|
38
|
+
runs-on: macos-latest
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/setup-python@v6
|
|
41
|
+
with:
|
|
42
|
+
python-version: "3.12"
|
|
43
|
+
|
|
44
|
+
- name: Install from git URL at this commit
|
|
45
|
+
run: |
|
|
46
|
+
python -m pip install --upgrade pip
|
|
47
|
+
pip install "git+https://github.com/${{ github.repository }}.git@${{ github.sha }}"
|
|
48
|
+
|
|
49
|
+
- name: Verify console script
|
|
50
|
+
run: serum-render --help
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Known Issues
|
|
2
|
+
|
|
3
|
+
Tracked user-visible limitations and upstream quirks. Not every
|
|
4
|
+
limitation is a bug — some are behavioural choices documented here so
|
|
5
|
+
they're easy to find.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Non-ASCII characters in preset paths fail to load (Windows only)
|
|
10
|
+
|
|
11
|
+
**Symptom:** a render reports `Error: (PluginProcessor::loadPreset) File not found: <path>` even though the file exists. The mangled path in the error typically shows `?` or replacement characters where the original had accented letters, CJK characters, emoji, etc.
|
|
12
|
+
|
|
13
|
+
**Cause:** DawDreamer's C++ `PluginProcessor::loadPreset` converts the Python `str` path into a narrow `std::string` via the Windows active code page; unrepresentable characters are dropped and the mangled path no longer matches the file. macOS uses UTF-8 paths end-to-end and is unaffected.
|
|
14
|
+
|
|
15
|
+
**Workaround:** rename affected presets/folders to ASCII, or pre-copy to an ASCII-safe location. serum-render handles the failure gracefully — the batch continues and these presets appear in the final error summary.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Long output paths can exceed Windows `MAX_PATH` (Windows only)
|
|
20
|
+
|
|
21
|
+
serum-render caps the filename *stem* at 196 characters (headroom for `_N` collision suffixes) but does not cap the full path. Deeply nested output directories can still exceed the 260-character limit. Keep the output directory shallow or use a shorter `--filename-template`.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Higher bit depths may trip the silent-output warning spuriously
|
|
26
|
+
|
|
27
|
+
The silence threshold is fixed at −90 dBFS (the 16-bit quantization floor). Legitimately quiet audio at 24-bit/32f, or presets with long attack envelopes, can trigger the `Silent output for preset` warning. The audio is still written correctly; the log line is advisory.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Windows reserved filenames are not filtered (Windows only)
|
|
32
|
+
|
|
33
|
+
A preset named `CON.fxp` (or `PRN`, `NUL`, `AUX`, `COM1`–`COM9`, `LPT1`–`LPT9`) renders to a file that Windows cannot open, rename, or delete normally. `sanitize()` does not special-case reserved device names. Rename the preset, or use a template that always prefixes something (e.g. `{folder}_{preset}`).
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Serum 2 cold-start audio anomaly is absorbed by a warmup render
|
|
38
|
+
|
|
39
|
+
Serum 2 lazy-loads sample data on first render; the cold render comes out at ~10× steady-state level. `EngineHost` issues a 0.1-second warmup render per synth at construction, absorbing the anomaly. Do not remove the warmup — this is a regression guard, not dead code.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Per-process tempfile directory is not cleaned up
|
|
44
|
+
|
|
45
|
+
Serum 2 jobs round-trip the converted state blob through a per-process tempfile (`$TMPDIR/serum_render_*/state.bin`, overwritten in place, typically <1 MB). loky doesn't run finalizers on worker exit, so directories accumulate over many runs. macOS and most Linux distros sweep `/tmp` periodically; wipe `$TMPDIR/serum_render_*` manually if needed.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## JUCE `attempt to map invalid URI` stderr noise on plugin load (macOS)
|
|
50
|
+
|
|
51
|
+
JUCE's plugin loader (via DawDreamer) logs a non-fatal `error: attempt to map invalid URI ...` line at worker startup. The render is unaffected. serum-render does not suppress JUCE's stderr — that would risk hiding genuine plugin errors on the same stream. Filter at the shell if needed:
|
|
52
|
+
`serum-render ... 2> >(grep -v "attempt to map invalid URI" >&2)`.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Quarantined plugin bundles fail to load (macOS)
|
|
57
|
+
|
|
58
|
+
A plugin downloaded via browser/AirDrop/unzip carries `com.apple.quarantine`; Gatekeeper refuses the `dlopen` for unsigned or un-notarized bundles and DawDreamer surfaces the generic `RuntimeError: Unable to load plugin.` Vendor installers (including Serum's) don't set the xattr, so official installs are unaffected.
|
|
59
|
+
|
|
60
|
+
**Detect:** `xattr -lr /path/to/Plugin.vst3 | grep com.apple.quarantine`
|
|
61
|
+
**Fix:** `xattr -dr com.apple.quarantine /path/to/Plugin.vst3` (only for bundles from a vendor you trust), or re-run the official installer.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## arm64-only Python can't load x86_64-only plugins, and vice versa (macOS)
|
|
66
|
+
|
|
67
|
+
DawDreamer's PyPI wheel is single-arch; an arm64 Python can only `dlopen` arm64 (or universal2) plugins. Serum 1 and Serum 2 ship universal2 and are unaffected on current installs; this only bites with very old single-arch Serum builds. Check with:
|
|
68
|
+
`file "/path/to/Plugin.vst3/Contents/MacOS/Plugin"` — you want `arm64` (or both). Worst case, use a Rosetta venv (`arch -x86_64 python -m venv ...`).
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## A worker crash mid-batch aborts the remaining jobs on that executor
|
|
73
|
+
|
|
74
|
+
loky flags the entire executor broken when any worker dies unexpectedly; every remaining job in the batch is reported as an error. Re-run with `--skip-existing` — completed outputs are skipped and only the tail re-renders.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Batch renders are not bit-reproducible by default — output depends on preset order
|
|
79
|
+
|
|
80
|
+
Serum 1 and Serum 2 retain internal DSP state (LFO phase, envelope residue, lazy-loaded sample buffers) that `load_preset` / `load_state` does not fully reset, so a preset rendered mid-batch differs from the same preset rendered alone. Measured across 1491 factory presets (2026-05): 97% show audible (max_abs ≥ 0.01) warm-vs-cold variation.
|
|
81
|
+
|
|
82
|
+
serum-render addresses this with `--deterministic`, which renders every preset in a fresh single-use process — bit-identical across runs and render orders, verified against real Serum 1 + 2. See the README's reproducibility section and `docs/decisions.md` for the probe data (including why in-process resets are not enough for Serum 1).
|