logicxkit 0.2.0__tar.gz → 0.3.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.
- logicxkit-0.3.0/CHANGELOG.md +192 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/CONTRIBUTING.md +9 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/MANIFEST.in +1 -1
- {logicxkit-0.2.0/src/logicxkit.egg-info → logicxkit-0.3.0}/PKG-INFO +22 -9
- {logicxkit-0.2.0 → logicxkit-0.3.0}/README.md +21 -8
- logicxkit-0.3.0/bin/regen_data.py +252 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/docs/CAPABILITIES.md +13 -8
- {logicxkit-0.2.0 → logicxkit-0.3.0}/docs/INSTALLATION.md +5 -3
- {logicxkit-0.2.0 → logicxkit-0.3.0}/docs/README.md +1 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/docs/commands.md +26 -3
- {logicxkit-0.2.0 → logicxkit-0.3.0}/pyproject.toml +2 -2
- logicxkit-0.3.0/src/logicxkit/data/donors/147-v5.slot +0 -0
- logicxkit-0.3.0/src/logicxkit/data/donors/150-v5.slot +0 -0
- logicxkit-0.3.0/src/logicxkit/data/donors/154-v5.slot +0 -0
- logicxkit-0.3.0/src/logicxkit/data/donors/157-v5.slot +0 -0
- logicxkit-0.3.0/src/logicxkit/data/donors/158-v5.slot +0 -0
- logicxkit-0.3.0/src/logicxkit/data/donors/166-v5.slot +0 -0
- logicxkit-0.3.0/src/logicxkit/data/donors/183-v5.slot +0 -0
- logicxkit-0.3.0/src/logicxkit/data/donors/236-v5.slot +0 -0
- logicxkit-0.3.0/src/logicxkit/data/donors/287-v5.slot +0 -0
- logicxkit-0.3.0/src/logicxkit/data/donors/manifest.json +65 -0
- logicxkit-0.3.0/src/logicxkit/data/logic/arrangement-track-12.3.1.json +45 -0
- logicxkit-0.3.0/src/logicxkit/data/logic/audio-channel-12.3.1.json +5 -0
- logicxkit-0.3.0/src/logicxkit/data/logic/audio-region-12.3.1.json +16 -0
- logicxkit-0.3.0/src/logicxkit/data/logic/aux-track-12.3.1.json +5 -0
- logicxkit-0.3.0/src/logicxkit/data/logic/group-12.3.1.json +15 -0
- logicxkit-0.3.0/src/logicxkit/data/logic/inst-track-12.3.1.json +7 -0
- logicxkit-0.3.0/src/logicxkit/data/logic/midi-region-12.3.1.json +20 -0
- logicxkit-0.3.0/src/logicxkit/data/logic/rba-sequence-12.3.1.json +9 -0
- logicxkit-0.3.0/src/logicxkit/data/logic/section-text-12.3.1.json +5 -0
- logicxkit-0.3.0/src/logicxkit/data/logic/send-12.3.1.json +5 -0
- logicxkit-0.3.0/src/logicxkit/data/logic/stack-folder-12.3.1.json +21 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/_capabilities.py +76 -13
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/_chains_cmd.py +2 -2
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/_groups.py +10 -6
- logicxkit-0.3.0/src/logicxkit/logic/_midi_cmd.py +118 -0
- logicxkit-0.3.0/src/logicxkit/logic/_patch_cmd.py +83 -0
- logicxkit-0.3.0/src/logicxkit/logic/_plugins_cmd.py +54 -0
- logicxkit-0.3.0/src/logicxkit/logic/_quantize_cmd.py +88 -0
- logicxkit-0.3.0/src/logicxkit/logic/_regions_cmd.py +88 -0
- logicxkit-0.3.0/src/logicxkit/logic/_sessionplayer_cmd.py +37 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/cli.py +15 -2
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/arrangement_write.py +72 -10
- logicxkit-0.3.0/src/logicxkit/logic/services/audio_regions.py +131 -0
- logicxkit-0.3.0/src/logicxkit/logic/services/audio_write.py +284 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/chain_report.py +2 -1
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/donors.py +17 -11
- logicxkit-0.3.0/src/logicxkit/logic/services/flexmarkers.py +124 -0
- logicxkit-0.3.0/src/logicxkit/logic/services/flexmode.py +48 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/groups.py +56 -57
- logicxkit-0.3.0/src/logicxkit/logic/services/midi.py +133 -0
- logicxkit-0.3.0/src/logicxkit/logic/services/midi_write.py +238 -0
- logicxkit-0.3.0/src/logicxkit/logic/services/onsets.py +137 -0
- logicxkit-0.3.0/src/logicxkit/logic/services/patch.py +225 -0
- logicxkit-0.3.0/src/logicxkit/logic/services/plugins.py +106 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/project.py +17 -2
- logicxkit-0.3.0/src/logicxkit/logic/services/quantize_drums.py +247 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/regions.py +16 -1
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/registry.py +13 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/sends_write.py +13 -8
- logicxkit-0.3.0/src/logicxkit/logic/services/sessionplayer.py +95 -0
- logicxkit-0.3.0/src/logicxkit/logic/services/smf.py +91 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/stack_create.py +53 -15
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/tempo.py +2 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/tempo_write.py +33 -12
- logicxkit-0.3.0/src/logicxkit/utils/data.py +58 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0/src/logicxkit.egg-info}/PKG-INFO +22 -9
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit.egg-info/SOURCES.txt +64 -0
- logicxkit-0.3.0/tests/goldens/corpus.json +4 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/manifest.json +1034 -0
- logicxkit-0.3.0/tests/goldens/test_audio_regions.py +36 -0
- logicxkit-0.3.0/tests/goldens/test_audio_write.py +267 -0
- logicxkit-0.3.0/tests/goldens/test_bypass.py +25 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_groups.py +20 -0
- logicxkit-0.3.0/tests/goldens/test_midi.py +94 -0
- logicxkit-0.3.0/tests/goldens/test_midi_export.py +129 -0
- logicxkit-0.3.0/tests/goldens/test_midi_targeting.py +119 -0
- logicxkit-0.3.0/tests/goldens/test_midi_write.py +158 -0
- logicxkit-0.3.0/tests/goldens/test_patch.py +55 -0
- logicxkit-0.3.0/tests/goldens/test_plugins.py +20 -0
- logicxkit-0.3.0/tests/goldens/test_quantize_drums.py +186 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_reorder.py +11 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_routing.py +15 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_sends_level.py +13 -5
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_sends_write.py +19 -0
- logicxkit-0.3.0/tests/goldens/test_sessionplayer.py +46 -0
- logicxkit-0.3.0/tests/goldens/test_signature.py +54 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_signature_write.py +45 -2
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_song_write.py +68 -1
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_stack_create.py +41 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_transplant.py +15 -0
- logicxkit-0.3.0/tests/logic/test_audio_write.py +79 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_capabilities.py +10 -7
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_cli_paths.py +2 -1
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_donors.py +19 -0
- logicxkit-0.3.0/tests/logic/test_flexmarkers.py +100 -0
- logicxkit-0.3.0/tests/logic/test_flexmode.py +48 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_groups.py +48 -9
- logicxkit-0.3.0/tests/logic/test_midi_write.py +56 -0
- logicxkit-0.3.0/tests/logic/test_onsets.py +170 -0
- logicxkit-0.3.0/tests/logic/test_patch.py +87 -0
- logicxkit-0.3.0/tests/logic/test_patch_build.py +196 -0
- logicxkit-0.3.0/tests/logic/test_plugins.py +88 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_project.py +26 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_regions.py +32 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_sends_write.py +8 -5
- logicxkit-0.3.0/tests/logic/test_smf.py +97 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_stack_create.py +36 -0
- logicxkit-0.3.0/tests/logic/test_tempo_write.py +27 -0
- logicxkit-0.3.0/tests/test_clean_install_writers.py +59 -0
- logicxkit-0.3.0/tests/test_packaged_data.py +76 -0
- logicxkit-0.3.0/tests/test_regen_data.py +35 -0
- logicxkit-0.2.0/CHANGELOG.md +0 -102
- logicxkit-0.2.0/src/logicxkit/utils/data.py +0 -49
- logicxkit-0.2.0/tests/goldens/corpus.json +0 -2
- logicxkit-0.2.0/tests/goldens/test_signature.py +0 -32
- {logicxkit-0.2.0 → logicxkit-0.3.0}/.env.example +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/CODE_OF_CONDUCT.md +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/LICENSE +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/NOTICE +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/bin/fetch_corpus.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/config/example-chains.json +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/config/example-psts.json +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/config/example-retrack.json +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/config/example-strips.json +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/setup.cfg +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/__init__.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/au/__init__.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/au/_views.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/au/cli.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/au/services/__init__.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/au/services/aupreset.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/au/services/embed.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/au/services/ffp.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/au/services/host.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/au/services/juce.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/au/services/report.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/au/services/sonible.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/au/services/tables.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/au/services/tr5.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/au/services/waves.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/cli.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/__init__.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/_apply.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/_apply_template.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/_apply_tracks.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/_binary.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/_controlbar.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/_diagnose.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/_edit.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/_header.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/_inspect.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/_metronome.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/_modes.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/_prefs.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/_song.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/_toolbar.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/_width.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/orchestrators/__init__.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/orchestrators/apply_ops.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/orchestrators/apply_template.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/orchestrators/ops.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/__init__.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/addtrack.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/arrangement.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/binding.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/chains.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/channel_alloc.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/comp.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/controlbar.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/environment.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/eq.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/events.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/graft.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/header.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/inputs_create.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/insert.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/instout.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/integrity.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/keyflags.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/levels.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/library.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/limiter.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/manifest.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/metronome.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/modes.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/neural.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/ocr.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/pairing.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/prefs.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/prefs_table.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/projdiff.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/pst.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/recbuild.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/recdiff.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/records.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/reorder.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/retrack.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/routing.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/selection.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/sends.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/sequence.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/settings.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/signature.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/signature_write.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/slotkeys.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/slots.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/spec.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/stacks.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/stripsave.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/toolbar.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/tracklist.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/transplant.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logic/services/validate.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logicx/__init__.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/logicx/container.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/native/aulatency.swift +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/native/auprobe.swift +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/native/vision_ocr.swift +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/py.typed +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/utils/__init__.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/utils/env.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit/utils/swiftrun.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit.egg-info/dependency_links.txt +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit.egg-info/entry_points.txt +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit.egg-info/requires.txt +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/src/logicxkit.egg-info/top_level.txt +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/_data.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/_goldens.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/_liveguard.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/_paths.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/au/_juce.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/au/test_au_report.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/au/test_aupreset.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/au/test_cli_views.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/au/test_embed.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/au/test_ffp.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/au/test_host.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/au/test_juce.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/au/test_sonible.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/au/test_strip_path.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/au/test_tables.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/au/test_tr5.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/au/test_waves.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/conftest.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/__init__.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_addtrack.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_apply_template.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_binding.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_chains.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_channel_create.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_controlbar.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_environment.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_groups_leave.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_header.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_instout.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_integrity.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_levels.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_manifest.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_metronome.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_modes.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_neural.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_projdiff.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_project.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_sends.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_sequence.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_settings.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_song.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_stack_moves.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_stacks.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_toolbar.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/goldens/test_width.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/__init__.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/_fixtures.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/_invariants.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/_records.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_addtrack.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_apply_template.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_binding.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_build_cli.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_chains.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_channel_alloc.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_channel_width.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_chunks.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_controlbar.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_donor_version.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_environment.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_events.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_graft.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_header.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_insert.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_instout.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_keyflags.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_levels.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_library.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_logic.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_manifest.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_modes.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_neural.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_ocr.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_pairing.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_prefs.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_projdiff.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_pst.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_recdiff.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_registry.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_reorder.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_retrack.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_routing.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_selection.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_sends.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_sequence.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_settings.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_signature.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_slot_base.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_slotkeys.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_slots.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_song.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_song_write.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_stacks.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_strip_values.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_stripsave.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_toolbar.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_transplant.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_validate.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/logic/test_writer_gate.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/rig/test_rig_preflight.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/test_clean_clone.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/test_cli_dispatch.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/test_env.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/test_fetch_corpus.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/test_goldens_isolation.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/test_goldens_report.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/test_goldens_resolution.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/test_native_assets.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/test_no_live_library.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/test_package_layering.py +0 -0
- {logicxkit-0.2.0 → logicxkit-0.3.0}/tests/test_typed_marker.py +0 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Notable changes to logicxkit. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
|
|
4
|
+
versions follow [semantic versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
|
+
|
|
6
|
+
## 0.3.0 — 2026-09-14
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- `logic quantize-drums`: the live-drum quantize on a copy without Logic — the drum group with
|
|
11
|
+
Editing (Selection) and Quantize-Locked (Audio), other groups off, Q-Reference on the
|
|
12
|
+
reference tracks, flex Slicing, and one flex marker per hit found in the reference audio with
|
|
13
|
+
its target on the 1/N grid (`services/onsets.py`, `flexmarkers.py`, `quantize_drums.py`).
|
|
14
|
+
CONFIRMED: Logic re-saved a written take with every marker intact.
|
|
15
|
+
- `logic regions` reads a region's first frame within its file.
|
|
16
|
+
- `midi --region` and `regions --audio` place their entry correctly on a project whose regions
|
|
17
|
+
carry flex markers.
|
|
18
|
+
- `logic regions` maps an entry to its region record by ranking the counters of every audio
|
|
19
|
+
entry in every sequence, take folders included; the earlier base-offset rule mislabelled a
|
|
20
|
+
project with take folders.
|
|
21
|
+
- `logic quantize-drums` takes a reference's audio from the file named after its region,
|
|
22
|
+
handles member regions that start at different bars, and names the files it used.
|
|
23
|
+
- `logic project` names a native insert from its type id when the slot carries no name string,
|
|
24
|
+
as `plugins` does; a built patch's eight inserts read as eight.
|
|
25
|
+
- The drum-quantize onset detector is tuned against the transients Logic marked across four
|
|
26
|
+
grids: 78% of them found, 88% of its own among them (was 55% and 90%).
|
|
27
|
+
- The package carries Logic's own record templates and native plug-in donor slots, regenerated
|
|
28
|
+
from the public corpus by `bin/regen_data.py`, so `add-track`, `stack-create`, `send --add`,
|
|
29
|
+
`arrangement --add` and native `chains` work after `pip install` alone; a data root
|
|
30
|
+
(`LOGICXKIT_DATA`) still takes precedence, and `chains` reads both donor libraries.
|
|
31
|
+
- `stack-create` works on a session with no stack, patterning on Logic's own first stack;
|
|
32
|
+
`arrangement --add` makes the arrangement track when the song has none; `send --add` works on
|
|
33
|
+
a project with no send to clone.
|
|
34
|
+
- `logic midi` reads a song's MIDI regions — notes, controllers, program changes, pitch bends,
|
|
35
|
+
loop flag — and `--export` writes a format-1 Standard MIDI File.
|
|
36
|
+
- `logic plugins` names every slot's plug-in and reports which third-party components this Mac
|
|
37
|
+
lacks, for one project or a folder of them.
|
|
38
|
+
- `logic patch` reads a Library patch bundle — its nodes, each channel's settings, strip and
|
|
39
|
+
plug-ins — in both shapes Logic writes.
|
|
40
|
+
- The group-events loss reported on 2026-09-08 does not reproduce on the blank-born project;
|
|
41
|
+
`tests/goldens/test_groups.py` pins create, assign, add, assign.
|
|
42
|
+
- `logic midi --region` and `--note` write an empty MIDI region and notes on a copy; Logic re-saved
|
|
43
|
+
one with its notes intact.
|
|
44
|
+
- `logic regions` reads every MIDI and audio region with its file record (name, format, frames,
|
|
45
|
+
rate, channels, bits); `--audio` imports a PCM WAV at the project's rate as a region.
|
|
46
|
+
- `logic sessionplayer` reads a Session Player region's drummer, preset and settings (the JSON in
|
|
47
|
+
its record) and its generated notes; Complexity, Fill Amount and Swing pinned by one editor
|
|
48
|
+
move per save.
|
|
49
|
+
- `logic patch --build` writes a patch bundle from a `.cst`, into Logic's own library only with
|
|
50
|
+
`--install`; a built patch loaded from the Library with all its inserts.
|
|
51
|
+
- Nine more public goldens: a third send and the slot base it moves to 4, the Signature List's
|
|
52
|
+
meter and key creates and edits after bar 1, and a Tempo List point and its edit.
|
|
53
|
+
- Sixty-two more public goldens: MIDI region writes and their re-saves, audio region writes and
|
|
54
|
+
a re-save, three audio imports, a Session Player track and three settings, a Library-saved
|
|
55
|
+
patch and a built one loaded.
|
|
56
|
+
- `tools/driver` and `tools/stage_public.py`: the accessibility driver that records goldens from
|
|
57
|
+
Logic, with the recipe and the traps; CONTRIBUTING describes recording one.
|
|
58
|
+
- Forty-three public goldens: the arrangement track and sections, an instrument track, a track
|
|
59
|
+
header click, eight native inserts, MIDI events one field per save, meter changes, and
|
|
60
|
+
Logic's re-saves of route, transplant, bypass, send, stack-create, arrangement and reorder
|
|
61
|
+
outputs.
|
|
62
|
+
|
|
63
|
+
### Changed
|
|
64
|
+
|
|
65
|
+
- `tempo --add` and `--ramp` write each point's time word (data +8, 1/2000 s from the SMPTE
|
|
66
|
+
origin) as Logic computes it; Logic's own points and curve runs match to the digit.
|
|
67
|
+
- `route`, `transplant`, `bypass` and `reorder` are CONFIRMED: Logic re-saved one write of each
|
|
68
|
+
with the written bytes intact.
|
|
69
|
+
- `logic donors` names Logic's plug-ins that ship without factory presets (Gain, EnVerb).
|
|
70
|
+
|
|
71
|
+
### Fixed
|
|
72
|
+
|
|
73
|
+
- `logic regions --audio` numbers an import as Logic's own imports do — entry word, record header
|
|
74
|
+
slots, file ordinal and link chain, registry entry, current and selected marks — and refuses a
|
|
75
|
+
project whose audio regions are in any other layout, or a WAV whose name it already holds,
|
|
76
|
+
before anything is copied. Logic re-saved two such imports with every region record kept.
|
|
77
|
+
- `logic midi --note` goes into the region on the named track that holds its bar, never another
|
|
78
|
+
track's region at the same tick, and is refused when no region or several hold it.
|
|
79
|
+
- `logic midi --region` writes a region's length and track for a name of any length; Logic
|
|
80
|
+
re-saved regions named four and nine bytes long as written.
|
|
81
|
+
- `logic midi --export` writes the song's tempo map and time signatures, and refuses events
|
|
82
|
+
before bar 1 instead of raising.
|
|
83
|
+
- `logic quantize-drums` reads 32/64-bit float and extensible WAVs, and refuses a song whose tempo
|
|
84
|
+
changes and reference audio with no hits.
|
|
85
|
+
- `logic patch --build` refuses a file that is not a channel strip, leaves nothing behind when it
|
|
86
|
+
fails, and replaces a bundle with `--overwrite` only once the new one is complete, exiting 1
|
|
87
|
+
with the path of a replaced bundle it could not remove.
|
|
88
|
+
- A channel's group field is a bitmask, one bit per group: `logic group` reads a channel that
|
|
89
|
+
is in several groups, or in group 3 or higher, as Logic does; `--create` keeps a member's
|
|
90
|
+
other groups, `--assign` moves it out of every other one.
|
|
91
|
+
- `logic group` shows a switched-off group and `--group N --on/--off` sets it (flags bit 31).
|
|
92
|
+
- `logic regions` reads a recorded project's regions: the entry counter's base, the flex
|
|
93
|
+
marker blocks after a flexed entry, and a flexed audio region's RBA sequence is not a MIDI
|
|
94
|
+
region.
|
|
95
|
+
|
|
96
|
+
## 0.2.0 — 2026-09-12
|
|
97
|
+
|
|
98
|
+
### Fixed
|
|
99
|
+
|
|
100
|
+
- `validate_project` checks third-party plugin slots for duplicate keys and index collisions.
|
|
101
|
+
- `apply-template` places each added track after the one added just above it, so a run of new
|
|
102
|
+
tracks lands in template order in one pass.
|
|
103
|
+
- `stacks --move` and `levels --to` write through the integrity gate; a refused result is
|
|
104
|
+
discarded.
|
|
105
|
+
- A project writer whose step fails part-way discards the whole copy, including alternatives
|
|
106
|
+
it had already written.
|
|
107
|
+
- `logic project` reads a channel's inserts from its plugin-slot records at slot base 2, 3 or 4;
|
|
108
|
+
a plugin name in a property record, such as an aux's input source, is not an insert.
|
|
109
|
+
- `logic header` computes the header width from the project's own name-column width and never
|
|
110
|
+
below Logic's 180-pixel floor, and says when a width grown from one stored on that floor is
|
|
111
|
+
an estimate.
|
|
112
|
+
- `logic controlbar` keeps a project's stored button order and inserts a newly shown control
|
|
113
|
+
where Logic does.
|
|
114
|
+
- `logic stacks` reads summing stacks (a grouping header bound to an Aux with members under
|
|
115
|
+
it) as well as folder stacks; `--move` refuses a summing stack.
|
|
116
|
+
- `logic reorder` moves a stack header together with its members, and refuses to move a row into
|
|
117
|
+
or out of a summing stack.
|
|
118
|
+
- `apply-template` moves a project's slot keys from base 2 to 4 only when a channel carries a
|
|
119
|
+
send at key 2.
|
|
120
|
+
- Bar numbers given to `arrangement`, `tempo` and `signature` follow the song's time signatures.
|
|
121
|
+
- `logic arrangement` refuses a non-ASCII section name.
|
|
122
|
+
- `build` and `pst` refuse Logic's own library without `--install` whatever
|
|
123
|
+
`LOGICXKIT_AUDIO_MUSIC_APPS` is set to, and refuse under the folder it names.
|
|
124
|
+
- `build`, `verify` and `pst` refuse a preset name that is not a plain file name.
|
|
125
|
+
- `logic pst` marks a failed preset `!!` and exits 1.
|
|
126
|
+
- `apply-template` without `--out`, `--plan` or `--propose-map` exits 2 with a message.
|
|
127
|
+
- A quoted `~` expands in every path argument, including lists such as `recdiff --baseline`.
|
|
128
|
+
- A JUCE plugin state nested past the recursion limit decodes to nothing.
|
|
129
|
+
- Scanning a file for embedded plists takes one parse per plist.
|
|
130
|
+
|
|
131
|
+
### Added
|
|
132
|
+
|
|
133
|
+
- `apply-template --plan` names the session tracks the template has no counterpart for.
|
|
134
|
+
- `chains` and `levels --to` are CONFIRMED.
|
|
135
|
+
- Sends read their level (`Send.level`, `Send.level_exact`).
|
|
136
|
+
- Leaving a group is measured against Logic's own No Group save.
|
|
137
|
+
- The slot base is read from the channel records' own word when they agree, and follows the
|
|
138
|
+
number of sends (2, 3 or 4).
|
|
139
|
+
- `bin/run fetch-corpus` downloads the public golden corpus; `LOGICXKIT_REQUIRE_GOLDENS=public`
|
|
140
|
+
and `LOGICXKIT_GOLDENS=owner` steer the goldens between the two corpora.
|
|
141
|
+
- `logic image --overwrite`; without it an existing file is kept.
|
|
142
|
+
|
|
143
|
+
## 0.1.1 — 2026-09-11
|
|
144
|
+
|
|
145
|
+
### Fixed
|
|
146
|
+
|
|
147
|
+
- The headless AU host finds `auprobe.swift` inside the installed package, so `au params` and
|
|
148
|
+
state decodes use it.
|
|
149
|
+
|
|
150
|
+
### Added
|
|
151
|
+
|
|
152
|
+
- `docs/README.md` (documentation index), `docs/INSTALLATION.md` and `docs/commands.md`.
|
|
153
|
+
- `py.typed`: the package is annotated and now says so to type checkers.
|
|
154
|
+
- `aulatency.swift` is documented in `src/logicxkit/au/README.md`; it ships in the wheel and
|
|
155
|
+
is run directly with `swift`.
|
|
156
|
+
|
|
157
|
+
## 0.1.0 — 2026-09-10
|
|
158
|
+
|
|
159
|
+
First public release.
|
|
160
|
+
|
|
161
|
+
### Added
|
|
162
|
+
|
|
163
|
+
- `logicxkit logic` — Logic Pro channel-strip (`.cst`) build and decode, read-only `.logicx`
|
|
164
|
+
project analysis, and writers for the track list, stacks, groups, sends, routing, channel
|
|
165
|
+
levels and width, arrangement, tempo, time and key signatures, transport modes, metronome,
|
|
166
|
+
track headers, control bar, toolbar and Logic's own settings. Every project-mutating command
|
|
167
|
+
requires `--out` and works on a copy.
|
|
168
|
+
- `logicxkit logic apply-template` — migrate a session onto a template, pairing by Environment
|
|
169
|
+
object id within a lineage and by an explicit map across lineages.
|
|
170
|
+
- `logicxkit logic diff` — project-to-project and project-to-strip-library drift.
|
|
171
|
+
- `logicxkit logic image` / `ocr` — extract and OCR the auto-saved WindowImage via Apple Vision.
|
|
172
|
+
- `logicxkit au` — Audio Unit preset and state decode: FabFilter `.ffp` and `.aupreset`, Waves
|
|
173
|
+
XPst, TR5 chain XML, sonible protobuf field walk, and the third-party states embedded in
|
|
174
|
+
`.cst` strips and `.logicx` projects.
|
|
175
|
+
- Headless AU host (`auprobe.swift`) — loads an installed plugin's state and dumps every
|
|
176
|
+
parameter with real names and UI-formatted values.
|
|
177
|
+
- `logicxkit logic capabilities` — what each command is trusted for, generated from
|
|
178
|
+
`logic/_capabilities.py`.
|
|
179
|
+
- `logicxkit --version` (`-V`) prints the installed version.
|
|
180
|
+
- Write gate (`logic/services/integrity.py`): every project write is held against its input and
|
|
181
|
+
refused on any regression; a refused run discards the copy.
|
|
182
|
+
- A write by a command whose capability level is `CLAIMED`, `DERIVED` or `BROKEN` prints a
|
|
183
|
+
one-line notice naming that level (`LOGICXKIT_NO_NOTICE=1` silences it).
|
|
184
|
+
- `logic transplant` refuses a clone that overruns the channel's slot key range or crosses a
|
|
185
|
+
record class version; `--force` overrides.
|
|
186
|
+
- `logic build` and `logic pst` refuse to write into Logic's own library unless `--install` is
|
|
187
|
+
passed.
|
|
188
|
+
- Strip specs take `output_root`, which moves where built strips land without moving where
|
|
189
|
+
their sources are read from.
|
|
190
|
+
- CI on a macOS runner; `v*` tags build an sdist + wheel, check the wheel carries the Swift
|
|
191
|
+
helpers and no reference material, and publish to PyPI through a trusted publisher gated on
|
|
192
|
+
reviewer approval.
|
|
@@ -52,6 +52,15 @@ diff the record list against the input.
|
|
|
52
52
|
and not history.
|
|
53
53
|
- Tests travel with the change.
|
|
54
54
|
|
|
55
|
+
## Recording a golden
|
|
56
|
+
|
|
57
|
+
Every format claim rests on one deliberate change in Logic and a byte diff against the save
|
|
58
|
+
before it. `tools/driver/README.md` has the loop and the traps; `tools/driver/drive.py` performs
|
|
59
|
+
one action and one Save As per step, `tools/driver/diff.py` diffs two saves, and
|
|
60
|
+
`tools/stage_public.py` files the saves under `resources/public/` with a manifest key and the
|
|
61
|
+
facts a test may assert. Only saves of a project born in Logic on your own machine go into the
|
|
62
|
+
public corpus; a session that holds anyone's music or third-party plug-in state stays out.
|
|
63
|
+
|
|
55
64
|
## Licensing of contributions
|
|
56
65
|
|
|
57
66
|
logicxkit is Apache-2.0. Under section 5 of that licence, anything you deliberately submit for
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# `test`. conftest.py and the `_*.py` helpers it imports are left behind, so the tree cannot collect.
|
|
3
3
|
recursive-include tests *.py
|
|
4
4
|
# Named, not globbed: an sdist is built from the working tree, where untracked files can sit.
|
|
5
|
-
include bin/fetch_corpus.py tests/goldens/corpus.json tests/goldens/manifest.json
|
|
5
|
+
include bin/fetch_corpus.py bin/regen_data.py tests/goldens/corpus.json tests/goldens/manifest.json
|
|
6
6
|
# test_capabilities.py reads these two; without them the shipped suite fails rather than skips.
|
|
7
7
|
recursive-include docs *.md
|
|
8
8
|
recursive-include config *.json
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: logicxkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Logic Pro project and channel-strip tooling, and Audio Unit preset and state decoding
|
|
5
5
|
Author: Mike Farr
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -61,8 +61,8 @@ deliberate change per Logic save, then a byte diff against the save before it. T
|
|
|
61
61
|
is the tidiest example — every button id was pinned on fifty single-toggle saves (2026-09-04),
|
|
62
62
|
and a control bar written by this tool and copied whole onto another project came up in Logic
|
|
63
63
|
with that exact set. That standard is not uniform across the tool. Some commands have been
|
|
64
|
-
opened in Logic and confirmed, some are reasoned from diffs and never opened, and a
|
|
65
|
-
|
|
64
|
+
opened in Logic and confirmed, some are reasoned from diffs and never opened, and a defect
|
|
65
|
+
reproduced on a real project is recorded until a regression test closes it. **[`docs/CAPABILITIES.md`][caps]
|
|
66
66
|
carries the level and the evidence for every command, and `bin/run logic capabilities` prints
|
|
67
67
|
the same table.** Read it before you point a writer at a session you care about.
|
|
68
68
|
|
|
@@ -90,7 +90,8 @@ pip install logicxkit
|
|
|
90
90
|
logicxkit logic project ~/Music/Logic/Song.logicx # read-only, to see it working
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
-
That gets you the `logicxkit` command
|
|
93
|
+
That gets you the `logicxkit` command, and with it the record templates and native plug-in
|
|
94
|
+
donors the writers need — Logic's own, taken from its saves of a blank project. To work on it instead, clone it and let `bin/run` build
|
|
94
95
|
the venv — every command below is written that way, and `bin/run logic …` and
|
|
95
96
|
`logicxkit logic …` are the same thing:
|
|
96
97
|
|
|
@@ -112,6 +113,12 @@ bin/run logic levels "<song.logicx>" # channel fader + pan
|
|
|
112
113
|
bin/run logic stacks "<song.logicx>" [--tracks] # track stacks / arrange list
|
|
113
114
|
bin/run logic ocr "<song.logicx>" # OCR the auto-saved WindowImage
|
|
114
115
|
bin/run logic neural "<strip.cst | song.logicx>" # Neural DSP knob values
|
|
116
|
+
bin/run logic midi "<song.logicx>" [--export out.mid] # MIDI regions, or a .mid of them
|
|
117
|
+
bin/run logic plugins "<song.logicx | folder>" # plug-ins referenced, and which are missing
|
|
118
|
+
bin/run logic patch "<name.patch | folder>" # a Library patch: channels, strips, plug-ins
|
|
119
|
+
bin/run logic regions "<song.logicx>" # every MIDI and audio region, with files
|
|
120
|
+
bin/run logic sessionplayer "<song.logicx>" # Session Player drummer, preset, settings
|
|
121
|
+
bin/run logic quantize-drums "<song.logicx>" --out DIR # quantize a drum take to the grid, no Logic
|
|
115
122
|
bin/run au strip "<strip.cst | song.logicx>" # every embedded 3rd-party state
|
|
116
123
|
bin/run au preset "<preset.aupreset | .ffp>" # a preset file, named, in real units
|
|
117
124
|
bin/run logic capabilities -v # what each writer is trusted for
|
|
@@ -125,7 +132,12 @@ bin/run logic capabilities -v # what each writer is t
|
|
|
125
132
|
(extract the auto-saved WindowImage) and **`logic ocr`** (Apple-Vision OCR of it — reads the
|
|
126
133
|
mixer as Logic drew it); **`logic levels`** (fader + pan, read and copy between projects);
|
|
127
134
|
**`logic stacks`** (folder stacks and the arrange track list, and `--move` to put a track into
|
|
128
|
-
a stack);
|
|
135
|
+
a stack); **`logic midi`** (MIDI regions and their export as a `.mid`); **`logic plugins`**
|
|
136
|
+
(every referenced plug-in, and which this Mac lacks); **`logic patch`** (a Library patch
|
|
137
|
+
bundle's channels, strips and plug-ins, and `--build` to make one); **`logic regions`** (MIDI
|
|
138
|
+
and audio regions with their files, and `--audio` to import a WAV); **`logic sessionplayer`**
|
|
139
|
+
(a Session Player region's settings and generated notes); **Neural DSP state decode** (`logic
|
|
140
|
+
neural`); and the project editors — MIDI regions and notes, track
|
|
129
141
|
header, control bar, toolbar, transport modes, metronome, channel width, mixer groups,
|
|
130
142
|
arrangement sections, tempo, time signature and key, track add/rename/colour/hide/reorder,
|
|
131
143
|
sends, routing, and `apply-template` to move a session onto another project's layout. See
|
|
@@ -182,7 +194,7 @@ Three more things write outside `--out`, and one warning:
|
|
|
182
194
|
slot keys, ending at its `.cst` reference record; a clone that overruns that run deletes the
|
|
183
195
|
reference, and `validate_project` cannot see the loss. A clone across record class versions
|
|
184
196
|
cannot be legalised either. Both stop the run and say what to do instead; `--force` writes
|
|
185
|
-
anyway.
|
|
197
|
+
anyway. Any open defect is listed in
|
|
186
198
|
[`docs/CAPABILITIES.md`][caps].
|
|
187
199
|
|
|
188
200
|
## Layout
|
|
@@ -224,9 +236,10 @@ release asset and fetched by `bin/run fetch-corpus` into `resources/public/`, pi
|
|
|
224
236
|
checksum in `tests/goldens/corpus.json`. `tests/goldens/manifest.json` names each save by a
|
|
225
237
|
neutral key with the facts a test may assert. The **owner's corpus** — controlled saves cut
|
|
226
238
|
from real sessions, project templates, finished mixes, a channel-strip library snapshot — is
|
|
227
|
-
Logic-authored material containing real music and **is not here
|
|
228
|
-
|
|
229
|
-
|
|
239
|
+
Logic-authored material containing real music and **is not here**. The record templates and
|
|
240
|
+
native plug-in donors the writers need ship inside the package (`src/logicxkit/data`, Logic's
|
|
241
|
+
own from the public corpus); a data root (`LOGICXKIT_DATA`) adds third-party donors and AU
|
|
242
|
+
parameter tables, which are not ours to publish.
|
|
230
243
|
|
|
231
244
|
The consequence for a fresh clone: **`bin/run pytest` runs green, but every golden skips until
|
|
232
245
|
`bin/run fetch-corpus` has run**, and the keys only the owner's corpus has skip regardless. The
|
|
@@ -31,8 +31,8 @@ deliberate change per Logic save, then a byte diff against the save before it. T
|
|
|
31
31
|
is the tidiest example — every button id was pinned on fifty single-toggle saves (2026-09-04),
|
|
32
32
|
and a control bar written by this tool and copied whole onto another project came up in Logic
|
|
33
33
|
with that exact set. That standard is not uniform across the tool. Some commands have been
|
|
34
|
-
opened in Logic and confirmed, some are reasoned from diffs and never opened, and a
|
|
35
|
-
|
|
34
|
+
opened in Logic and confirmed, some are reasoned from diffs and never opened, and a defect
|
|
35
|
+
reproduced on a real project is recorded until a regression test closes it. **[`docs/CAPABILITIES.md`][caps]
|
|
36
36
|
carries the level and the evidence for every command, and `bin/run logic capabilities` prints
|
|
37
37
|
the same table.** Read it before you point a writer at a session you care about.
|
|
38
38
|
|
|
@@ -60,7 +60,8 @@ pip install logicxkit
|
|
|
60
60
|
logicxkit logic project ~/Music/Logic/Song.logicx # read-only, to see it working
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
That gets you the `logicxkit` command
|
|
63
|
+
That gets you the `logicxkit` command, and with it the record templates and native plug-in
|
|
64
|
+
donors the writers need — Logic's own, taken from its saves of a blank project. To work on it instead, clone it and let `bin/run` build
|
|
64
65
|
the venv — every command below is written that way, and `bin/run logic …` and
|
|
65
66
|
`logicxkit logic …` are the same thing:
|
|
66
67
|
|
|
@@ -82,6 +83,12 @@ bin/run logic levels "<song.logicx>" # channel fader + pan
|
|
|
82
83
|
bin/run logic stacks "<song.logicx>" [--tracks] # track stacks / arrange list
|
|
83
84
|
bin/run logic ocr "<song.logicx>" # OCR the auto-saved WindowImage
|
|
84
85
|
bin/run logic neural "<strip.cst | song.logicx>" # Neural DSP knob values
|
|
86
|
+
bin/run logic midi "<song.logicx>" [--export out.mid] # MIDI regions, or a .mid of them
|
|
87
|
+
bin/run logic plugins "<song.logicx | folder>" # plug-ins referenced, and which are missing
|
|
88
|
+
bin/run logic patch "<name.patch | folder>" # a Library patch: channels, strips, plug-ins
|
|
89
|
+
bin/run logic regions "<song.logicx>" # every MIDI and audio region, with files
|
|
90
|
+
bin/run logic sessionplayer "<song.logicx>" # Session Player drummer, preset, settings
|
|
91
|
+
bin/run logic quantize-drums "<song.logicx>" --out DIR # quantize a drum take to the grid, no Logic
|
|
85
92
|
bin/run au strip "<strip.cst | song.logicx>" # every embedded 3rd-party state
|
|
86
93
|
bin/run au preset "<preset.aupreset | .ffp>" # a preset file, named, in real units
|
|
87
94
|
bin/run logic capabilities -v # what each writer is trusted for
|
|
@@ -95,7 +102,12 @@ bin/run logic capabilities -v # what each writer is t
|
|
|
95
102
|
(extract the auto-saved WindowImage) and **`logic ocr`** (Apple-Vision OCR of it — reads the
|
|
96
103
|
mixer as Logic drew it); **`logic levels`** (fader + pan, read and copy between projects);
|
|
97
104
|
**`logic stacks`** (folder stacks and the arrange track list, and `--move` to put a track into
|
|
98
|
-
a stack);
|
|
105
|
+
a stack); **`logic midi`** (MIDI regions and their export as a `.mid`); **`logic plugins`**
|
|
106
|
+
(every referenced plug-in, and which this Mac lacks); **`logic patch`** (a Library patch
|
|
107
|
+
bundle's channels, strips and plug-ins, and `--build` to make one); **`logic regions`** (MIDI
|
|
108
|
+
and audio regions with their files, and `--audio` to import a WAV); **`logic sessionplayer`**
|
|
109
|
+
(a Session Player region's settings and generated notes); **Neural DSP state decode** (`logic
|
|
110
|
+
neural`); and the project editors — MIDI regions and notes, track
|
|
99
111
|
header, control bar, toolbar, transport modes, metronome, channel width, mixer groups,
|
|
100
112
|
arrangement sections, tempo, time signature and key, track add/rename/colour/hide/reorder,
|
|
101
113
|
sends, routing, and `apply-template` to move a session onto another project's layout. See
|
|
@@ -152,7 +164,7 @@ Three more things write outside `--out`, and one warning:
|
|
|
152
164
|
slot keys, ending at its `.cst` reference record; a clone that overruns that run deletes the
|
|
153
165
|
reference, and `validate_project` cannot see the loss. A clone across record class versions
|
|
154
166
|
cannot be legalised either. Both stop the run and say what to do instead; `--force` writes
|
|
155
|
-
anyway.
|
|
167
|
+
anyway. Any open defect is listed in
|
|
156
168
|
[`docs/CAPABILITIES.md`][caps].
|
|
157
169
|
|
|
158
170
|
## Layout
|
|
@@ -194,9 +206,10 @@ release asset and fetched by `bin/run fetch-corpus` into `resources/public/`, pi
|
|
|
194
206
|
checksum in `tests/goldens/corpus.json`. `tests/goldens/manifest.json` names each save by a
|
|
195
207
|
neutral key with the facts a test may assert. The **owner's corpus** — controlled saves cut
|
|
196
208
|
from real sessions, project templates, finished mixes, a channel-strip library snapshot — is
|
|
197
|
-
Logic-authored material containing real music and **is not here
|
|
198
|
-
|
|
199
|
-
|
|
209
|
+
Logic-authored material containing real music and **is not here**. The record templates and
|
|
210
|
+
native plug-in donors the writers need ship inside the package (`src/logicxkit/data`, Logic's
|
|
211
|
+
own from the public corpus); a data root (`LOGICXKIT_DATA`) adds third-party donors and AU
|
|
212
|
+
parameter tables, which are not ours to publish.
|
|
200
213
|
|
|
201
214
|
The consequence for a fresh clone: **`bin/run pytest` runs green, but every golden skips until
|
|
202
215
|
`bin/run fetch-corpus` has run**, and the keys only the owner's corpus has skip regardless. The
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"""Regenerate src/logicxkit/data/logic from the public golden corpus (`bin/run fetch-corpus`).
|
|
2
|
+
|
|
3
|
+
bin/run python bin/regen_data.py
|
|
4
|
+
|
|
5
|
+
Every template is a record Logic wrote in a public save of a blank project, so the package
|
|
6
|
+
ships Logic's own bytes and nothing vendor-authored.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import struct
|
|
12
|
+
import sys
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
16
|
+
sys.path.insert(0, str(ROOT / "tests"))
|
|
17
|
+
import _goldens # noqa: E402
|
|
18
|
+
from logicxkit.logic.services.binding import channels # noqa: E402
|
|
19
|
+
from logicxkit.logic.services.channel_alloc import mixer_record # noqa: E402
|
|
20
|
+
from logicxkit.logic.services.environment import ENV_TAG, object_id_of # noqa: E402
|
|
21
|
+
from logicxkit.logic.services.groups import ( # noqa: E402
|
|
22
|
+
EVENT,
|
|
23
|
+
EVENT_FADER_AT,
|
|
24
|
+
EVENT_OBJECT_AT,
|
|
25
|
+
EVENT_VALUE_AT,
|
|
26
|
+
EVENT_VALUE_HI_AT,
|
|
27
|
+
EVENT_VALUE_LO_AT,
|
|
28
|
+
FADER_IDS,
|
|
29
|
+
TAIL,
|
|
30
|
+
read_groups,
|
|
31
|
+
)
|
|
32
|
+
from logicxkit.logic.services.insert import HEADER, project_records # noqa: E402
|
|
33
|
+
from logicxkit.logic.services.recbuild import slot_of # noqa: E402
|
|
34
|
+
from logicxkit.logic.services.recdiff import load_project_data # noqa: E402
|
|
35
|
+
from logicxkit.logic.services.sends import is_send # noqa: E402
|
|
36
|
+
from logicxkit.logic.services.stacks import read_stacks # noqa: E402
|
|
37
|
+
from logicxkit.logic.services.tracklist import arrange_run, flat_run, row_object # noqa: E402
|
|
38
|
+
|
|
39
|
+
TEXT_HEAD = 98 # a qSxT's fixed head; the section name follows
|
|
40
|
+
SLOT_KEYS = range(2, 20) # a channel's plug-in and property records
|
|
41
|
+
|
|
42
|
+
OUT = ROOT / "src" / "logicxkit" / "data"
|
|
43
|
+
SOURCE = ("Logic Pro 12.3.1 on a blank project: {what} (public corpus {a} -> {b}; "
|
|
44
|
+
"regenerated by bin/regen_data.py)")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _load(key: str) -> bytes:
|
|
48
|
+
p = _goldens.path(key)
|
|
49
|
+
if p is None:
|
|
50
|
+
raise SystemExit(f"golden {key!r} is not on this machine — run bin/run fetch-corpus")
|
|
51
|
+
return load_project_data(p)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _new(pair: tuple[str, str]) -> list:
|
|
55
|
+
"""Records of the second save whose (tag, payload) the first lacks."""
|
|
56
|
+
a, b = (project_records(_load(k)) for k in pair)
|
|
57
|
+
seen = {(r.tag, r.raw[HEADER:]) for r in a}
|
|
58
|
+
return [r for r in b if (r.tag, r.raw[HEADER:]) not in seen]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _split(raw: bytes) -> dict:
|
|
62
|
+
return {"header": raw[:HEADER].hex(), "payload": raw[HEADER:].hex()}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _source(pair: tuple[str, str], what: str) -> str:
|
|
66
|
+
return SOURCE.format(what=what, a=pair[0], b=pair[1])
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def channel(pair: tuple[str, str], label: str) -> dict:
|
|
70
|
+
"""The mixer record of the strip labelled ``label`` in the second save."""
|
|
71
|
+
data = _load(pair[1])
|
|
72
|
+
owner = next((o for o, c in channels(data).items() if c.label == label), None)
|
|
73
|
+
if owner is None:
|
|
74
|
+
raise SystemExit(f"{pair[1]}: no strip labelled {label!r}")
|
|
75
|
+
return {"source": _source(pair, f"the {label} strip"), **_split(mixer_record(project_records(data), owner))}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def send(pair: tuple[str, str]) -> dict:
|
|
79
|
+
"""The one send the second save added."""
|
|
80
|
+
added = [r for r in _new(pair) if is_send(r)]
|
|
81
|
+
if len(added) != 1:
|
|
82
|
+
raise SystemExit(f"{pair}: {len(added)} new send record(s), wanted one")
|
|
83
|
+
return {"source": _source(pair, "the send the second save added"), **_split(added[0].raw)}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _bare_event(event: bytes, fader: str) -> str:
|
|
87
|
+
e = bytearray(event)
|
|
88
|
+
struct.pack_into("<I", e, EVENT_OBJECT_AT, 0)
|
|
89
|
+
struct.pack_into("<I", e, EVENT_FADER_AT, FADER_IDS[fader])
|
|
90
|
+
struct.pack_into("<I", e, EVENT_VALUE_AT, 0)
|
|
91
|
+
struct.pack_into("<H", e, EVENT_VALUE_HI_AT, 0)
|
|
92
|
+
struct.pack_into("<H", e, EVENT_VALUE_LO_AT, 0)
|
|
93
|
+
return bytes(e).hex()
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def group(pair: tuple[str, str], name: str) -> dict:
|
|
97
|
+
"""The group triple named ``name``: the `qeSM`, its `karT` marker and its `qSvE` events."""
|
|
98
|
+
records = project_records(_load(pair[1]))
|
|
99
|
+
g = next((g for g in read_groups(_load(pair[1])) if g.name == name), None)
|
|
100
|
+
if g is None:
|
|
101
|
+
raise SystemExit(f"{pair[1]}: no group named {name!r}")
|
|
102
|
+
qesm, marker, qsve = (records[g.start + k].raw for k in range(3))
|
|
103
|
+
events = qsve[HEADER:-TAIL]
|
|
104
|
+
if not events or len(events) % EVENT:
|
|
105
|
+
raise SystemExit(f"{pair[1]}: group {name!r} has no member events to pattern on")
|
|
106
|
+
return {"source": _source(pair, f"the group {name!r}"),
|
|
107
|
+
"qesm_header": qesm[:HEADER].hex(), "qesm_payload": qesm[HEADER:].hex(),
|
|
108
|
+
"marker_header": marker[:HEADER].hex(), "qsve_header": qsve[:HEADER].hex(),
|
|
109
|
+
"qsve_tail": qsve[-TAIL:].hex(),
|
|
110
|
+
"member_events": {f: _bare_event(events[:EVENT], f) for f in FADER_IDS},
|
|
111
|
+
"notes": "member_events: one event per linked fader, from the group's first event "
|
|
112
|
+
"with the object id (+4), fader id (+12) and value (+8, +20, +30) reset; "
|
|
113
|
+
"the writer stamps all three"}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def stack_folder(pair: tuple[str, str], name: str) -> dict:
|
|
117
|
+
"""The folder stack ``name``: its Sub strip, header object, arrange row and flat row."""
|
|
118
|
+
data = _load(pair[1])
|
|
119
|
+
records = project_records(data)
|
|
120
|
+
stack = next((s for s in read_stacks(data, None) if s.name == name), None)
|
|
121
|
+
if stack is None:
|
|
122
|
+
raise SystemExit(f"{pair[1]}: no stack named {name!r}")
|
|
123
|
+
obj = next(r.raw for r in records if r.tag == ENV_TAG and object_id_of(r) == stack.object_id)
|
|
124
|
+
run = arrange_run(records)
|
|
125
|
+
row = next(records[i].raw for i in run if row_object(records[i].raw) == stack.object_id)
|
|
126
|
+
flat = next(records[i].raw for i in flat_run(records, run)
|
|
127
|
+
if row_object(records[i].raw) == stack.object_id)
|
|
128
|
+
roles = {"strip": mixer_record(records, stack.owner), "object": obj, "row": row, "flat_row": flat}
|
|
129
|
+
return {"source": _source(pair, f"the folder stack {name!r} Logic created over three tracks"),
|
|
130
|
+
"records": {role: _split(raw) for role, raw in roles.items()}}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def inst_track(pair: tuple[str, str], label: str) -> dict:
|
|
134
|
+
"""The instrument slot and property records Logic gave the strip labelled ``label``."""
|
|
135
|
+
data = _load(pair[1])
|
|
136
|
+
owner = next((o for o, c in channels(data).items() if c.label == label), None)
|
|
137
|
+
if owner is None:
|
|
138
|
+
raise SystemExit(f"{pair[1]}: no strip labelled {label!r}")
|
|
139
|
+
recs = [r for r in project_records(data) if r.tag == b"UCuA" and r.owner == owner and r.key in SLOT_KEYS]
|
|
140
|
+
return {"_": _source(pair, f"the {label} strip's slot records; owner re-stamped on use"),
|
|
141
|
+
"records": {str(r.key): r.raw.hex() for r in sorted(recs, key=lambda r: r.key)}}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def section_text(pair: tuple[str, str], name: str) -> dict:
|
|
145
|
+
"""The name record of the section called ``name``, less the name itself."""
|
|
146
|
+
text = name.encode("ascii") + b"\0"
|
|
147
|
+
text += b"\0" * (len(text) % 2)
|
|
148
|
+
hit = next((r for r in _new(pair) if r.tag == b"qSxT" and r.raw[HEADER + TEXT_HEAD:] == text), None)
|
|
149
|
+
if hit is None:
|
|
150
|
+
raise SystemExit(f"{pair}: no new section text record named {name!r}")
|
|
151
|
+
return {"source": _source(pair, f"the section {name!r}'s name record"),
|
|
152
|
+
"header": hit.raw[:HEADER].hex(), "head": hit.raw[HEADER:HEADER + TEXT_HEAD].hex()}
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def arrangement_track(pair: tuple[str, str]) -> dict:
|
|
156
|
+
"""Everything Logic added for the arrangement track and its first section: the section
|
|
157
|
+
and track sequence triples with the `snrT` after the last `tSxT`, the `OgnS` before the
|
|
158
|
+
first `rpyH`, the last `MneG` and the empty text record."""
|
|
159
|
+
records = project_records(_load(pair[1]))
|
|
160
|
+
anchor = max(i for i, r in enumerate(records) if r.tag == b"tSxT")
|
|
161
|
+
run = records[anchor + 1:anchor + 8]
|
|
162
|
+
if [r.tag for r in run] != [b"qeSM", b"karT", b"qSvE"] * 2 + [b"snrT"]:
|
|
163
|
+
raise SystemExit(f"{pair[1]}: no two sequence triples and snrT after the last tSxT")
|
|
164
|
+
first_rpyh = next(i for i, r in enumerate(records) if r.tag == b"rpyH")
|
|
165
|
+
ogns = max(i for i, r in enumerate(records[:first_rpyh]) if r.tag == b"OgnS")
|
|
166
|
+
mneg = max(i for i, r in enumerate(records) if r.tag == b"MneG")
|
|
167
|
+
empty = next(r for r in records if r.tag == b"qSxT" and slot_of(r.raw) == 0 and r.raw[HEADER + TEXT_HEAD:] == b"\0")
|
|
168
|
+
roles = dict(zip(("section_qesm", "section_marker", "section_qsve", "track_qesm", "track_marker",
|
|
169
|
+
"track_qsve", "snrt"), (r.raw for r in run)))
|
|
170
|
+
roles |= {"ogns": records[ogns].raw, "mneg": records[mneg].raw, "text_empty": empty.raw}
|
|
171
|
+
return {"source": _source(pair, "the arrangement track shown and its first section added"),
|
|
172
|
+
"records": {role: _split(raw) for role, raw in roles.items()}}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def midi_region(pair: tuple[str, str], track_label: str) -> dict:
|
|
176
|
+
"""The region Logic made on the strip labelled ``track_label``: its sequence triple and the
|
|
177
|
+
song container's 80-byte entry for it."""
|
|
178
|
+
from logicxkit.logic.services.regions import ENTRY, TAIL, placements, song_container
|
|
179
|
+
from logicxkit.logic.services.sequence import sequences, triple_by_slot
|
|
180
|
+
data = _load(pair[1])
|
|
181
|
+
records = project_records(data)
|
|
182
|
+
run = arrange_run(records)
|
|
183
|
+
song = song_container(records, run)
|
|
184
|
+
payload = records[song.end].raw[HEADER:]
|
|
185
|
+
labels = {}
|
|
186
|
+
from logicxkit.logic.services.stacks import read_tracks
|
|
187
|
+
names = {r["object_id"]: r["name"] for r in read_tracks(data, None)}
|
|
188
|
+
entries = [(off, oid) for off, oid, _row in placements(records) if names.get(oid) == track_label]
|
|
189
|
+
if len(entries) != 1:
|
|
190
|
+
raise SystemExit(f"{pair[1]}: {len(entries)} region(s) on {track_label!r}, wanted one")
|
|
191
|
+
off, _oid = entries[0]
|
|
192
|
+
entry = payload[off:off + ENTRY]
|
|
193
|
+
slot = struct.unpack_from("<I", entry, 32)[0]
|
|
194
|
+
t = triple_by_slot(sequences(records), slot)
|
|
195
|
+
roles = {"qesm": records[t.start].raw, "marker": records[t.start + 1].raw, "qsve": records[t.end].raw, "entry": entry}
|
|
196
|
+
return {"source": _source(pair, f"the empty MIDI region on {track_label!r} (Pencil tool click)"),
|
|
197
|
+
"records": {role: _split(raw) if role != "entry" else {"payload": raw.hex()} for role, raw in roles.items()}}
|
|
198
|
+
|
|
199
|
+
def audio_region(pair: tuple[str, str]) -> dict:
|
|
200
|
+
"""The audio file record, region record and song-container entry Logic added for one
|
|
201
|
+
imported WAV."""
|
|
202
|
+
from logicxkit.logic.services.regions import ENTRY, TAIL, song_container
|
|
203
|
+
new = _new(pair)
|
|
204
|
+
lfua = [r for r in new if r.tag == b"lFuA"]
|
|
205
|
+
grua = [r for r in new if r.tag == b"gRuA"]
|
|
206
|
+
if len(lfua) != 1 or len(grua) != 1:
|
|
207
|
+
raise SystemExit(f"{pair}: {len(lfua)} file and {len(grua)} region record(s), wanted one each")
|
|
208
|
+
records = project_records(_load(pair[1]))
|
|
209
|
+
song = song_container(records, arrange_run(records))
|
|
210
|
+
payload = records[song.end].raw[HEADER:]
|
|
211
|
+
entries = [payload[i:i + ENTRY] for i in range(0, len(payload) - TAIL, ENTRY)]
|
|
212
|
+
audio = [e for e in entries if struct.unpack_from("<H", e, 0)[0] == 0x24]
|
|
213
|
+
if len(audio) != 1:
|
|
214
|
+
raise SystemExit(f"{pair[1]}: {len(audio)} audio entries, wanted one")
|
|
215
|
+
return {"source": _source(pair, "one WAV imported with File > Import > Audio File"),
|
|
216
|
+
"records": {"lfua": _split(lfua[0].raw), "grua": _split(grua[0].raw), "entry": {"payload": audio[0].hex()}}}
|
|
217
|
+
|
|
218
|
+
# name -> (golden keys the extraction reads, how to make it)
|
|
219
|
+
TEMPLATES = {
|
|
220
|
+
"audio-channel-12.3.1.json": (("tracks-two-audio-logic",),
|
|
221
|
+
lambda: channel(("blank-base", "tracks-two-audio-logic"), "Audio 2")),
|
|
222
|
+
"aux-track-12.3.1.json": (("stack-summing-logic",),
|
|
223
|
+
lambda: channel(("stack-folder-flattened-logic", "stack-summing-logic"), "Aux 1")),
|
|
224
|
+
"group-12.3.1.json": (("group-drums-logic",),
|
|
225
|
+
lambda: group(("stack-folder-flattened-logic", "group-drums-logic"), "Drums")),
|
|
226
|
+
"send-12.3.1.json": (("levels-resave-logic", "send-bus-1-logic"),
|
|
227
|
+
lambda: send(("levels-resave-logic", "send-bus-1-logic"))),
|
|
228
|
+
"stack-folder-12.3.1.json": (("stack-folder-logic",),
|
|
229
|
+
lambda: stack_folder(("tracks-three-audio-logic", "stack-folder-logic"), "Sub 1")),
|
|
230
|
+
"inst-track-12.3.1.json": (("tracks-instrument-logic",),
|
|
231
|
+
lambda: inst_track(("arrangement-section-1-selected-logic", "tracks-instrument-logic"), "Inst 1")),
|
|
232
|
+
"section-text-12.3.1.json": (("arrangement-track-logic", "arrangement-first-section-logic"),
|
|
233
|
+
lambda: section_text(("arrangement-track-logic", "arrangement-first-section-logic"), "Intro")),
|
|
234
|
+
"arrangement-track-12.3.1.json": (("arrangement-first-section-logic",),
|
|
235
|
+
lambda: arrangement_track(("stack-folder-flattened-logic", "arrangement-first-section-logic"))),
|
|
236
|
+
"midi-region-12.3.1.json": (("midi-empty-region-logic",),
|
|
237
|
+
lambda: midi_region(("inserts-native-all-logic", "midi-empty-region-logic"), "Inst 1")),
|
|
238
|
+
"audio-region-12.3.1.json": (("midi-write-resave-logic", "audio-one-region-logic"),
|
|
239
|
+
lambda: audio_region(("midi-write-resave-logic", "audio-one-region-logic"))),
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def main() -> int:
|
|
244
|
+
(OUT / "logic").mkdir(parents=True, exist_ok=True)
|
|
245
|
+
for name, (_keys, make) in TEMPLATES.items():
|
|
246
|
+
(OUT / "logic" / name).write_text(json.dumps(make(), indent=1) + "\n")
|
|
247
|
+
print("wrote", name)
|
|
248
|
+
return 0
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
if __name__ == "__main__":
|
|
252
|
+
sys.exit(main())
|