dancerudiments 0.2.4__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.
Files changed (244) hide show
  1. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/.gitattributes +7 -0
  2. dancerudiments-0.3.0/.github/workflows/recordings.yml +55 -0
  3. dancerudiments-0.3.0/AGENTS.md +25 -0
  4. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/CMakeLists.txt +4 -1
  5. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/PKG-INFO +87 -5
  6. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/README.md +86 -4
  7. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/bindings/csharp/DanceRudiments/DanceRudiments.csproj +1 -1
  8. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/bindings/python/module.cpp +11 -0
  9. dancerudiments-0.3.0/docs/recordings.md +196 -0
  10. dancerudiments-0.3.0/docs/released-viewers.md +221 -0
  11. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/harness/index.html +1 -1
  12. dancerudiments-0.3.0/include/dancerudiments/recording.hpp +54 -0
  13. dancerudiments-0.3.0/include/dancerudiments/recording_view.hpp +19 -0
  14. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/package-lock.json +2 -2
  15. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/package.json +1 -1
  16. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/pyproject.toml +2 -2
  17. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/python/dancerudiments_authoring/__init__.py +3 -1
  18. dancerudiments-0.3.0/python/dancerudiments_authoring/harness.py +128 -0
  19. {dancerudiments-0.2.4/tools/release_demo_assets → dancerudiments-0.3.0/python/dancerudiments_authoring/harness_assets}/app.js +173 -29
  20. dancerudiments-0.3.0/python/dancerudiments_authoring/harness_assets/recording.wasm.b64 +1 -0
  21. dancerudiments-0.3.0/python/dancerudiments_authoring/harness_assets/recording.wasm.json +5 -0
  22. {dancerudiments-0.2.4/tools/release_demo_assets → dancerudiments-0.3.0/python/dancerudiments_authoring/harness_assets}/style.css +13 -0
  23. {dancerudiments-0.2.4/tools/release_demo_assets → dancerudiments-0.3.0/python/dancerudiments_authoring/harness_assets}/template.html +1 -0
  24. dancerudiments-0.3.0/python/dancerudiments_authoring/harness_checks.py +70 -0
  25. dancerudiments-0.3.0/python/dancerudiments_authoring/recording.py +172 -0
  26. dancerudiments-0.3.0/tests/cpp/test_recording.cpp +26 -0
  27. dancerudiments-0.3.0/tests/python/test_harness_display.py +38 -0
  28. dancerudiments-0.3.0/tests/python/test_recording.py +126 -0
  29. dancerudiments-0.3.0/tests/python/test_recording_source_eol.py +39 -0
  30. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/typescript/beat-player.test.js +1 -1
  31. dancerudiments-0.3.0/tools/build_recording_wasm.py +29 -0
  32. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/export_harness_midi.mjs +1 -1
  33. dancerudiments-0.3.0/tools/recording_wasm.cpp +8 -0
  34. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/release_demo.py +7 -15
  35. dancerudiments-0.3.0/tools/test_recording_harness.py +157 -0
  36. dancerudiments-0.2.4/AGENTS.md +0 -9
  37. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/.github/workflows/ci.yml +0 -0
  38. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/.github/workflows/csharp.yml +0 -0
  39. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/.github/workflows/demo-release.yml +0 -0
  40. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/.github/workflows/initial-collection.yml +0 -0
  41. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/.github/workflows/nuget-verify.yml +0 -0
  42. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/.github/workflows/patterns.yml +0 -0
  43. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/.github/workflows/release-tag.yml +0 -0
  44. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/.github/workflows/release.yml +0 -0
  45. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/.github/workflows/visualizer.yml +0 -0
  46. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/.gitignore +0 -0
  47. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/AUTHORS.md +0 -0
  48. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/CHANGELOG.md +0 -0
  49. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/CONTRIBUTING.md +0 -0
  50. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/LICENSE +0 -0
  51. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/bindings/csharp/.gitignore +0 -0
  52. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/bindings/csharp/DanceRudiments/NativeMethods.cs +0 -0
  53. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/bindings/csharp/DanceRudiments/Offset3.cs +0 -0
  54. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/bindings/csharp/DanceRudiments/PatternDefinition.cs +0 -0
  55. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/bindings/csharp/DanceRudiments/PatternLibrary.cs +0 -0
  56. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/bindings/csharp/DanceRudiments/Rudiments.cs +0 -0
  57. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/bindings/csharp/README.md +0 -0
  58. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/bindings/csharp/native/CMakeLists.txt +0 -0
  59. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/bindings/csharp/native/c_api.cpp +0 -0
  60. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/bindings/csharp/platforms.json +0 -0
  61. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/bindings/typescript/defaults.ts +0 -0
  62. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/bindings/typescript/index.ts +0 -0
  63. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/bindings/typescript/wasm.cpp +0 -0
  64. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/cmake/DanceRudimentsConfig.cmake.in +0 -0
  65. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/atlas/README.md +0 -0
  66. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/atlas/atlas.score.json +0 -0
  67. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/atlas/definitions.py +0 -0
  68. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/atlas/manifest.json +0 -0
  69. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/club/README.md +0 -0
  70. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/club/club.score.json +0 -0
  71. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/club/definitions.py +0 -0
  72. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/club/manifest.json +0 -0
  73. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/club/rhythms.json +0 -0
  74. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/club/sources.json +0 -0
  75. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/continuum/README.md +0 -0
  76. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/continuum/continuum.score.json +0 -0
  77. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/continuum/definitions.py +0 -0
  78. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/continuum/manifest.json +0 -0
  79. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/dancefloor/README.md +0 -0
  80. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/dancefloor/definitions.py +0 -0
  81. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/dancefloor/manifest.json +0 -0
  82. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/dancefloor/recipes.json +0 -0
  83. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/dancefloor/rhythms.json +0 -0
  84. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/dancefloor/sources.json +0 -0
  85. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/defaults.json +0 -0
  86. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/expansion/README.md +0 -0
  87. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/expansion/definitions.py +0 -0
  88. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/expansion/expansion.score.json +0 -0
  89. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/expansion/manifest.json +0 -0
  90. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/initial/README.md +0 -0
  91. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/initial/THIRD_PARTY_NOTICES.md +0 -0
  92. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/initial/definitions.py +0 -0
  93. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/initial/initial.score.json +0 -0
  94. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/initial/manifest.json +0 -0
  95. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/initial/preview_data.inc +0 -0
  96. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/initial/preview_sampler.cpp +0 -0
  97. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/initial/preview_wasm.json +0 -0
  98. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/initial/sources/akwf/AKWF_0001_256.h +0 -0
  99. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/initial/sources/akwf/AKWF_0010_256.h +0 -0
  100. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/initial/sources/akwf/AKWF_R_asym_saw_05_256.h +0 -0
  101. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/initial/sources/akwf/AKWF_R_asym_saw_15_256.h +0 -0
  102. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/initial/sources/d3-ease/LICENSE +0 -0
  103. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/collections/initial/sources/groove/1_funk_80_beat_4-4.mid.b64 +0 -0
  104. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/conanfile.py +0 -0
  105. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/docs/authoring.md +0 -0
  106. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/docs/bindings.md +0 -0
  107. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/docs/branding/README.md +0 -0
  108. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/docs/branding/logo-monochrome.svg +0 -0
  109. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/docs/branding/logo.png +0 -0
  110. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/docs/branding/logo.svg +0 -0
  111. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/docs/ci-reproducibility.md +0 -0
  112. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/docs/csharp.md +0 -0
  113. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/docs/dancefloor.md +0 -0
  114. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/docs/default-library.md +0 -0
  115. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/docs/images/capture.json +0 -0
  116. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/docs/images/visualizer-amen-desktop.png +0 -0
  117. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/docs/images/visualizer-midi-score.png +0 -0
  118. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/docs/images/visualizer-mobile.png +0 -0
  119. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/docs/midi-harness.md +0 -0
  120. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/docs/release-demo.md +0 -0
  121. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/docs/troubleshooting.md +0 -0
  122. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/examples/bindings/README.md +0 -0
  123. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/examples/bindings/cpp/CMakeLists.txt +0 -0
  124. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/examples/bindings/cpp/main.cpp +0 -0
  125. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/examples/bindings/csharp/.gitignore +0 -0
  126. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/examples/bindings/csharp/Program.cs +0 -0
  127. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/examples/bindings/csharp/Quickstart.csproj +0 -0
  128. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/examples/bindings/javascript/node.mjs +0 -0
  129. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/examples/bindings/pulse.score.json +0 -0
  130. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/examples/bindings/python/quickstart.py +0 -0
  131. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/examples/bindings/python/sample_motion.py +0 -0
  132. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/examples/bindings/typescript/.gitignore +0 -0
  133. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/examples/bindings/typescript/app.ts +0 -0
  134. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/examples/bindings/typescript/index.html +0 -0
  135. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/examples/bindings/typescript/tsconfig.json +0 -0
  136. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/examples/bindings/typescript/wasm.d.ts +0 -0
  137. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/examples/patterns/starter.json +0 -0
  138. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/harness/app.js +0 -0
  139. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/harness/beats.json +0 -0
  140. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/harness/dancerudiments.js +0 -0
  141. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/harness/index.json +0 -0
  142. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/harness/initial/app.js +0 -0
  143. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/harness/initial/style.css +0 -0
  144. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/harness/initial/template.html +0 -0
  145. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/harness/initial-collection.html +0 -0
  146. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/harness/style.css +0 -0
  147. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/include/dancerudiments/c_api.h +0 -0
  148. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/include/dancerudiments/collections/atlas.hpp +0 -0
  149. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/include/dancerudiments/collections/club.hpp +0 -0
  150. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/include/dancerudiments/collections/continuum.hpp +0 -0
  151. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/include/dancerudiments/collections/dancefloor.hpp +0 -0
  152. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/include/dancerudiments/collections/expansion.hpp +0 -0
  153. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/include/dancerudiments/collections/initial.hpp +0 -0
  154. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/include/dancerudiments/dance_rudiments.hpp +0 -0
  155. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/include/dancerudiments/detail/default_includes.inc +0 -0
  156. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/include/dancerudiments/detail/default_registry.inc +0 -0
  157. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/include/dancerudiments/sampled_pattern.hpp +0 -0
  158. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/packaging/CPP_BINARY_README.md +0 -0
  159. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/python/dancerudiments_authoring/__main__.py +0 -0
  160. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/python/dancerudiments_authoring/_reproducibility.py +0 -0
  161. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/python/dancerudiments_authoring/_validation.py +0 -0
  162. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/python/dancerudiments_authoring/cli.py +0 -0
  163. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/python/dancerudiments_authoring/collections.py +0 -0
  164. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/python/dancerudiments_authoring/compiler.py +0 -0
  165. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/python/dancerudiments_authoring/curves.py +0 -0
  166. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/python/dancerudiments_authoring/events.py +0 -0
  167. {dancerudiments-0.2.4/tools/release_demo_assets → dancerudiments-0.3.0/python/dancerudiments_authoring/harness_assets}/beat_player.js +0 -0
  168. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/python/dancerudiments_authoring/initial_sources.py +0 -0
  169. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/python/dancerudiments_authoring/io.py +0 -0
  170. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/python/dancerudiments_authoring/model.py +0 -0
  171. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/python/dancerudiments_authoring/packs/THIRD_PARTY_NOTICES.md +0 -0
  172. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/python/dancerudiments_authoring/packs/atlas.json +0 -0
  173. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/python/dancerudiments_authoring/packs/club.json +0 -0
  174. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/python/dancerudiments_authoring/packs/continuum.json +0 -0
  175. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/python/dancerudiments_authoring/packs/dancefloor.json +0 -0
  176. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/python/dancerudiments_authoring/packs/expansion.json +0 -0
  177. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/python/dancerudiments_authoring/packs/initial.json +0 -0
  178. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/release_version-v3.py +0 -0
  179. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/src/dance_rudiments.cpp +0 -0
  180. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/src/sampled_pattern.cpp +0 -0
  181. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/browser/test_initial_gallery.py +0 -0
  182. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/consumer/CMakeLists.txt +0 -0
  183. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/consumer/main.cpp +0 -0
  184. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/cpp/test_c_api.cpp +0 -0
  185. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/cpp/test_club.cpp +0 -0
  186. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/cpp/test_continuum.cpp +0 -0
  187. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/cpp/test_dancefloor.cpp +0 -0
  188. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/cpp/test_default_registration.cpp +0 -0
  189. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/cpp/test_initial_collection.cpp +0 -0
  190. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/cpp/test_main.cpp +0 -0
  191. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/cpp/test_sampled_pattern.cpp +0 -0
  192. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/csharp/.gitignore +0 -0
  193. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/csharp/DanceRudiments.Tests.csproj +0 -0
  194. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/csharp/Program.cs +0 -0
  195. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/csharp/packaging/test_packaging.py +0 -0
  196. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/csharp/packaging/test_publish_verification.py +0 -0
  197. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/python/test_atlas.py +0 -0
  198. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/python/test_authoring.py +0 -0
  199. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/python/test_club.py +0 -0
  200. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/python/test_continuum.py +0 -0
  201. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/python/test_dancefloor.py +0 -0
  202. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/python/test_defaults.py +0 -0
  203. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/python/test_expansion.py +0 -0
  204. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/python/test_harness_beats.py +0 -0
  205. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/python/test_initial_collection.py +0 -0
  206. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/python/test_native.py +0 -0
  207. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/python/test_package_docs.py +0 -0
  208. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/python/test_release_demo_compact.py +0 -0
  209. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/python/test_release_version.py +0 -0
  210. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/python/test_reproducibility.py +0 -0
  211. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/typescript/defaults.test.js +0 -0
  212. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/typescript/initial-wasm.test.mjs +0 -0
  213. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/typescript/patterns.test.js +0 -0
  214. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/typescript/runtime.test.js +0 -0
  215. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tests/typescript/wasm-smoke.mjs +0 -0
  216. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/build_atlas_collection.py +0 -0
  217. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/build_club_collection.py +0 -0
  218. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/build_club_demo.py +0 -0
  219. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/build_continuum_collection.py +0 -0
  220. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/build_dancefloor_collection.py +0 -0
  221. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/build_dancefloor_demo.py +0 -0
  222. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/build_default_catalogue.py +0 -0
  223. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/build_expansion_collection.py +0 -0
  224. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/build_harness.py +0 -0
  225. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/build_initial_collection.py +0 -0
  226. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/capture_readme_screenshots.py +0 -0
  227. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/check_package_docs.py +0 -0
  228. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/check_release_version.py +0 -0
  229. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/club_demo_assets/app.js +0 -0
  230. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/club_demo_assets/style.css +0 -0
  231. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/club_demo_assets/template.html +0 -0
  232. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/compile_patterns.py +0 -0
  233. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/csharp_package.py +0 -0
  234. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/dancefloor_demo_assets/app.js +0 -0
  235. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/dancefloor_demo_assets/style.css +0 -0
  236. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/dancefloor_demo_assets/template.html +0 -0
  237. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/export_default_catalogue.cpp +0 -0
  238. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/harness_beats.py +0 -0
  239. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/release_version.py +0 -0
  240. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/render_club_demo.py +0 -0
  241. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/render_dancefloor_demo.py +0 -0
  242. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/render_release_demo.py +0 -0
  243. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tools/test_midi_harness.py +0 -0
  244. {dancerudiments-0.2.4 → dancerudiments-0.3.0}/tsconfig.json +0 -0
