danceflow-stemlab 1.3.4__tar.gz → 1.4.1__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.
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/PKG-INFO +66 -1
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/README.md +63 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/mcp-registry/server.json +2 -2
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/plugins/stemlab/.codex-plugin/plugin.json +1 -1
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/plugins/stemlab/plugin.json +1 -1
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/plugins/stemlab/skills/stemlab/SKILL.md +8 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/pyproject.toml +2 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/danceflow_stemlab.egg-info/PKG-INFO +66 -1
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/danceflow_stemlab.egg-info/SOURCES.txt +6 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/danceflow_stemlab.egg-info/requires.txt +3 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/__init__.py +1 -1
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/loops.py +57 -10
- danceflow_stemlab-1.4.1/src/stemlab/analysis/phrase_loops.py +306 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/registry.py +10 -0
- danceflow_stemlab-1.4.1/src/stemlab/analysis/rudiment_matches.py +461 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/runner.py +16 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/cli.py +75 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/pipeline.py +3 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/types.py +4 -0
- danceflow_stemlab-1.4.1/tests/test_arcadians_rudiments.py +39 -0
- danceflow_stemlab-1.4.1/tests/test_bar_phase.py +74 -0
- danceflow_stemlab-1.4.1/tests/test_phrase_loops.py +183 -0
- danceflow_stemlab-1.4.1/tests/test_rudiment_matches.py +123 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/.agents/plugins/marketplace.json +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/.github/workflows/codex-plugin.yml +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/.github/workflows/release.yml +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/AGENTS.md +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/CONTRIBUTING.md +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/LICENSE +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/MANIFEST.in +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/docs/branding/README.md +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/docs/branding/logo-monochrome.svg +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/docs/branding/logo.png +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/docs/branding/logo.svg +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/docs/codex-plugin.md +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/docs/codex-publishing.md +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/docs/cue-alignment.md +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/docs/evidence-models.md +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/docs/midi.md +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/plugins/stemlab/.mcp.json +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/plugins/stemlab/README.md +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/plugins/stemlab/branding/logo-monochrome.svg +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/plugins/stemlab/branding/logo.png +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/plugins/stemlab/branding/logo.svg +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/plugins/stemlab/mcp.json +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/plugins/stemlab/skills/stemlab/references/tools.md +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/scripts/benchmark_evidence_corpus.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/scripts/build_codex_plugin.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/scripts/capture_loop_workflow.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/scripts/check_codex_timeline.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/scripts/loop_demo.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/scripts/prepare_codex_marketplace.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/scripts/publish_mcp_registry.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/scripts/smoke_midi.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/setup.cfg +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/danceflow_stemlab.egg-info/dependency_links.txt +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/danceflow_stemlab.egg-info/entry_points.txt +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/danceflow_stemlab.egg-info/top_level.txt +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/__main__.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/__init__.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/evidence/__init__.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/evidence/adapters.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/evidence/registry.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/evidence/runner.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/evidence/worker.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/harmony.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/lyrics.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/midi/__init__.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/midi/adapters.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/midi/artifacts.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/midi/registry.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/midi/runner.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/midi/worker.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/rhythm.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/semantic_audio.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/semantic_text.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/song_map.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/sonic.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/structure.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/tempo_regimes.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/analysis/transcription.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/audio.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/beats/__init__.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/beats/base.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/beats/beatnet.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/beats/beatthis.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/beats/beattransformer.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/beats/common.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/bootstrap.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/branding.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/canonical.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/codex/__init__.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/codex/__main__.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/codex/bridge.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/codex/jobs.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/codex/paths.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/codex/server.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/codex/tasks.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/codex/timeline.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/codex/worker.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/cue_cli.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/cues.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/lyric_lrc.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/manifest.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/models.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/separators/__init__.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/separators/base.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/separators/bsroformer.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/separators/common.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/separators/demucs.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/separators/openunmix.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/separators/scnet.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/server.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/sonic_visualiser.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/spectrogram.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/spectrogram_repair.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/speech.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/util.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/vamp.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/vamp_runtime.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/web/app.js +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/web/arcadians-cover.jpg +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/web/arcadians-reference.json +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/web/index.html +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/web/style.css +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/web/timeline.css +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/webui.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/stemlab/word_alignment.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_analysis_registry.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_arcadians_fixture.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_audio_normalization.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_beat_consensus.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_beat_this_bootstrap.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_beatnet_bootstrap.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_beatnet_compat.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_branding.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_bsroformer_adapter.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_canonical.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_canonical_extended.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_codex_bridge.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_codex_jobs.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_codex_package.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_codex_protocol.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_codex_release.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_codex_timeline.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_cue_integration.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_cues.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_deep_harmony.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_deep_lyrics.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_deep_sonic_rhythm.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_distribution_identity.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_docker_contract.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_evidence_models.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_loop_integration.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_loops.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_lyric_lrc.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_manifest.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_midi_adapters.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_midi_extraction.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_midi_integration.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_midi_registry.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_models.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_repository_hygiene.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_server.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_song_map.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_sonic_visualiser.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_sonic_visualiser_workspace.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_spectrogram.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_speech_repetition.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_vamp.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_vamp_runtime.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_web_frontend.py +0 -0
- {danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/tests/test_word_alignment.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: danceflow-stemlab
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.1
|
|
4
4
|
Summary: Local music analysis for stems, timing, beats, harmony, lyrics, MIDI and loopable sections for Shorts. https://kieransimkin.co.uk/
|
|
5
5
|
Author: Kieran Simkin
|
|
6
6
|
Maintainer: Kieran Simkin
|
|
@@ -38,6 +38,8 @@ Requires-Dist: platformdirs>=4.2
|
|
|
38
38
|
Requires-Dist: librosa>=0.11
|
|
39
39
|
Requires-Dist: pyloudnorm>=0.2
|
|
40
40
|
Requires-Dist: cmudict>=1.1.3
|
|
41
|
+
Provides-Extra: rudiments
|
|
42
|
+
Requires-Dist: dancerudiments<1,>=0.2.4; extra == "rudiments"
|
|
41
43
|
Provides-Extra: codex
|
|
42
44
|
Requires-Dist: mcp<3,>=2.2; extra == "codex"
|
|
43
45
|
Requires-Dist: fastapi<1,>=0.115; extra == "codex"
|
|
@@ -255,6 +257,55 @@ The derived artifacts live under `deep/` (`sonic/`, `rhythm/`, `harmony/`, `stru
|
|
|
255
257
|
`lyrics/`, `semantic_text/`, optional `semantic_audio/` and `basic_pitch/`, plus
|
|
256
258
|
`song_map/song_map.json` and `summary.json`).
|
|
257
259
|
|
|
260
|
+
## Find DanceRudiments matching candidates from each stem
|
|
261
|
+
|
|
262
|
+
Install the separately maintained **optional** native package with
|
|
263
|
+
`python -m pip install -e ".[rudiments]"`. StemLab calls
|
|
264
|
+
[`dancerudiments.catalogue()` and `dancerudiments.sample()`](https://github.com/kieransimkin/DanceRudiments)
|
|
265
|
+
directly—no cloned movement functions or bundled catalogue. It extracts per-stem
|
|
266
|
+
RMS attack peaks, maps them onto detected beats, and ranks 16-beat windows
|
|
267
|
+
against position/speed/acceleration salience of actual native motion samples.
|
|
268
|
+
Scores are **comparative similarities**, not probabilities or guarantees of a good motion.
|
|
269
|
+
|
|
270
|
+
```sh
|
|
271
|
+
# Match the mix plus every saved stem without running separation again.
|
|
272
|
+
stemlab rudiments ./analysis-master --top 20
|
|
273
|
+
# Limit an existing analysis to two stems, or choose one of six official collections.
|
|
274
|
+
stemlab rudiments ./analysis-master --stem drums --stem bass --collection club
|
|
275
|
+
# Include matching as an opt-in part of a new full analysis.
|
|
276
|
+
stemlab analyze master.wav --output ./analysis-master --rudiments
|
|
277
|
+
# The bundled Arcadians MP3 has a documented BPM but no pre-separated stems.
|
|
278
|
+
stemlab rudiments 'examples/arcadians/Arcadians - 320kbps.mp3' \
|
|
279
|
+
--bpm 145 --collection initial --top 28 --output ./arcadians-rudiments
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Measured Arcadians benchmark
|
|
283
|
+
|
|
284
|
+
The mix-only offline benchmark found **1,821** energy-rise peaks and **15 candidate
|
|
285
|
+
patterns at ≥0.68** similarity among the authentic 28-pattern DanceRudiments
|
|
286
|
+
initial collection, led by `groove_b_played` (0.739), `groove_b_grid` (0.737),
|
|
287
|
+
and `lfo_morph` (0.724). The benchmark read DanceRudiments' officially compiled
|
|
288
|
+
sample tables through an **offline adapter**; the native C++-extension test is
|
|
289
|
+
separate and skips when that optional package is not installed. The BPM grid
|
|
290
|
+
starts at zero with **unverified beat phase**. No stem separation was claimed.
|
|
291
|
+
|
|
292
|
+
These are **actual Chromium screenshots** of the measured local HTML report,
|
|
293
|
+
not schematic illustrations or captures of the main React timeline.
|
|
294
|
+
|
|
295
|
+

|
|
296
|
+
|
|
297
|
+

|
|
298
|
+
|
|
299
|
+
The analysis writes `deep/rudiments/report.json`, `index.html`, and `overview.png`.
|
|
300
|
+
See **[method, score caveats, provenance, examples and screenshot reproduction](docs/rudiments.md)**.
|
|
301
|
+
|
|
302
|
+
**Native CI coverage:** the main GitHub Actions `CI` workflow now has a dedicated
|
|
303
|
+
Python 3.10/3.11 job that installs the official DanceRudiments *binary wheel*,
|
|
304
|
+
requires the actual C++ module, and runs the Arcadians test with skipping disabled.
|
|
305
|
+
Fewer than **12 real-audio matches** fails CI; passing runs publish the JSON,
|
|
306
|
+
HTML and measured overlay PNG as downloadable CI artifacts. See the
|
|
307
|
+
[native CI validation instructions](docs/rudiments.md#native-github-actions-gate).
|
|
308
|
+
|
|
258
309
|
## Timeline analysis gallery
|
|
259
310
|
|
|
260
311
|
The browser workspace is intended to make the analysis output inspectable, not just downloadable. The illustrations below explain the main analysis families using the same shared timeline vocabulary as the frontend: one clock, one playhead, zoomable lanes, and consistent section alignment across spectrogram, beat, lyric and deep-analysis layers.
|
|
@@ -368,6 +419,16 @@ exported WAVs and writes no files. **Zoom to loop** focuses the shared timeline.
|
|
|
368
419
|
See [the complete loop workflow](docs/loops.md) for installation of both patches,
|
|
369
420
|
sample indexing, thresholds, limitations and reproducible screenshot examples.
|
|
370
421
|
|
|
422
|
+
For line-complete alternatives when a musically satisfying restart is the goal,
|
|
423
|
+
use `stemlab phrase-loops analysis-master --feedback artist-ratings.json --export-audio`.
|
|
424
|
+
This opt-in search compares the musical context around both cuts, preserves
|
|
425
|
+
canonical lyric occurrences, uses explicit artist feedback and selects diverse
|
|
426
|
+
passages. Suggestions remain separate from strict accepted loops. See
|
|
427
|
+
[phrase-loop methodology, feedback format and synthetic example](docs/phrase-loops.md).
|
|
428
|
+
For longer excerpts, add `--min-seconds 20 --max-seconds 30`; optional
|
|
429
|
+
`--max-bar-phase-error 0.125` also screens out mismatched measured bar positions.
|
|
430
|
+
These filters suggest auditions and do not establish seamless or listening approval.
|
|
431
|
+
|
|
371
432
|

|
|
372
433
|
|
|
373
434
|
## Audio-to-MIDI model comparison
|
|
@@ -611,6 +672,10 @@ analysis-master/
|
|
|
611
672
|
lyrics/
|
|
612
673
|
semantic_text/
|
|
613
674
|
song_map/
|
|
675
|
+
rudiments/ # optional native DanceRudiments candidate report
|
|
676
|
+
report.json
|
|
677
|
+
index.html
|
|
678
|
+
overview.png
|
|
614
679
|
summary.json
|
|
615
680
|
sonic_visualiser/
|
|
616
681
|
session.sv
|
|
@@ -130,6 +130,55 @@ The derived artifacts live under `deep/` (`sonic/`, `rhythm/`, `harmony/`, `stru
|
|
|
130
130
|
`lyrics/`, `semantic_text/`, optional `semantic_audio/` and `basic_pitch/`, plus
|
|
131
131
|
`song_map/song_map.json` and `summary.json`).
|
|
132
132
|
|
|
133
|
+
## Find DanceRudiments matching candidates from each stem
|
|
134
|
+
|
|
135
|
+
Install the separately maintained **optional** native package with
|
|
136
|
+
`python -m pip install -e ".[rudiments]"`. StemLab calls
|
|
137
|
+
[`dancerudiments.catalogue()` and `dancerudiments.sample()`](https://github.com/kieransimkin/DanceRudiments)
|
|
138
|
+
directly—no cloned movement functions or bundled catalogue. It extracts per-stem
|
|
139
|
+
RMS attack peaks, maps them onto detected beats, and ranks 16-beat windows
|
|
140
|
+
against position/speed/acceleration salience of actual native motion samples.
|
|
141
|
+
Scores are **comparative similarities**, not probabilities or guarantees of a good motion.
|
|
142
|
+
|
|
143
|
+
```sh
|
|
144
|
+
# Match the mix plus every saved stem without running separation again.
|
|
145
|
+
stemlab rudiments ./analysis-master --top 20
|
|
146
|
+
# Limit an existing analysis to two stems, or choose one of six official collections.
|
|
147
|
+
stemlab rudiments ./analysis-master --stem drums --stem bass --collection club
|
|
148
|
+
# Include matching as an opt-in part of a new full analysis.
|
|
149
|
+
stemlab analyze master.wav --output ./analysis-master --rudiments
|
|
150
|
+
# The bundled Arcadians MP3 has a documented BPM but no pre-separated stems.
|
|
151
|
+
stemlab rudiments 'examples/arcadians/Arcadians - 320kbps.mp3' \
|
|
152
|
+
--bpm 145 --collection initial --top 28 --output ./arcadians-rudiments
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Measured Arcadians benchmark
|
|
156
|
+
|
|
157
|
+
The mix-only offline benchmark found **1,821** energy-rise peaks and **15 candidate
|
|
158
|
+
patterns at ≥0.68** similarity among the authentic 28-pattern DanceRudiments
|
|
159
|
+
initial collection, led by `groove_b_played` (0.739), `groove_b_grid` (0.737),
|
|
160
|
+
and `lfo_morph` (0.724). The benchmark read DanceRudiments' officially compiled
|
|
161
|
+
sample tables through an **offline adapter**; the native C++-extension test is
|
|
162
|
+
separate and skips when that optional package is not installed. The BPM grid
|
|
163
|
+
starts at zero with **unverified beat phase**. No stem separation was claimed.
|
|
164
|
+
|
|
165
|
+
These are **actual Chromium screenshots** of the measured local HTML report,
|
|
166
|
+
not schematic illustrations or captures of the main React timeline.
|
|
167
|
+
|
|
168
|
+

|
|
169
|
+
|
|
170
|
+

|
|
171
|
+
|
|
172
|
+
The analysis writes `deep/rudiments/report.json`, `index.html`, and `overview.png`.
|
|
173
|
+
See **[method, score caveats, provenance, examples and screenshot reproduction](docs/rudiments.md)**.
|
|
174
|
+
|
|
175
|
+
**Native CI coverage:** the main GitHub Actions `CI` workflow now has a dedicated
|
|
176
|
+
Python 3.10/3.11 job that installs the official DanceRudiments *binary wheel*,
|
|
177
|
+
requires the actual C++ module, and runs the Arcadians test with skipping disabled.
|
|
178
|
+
Fewer than **12 real-audio matches** fails CI; passing runs publish the JSON,
|
|
179
|
+
HTML and measured overlay PNG as downloadable CI artifacts. See the
|
|
180
|
+
[native CI validation instructions](docs/rudiments.md#native-github-actions-gate).
|
|
181
|
+
|
|
133
182
|
## Timeline analysis gallery
|
|
134
183
|
|
|
135
184
|
The browser workspace is intended to make the analysis output inspectable, not just downloadable. The illustrations below explain the main analysis families using the same shared timeline vocabulary as the frontend: one clock, one playhead, zoomable lanes, and consistent section alignment across spectrogram, beat, lyric and deep-analysis layers.
|
|
@@ -243,6 +292,16 @@ exported WAVs and writes no files. **Zoom to loop** focuses the shared timeline.
|
|
|
243
292
|
See [the complete loop workflow](docs/loops.md) for installation of both patches,
|
|
244
293
|
sample indexing, thresholds, limitations and reproducible screenshot examples.
|
|
245
294
|
|
|
295
|
+
For line-complete alternatives when a musically satisfying restart is the goal,
|
|
296
|
+
use `stemlab phrase-loops analysis-master --feedback artist-ratings.json --export-audio`.
|
|
297
|
+
This opt-in search compares the musical context around both cuts, preserves
|
|
298
|
+
canonical lyric occurrences, uses explicit artist feedback and selects diverse
|
|
299
|
+
passages. Suggestions remain separate from strict accepted loops. See
|
|
300
|
+
[phrase-loop methodology, feedback format and synthetic example](docs/phrase-loops.md).
|
|
301
|
+
For longer excerpts, add `--min-seconds 20 --max-seconds 30`; optional
|
|
302
|
+
`--max-bar-phase-error 0.125` also screens out mismatched measured bar positions.
|
|
303
|
+
These filters suggest auditions and do not establish seamless or listening approval.
|
|
304
|
+
|
|
246
305
|

|
|
247
306
|
|
|
248
307
|
## Audio-to-MIDI model comparison
|
|
@@ -486,6 +545,10 @@ analysis-master/
|
|
|
486
545
|
lyrics/
|
|
487
546
|
semantic_text/
|
|
488
547
|
song_map/
|
|
548
|
+
rudiments/ # optional native DanceRudiments candidate report
|
|
549
|
+
report.json
|
|
550
|
+
index.html
|
|
551
|
+
overview.png
|
|
489
552
|
summary.json
|
|
490
553
|
sonic_visualiser/
|
|
491
554
|
session.sv
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "io.github.kieransimkin/stemlab",
|
|
4
4
|
"title": "StemLab \u2014 timing and loops for Shorts",
|
|
5
5
|
"description": "Music and stem analysis, timing and loopable Shorts. https://kieransimkin.co.uk/",
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.4.1",
|
|
7
7
|
"websiteUrl": "https://kieransimkin.co.uk/my-songs/",
|
|
8
8
|
"repository": {
|
|
9
9
|
"url": "https://github.com/kieransimkin/stemlab",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
{
|
|
14
14
|
"registryType": "pypi",
|
|
15
15
|
"identifier": "danceflow-stemlab",
|
|
16
|
-
"version": "1.
|
|
16
|
+
"version": "1.4.1",
|
|
17
17
|
"runtimeHint": "uvx",
|
|
18
18
|
"runtimeArguments": [
|
|
19
19
|
{
|
{danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/plugins/stemlab/.codex-plugin/plugin.json
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stemlab",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "Local music analysis for stems, timing, beats, harmony, lyrics, MIDI and loopable sections for Shorts. https://kieransimkin.co.uk/",
|
|
5
5
|
"skills": "./skills/",
|
|
6
6
|
"mcpServers": "./.mcp.json",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "stemlab",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.4.1",
|
|
5
5
|
"description": "Local music analysis for stems, timing, beats, harmony, lyrics, MIDI and loopable sections for Shorts. https://kieransimkin.co.uk/",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Kieran Simkin",
|
|
@@ -39,6 +39,14 @@ Created by Kieran Simkin — https://kieransimkin.co.uk/my-songs/
|
|
|
39
39
|
9. Quote `start_sample` inclusive and `end_sample` exclusive in native source sample
|
|
40
40
|
frames, not interleaved channel values. Do not move cuts to force a preferred
|
|
41
41
|
lyric. Vocal energy and timing gates are heuristics; listening QA is still needed.
|
|
42
|
+
10. For complete lyric-line alternatives and musical restarts, the local CLI
|
|
43
|
+
`stemlab phrase-loops RESULTS --feedback RATINGS.json --export-audio` reuses
|
|
44
|
+
explicit canonical timings and saved evidence. See `docs/phrase-loops.md`.
|
|
45
|
+
Use caller-selected duration bounds and optional `--max-bar-phase-error` to
|
|
46
|
+
screen measured bar-position mismatches; preserve unknown context and listening holds.
|
|
47
|
+
Its separate report contains suggestions, never strict accepted loops or
|
|
48
|
+
listening approval. Artist ratings are source-hash-bound; keep private
|
|
49
|
+
ratings and song material out of public examples and packages.
|
|
42
50
|
|
|
43
51
|
## Evidence and privacy
|
|
44
52
|
|
|
@@ -50,6 +50,8 @@ Changelog = "https://github.com/kieransimkin/stemlab/blob/main/CHANGELOG.md"
|
|
|
50
50
|
Contact = "https://kieransimkin.co.uk/contact/"
|
|
51
51
|
|
|
52
52
|
[project.optional-dependencies]
|
|
53
|
+
# Explicit opt-in: native motions/catalogue stay entirely in DanceRudiments.
|
|
54
|
+
rudiments = ["dancerudiments>=0.2.4,<1"]
|
|
53
55
|
# Local MCP transport and read-only viewer for the existing React timeline.
|
|
54
56
|
# Model backends remain separately selected; no duplicate React bundle is shipped.
|
|
55
57
|
codex = ["mcp>=2.2,<3", "fastapi>=0.115,<1", "uvicorn>=0.34,<1"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: danceflow-stemlab
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.1
|
|
4
4
|
Summary: Local music analysis for stems, timing, beats, harmony, lyrics, MIDI and loopable sections for Shorts. https://kieransimkin.co.uk/
|
|
5
5
|
Author: Kieran Simkin
|
|
6
6
|
Maintainer: Kieran Simkin
|
|
@@ -38,6 +38,8 @@ Requires-Dist: platformdirs>=4.2
|
|
|
38
38
|
Requires-Dist: librosa>=0.11
|
|
39
39
|
Requires-Dist: pyloudnorm>=0.2
|
|
40
40
|
Requires-Dist: cmudict>=1.1.3
|
|
41
|
+
Provides-Extra: rudiments
|
|
42
|
+
Requires-Dist: dancerudiments<1,>=0.2.4; extra == "rudiments"
|
|
41
43
|
Provides-Extra: codex
|
|
42
44
|
Requires-Dist: mcp<3,>=2.2; extra == "codex"
|
|
43
45
|
Requires-Dist: fastapi<1,>=0.115; extra == "codex"
|
|
@@ -255,6 +257,55 @@ The derived artifacts live under `deep/` (`sonic/`, `rhythm/`, `harmony/`, `stru
|
|
|
255
257
|
`lyrics/`, `semantic_text/`, optional `semantic_audio/` and `basic_pitch/`, plus
|
|
256
258
|
`song_map/song_map.json` and `summary.json`).
|
|
257
259
|
|
|
260
|
+
## Find DanceRudiments matching candidates from each stem
|
|
261
|
+
|
|
262
|
+
Install the separately maintained **optional** native package with
|
|
263
|
+
`python -m pip install -e ".[rudiments]"`. StemLab calls
|
|
264
|
+
[`dancerudiments.catalogue()` and `dancerudiments.sample()`](https://github.com/kieransimkin/DanceRudiments)
|
|
265
|
+
directly—no cloned movement functions or bundled catalogue. It extracts per-stem
|
|
266
|
+
RMS attack peaks, maps them onto detected beats, and ranks 16-beat windows
|
|
267
|
+
against position/speed/acceleration salience of actual native motion samples.
|
|
268
|
+
Scores are **comparative similarities**, not probabilities or guarantees of a good motion.
|
|
269
|
+
|
|
270
|
+
```sh
|
|
271
|
+
# Match the mix plus every saved stem without running separation again.
|
|
272
|
+
stemlab rudiments ./analysis-master --top 20
|
|
273
|
+
# Limit an existing analysis to two stems, or choose one of six official collections.
|
|
274
|
+
stemlab rudiments ./analysis-master --stem drums --stem bass --collection club
|
|
275
|
+
# Include matching as an opt-in part of a new full analysis.
|
|
276
|
+
stemlab analyze master.wav --output ./analysis-master --rudiments
|
|
277
|
+
# The bundled Arcadians MP3 has a documented BPM but no pre-separated stems.
|
|
278
|
+
stemlab rudiments 'examples/arcadians/Arcadians - 320kbps.mp3' \
|
|
279
|
+
--bpm 145 --collection initial --top 28 --output ./arcadians-rudiments
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Measured Arcadians benchmark
|
|
283
|
+
|
|
284
|
+
The mix-only offline benchmark found **1,821** energy-rise peaks and **15 candidate
|
|
285
|
+
patterns at ≥0.68** similarity among the authentic 28-pattern DanceRudiments
|
|
286
|
+
initial collection, led by `groove_b_played` (0.739), `groove_b_grid` (0.737),
|
|
287
|
+
and `lfo_morph` (0.724). The benchmark read DanceRudiments' officially compiled
|
|
288
|
+
sample tables through an **offline adapter**; the native C++-extension test is
|
|
289
|
+
separate and skips when that optional package is not installed. The BPM grid
|
|
290
|
+
starts at zero with **unverified beat phase**. No stem separation was claimed.
|
|
291
|
+
|
|
292
|
+
These are **actual Chromium screenshots** of the measured local HTML report,
|
|
293
|
+
not schematic illustrations or captures of the main React timeline.
|
|
294
|
+
|
|
295
|
+

|
|
296
|
+
|
|
297
|
+

|
|
298
|
+
|
|
299
|
+
The analysis writes `deep/rudiments/report.json`, `index.html`, and `overview.png`.
|
|
300
|
+
See **[method, score caveats, provenance, examples and screenshot reproduction](docs/rudiments.md)**.
|
|
301
|
+
|
|
302
|
+
**Native CI coverage:** the main GitHub Actions `CI` workflow now has a dedicated
|
|
303
|
+
Python 3.10/3.11 job that installs the official DanceRudiments *binary wheel*,
|
|
304
|
+
requires the actual C++ module, and runs the Arcadians test with skipping disabled.
|
|
305
|
+
Fewer than **12 real-audio matches** fails CI; passing runs publish the JSON,
|
|
306
|
+
HTML and measured overlay PNG as downloadable CI artifacts. See the
|
|
307
|
+
[native CI validation instructions](docs/rudiments.md#native-github-actions-gate).
|
|
308
|
+
|
|
258
309
|
## Timeline analysis gallery
|
|
259
310
|
|
|
260
311
|
The browser workspace is intended to make the analysis output inspectable, not just downloadable. The illustrations below explain the main analysis families using the same shared timeline vocabulary as the frontend: one clock, one playhead, zoomable lanes, and consistent section alignment across spectrogram, beat, lyric and deep-analysis layers.
|
|
@@ -368,6 +419,16 @@ exported WAVs and writes no files. **Zoom to loop** focuses the shared timeline.
|
|
|
368
419
|
See [the complete loop workflow](docs/loops.md) for installation of both patches,
|
|
369
420
|
sample indexing, thresholds, limitations and reproducible screenshot examples.
|
|
370
421
|
|
|
422
|
+
For line-complete alternatives when a musically satisfying restart is the goal,
|
|
423
|
+
use `stemlab phrase-loops analysis-master --feedback artist-ratings.json --export-audio`.
|
|
424
|
+
This opt-in search compares the musical context around both cuts, preserves
|
|
425
|
+
canonical lyric occurrences, uses explicit artist feedback and selects diverse
|
|
426
|
+
passages. Suggestions remain separate from strict accepted loops. See
|
|
427
|
+
[phrase-loop methodology, feedback format and synthetic example](docs/phrase-loops.md).
|
|
428
|
+
For longer excerpts, add `--min-seconds 20 --max-seconds 30`; optional
|
|
429
|
+
`--max-bar-phase-error 0.125` also screens out mismatched measured bar positions.
|
|
430
|
+
These filters suggest auditions and do not establish seamless or listening approval.
|
|
431
|
+
|
|
371
432
|

|
|
372
433
|
|
|
373
434
|
## Audio-to-MIDI model comparison
|
|
@@ -611,6 +672,10 @@ analysis-master/
|
|
|
611
672
|
lyrics/
|
|
612
673
|
semantic_text/
|
|
613
674
|
song_map/
|
|
675
|
+
rudiments/ # optional native DanceRudiments candidate report
|
|
676
|
+
report.json
|
|
677
|
+
index.html
|
|
678
|
+
overview.png
|
|
614
679
|
summary.json
|
|
615
680
|
sonic_visualiser/
|
|
616
681
|
session.sv
|
{danceflow_stemlab-1.3.4 → danceflow_stemlab-1.4.1}/src/danceflow_stemlab.egg-info/SOURCES.txt
RENAMED
|
@@ -69,8 +69,10 @@ src/stemlab/analysis/__init__.py
|
|
|
69
69
|
src/stemlab/analysis/harmony.py
|
|
70
70
|
src/stemlab/analysis/loops.py
|
|
71
71
|
src/stemlab/analysis/lyrics.py
|
|
72
|
+
src/stemlab/analysis/phrase_loops.py
|
|
72
73
|
src/stemlab/analysis/registry.py
|
|
73
74
|
src/stemlab/analysis/rhythm.py
|
|
75
|
+
src/stemlab/analysis/rudiment_matches.py
|
|
74
76
|
src/stemlab/analysis/runner.py
|
|
75
77
|
src/stemlab/analysis/semantic_audio.py
|
|
76
78
|
src/stemlab/analysis/semantic_text.py
|
|
@@ -120,7 +122,9 @@ src/stemlab/web/style.css
|
|
|
120
122
|
src/stemlab/web/timeline.css
|
|
121
123
|
tests/test_analysis_registry.py
|
|
122
124
|
tests/test_arcadians_fixture.py
|
|
125
|
+
tests/test_arcadians_rudiments.py
|
|
123
126
|
tests/test_audio_normalization.py
|
|
127
|
+
tests/test_bar_phase.py
|
|
124
128
|
tests/test_beat_consensus.py
|
|
125
129
|
tests/test_beat_this_bootstrap.py
|
|
126
130
|
tests/test_beatnet_bootstrap.py
|
|
@@ -152,7 +156,9 @@ tests/test_midi_extraction.py
|
|
|
152
156
|
tests/test_midi_integration.py
|
|
153
157
|
tests/test_midi_registry.py
|
|
154
158
|
tests/test_models.py
|
|
159
|
+
tests/test_phrase_loops.py
|
|
155
160
|
tests/test_repository_hygiene.py
|
|
161
|
+
tests/test_rudiment_matches.py
|
|
156
162
|
tests/test_server.py
|
|
157
163
|
tests/test_song_map.py
|
|
158
164
|
tests/test_sonic_visualiser.py
|
|
@@ -264,6 +264,38 @@ def grid_metrics(beats: np.ndarray, down_indices: np.ndarray, first: int, last:
|
|
|
264
264
|
last_interval_seconds=float(intervals[-1]))
|
|
265
265
|
|
|
266
266
|
|
|
267
|
+
def detected_bar_phase_metrics(beats: np.ndarray, down_indices: np.ndarray,
|
|
268
|
+
start_seconds: float, end_seconds: float) -> dict:
|
|
269
|
+
"""Compare cut positions on one measured bar grid without assuming 4/4."""
|
|
270
|
+
try:
|
|
271
|
+
beats = _ordered_times(beats)
|
|
272
|
+
indices = np.asarray(down_indices)
|
|
273
|
+
if (indices.ndim != 1 or len(indices) < 2 or indices.dtype.kind not in 'iu'
|
|
274
|
+
or np.any(indices < 0) or np.any(indices >= len(beats))
|
|
275
|
+
or np.any(indices[1:] <= indices[:-1])):
|
|
276
|
+
raise ValueError('Need ordered, distinct measured downbeat indices')
|
|
277
|
+
if (not np.isfinite([start_seconds, end_seconds]).all()
|
|
278
|
+
or not 0 <= start_seconds < end_seconds):
|
|
279
|
+
raise ValueError('Need finite ordered cut times')
|
|
280
|
+
downbeats = beats[indices]
|
|
281
|
+
if start_seconds < downbeats[0] or end_seconds > downbeats[-1]:
|
|
282
|
+
raise ValueError('Cuts lack enclosing measured downbeats')
|
|
283
|
+
first = max(0, int(np.searchsorted(downbeats, start_seconds, side='right'))-1)
|
|
284
|
+
last = max(0, int(np.searchsorted(downbeats, end_seconds, side='left'))-1)
|
|
285
|
+
counts = np.diff(indices)[first:last+1]
|
|
286
|
+
if not len(counts) or np.any(counts != counts[0]) or not 2 <= counts[0] <= 12:
|
|
287
|
+
raise ValueError('Detected beats per bar are unsupported or change across the cut')
|
|
288
|
+
coordinates = np.interp([start_seconds, end_seconds], downbeats,
|
|
289
|
+
np.arange(len(downbeats)))
|
|
290
|
+
span = float(coordinates[1]-coordinates[0])
|
|
291
|
+
return dict(available=True, detected_beats_per_bar=int(counts[0]),
|
|
292
|
+
fractional_bar_count=span, bar_phase_error=abs(span-round(span)),
|
|
293
|
+
start_bar_coordinate=float(coordinates[0]), end_bar_coordinate=float(coordinates[1]),
|
|
294
|
+
interpretation='Measured relative bar position; not artist-confirmed meter or loop approval')
|
|
295
|
+
except (ValueError, TypeError, IndexError) as exc:
|
|
296
|
+
return dict(available=False, reason=str(exc))
|
|
297
|
+
|
|
298
|
+
|
|
267
299
|
def optimize_join(audio: np.ndarray, sr: int, start: int, end: int,
|
|
268
300
|
lower: int, upper: int, cfg: LoopConfig, *, allow_failed: bool = False) -> dict | None:
|
|
269
301
|
"""Same offset at both cuts keeps length fixed. Score every channel."""
|
|
@@ -546,11 +578,9 @@ def _inside(root: Path, value: str) -> Path:
|
|
|
546
578
|
return path
|
|
547
579
|
|
|
548
580
|
|
|
549
|
-
def
|
|
550
|
-
|
|
551
|
-
"""Reuse saved models. Also works after canonical section/timing edits."""
|
|
581
|
+
def load_loop_evidence(root: Path) -> dict:
|
|
582
|
+
"""Load verified native master and saved evidence for both search strategies."""
|
|
552
583
|
from .song_map import _choose_sections
|
|
553
|
-
from stemlab.manifest import write_manifest
|
|
554
584
|
|
|
555
585
|
root = root.expanduser().resolve()
|
|
556
586
|
analysis = _read(root / "analysis.json")
|
|
@@ -577,21 +607,38 @@ def analyze_existing_loops(root: Path, *, export_audio: bool = False,
|
|
|
577
607
|
stems.append(StemArtifact(str(item["model"]), str(item["stem"]), _inside(root, item["path"])))
|
|
578
608
|
for item in model.get("normalization", []):
|
|
579
609
|
gains[str(_inside(root, item["path"]))] = float(item.get("gain_db", 0.0))
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
610
|
+
return dict(audio_path=master, song_map=song_map, beat_results=results, stems=stems,
|
|
611
|
+
whisper_result=_read(root / "speech/whisper.json"),
|
|
612
|
+
lyrics_result=_read(root / "deep/lyrics/lyrics.json"), canonical=canonical,
|
|
613
|
+
normalization_gains=gains)
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
def index_loop_report(root: Path, name: str, relative_path: str) -> None:
|
|
617
|
+
"""Refresh existing analysis discovery and hashes after an explicit rescan."""
|
|
618
|
+
from stemlab.manifest import write_manifest
|
|
619
|
+
|
|
620
|
+
analysis = _read(root / "analysis.json")
|
|
584
621
|
summary_path = root / "deep/summary.json"
|
|
585
622
|
summary = _read(summary_path)
|
|
586
|
-
summary.setdefault("analyses", {})[
|
|
623
|
+
summary.setdefault("analyses", {})[name] = {"available": True, "path": relative_path}
|
|
587
624
|
_write_json(summary_path, summary)
|
|
588
625
|
if not isinstance(analysis.get("deep_analysis"), dict):
|
|
589
626
|
analysis["deep_analysis"] = {}
|
|
590
|
-
analysis["deep_analysis"].setdefault("analyses", {})[
|
|
627
|
+
analysis["deep_analysis"].setdefault("analyses", {})[name] = summary["analyses"][name]
|
|
591
628
|
_write_json(root / "analysis.json", analysis)
|
|
592
629
|
old_manifest = _read(root / "manifest.json")
|
|
593
630
|
write_manifest(root, extra={k: v for k, v in old_manifest.items()
|
|
594
631
|
if k not in {"generated_at", "platform", "python", "files"}})
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
def analyze_existing_loops(root: Path, *, export_audio: bool = False,
|
|
635
|
+
config: LoopConfig | None = None) -> dict:
|
|
636
|
+
"""Reuse saved models. Also works after canonical section/timing edits."""
|
|
637
|
+
root = root.expanduser().resolve()
|
|
638
|
+
evidence = load_loop_evidence(root)
|
|
639
|
+
report = analyze_loops(output_dir=root / "deep/loops", **evidence,
|
|
640
|
+
export_audio=export_audio, config=config)
|
|
641
|
+
index_loop_report(root, "loops", "deep/loops/loops.json")
|
|
595
642
|
return report
|
|
596
643
|
|
|
597
644
|
|