@@ -30,3 +30,10 @@ harness/beats.json text eol=lf
30
30
  # Keep original DanceFlow artwork byte-identical across platform packages.
31
31
  **/branding/*.svg text eol=lf
32
32
  **/branding/*.png binary
33
+
34
+ # Recording WASM provenance hashes the actual C++ source bytes. Do not let
35
+ # core.autocrlf turn these into CRLF on Windows; do not weaken the SHA tests.
36
+ tools/recording_wasm.cpp text eol=lf
37
+ include/dancerudiments/recording_view.hpp text eol=lf
38
+ python/dancerudiments_authoring/harness_assets/recording.wasm.json text eol=lf
39
+ python/dancerudiments_authoring/harness_assets/recording.wasm.b64 text eol=lf
@@ -0,0 +1,55 @@
1
+ name: Recording harness and native compatibility
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ pull_request:
6
+ workflow_dispatch:
7
+ permissions:
8
+ contents: read
9
+ jobs:
10
+ recording-harness:
11
+ runs-on: ubuntu-latest
12
+ timeout-minutes: 25
13
+ env:
14
+ DANCERUDIMENTS_REQUIRE_NATIVE: '1'
15
+ steps:
16
+ - uses: actions/checkout@v6
17
+ - uses: actions/setup-python@v6
18
+ with:
19
+ python-version: '3.13'
20
+ - uses: actions/setup-node@v6
21
+ with:
22
+ node-version: 22
23
+ - name: Build and install actual native/authoring package
24
+ run: |
25
+ python -m pip install . playwright
26
+ python -m playwright install --with-deps chromium
27
+ - name: Check committed sampler/source hashes and shared package APIs
28
+ run: |
29
+ python -m unittest discover -s tests/python -p test_recording.py -v
30
+ python -m unittest discover -s tests/python -p test_harness_display.py -v
31
+ - name: Check unchanged beat-player contracts after moving shared assets
32
+ run: node --test tests/typescript/beat-player.test.js
33
+ - name: Rebuild the generic native recording accessor
34
+ run: |
35
+ sudo apt-get update
36
+ sudo apt-get install -y clang lld
37
+ python tools/build_recording_wasm.py
38
+ - name: Native C++ recording and existing-pattern regressions
39
+ run: |
40
+ cmake -S . -B build-recordings -DCMAKE_BUILD_TYPE=Release
41
+ cmake --build build-recordings --parallel 2
42
+ ctest --test-dir build-recordings --output-on-failure
43
+ - name: Verify a freshly built recording page with real local-file loading
44
+ env:
45
+ PYTHONPATH: python
46
+ run: python tools/test_recording_harness.py recording-harness/index.html --make-fixture --screenshot recording-comparison.png
47
+ - uses: actions/upload-artifact@v6
48
+ if: always()
49
+ with:
50
+ name: native-recording-harness
51
+ path: |
52
+ recording-harness/
53
+ recording-comparison.png
54
+ recording-comparison-bars.png
55
+ if-no-files-found: warn
@@ -0,0 +1,25 @@
1
+ # Agent instructions for DanceRudiments
2
+
3
+ - Read README.md, docs/ and the binding examples. Check the native catalogue, integer-pip timing, compositions and the C++, Python, WASM and C# bindings before adding a pattern or adapter.
4
+ - Use the relevant existing capabilities as fully as the task warrants. Always look for a useful improvement to this tool. Fix a reusable gap here rather than copying its engine or carrying a private workaround; record why no change is justified when that is the result.
5
+ - Submit improvements as PRs to https://github.com/kieransimkin/DanceRudiments/pulls. Never leave a completed reusable improvement only in a local fork. Keep changes focused and preserve the tool's identity and compatibility contracts.
6
+ - Add relevant regression coverage, documentation and a reproducible example when behaviour changes. Use tools/release_version.py and tools/check_release_version.py. Run the native and relevant binding tests from .github/workflows/ before packaging.
7
+ - Keep credentials, account data, private media and rights records out of source, fixtures, logs and packages. Use synthetic fixtures or explicitly authorised public examples and preserve their provenance/licences.
8
+ - Use [CONTRIBUTING.md](CONTRIBUTING.md) for review. Maintainers publish accepted, validated improvements as a fresh matching GitHub release and verify configured registry destinations. Preserve existing tags and release assets; publication and deployment remain subject to applicable authorisation.
9
+ - Website: https://kieransimkin.co.uk/danceflow/
10
+
11
+ ## Keep README documentation current
12
+
13
+ - Whenever a feature is added or changed, update the relevant README.md in the same change to reflect the resulting behaviour. This applies to every feature addition or modification, including APIs, commands, configuration, defaults, integrations and removals.
14
+ - Update affected feature descriptions, usage instructions, options, examples and limitations. For tools or packages with their own README.md, update that README as well as the repository README when its overview or usage is affected.
15
+ - Check the README against the implemented and verified behaviour before committing, opening or merging a PR, publishing a release or handing off the work. A feature change is incomplete until its README documentation is current; a changelog or other documentation does not replace this requirement.
16
+
17
+ ## Released viewers and coordinated CI/CD
18
+
19
+ - Read [docs/released-viewers.md](docs/released-viewers.md) before changing dependency, build or release behaviour. The sibling layout is `DanceRudiments/`, `react-timeline-sequence/`, and `stemlab/` under one parent.
20
+ - Release the two companions first (React Timeline Sequence, then DanceRudiments), verify their configured publication workflows and public registries, then synchronize/rebuild/test/release StemLab. A merge or tag alone is not a verified release.
21
+ - StemLab must target the **latest stable official releases**. No unreleased main commit, mutable development override, local `file:` archive or vendor package may substitute for a missing release. Latest is resolved before the build and locked to exact published versions, release commits and registry hashes for that run; fail visibly on stale or incompatible dependencies rather than falling back silently.
22
+ - Use the canonical `stemlab/scripts/Release-DanceFlow.ps1` coordinator from the shared parent directory. Preview first; `-Execute` explicitly performs scoped commits, normal pushes and fresh tags; `-Resume` must preserve source identities and existing tags/assets. Never force-push, move release tags, commit unrelated work, bypass failed checks or change approvals/protections/credentials to manufacture a pass.
23
+ - Update this repository's README.md and AGENTS.md in the same change whenever release policy, dependency rules, automation, commands, controls or validation change. Keep relevant package/feature READMEs current too. A separate guide or changelog does not replace the root README update.
24
+ - Preserve the master-plus-drums/percussion beat-routing contract, 64-pips-per-beat native recording clock, per-dimension bars and combined waveform/spectrogram lanes. New UI features must be supplied by released shared APIs and genuinely rebuilt frontend assets, not copied engines or marker strings.
25
+ - Distinguish source CI, package publication, full-song inference, browser/audio QA, artifact packaging, actual Pages deployment and live URL/audio verification. Keep all four Arcadians pages together; do not report unexecuted, skipped or older-commit checks as success. CI playback evidence is not subjective listening QA.
@@ -1,5 +1,5 @@
1
1
  cmake_minimum_required(VERSION 3.20)
2
- project(DanceRudiments VERSION 0.2.4
2
+ project(DanceRudiments VERSION 0.3.0
3
3
  DESCRIPTION "Deterministic rhythmic position functions for DanceFlow. Music: https://kieransimkin.co.uk/my-songs/"
4
4
  HOMEPAGE_URL "https://kieransimkin.co.uk/my-songs/"
5
5
  LANGUAGES CXX)
@@ -59,6 +59,9 @@ endif()
59
59
 
60
60
  if(DANCERUDIMENTS_BUILD_TESTS)
61
61
  enable_testing()
62
+ add_executable(dancerudiments_recording_tests tests/cpp/test_recording.cpp)
63
+ target_link_libraries(dancerudiments_recording_tests PRIVATE DanceRudiments::DanceRudiments)
64
+ add_test(NAME dancerudiments_recordings COMMAND dancerudiments_recording_tests)
62
65
  add_executable(dancerudiments_tests tests/cpp/test_main.cpp)
63
66
  target_link_libraries(dancerudiments_tests PRIVATE DanceRudiments::DanceRudiments)
64
67
  add_test(NAME dancerudiments_cpp COMMAND dancerudiments_tests)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dancerudiments
3
- Version: 0.2.4
3
+ Version: 0.3.0
4
4
  Summary: Deterministic rhythmic position functions with C++, Python, TypeScript/WASM and C# bindings. https://kieransimkin.co.uk/my-songs/
5
5
  Author: Kieran Simkin
6
6
  License-Expression: MIT AND BSD-3-Clause AND CC0-1.0 AND CC-BY-4.0
@@ -42,7 +42,9 @@ Project website: [https://kieransimkin.co.uk/danceflow/](https://kieransimkin.co
42
42
 
43
43
  ## ▶ Try the pattern visualizer
44
44
 
45
- **[Download the self-contained visualizer — HTML](https://github.com/kieransimkin/DanceRudiments/raw/refs/heads/main/harness/index.html)**
45
+ **[▶ Open the movement visualiser](https://kieransimkin.github.io/DanceRudiments/)**
46
+
47
+ [Download the self-contained HTML for offline use](https://github.com/kieransimkin/DanceRudiments/raw/refs/heads/main/harness/index.html)
46
48
 
47
49
  Save the file and open it in a browser. **No install, server, CDN, soundfont or
48
50
  separate WASM file is required.** The complete 1,731-movement catalogue includes a
@@ -59,13 +61,13 @@ The downloadable file is the checked-in snapshot; the **Pattern visualizer**
59
61
  workflow also rebuilds a fresh version from every pushed native catalogue.
60
62
  [Beat player, rebuilding, release downloads and optional live hosting](docs/midi-harness.md).
61
63
 
62
- [![Four Amen-based movements compared in the real self-contained visualizer](https://raw.githubusercontent.com/kieransimkin/DanceRudiments/main/docs/images/visualizer-amen-desktop.png)](https://github.com/kieransimkin/DanceRudiments/raw/refs/heads/main/harness/index.html)
64
+ [![Four Amen-based movements compared in the real self-contained visualizer](https://raw.githubusercontent.com/kieransimkin/DanceRudiments/main/docs/images/visualizer-amen-desktop.png)](https://kieransimkin.github.io/DanceRudiments/)
63
65
 
64
66
  *Captured from the running C++/WebAssembly visualizer at beat 2.75. Audio was not started during this screenshot.*
65
67
 
66
68
 
67
69
 
68
- DanceRudiments is a C++17 library of deterministic rhythmic position functions. One integer pip is `1/64` of a beat. Built-in and custom patterns each keep their own loop period. Every sampler wraps positive or negative input into its own loop before sampling.
70
+ DanceRudiments is a C++17 library of deterministic rhythmic position functions. One integer pip is `1/64` of a beat. Built-in and custom patterns each keep their own loop period. Every legacy pattern sampler wraps positive or negative input into its own loop before sampling.
69
71
 
70
72
  Outputs are dimensionless offsets, normally in `[-1, 1]`. The caller chooses pixels, CSS units, metres, or another scale. There is deliberately no time interpolation in the public API: renderers advance with integer pips and get one exact sample per pip.
71
73
 
@@ -105,6 +107,33 @@ python tools/build_default_catalogue.py --check
105
107
  The release workflow builds the complete native catalogue demo plus an offline,
106
108
  audio-enabled 68-rhythm / 16-mapping comparison page. All live motion comes from C++/WASM.
107
109
 
110
+ ## BPM-synchronised N-dimensional recordings and comparison harness
111
+
112
+ The additive `dancerudiments.recording` format stores **any number of named
113
+ float64 dimensions** at **64 pips per beat**, with a mandatory BPM estimate or
114
+ confirmed value and separate beat-phase provenance. `SampledRecording` provides
115
+ native non-looping C++/Python playback, including captures beyond the old
116
+ 65,535-pip loop length; existing XYZ patterns and their wrapping rules are unchanged.
117
+
118
+ ```python
119
+ from dancerudiments_authoring import load_recording
120
+ from dancerudiments_authoring.harness import build_recording_harness
121
+ r = load_recording("output/recordings/audio_recording.json")
122
+ values = r.to_native().sample(r.clock.start_pip) # every dimension, real C++
123
+ build_recording_harness([r], "output/index.html")
124
+ ```
125
+
126
+ The exported page uses **the same packaged visualizer assets as catalogue/release
127
+ pages**, with native WASM sampling, shared transport, four-card comparison and
128
+ review import/export. Recording mode adds arbitrary-axis X/Y/Z selection, locked
129
+ BPM/origin and non-looping completion. The tiny generic WASM sampler is bundled;
130
+ no compiler or CDN is needed at export time. StemLab's `record-rudiment` and
131
+ `generate-batch` produce individual or varied stem/filter combinations using this
132
+ API without copying it. Music is not bundled unless explicitly requested.
133
+
134
+ See **[recording format, harness, native API and compatibility](docs/recordings.md)**.
135
+ Build the updated source to use this unreleased API; earlier releases lack it.
136
+
108
137
  ## Curve and event authoring
109
138
 
110
139
  The optional **Python authoring tools** compile LFOs, segmented curves, sampled
@@ -170,7 +199,7 @@ another movement implementation. The examples below are checked into
170
199
  | TypeScript / JavaScript | `catalogue` (an array, not a function) | `sample(name, pip)` after `bindNative()` | `info.periodPips` | Call the custom bank's `dispose()` |
171
200
 
172
201
  **One beat is 64 pips.** Supply a signed 32-bit integer, not milliseconds or a
173
- floating-point beat number. Every sampler wraps negative as well as positive
202
+ floating-point beat number. Every legacy pattern sampler wraps negative as well as positive
174
203
  positions into its own loop. Offsets are `{x, y, z}`, dimensionless and bounded to
175
204
  `[-1, 1]`; choose the pixel/metre scale in your application. There is no automatic
176
205
  interpolation, BPM detection or audio playback inside a binding.
@@ -548,3 +577,56 @@ Apply an output to a bounded subject. DanceRudiments changes position only; it m
548
577
  ## Troubleshooting history
549
578
 
550
579
  See [build and release troubleshooting](docs/troubleshooting.md) for prior failures, causes and fixes.
580
+
581
+ ### Switch the shared visualizer between trajectories and bars
582
+
583
+ Use **Movement view → X–Y plot / Bar graph**. Bar view uses the same native
584
+ C++/WebAssembly sample and the existing clock, shows **every dimension** as a
585
+ separate signed bar, and keeps comparison cards on one common scale. Switching
586
+ views does not interrupt playback or change the underlying recording. The
587
+ selection is local to your browser; long dimension sets scroll horizontally.
588
+ This applies to catalogue/release pages and recording harnesses exported by
589
+ StemLab. Rebuild old generated pages to include the control. See
590
+ [recording visualizer controls](docs/recordings.md#trajectory-or-all-dimension-bars).
591
+
592
+
593
+ ## Coordinated releases and latest-release dependencies
594
+
595
+ Keep `DanceRudiments`, `react-timeline-sequence`, and `stemlab` as sibling
596
+ checkouts. The release order is **React Timeline Sequence → DanceRudiments →
597
+ StemLab**: publish and verify the companions before updating the consuming app.
598
+ StemLab must use their **latest stable official releases**, not an unreleased
599
+ commit, sibling source package or vendored npm archive. Each build records exact
600
+ released versions/commits/integrity after latest-release resolution so all its
601
+ jobs use the same bytes; CI fails clearly when that snapshot is stale.
602
+
603
+ From the parent folder, preview and then explicitly execute:
604
+
605
+ ```powershell
606
+ .\stemlab\scripts\Release-DanceFlow.ps1 -ProjectsRoot .
607
+ .\stemlab\scripts\Release-DanceFlow.ps1 -ProjectsRoot . -Execute
608
+ ```
609
+
610
+ For the first migration use the same commands with
611
+ `.\release-kit\Release-DanceFlow.ps1` after extracting the complete kit. It
612
+ applies the three repository patches, updates versions, performs scoped commits
613
+ and normal pushes, waits for exact-commit CI, then creates **new** release tags.
614
+ The existing GitHub Actions workflows publish packages; no protection rules,
615
+ credentials, old tags or assets are changed. The initial expected next-minor
616
+ versions are only a preview proposal, never a claim that they are published.
617
+
618
+ Use Node.js 24, Python 3.11+, PowerShell 5.1+, Git and authenticated GitHub CLI.
619
+ Required main/registry/Pages jobs must pass; missing approvals and partial
620
+ publication are blockers, not success. The default script releases all three
621
+ projects, is opt-in via `-Execute`, and supports a saved exact-source `-Resume`
622
+ session. A changed checkout or pre-commit build failure requires review rather
623
+ than an automatic stash/reset or guessed recovery.
624
+
625
+ StemLab's helper updates npm manifests/lock, optional native dependency minimum,
626
+ release/build metadata and the genuinely rebuilt JS/CSS together. Its daily
627
+ release-update workflow proposes subsequent updates by PR without auto-merging.
628
+ A tagged StemLab release generates all four Arcadians pages and validates actual
629
+ Pages deployment, live HTML and seekable audio. Do not start a duplicate manual
630
+ Arcadians analysis during that tagged build. Read
631
+ **[the full release, resume, verification and troubleshooting guide](docs/released-viewers.md)**
632
+ before running it. Keep this README and AGENTS.md synchronized with future changes.
@@ -24,7 +24,9 @@ Project website: [https://kieransimkin.co.uk/danceflow/](https://kieransimkin.co
24
24
 
25
25
  ## ▶ Try the pattern visualizer
26
26
 
27
- **[Download the self-contained visualizer — HTML](https://github.com/kieransimkin/DanceRudiments/raw/refs/heads/main/harness/index.html)**
27
+ **[▶ Open the movement visualiser](https://kieransimkin.github.io/DanceRudiments/)**
28
+
29
+ [Download the self-contained HTML for offline use](https://github.com/kieransimkin/DanceRudiments/raw/refs/heads/main/harness/index.html)
28
30
 
29
31
  Save the file and open it in a browser. **No install, server, CDN, soundfont or
30
32
  separate WASM file is required.** The complete 1,731-movement catalogue includes a
@@ -41,13 +43,13 @@ The downloadable file is the checked-in snapshot; the **Pattern visualizer**
41
43
  workflow also rebuilds a fresh version from every pushed native catalogue.
42
44
  [Beat player, rebuilding, release downloads and optional live hosting](docs/midi-harness.md).
43
45
 
44
- [![Four Amen-based movements compared in the real self-contained visualizer](https://raw.githubusercontent.com/kieransimkin/DanceRudiments/main/docs/images/visualizer-amen-desktop.png)](https://github.com/kieransimkin/DanceRudiments/raw/refs/heads/main/harness/index.html)
46
+ [![Four Amen-based movements compared in the real self-contained visualizer](https://raw.githubusercontent.com/kieransimkin/DanceRudiments/main/docs/images/visualizer-amen-desktop.png)](https://kieransimkin.github.io/DanceRudiments/)
45
47
 
46
48
  *Captured from the running C++/WebAssembly visualizer at beat 2.75. Audio was not started during this screenshot.*
47
49
 
48
50
 
49
51
 
50
- DanceRudiments is a C++17 library of deterministic rhythmic position functions. One integer pip is `1/64` of a beat. Built-in and custom patterns each keep their own loop period. Every sampler wraps positive or negative input into its own loop before sampling.
52
+ DanceRudiments is a C++17 library of deterministic rhythmic position functions. One integer pip is `1/64` of a beat. Built-in and custom patterns each keep their own loop period. Every legacy pattern sampler wraps positive or negative input into its own loop before sampling.
51
53
 
52
54
  Outputs are dimensionless offsets, normally in `[-1, 1]`. The caller chooses pixels, CSS units, metres, or another scale. There is deliberately no time interpolation in the public API: renderers advance with integer pips and get one exact sample per pip.
53
55
 
@@ -87,6 +89,33 @@ python tools/build_default_catalogue.py --check
87
89
  The release workflow builds the complete native catalogue demo plus an offline,
88
90
  audio-enabled 68-rhythm / 16-mapping comparison page. All live motion comes from C++/WASM.
89
91
 
92
+ ## BPM-synchronised N-dimensional recordings and comparison harness
93
+
94
+ The additive `dancerudiments.recording` format stores **any number of named
95
+ float64 dimensions** at **64 pips per beat**, with a mandatory BPM estimate or
96
+ confirmed value and separate beat-phase provenance. `SampledRecording` provides
97
+ native non-looping C++/Python playback, including captures beyond the old
98
+ 65,535-pip loop length; existing XYZ patterns and their wrapping rules are unchanged.
99
+
100
+ ```python
101
+ from dancerudiments_authoring import load_recording
102
+ from dancerudiments_authoring.harness import build_recording_harness
103
+ r = load_recording("output/recordings/audio_recording.json")
104
+ values = r.to_native().sample(r.clock.start_pip) # every dimension, real C++
105
+ build_recording_harness([r], "output/index.html")
106
+ ```
107
+
108
+ The exported page uses **the same packaged visualizer assets as catalogue/release
109
+ pages**, with native WASM sampling, shared transport, four-card comparison and
110
+ review import/export. Recording mode adds arbitrary-axis X/Y/Z selection, locked
111
+ BPM/origin and non-looping completion. The tiny generic WASM sampler is bundled;
112
+ no compiler or CDN is needed at export time. StemLab's `record-rudiment` and
113
+ `generate-batch` produce individual or varied stem/filter combinations using this
114
+ API without copying it. Music is not bundled unless explicitly requested.
115
+
116
+ See **[recording format, harness, native API and compatibility](docs/recordings.md)**.
117
+ Build the updated source to use this unreleased API; earlier releases lack it.
118
+
90
119
  ## Curve and event authoring
91
120
 
92
121
  The optional **Python authoring tools** compile LFOs, segmented curves, sampled
@@ -152,7 +181,7 @@ another movement implementation. The examples below are checked into
152
181
  | TypeScript / JavaScript | `catalogue` (an array, not a function) | `sample(name, pip)` after `bindNative()` | `info.periodPips` | Call the custom bank's `dispose()` |
153
182
 
154
183
  **One beat is 64 pips.** Supply a signed 32-bit integer, not milliseconds or a
155
- floating-point beat number. Every sampler wraps negative as well as positive
184
+ floating-point beat number. Every legacy pattern sampler wraps negative as well as positive
156
185
  positions into its own loop. Offsets are `{x, y, z}`, dimensionless and bounded to
157
186
  `[-1, 1]`; choose the pixel/metre scale in your application. There is no automatic
158
187
  interpolation, BPM detection or audio playback inside a binding.
@@ -530,3 +559,56 @@ Apply an output to a bounded subject. DanceRudiments changes position only; it m
530
559
  ## Troubleshooting history
531
560
 
532
561
  See [build and release troubleshooting](docs/troubleshooting.md) for prior failures, causes and fixes.
562
+
563
+ ### Switch the shared visualizer between trajectories and bars
564
+
565
+ Use **Movement view → X–Y plot / Bar graph**. Bar view uses the same native
566
+ C++/WebAssembly sample and the existing clock, shows **every dimension** as a
567
+ separate signed bar, and keeps comparison cards on one common scale. Switching
568
+ views does not interrupt playback or change the underlying recording. The
569
+ selection is local to your browser; long dimension sets scroll horizontally.
570
+ This applies to catalogue/release pages and recording harnesses exported by
571
+ StemLab. Rebuild old generated pages to include the control. See
572
+ [recording visualizer controls](docs/recordings.md#trajectory-or-all-dimension-bars).
573
+
574
+
575
+ ## Coordinated releases and latest-release dependencies
576
+
577
+ Keep `DanceRudiments`, `react-timeline-sequence`, and `stemlab` as sibling
578
+ checkouts. The release order is **React Timeline Sequence → DanceRudiments →
579
+ StemLab**: publish and verify the companions before updating the consuming app.
580
+ StemLab must use their **latest stable official releases**, not an unreleased
581
+ commit, sibling source package or vendored npm archive. Each build records exact
582
+ released versions/commits/integrity after latest-release resolution so all its
583
+ jobs use the same bytes; CI fails clearly when that snapshot is stale.
584
+
585
+ From the parent folder, preview and then explicitly execute:
586
+
587
+ ```powershell
588
+ .\stemlab\scripts\Release-DanceFlow.ps1 -ProjectsRoot .
589
+ .\stemlab\scripts\Release-DanceFlow.ps1 -ProjectsRoot . -Execute
590
+ ```
591
+
592
+ For the first migration use the same commands with
593
+ `.\release-kit\Release-DanceFlow.ps1` after extracting the complete kit. It
594
+ applies the three repository patches, updates versions, performs scoped commits
595
+ and normal pushes, waits for exact-commit CI, then creates **new** release tags.
596
+ The existing GitHub Actions workflows publish packages; no protection rules,
597
+ credentials, old tags or assets are changed. The initial expected next-minor
598
+ versions are only a preview proposal, never a claim that they are published.
599
+
600
+ Use Node.js 24, Python 3.11+, PowerShell 5.1+, Git and authenticated GitHub CLI.
601
+ Required main/registry/Pages jobs must pass; missing approvals and partial
602
+ publication are blockers, not success. The default script releases all three
603
+ projects, is opt-in via `-Execute`, and supports a saved exact-source `-Resume`
604
+ session. A changed checkout or pre-commit build failure requires review rather
605
+ than an automatic stash/reset or guessed recovery.
606
+
607
+ StemLab's helper updates npm manifests/lock, optional native dependency minimum,
608
+ release/build metadata and the genuinely rebuilt JS/CSS together. Its daily
609
+ release-update workflow proposes subsequent updates by PR without auto-merging.
610
+ A tagged StemLab release generates all four Arcadians pages and validates actual
611
+ Pages deployment, live HTML and seekable audio. Do not start a duplicate manual
612
+ Arcadians analysis during that tagged build. Read
613
+ **[the full release, resume, verification and troubleshooting guide](docs/released-viewers.md)**
614
+ before running it. Keep this README and AGENTS.md synchronized with future changes.
@@ -2,7 +2,7 @@
2
2
  <!-- Kieran Simkin / DanceFlow: https://kieransimkin.co.uk/my-songs/ -->
3
3
  <PropertyGroup>
4
4
  <TargetFramework>net8.0</TargetFramework>
5
- <Version>0.2.4</Version>
5
+ <Version>0.3.0</Version>
6
6
  <PackageId>DanceRudiments</PackageId>
7
7
  <Authors>Kieran Simkin</Authors>
8
8
  <Description>Deterministic rhythmic position functions with native C# bindings. Music: https://kieransimkin.co.uk/my-songs/</Description>
@@ -3,6 +3,7 @@
3
3
  #include <utility>
4
4
  #include "dancerudiments/dance_rudiments.hpp"
5
5
  #include "dancerudiments/sampled_pattern.hpp"
6
+ #include "dancerudiments/recording.hpp"
6
7
 
7
8
  namespace py = pybind11;
8
9
  using namespace dancerudiments;
@@ -11,6 +12,16 @@ using namespace pybind11::literals;
11
12
  PYBIND11_MODULE(dancerudiments, m) {
12
13
  m.doc() = "Integer-pip rhythmic position functions";
13
14
  m.attr("PIPS_PER_BEAT") = pips_per_beat;
15
+ py::class_<SampledRecording>(m, "SampledRecording")
16
+ .def(py::init<std::string, std::vector<std::string>, const std::vector<std::vector<double>>&, double, double, int>(),
17
+ py::arg("name"), py::arg("dimensions"), py::arg("samples"), py::arg("bpm"), py::arg("beat_zero_seconds")=0., py::arg("start_pip")=0)
18
+ .def_property_readonly("name", &SampledRecording::name)
19
+ .def_property_readonly("dimensions", &SampledRecording::dimensions)
20
+ .def_property_readonly("sample_count", &SampledRecording::sample_count)
21
+ .def_property_readonly("bpm", &SampledRecording::bpm)
22
+ .def_property_readonly("beat_zero_seconds", &SampledRecording::beat_zero_seconds)
23
+ .def_property_readonly("start_pip", &SampledRecording::start_pip)
24
+ .def("sample", &SampledRecording::sample, py::arg("pip").noconvert());
14
25
  py::class_<Offset3>(m, "Offset3")
15
26
  .def_readonly("x", &Offset3::x).def_readonly("y", &Offset3::y).def_readonly("z", &Offset3::z)
16
27
  .def("as_tuple", [](const Offset3& v) { return py::make_tuple(v.x, v.y, v.z); });
@@ -0,0 +1,196 @@
1
+ # N-dimensional BPM-synchronised recordings and the shared visualizer
2
+
3
+ This additive API represents **non-looping named dimensions**, sampled at **64
4
+ pips per quarter-note beat**, with mandatory BPM provenance. It does not change
5
+ `Offset3`, the built-in catalogue, existing XYZ compiled-pack schema, or legacy
6
+ loop rules. Audio DSP remains in StemLab, not in this library.
7
+
8
+ ## Contract
9
+
10
+ `dancerudiments.recording` schema 1 declares `sample_dtype: float64` and
11
+ `sample_decimals: 12`. Components are finite, within [-1,1], and rounded to twelve
12
+ decimal places. Native code checks the IEEE-754 binary64 contract. JSON numbers
13
+ are not PCM audio samples with a 16/24-bit depth.
14
+
15
+ Each recording requires a finite positive BPM, `bpm_status` (`estimated` or
16
+ `confirmed`), and nonempty `bpm_source`. `phase_status` is separately `unverified`,
17
+ `estimated` or `supplied`; confirming BPM does not confirm the beat origin.
18
+
19
+ ```
20
+ time(i) = beat_zero_seconds + (start_pip + i) * 60 / (64 * bpm)
21
+ ```
22
+
23
+ A row is one pip window/sample; `start_pip` may be negative. `RecordingClock`
24
+ uses fractions derived from the decimal BPM instead of repeatedly rounded hops.
25
+ The rate in seconds is 128 Hz at 120 BPM. This contract is constant-tempo, not a
26
+ variable-tempo map; no default BPM, automatic beat detection or phase guessing.
27
+
28
+ ```json
29
+ {
30
+ "format": "dancerudiments.recording",
31
+ "schema_version": 1,
32
+ "pips_per_beat": 64,
33
+ "sample_dtype": "float64",
34
+ "sample_decimals": 12,
35
+ "name": "four_axes",
36
+ "dimensions": ["bass", "treble", "drums", "vocals"],
37
+ "clock": {
38
+ "bpm": 145.0,
39
+ "bpm_status": "estimated",
40
+ "bpm_source": "artist estimate",
41
+ "beat_zero_seconds": 0.0,
42
+ "phase_status": "unverified",
43
+ "start_pip": 0
44
+ },
45
+ "samples": [[0.2, 0.6, 0.9, 0.1], [0.3, 0.4, 0.2, 0.7]],
46
+ "provenance": {"producer": "example"}
47
+ }
48
+ ```
49
+
50
+ Dimension identifiers are unique `[a-z][a-z0-9_]{0,127}` strings; every row has
51
+ exactly their number of values, in the declared order. Unknown fields, malformed
52
+ headers, ragged rows, non-finite data/metadata, duplicate JSON keys, missing BPM
53
+ and invalid dimensions fail validation. At least one row/dimension is required.
54
+ The resource guards are 8,388,608 scalar values and 64 MiB per JSON document,
55
+ not a fixed XYZ dimension ceiling. Recordings can exceed 65,535 pips. Clock/index
56
+ validation keeps recorded absolute pips representable by the native int API.
57
+
58
+ ## Python and C++ playback
59
+
60
+ ```python
61
+ from dancerudiments_authoring import load_recording, write_recording
62
+ r = load_recording("recordings/four_axes.json")
63
+ write_recording("copy.json", r) # refuses replacement unless explicitly requested
64
+ native = r.to_native() # real C++ SampledRecording, not a Python fallback
65
+ values = native.sample(r.clock.start_pip)
66
+ assert len(values) == len(r.dimensions)
67
+ ```
68
+
69
+ `SampledRecording` owns and validates the data and returns **all dimensions**.
70
+ An out-of-range absolute pip raises instead of looping. Name, dimensions, count,
71
+ BPM, beat origin and first pip are readonly metadata. A moved-from object cannot
72
+ be sampled. Data I/O is standard-library-only; Python native playback needs the
73
+ updated extension, and raises clearly with an older/missing build.
74
+
75
+ ```cpp
76
+ #include <dancerudiments/recording.hpp>
77
+ using namespace dancerudiments;
78
+ SampledRecording r("four_axes", {"bass","treble","drums","vocals"},
79
+ {{.2,.6,.9,.1}, {.3,.4,.2,.7}}, 145.0, 0.0, 0);
80
+ auto values = r.sample(1); // four doubles, non-looping
81
+ ```
82
+
83
+ `recording_view.hpp` supplies the dependency-free `recording_component` accessor
84
+ shared by this C++ object and the generic recording WASM. The browser does not
85
+ regenerate mathematical curves or run a JavaScript motion fallback.
86
+
87
+ ## Generate the actual DanceRudiments comparison harness
88
+
89
+ ```python
90
+ from pathlib import Path
91
+ from dancerudiments_authoring.harness import build_recording_harness
92
+ from dancerudiments_authoring import load_recording
93
+ paths = sorted(Path("output/recordings").glob("*.json"))
94
+ report = build_recording_harness((load_recording(p) for p in paths),
95
+ "output/index.html", collection_id="my-audio-batch")
96
+ ```
97
+
98
+ All candidates must share the exact clock and captured extent; their dimension
99
+ counts can differ. Input is consumed one recording at a time. Duplicate float64
100
+ columns are pooled by checksum, retaining all samples without repeating the same
101
+ stem/filter data for every candidate. Bounds are 1024 candidates and 128 MiB of
102
+ unique binary sample columns. The audit JSON reports native WASM/data/page hashes,
103
+ clock, counts, unique columns and byte size.
104
+
105
+ The builder uses **the same template, CSS, beat player and app as release/catalogue
106
+ pages**. Their former `tools/release_demo_assets` files now live under packaged
107
+ `python/dancerudiments_authoring/harness_assets`; `tools/release_demo.py` delegates
108
+ to this shared `render_template`. Do not create another renderer in StemLab.
109
+
110
+ The page retains search, original four-card comparison, review choices/notes and
111
+ review JSON import/download. Recording mode adds any-axis X/Y/Z projections and
112
+ JSON download links for files under the sibling `recordings/` directory. It locks
113
+ exported tempo/beat origin and stops at the end. Nothing autoplays. Paths are not
114
+ closed across a non-looping seam. Display-decimated traces do not change full
115
+ native samples. The exported JSON keeps axes not currently visible in the view.
116
+
117
+ Optional `audio` metadata may include a **plain local basename** `relative_path`
118
+ for a caller-copied track, plus its source hash and extent. The exporter does not
119
+ copy or upload music. Without included audio, the existing local-file picker or
120
+ silent transport can be used. Media playback uses the audio-element clock.
121
+ Source codec/security support is a browser concern; do not disable browser policy
122
+ to force loading. Review local storage is best-effort; download JSON for portability.
123
+
124
+ All HTML/JS/CSS, native WASM and sampled columns are embedded. No compiler/CDN or
125
+ external request is required to build/use a recording page. Keep any optional
126
+ media and recording JSON companions with the HTML for their controls to work.
127
+ The page is control-data inspection, not proof of comfortable full-body choreography.
128
+
129
+ ## Rebuilding the tiny generic native sampler
130
+
131
+ ```sh
132
+ python tools/build_recording_wasm.py
133
+ ```
134
+
135
+ This uses clang++/wasm-ld, targets freestanding wasm32, and stores base64 bytes plus
136
+ source/binary hashes in packaged assets. Rebuild when `recording_view.hpp` or
137
+ `tools/recording_wasm.cpp` changes. Normal exports verify the bundled WASM checksum;
138
+ end users need no compiler. CI rebuilds and checks for a stale committed asset.
139
+ The existing catalogue export still follows its original native snapshot workflow.
140
+
141
+ ## Explicit legacy XYZ conversion
142
+
143
+ ```python
144
+ from pathlib import Path
145
+ from dancerudiments_authoring import emit_json
146
+ pack = r.to_compiled_pack(dimensions=("bass", "treble"), start=0, count=2)
147
+ Path("xy.compiled.json").write_text(emit_json(pack), encoding="utf-8")
148
+ ```
149
+
150
+ Select 1..3 axes and at most 65,535 samples; missing XYZ components become zero.
151
+ A larger recording requires explicit axis/excerpt selection, never truncation.
152
+ The legacy sampler loops, so conversion retains a seam warning and source clock;
153
+ it does not smooth/close an arbitrary excerpt. The new native recording API does
154
+ not loop. Existing C#/TypeScript XYZ interfaces remain unchanged; this addition
155
+ provides N-D C++/Python and the recording harness, not new general N-D wrappers
156
+ for all languages.
157
+
158
+ ## Validation
159
+
160
+ ```sh
161
+ PYTHONPATH=python python -m unittest discover -s tests/python -p test_recording.py -v
162
+ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
163
+ cmake --build build --config Release
164
+ ctest --test-dir build -C Release --output-on-failure
165
+ node --test tests/typescript/beat-player.test.js
166
+ python tools/test_recording_harness.py ./test-harness/index.html --make-fixture
167
+ ```
168
+
169
+ Browser testing requires Playwright/Chromium. Normal mode tests local-file loading;
170
+ `--in-memory` is an explicitly labelled fallback only for restricted environments.
171
+ With copied audio, that fallback hydrates the exact bytes into the existing audio
172
+ element as a Blob, retaining actual decoding/transport/renderer/native WASM. Its
173
+ report does not claim file loading or audible listening QA. The checker samples
174
+ every candidate against retained JSON and tests transport, arbitrary-axis
175
+ projection, non-looping bounds, no autoplay, search/comparison and review download.
176
+ Set `DANCERUDIMENTS_REQUIRE_NATIVE=1` to reject missing native tests rather than skip.
177
+
178
+ Existing versions/tags are unchanged by this patch. Publish a fresh matching
179
+ release only after review and CI. This source API is not present in earlier
180
+ published packages.
181
+
182
+ ## Trajectory or all-dimension bars
183
+
184
+ The shared harness has a **Movement view** selector. X–Y plot is the default and
185
+ retains its existing selectable X/Y/Z projection. **Bar graph** draws one
186
+ signed bar for every recorded dimension (X/Y/Z for native catalogue patterns),
187
+ not just the selected projection. Positive values extend above zero and negative
188
+ values below it. The numerical value is the actual native sample; the existing
189
+ amplitude slider scales the geometry only. All bars share the same fixed scale.
190
+ Long channel sets scroll horizontally; no channels are silently dropped.
191
+
192
+ The selector affects catalogue and comparison cards without seeking, pausing,
193
+ changing the selected MIDI beat, changing review choices or rewriting recordings.
194
+ It is remembered per collection when browser storage is available. Offscreen bars
195
+ are released and reconstructed on demand, just like the existing canvas cache.
196
+ Rebuild exported HTML to get a new UI: already-published snapshots are immutable.