fourier-samples 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. fourier_samples-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +63 -0
  2. fourier_samples-0.1.0/.github/ISSUE_TEMPLATE/config.yml +8 -0
  3. fourier_samples-0.1.0/.github/ISSUE_TEMPLATE/device_request.yml +46 -0
  4. fourier_samples-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +21 -0
  5. fourier_samples-0.1.0/.github/dependabot.yml +11 -0
  6. fourier_samples-0.1.0/.github/pull_request_template.md +13 -0
  7. fourier_samples-0.1.0/.github/workflows/ci.yml +128 -0
  8. fourier_samples-0.1.0/.github/workflows/release.yml +64 -0
  9. fourier_samples-0.1.0/.gitignore +18 -0
  10. fourier_samples-0.1.0/AGENTS.md +13 -0
  11. fourier_samples-0.1.0/CHANGELOG.md +125 -0
  12. fourier_samples-0.1.0/CLAUDE.md +355 -0
  13. fourier_samples-0.1.0/CODE_OF_CONDUCT.md +134 -0
  14. fourier_samples-0.1.0/CONTRIBUTING.md +93 -0
  15. fourier_samples-0.1.0/LICENSE +202 -0
  16. fourier_samples-0.1.0/NOTICE +10 -0
  17. fourier_samples-0.1.0/PKG-INFO +327 -0
  18. fourier_samples-0.1.0/README.md +273 -0
  19. fourier_samples-0.1.0/SECURITY.md +39 -0
  20. fourier_samples-0.1.0/THIRD_PARTY_NOTICES.md +60 -0
  21. fourier_samples-0.1.0/config/devices/digitakt_2.yaml +90 -0
  22. fourier_samples-0.1.0/config/devices/generic_44k.yaml +21 -0
  23. fourier_samples-0.1.0/config/devices/generic_48k.yaml +21 -0
  24. fourier_samples-0.1.0/config/devices/generic_folder.yaml +21 -0
  25. fourier_samples-0.1.0/config/devices/generic_sd_card.yaml +25 -0
  26. fourier_samples-0.1.0/config/devices/m8_tracker.yaml +118 -0
  27. fourier_samples-0.1.0/config/presets/ambient-cinematic.yaml +35 -0
  28. fourier_samples-0.1.0/config/presets/balanced.yaml +12 -0
  29. fourier_samples-0.1.0/config/presets/breaks-acid.yaml +16 -0
  30. fourier_samples-0.1.0/config/presets/hiphop-lofi.yaml +29 -0
  31. fourier_samples-0.1.0/config/presets/house-techno.yaml +26 -0
  32. fourier_samples-0.1.0/config/presets/trap.yaml +30 -0
  33. fourier_samples-0.1.0/config/providers/path.yaml +60 -0
  34. fourier_samples-0.1.0/config/providers/sononym.yaml +36 -0
  35. fourier_samples-0.1.0/config/taxonomy.yaml +711 -0
  36. fourier_samples-0.1.0/config/tunables.yaml +388 -0
  37. fourier_samples-0.1.0/conftest.py +138 -0
  38. fourier_samples-0.1.0/docs/curation.md +977 -0
  39. fourier_samples-0.1.0/docs/design-history.md +58 -0
  40. fourier_samples-0.1.0/docs/device-profiles.md +174 -0
  41. fourier_samples-0.1.0/docs/guide.md +167 -0
  42. fourier_samples-0.1.0/docs/start.md +316 -0
  43. fourier_samples-0.1.0/docs/usage.md +322 -0
  44. fourier_samples-0.1.0/pyproject.toml +100 -0
  45. fourier_samples-0.1.0/scripts/capture_demo.py +59 -0
  46. fourier_samples-0.1.0/scripts/golden_compare.py +46 -0
  47. fourier_samples-0.1.0/scripts/golden_harness.py +268 -0
  48. fourier_samples-0.1.0/scripts/manifest_diff.py +20 -0
  49. fourier_samples-0.1.0/scripts/provider_report.py +161 -0
  50. fourier_samples-0.1.0/scripts/render_audit.py +71 -0
  51. fourier_samples-0.1.0/scripts/tunables_inventory.py +117 -0
  52. fourier_samples-0.1.0/src/fourier/__init__.py +28 -0
  53. fourier_samples-0.1.0/src/fourier/__main__.py +5 -0
  54. fourier_samples-0.1.0/src/fourier/analysis/__init__.py +14 -0
  55. fourier_samples-0.1.0/src/fourier/analysis/clap_features.py +442 -0
  56. fourier_samples-0.1.0/src/fourier/analysis/derived.py +248 -0
  57. fourier_samples-0.1.0/src/fourier/analysis/events.py +92 -0
  58. fourier_samples-0.1.0/src/fourier/analysis/key_detection.py +103 -0
  59. fourier_samples-0.1.0/src/fourier/analysis/librosa_features.py +257 -0
  60. fourier_samples-0.1.0/src/fourier/analysis/loop_trim.py +215 -0
  61. fourier_samples-0.1.0/src/fourier/analysis/resolution.py +70 -0
  62. fourier_samples-0.1.0/src/fourier/cli/__init__.py +24 -0
  63. fourier_samples-0.1.0/src/fourier/cli/__main__.py +3 -0
  64. fourier_samples-0.1.0/src/fourier/cli/_app.py +298 -0
  65. fourier_samples-0.1.0/src/fourier/cli/build.py +1403 -0
  66. fourier_samples-0.1.0/src/fourier/cli/config.py +317 -0
  67. fourier_samples-0.1.0/src/fourier/cli/db.py +305 -0
  68. fourier_samples-0.1.0/src/fourier/cli/devices.py +300 -0
  69. fourier_samples-0.1.0/src/fourier/cli/enrich.py +1604 -0
  70. fourier_samples-0.1.0/src/fourier/cli/ingest.py +267 -0
  71. fourier_samples-0.1.0/src/fourier/cli/opener.py +97 -0
  72. fourier_samples-0.1.0/src/fourier/cli/quiet.py +204 -0
  73. fourier_samples-0.1.0/src/fourier/cli/releases.py +582 -0
  74. fourier_samples-0.1.0/src/fourier/cli/review.py +271 -0
  75. fourier_samples-0.1.0/src/fourier/cli/search.py +513 -0
  76. fourier_samples-0.1.0/src/fourier/cli/setup.py +1957 -0
  77. fourier_samples-0.1.0/src/fourier/db/__init__.py +0 -0
  78. fourier_samples-0.1.0/src/fourier/db/migrations.py +131 -0
  79. fourier_samples-0.1.0/src/fourier/db/models.py +552 -0
  80. fourier_samples-0.1.0/src/fourier/db/session.py +307 -0
  81. fourier_samples-0.1.0/src/fourier/demo.py +384 -0
  82. fourier_samples-0.1.0/src/fourier/devices/__init__.py +0 -0
  83. fourier_samples-0.1.0/src/fourier/devices/convert.py +104 -0
  84. fourier_samples-0.1.0/src/fourier/devices/exporter.py +305 -0
  85. fourier_samples-0.1.0/src/fourier/devices/loader.py +399 -0
  86. fourier_samples-0.1.0/src/fourier/ingest/__init__.py +0 -0
  87. fourier_samples-0.1.0/src/fourier/ingest/ableton_tags.py +155 -0
  88. fourier_samples-0.1.0/src/fourier/ingest/chunks.py +92 -0
  89. fourier_samples-0.1.0/src/fourier/ingest/folder.py +159 -0
  90. fourier_samples-0.1.0/src/fourier/ingest/formats.py +65 -0
  91. fourier_samples-0.1.0/src/fourier/ingest/importer.py +802 -0
  92. fourier_samples-0.1.0/src/fourier/ingest/sononym.py +766 -0
  93. fourier_samples-0.1.0/src/fourier/ingest/walk.py +285 -0
  94. fourier_samples-0.1.0/src/fourier/knobs.py +559 -0
  95. fourier_samples-0.1.0/src/fourier/layers.py +400 -0
  96. fourier_samples-0.1.0/src/fourier/metadata/__init__.py +1 -0
  97. fourier_samples-0.1.0/src/fourier/metadata/providers.py +98 -0
  98. fourier_samples-0.1.0/src/fourier/metadata/resolve.py +357 -0
  99. fourier_samples-0.1.0/src/fourier/metadata/rows.py +519 -0
  100. fourier_samples-0.1.0/src/fourier/metadata/shadow.py +396 -0
  101. fourier_samples-0.1.0/src/fourier/metadata/sound.py +613 -0
  102. fourier_samples-0.1.0/src/fourier/metadata/store.py +208 -0
  103. fourier_samples-0.1.0/src/fourier/metadata/train.py +706 -0
  104. fourier_samples-0.1.0/src/fourier/metadata/vocab.py +52 -0
  105. fourier_samples-0.1.0/src/fourier/net.py +15 -0
  106. fourier_samples-0.1.0/src/fourier/packs/__init__.py +0 -0
  107. fourier_samples-0.1.0/src/fourier/packs/audiocache.py +175 -0
  108. fourier_samples-0.1.0/src/fourier/packs/audit.py +169 -0
  109. fourier_samples-0.1.0/src/fourier/packs/builddiff.py +225 -0
  110. fourier_samples-0.1.0/src/fourier/packs/curate.py +5550 -0
  111. fourier_samples-0.1.0/src/fourier/packs/curate_config.py +1233 -0
  112. fourier_samples-0.1.0/src/fourier/packs/device_lock.py +236 -0
  113. fourier_samples-0.1.0/src/fourier/packs/dryrun.py +131 -0
  114. fourier_samples-0.1.0/src/fourier/packs/golden.py +172 -0
  115. fourier_samples-0.1.0/src/fourier/packs/naming.py +685 -0
  116. fourier_samples-0.1.0/src/fourier/packs/progress.py +118 -0
  117. fourier_samples-0.1.0/src/fourier/packs/ratings.py +950 -0
  118. fourier_samples-0.1.0/src/fourier/packs/releases.py +467 -0
  119. fourier_samples-0.1.0/src/fourier/packs/render.py +687 -0
  120. fourier_samples-0.1.0/src/fourier/packs/report.py +209 -0
  121. fourier_samples-0.1.0/src/fourier/packs/review.py +300 -0
  122. fourier_samples-0.1.0/src/fourier/packs/rules.py +465 -0
  123. fourier_samples-0.1.0/src/fourier/packs/scale.py +123 -0
  124. fourier_samples-0.1.0/src/fourier/packs/sets.py +381 -0
  125. fourier_samples-0.1.0/src/fourier/packs/validate.py +186 -0
  126. fourier_samples-0.1.0/src/fourier/packs/vendors.py +238 -0
  127. fourier_samples-0.1.0/src/fourier/packs/verify.py +1073 -0
  128. fourier_samples-0.1.0/src/fourier/packs/why.py +566 -0
  129. fourier_samples-0.1.0/src/fourier/packs/why_log.py +212 -0
  130. fourier_samples-0.1.0/src/fourier/paths.py +52 -0
  131. fourier_samples-0.1.0/src/fourier/places.py +319 -0
  132. fourier_samples-0.1.0/src/fourier/platforms.py +308 -0
  133. fourier_samples-0.1.0/src/fourier/preparers.py +141 -0
  134. fourier_samples-0.1.0/src/fourier/safety.py +477 -0
  135. fourier_samples-0.1.0/src/fourier/settings.py +195 -0
  136. fourier_samples-0.1.0/src/fourier/synthlib.py +324 -0
  137. fourier_samples-0.1.0/src/fourier/taxonomy.py +335 -0
  138. fourier_samples-0.1.0/src/fourier/timings.py +173 -0
  139. fourier_samples-0.1.0/tests/__init__.py +0 -0
  140. fourier_samples-0.1.0/tests/data/cli_tree.json +3056 -0
  141. fourier_samples-0.1.0/tests/data/schema_v1/duckdb.sql +26 -0
  142. fourier_samples-0.1.0/tests/data/schema_v1/sqlite.sql +168 -0
  143. fourier_samples-0.1.0/tests/data/tunables_snapshot.json +8254 -0
  144. fourier_samples-0.1.0/tests/golden/synthetic-darwin.json +6851 -0
  145. fourier_samples-0.1.0/tests/golden/synthetic-linux.json +6851 -0
  146. fourier_samples-0.1.0/tests/golden/synthetic.toml +9 -0
  147. fourier_samples-0.1.0/tests/golden/synthetic_build.py +349 -0
  148. fourier_samples-0.1.0/tests/regressions/__init__.py +0 -0
  149. fourier_samples-0.1.0/tests/regressions/test_curate_regressions.py +274 -0
  150. fourier_samples-0.1.0/tests/regressions/test_ratings_regressions.py +236 -0
  151. fourier_samples-0.1.0/tests/regressions/test_releases_regressions.py +495 -0
  152. fourier_samples-0.1.0/tests/regressions/test_render_regressions.py +179 -0
  153. fourier_samples-0.1.0/tests/test_added_categories.py +144 -0
  154. fourier_samples-0.1.0/tests/test_analyze_selection.py +70 -0
  155. fourier_samples-0.1.0/tests/test_audiocache.py +109 -0
  156. fourier_samples-0.1.0/tests/test_audit_fixes.py +167 -0
  157. fourier_samples-0.1.0/tests/test_budget.py +119 -0
  158. fourier_samples-0.1.0/tests/test_builddiff.py +102 -0
  159. fourier_samples-0.1.0/tests/test_canonical_mapping.py +151 -0
  160. fourier_samples-0.1.0/tests/test_clap_offline.py +207 -0
  161. fourier_samples-0.1.0/tests/test_cli_tree.py +61 -0
  162. fourier_samples-0.1.0/tests/test_cloud_only.py +295 -0
  163. fourier_samples-0.1.0/tests/test_copies_and_moves_cli.py +121 -0
  164. fourier_samples-0.1.0/tests/test_curate.py +405 -0
  165. fourier_samples-0.1.0/tests/test_db.py +250 -0
  166. fourier_samples-0.1.0/tests/test_demo.py +141 -0
  167. fourier_samples-0.1.0/tests/test_device_citations.py +149 -0
  168. fourier_samples-0.1.0/tests/test_device_lock.py +166 -0
  169. fourier_samples-0.1.0/tests/test_device_profiles_user.py +252 -0
  170. fourier_samples-0.1.0/tests/test_events.py +96 -0
  171. fourier_samples-0.1.0/tests/test_export.py +148 -0
  172. fourier_samples-0.1.0/tests/test_fallback_routing.py +286 -0
  173. fourier_samples-0.1.0/tests/test_fallback_routing_cli.py +227 -0
  174. fourier_samples-0.1.0/tests/test_first_run.py +591 -0
  175. fourier_samples-0.1.0/tests/test_golden.py +181 -0
  176. fourier_samples-0.1.0/tests/test_golden_harness.py +185 -0
  177. fourier_samples-0.1.0/tests/test_guards_cli.py +376 -0
  178. fourier_samples-0.1.0/tests/test_instrument_cap.py +17 -0
  179. fourier_samples-0.1.0/tests/test_keys_routing.py +69 -0
  180. fourier_samples-0.1.0/tests/test_knobs.py +216 -0
  181. fourier_samples-0.1.0/tests/test_layers.py +188 -0
  182. fourier_samples-0.1.0/tests/test_library_walk.py +503 -0
  183. fourier_samples-0.1.0/tests/test_metadata_store.py +101 -0
  184. fourier_samples-0.1.0/tests/test_migrations.py +28 -0
  185. fourier_samples-0.1.0/tests/test_missing_library.py +225 -0
  186. fourier_samples-0.1.0/tests/test_naming.py +254 -0
  187. fourier_samples-0.1.0/tests/test_net.py +12 -0
  188. fourier_samples-0.1.0/tests/test_newcomer.py +223 -0
  189. fourier_samples-0.1.0/tests/test_other_libraries.py +285 -0
  190. fourier_samples-0.1.0/tests/test_other_libraries_cli.py +180 -0
  191. fourier_samples-0.1.0/tests/test_paths.py +92 -0
  192. fourier_samples-0.1.0/tests/test_places.py +126 -0
  193. fourier_samples-0.1.0/tests/test_platforms.py +88 -0
  194. fourier_samples-0.1.0/tests/test_preparers.py +147 -0
  195. fourier_samples-0.1.0/tests/test_presets.py +41 -0
  196. fourier_samples-0.1.0/tests/test_progress.py +30 -0
  197. fourier_samples-0.1.0/tests/test_provider_report.py +38 -0
  198. fourier_samples-0.1.0/tests/test_providers.py +169 -0
  199. fourier_samples-0.1.0/tests/test_prune_missing.py +59 -0
  200. fourier_samples-0.1.0/tests/test_public_commands.py +265 -0
  201. fourier_samples-0.1.0/tests/test_quiet_build.py +113 -0
  202. fourier_samples-0.1.0/tests/test_ratings.py +349 -0
  203. fourier_samples-0.1.0/tests/test_ratings_without_live.py +185 -0
  204. fourier_samples-0.1.0/tests/test_render.py +273 -0
  205. fourier_samples-0.1.0/tests/test_render_profile_fields.py +163 -0
  206. fourier_samples-0.1.0/tests/test_resolve.py +198 -0
  207. fourier_samples-0.1.0/tests/test_review.py +188 -0
  208. fourier_samples-0.1.0/tests/test_rows.py +140 -0
  209. fourier_samples-0.1.0/tests/test_safety.py +249 -0
  210. fourier_samples-0.1.0/tests/test_scale.py +195 -0
  211. fourier_samples-0.1.0/tests/test_second_library_cli.py +182 -0
  212. fourier_samples-0.1.0/tests/test_selection.py +728 -0
  213. fourier_samples-0.1.0/tests/test_sets.py +320 -0
  214. fourier_samples-0.1.0/tests/test_settings.py +147 -0
  215. fourier_samples-0.1.0/tests/test_setup_commands.py +1027 -0
  216. fourier_samples-0.1.0/tests/test_shadow.py +165 -0
  217. fourier_samples-0.1.0/tests/test_sononym.py +224 -0
  218. fourier_samples-0.1.0/tests/test_sound_model.py +571 -0
  219. fourier_samples-0.1.0/tests/test_sources_apart.py +293 -0
  220. fourier_samples-0.1.0/tests/test_speed_caches.py +56 -0
  221. fourier_samples-0.1.0/tests/test_synthetic_golden.py +63 -0
  222. fourier_samples-0.1.0/tests/test_taxonomy.py +1538 -0
  223. fourier_samples-0.1.0/tests/test_taxonomy_file.py +62 -0
  224. fourier_samples-0.1.0/tests/test_timings.py +25 -0
  225. fourier_samples-0.1.0/tests/test_tunables_inventory.py +53 -0
  226. fourier_samples-0.1.0/tests/test_validate.py +136 -0
  227. fourier_samples-0.1.0/tests/test_verify.py +293 -0
  228. fourier_samples-0.1.0/tests/test_why.py +204 -0
  229. fourier_samples-0.1.0/uv.lock +2329 -0
@@ -0,0 +1,63 @@
1
+ name: Bug report
2
+ description: Something Fourier Samples did wrong, or a crash
3
+ labels: [bug]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ Thanks for the report. Please leave out personal details (names, paths with your user
9
+ name, your library's pack names) and don't attach audio you don't have the rights to
10
+ share. A security problem goes to the private form instead: Security tab, "Report a
11
+ vulnerability".
12
+ - type: textarea
13
+ id: what-happened
14
+ attributes:
15
+ label: What happened
16
+ description: The command you ran and what it did. Paste the error or traceback if there was one.
17
+ render: text
18
+ validations:
19
+ required: true
20
+ - type: textarea
21
+ id: expected
22
+ attributes:
23
+ label: What you expected
24
+ validations:
25
+ required: true
26
+ - type: textarea
27
+ id: reproduce
28
+ attributes:
29
+ label: Steps to reproduce
30
+ description: If it happens with `fourier demo` too, say so; that's the easiest case to reproduce.
31
+ - type: input
32
+ id: version
33
+ attributes:
34
+ label: fourier --version
35
+ placeholder: fourier (Fourier Samples) 0.1.0
36
+ validations:
37
+ required: true
38
+ - type: input
39
+ id: os
40
+ attributes:
41
+ label: OS and version
42
+ placeholder: macOS 15.3 (Apple silicon), Ubuntu 24.04, ...
43
+ validations:
44
+ required: true
45
+ - type: input
46
+ id: python
47
+ attributes:
48
+ label: Python version
49
+ placeholder: "3.12.8"
50
+ validations:
51
+ required: true
52
+ - type: textarea
53
+ id: doctor
54
+ attributes:
55
+ label: fourier doctor output
56
+ description: The output of `fourier doctor` (edit out paths you'd rather not share).
57
+ render: text
58
+ - type: input
59
+ id: device
60
+ attributes:
61
+ label: Device and firmware
62
+ description: If it's about a render or sync.
63
+ placeholder: The device and its OS or firmware version
@@ -0,0 +1,8 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: A question, or help getting started
4
+ url: https://github.com/bvk7787/fourier-samples/discussions/categories/q-a
5
+ about: Ask in Discussions (Q&A). Include what you ran and what `fourier doctor` says.
6
+ - name: Share a device profile you made
7
+ url: https://github.com/bvk7787/fourier-samples/discussions/categories/show-and-tell
8
+ about: Profiles from `fourier devices new`, tried on your sampler, before they're cited from its manual.
@@ -0,0 +1,46 @@
1
+ name: Device request
2
+ description: Ask for a profile for a sampler Fourier Samples doesn't render for yet
3
+ labels: [device]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ A profile cites the device's manual for every value (docs/device-profiles.md). The
9
+ details below are what a profile needs; if you can draft one yourself, a pull request
10
+ is welcome. Until then, `generic_44k` or `generic_48k` renders a usable library.
11
+ - type: input
12
+ id: device
13
+ attributes:
14
+ label: Device
15
+ placeholder: Maker and model
16
+ validations:
17
+ required: true
18
+ - type: input
19
+ id: manual
20
+ attributes:
21
+ label: Manual
22
+ description: A link to the maker's current manual (PDF), and its firmware or OS version.
23
+ validations:
24
+ required: true
25
+ - type: textarea
26
+ id: format
27
+ attributes:
28
+ label: Sample format and limits
29
+ description: |
30
+ What you know, with manual pages if you have them: sample rate and bit depth, mono or
31
+ stereo, file formats, length or slice limits, path or file-name length limits, folder
32
+ depth or files per folder, and storage size.
33
+ validations:
34
+ required: true
35
+ - type: input
36
+ id: load
37
+ attributes:
38
+ label: How samples get onto it
39
+ description: An SD card or USB drive mounted on the computer, a transfer app, copying into a project, or something else.
40
+ validations:
41
+ required: true
42
+ - type: textarea
43
+ id: notes
44
+ attributes:
45
+ label: Anything else
46
+ description: Folder layout the device expects, how its browser sorts, quirks you've hit.
@@ -0,0 +1,21 @@
1
+ name: Feature request
2
+ description: An idea for Fourier Samples
3
+ labels: [enhancement]
4
+ body:
5
+ - type: textarea
6
+ id: problem
7
+ attributes:
8
+ label: What you're trying to do
9
+ description: The problem or the workflow, not only the solution you have in mind.
10
+ validations:
11
+ required: true
12
+ - type: textarea
13
+ id: proposal
14
+ attributes:
15
+ label: What you'd like
16
+ validations:
17
+ required: true
18
+ - type: textarea
19
+ id: alternatives
20
+ attributes:
21
+ label: What you've tried or considered
@@ -0,0 +1,11 @@
1
+ # GitHub Actions only: the Python dependencies are pinned in uv.lock and updated by hand
2
+ # (the synthetic golden's audio holds only for the locked versions).
3
+ version: 2
4
+ updates:
5
+ - package-ecosystem: github-actions
6
+ directory: /
7
+ schedule:
8
+ interval: monthly
9
+ groups:
10
+ actions:
11
+ patterns: ["*"]
@@ -0,0 +1,13 @@
1
+ ## What this changes
2
+
3
+ <!-- What and why. Link the issue if there is one. -->
4
+
5
+ ## Checks
6
+
7
+ - [ ] Tests run (`pytest -q -n auto tests`), and a behavior change comes with a test
8
+ - [ ] For a curation change: the synthetic golden (`python tests/golden/synthetic_build.py /tmp/syn --check`); if the output is meant to change, regenerated with `--update` and explained above
9
+ - [ ] For a command, option or help-text change: `python tests/test_cli_tree.py --write`
10
+ - [ ] `ruff check src tests`
11
+ - [ ] Docs updated (README, CLAUDE.md, docs/) where the behavior they describe changed
12
+ - [ ] No personal data and no real samples: fixtures use generated audio and invented names (Acme, Northwind, ...), and no real vendor, pack or person is named
13
+ - [ ] For a device profile: every value cites a manual page or is marked a convention, and what I checked on the hardware is described above
@@ -0,0 +1,128 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: ci-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ # Actions minutes are free on a public repository and metered on a private one, where the
17
+ # maintainer runs the same checks locally: every job skips while the repository is private.
18
+ jobs:
19
+ test:
20
+ name: test (${{ matrix.os }}, ${{ matrix.python-version }})
21
+ if: ${{ !github.event.repository.private }}
22
+ runs-on: ${{ matrix.os }}
23
+ timeout-minutes: 30
24
+ continue-on-error: ${{ matrix.os == 'windows-latest' }} # Windows: best effort for now
25
+ strategy:
26
+ fail-fast: false
27
+ matrix:
28
+ os: [ubuntu-latest, macos-latest, windows-latest]
29
+ python-version: ["3.11", "3.12"]
30
+ exclude:
31
+ - os: macos-latest
32
+ python-version: "3.11"
33
+ - os: windows-latest
34
+ python-version: "3.11"
35
+ include:
36
+ - os: ubuntu-latest
37
+ python-version: "3.13"
38
+ steps:
39
+ - uses: actions/checkout@v4
40
+ - uses: actions/setup-python@v5
41
+ with:
42
+ python-version: ${{ matrix.python-version }}
43
+ cache: pip
44
+ # the versions in uv.lock: the synthetic golden's audio holds only for these
45
+ # (CLAP and torch left out: the tests use a stand-in)
46
+ - name: Install (locked versions; core + dev)
47
+ shell: bash
48
+ run: |
49
+ python -m pip install --upgrade pip uv
50
+ uv export --frozen --no-hashes --extra dev --no-emit-project > "$RUNNER_TEMP/requirements.lock.txt"
51
+ pip install -r "$RUNNER_TEMP/requirements.lock.txt"
52
+ pip install -e . --no-deps
53
+ - name: Lint
54
+ run: ruff check src tests
55
+ - name: Tests
56
+ run: pytest -q -n auto tests # one worker per CPU (pytest-xdist; conftest.py isolates each)
57
+ - name: Synthetic golden build
58
+ shell: bash
59
+ run: python tests/golden/synthetic_build.py "$RUNNER_TEMP/synthetic" --check
60
+ # the variants below test the pipeline, not the Python version: 3.13 runs the golden only
61
+ - name: Without Sononym, without Live, without both
62
+ if: matrix.python-version != '3.13'
63
+ shell: bash
64
+ run: |
65
+ python tests/golden/synthetic_build.py "$RUNNER_TEMP/no-sononym" --without sononym
66
+ python tests/golden/synthetic_build.py "$RUNNER_TEMP/no-live" --without ableton
67
+ python tests/golden/synthetic_build.py "$RUNNER_TEMP/neither" --without sononym,ableton
68
+ - name: Each preset builds and passes verify
69
+ if: matrix.python-version != '3.13'
70
+ shell: bash
71
+ run: |
72
+ python tests/golden/synthetic_build.py "$RUNNER_TEMP/breaks-acid" --preset breaks-acid
73
+ python tests/golden/synthetic_build.py "$RUNNER_TEMP/balanced" --preset balanced
74
+ - name: A small library's master scales to it
75
+ if: matrix.python-version != '3.13'
76
+ shell: bash
77
+ run: python tests/golden/synthetic_build.py "$RUNNER_TEMP/scaled" --scale --preset balanced
78
+ - name: An interrupted build resumes to the same result
79
+ if: matrix.python-version != '3.13'
80
+ shell: bash
81
+ run: python tests/golden/synthetic_build.py "$RUNNER_TEMP/resume" --check --interrupt KICKS,PADS,VOX
82
+
83
+ package:
84
+ # what `pip install` gives a user: the sdist and wheel build, their metadata passes twine,
85
+ # and the wheel installed with its declared dependencies (no checkout, no [clap]) runs
86
+ name: package (build, install, run)
87
+ if: ${{ !github.event.repository.private }}
88
+ runs-on: ubuntu-latest
89
+ timeout-minutes: 20
90
+ steps:
91
+ - uses: actions/checkout@v4
92
+ - uses: actions/setup-python@v5
93
+ with:
94
+ python-version: "3.12"
95
+ cache: pip
96
+ - name: Build the sdist and wheel
97
+ run: |
98
+ python -m pip install --upgrade pip uv
99
+ uv build
100
+ ls -l dist
101
+ - name: Check the metadata
102
+ run: uvx twine check --strict dist/*
103
+ - name: Check what the wheel holds
104
+ run: |
105
+ python -m zipfile -l dist/*.whl > "$RUNNER_TEMP/wheel.txt"
106
+ for f in fourier/__main__.py fourier/demo.py fourier/_config/tunables.yaml \
107
+ fourier/_config/devices/m8_tracker.yaml licenses/LICENSE licenses/NOTICE; do
108
+ grep -q "$f" "$RUNNER_TEMP/wheel.txt" || { echo "missing from the wheel: $f"; exit 1; }
109
+ done
110
+ - name: Install the wheel into a clean venv
111
+ run: |
112
+ python -m venv "$RUNNER_TEMP/venv"
113
+ "$RUNNER_TEMP/venv/bin/pip" install --upgrade pip
114
+ "$RUNNER_TEMP/venv/bin/pip" install dist/*.whl
115
+ - name: Run the installed command
116
+ working-directory: ${{ runner.temp }} # away from the checkout's src/ and config/
117
+ shell: bash
118
+ run: |
119
+ export PATH="$RUNNER_TEMP/venv/bin:$PATH" FOURIER_HOME="$RUNNER_TEMP/home"
120
+ fourier --version
121
+ python -m fourier --help
122
+ fourier devices list
123
+ # no library and no database here, so doctor reports FAILs and exits non-zero:
124
+ # it only has to run to the end
125
+ fourier doctor > doctor.txt 2>&1 || true
126
+ cat doctor.txt
127
+ if grep -q Traceback doctor.txt; then exit 1; fi
128
+ fourier demo --dir "$RUNNER_TEMP/demo"
@@ -0,0 +1,64 @@
1
+ name: Release
2
+
3
+ # A version tag (v0.1.0) builds the package and publishes it to PyPI with Trusted Publishing:
4
+ # no token is stored anywhere. PyPI trusts this workflow once the project's trusted publisher
5
+ # names it (github.com/bvk7787/fourier-samples, workflow release.yml, environment pypi).
6
+ # The tag must match the version in pyproject.toml. Nothing runs while the repository is private.
7
+
8
+ on:
9
+ push:
10
+ tags: ["v*"]
11
+ workflow_dispatch:
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ jobs:
17
+ build:
18
+ if: ${{ !github.event.repository.private }}
19
+ runs-on: ubuntu-latest
20
+ timeout-minutes: 15
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+ - uses: actions/setup-python@v5
24
+ with:
25
+ python-version: "3.12"
26
+ - name: The tag matches the version
27
+ if: startsWith(github.ref, 'refs/tags/')
28
+ shell: bash
29
+ run: |
30
+ want="v$(python -c 'import tomllib; print(tomllib.load(open("pyproject.toml","rb"))["project"]["version"])')"
31
+ [ "$GITHUB_REF_NAME" = "$want" ] || { echo "tag $GITHUB_REF_NAME, pyproject says $want"; exit 1; }
32
+ - name: Build the sdist and wheel
33
+ run: |
34
+ python -m pip install --upgrade pip build twine
35
+ python -m build
36
+ twine check --strict dist/*
37
+ - name: The wheel installs and runs
38
+ shell: bash
39
+ run: |
40
+ python -m venv "$RUNNER_TEMP/v"
41
+ "$RUNNER_TEMP/v/bin/pip" install dist/*.whl
42
+ "$RUNNER_TEMP/v/bin/fourier" --version
43
+ "$RUNNER_TEMP/v/bin/fourier" demo --dir "$RUNNER_TEMP/demo"
44
+ - uses: actions/upload-artifact@v4
45
+ with:
46
+ name: dist
47
+ path: dist/
48
+
49
+ publish:
50
+ needs: build
51
+ if: ${{ !github.event.repository.private && startsWith(github.ref, 'refs/tags/') }}
52
+ runs-on: ubuntu-latest
53
+ timeout-minutes: 10
54
+ environment:
55
+ name: pypi
56
+ url: https://pypi.org/project/fourier-samples/
57
+ permissions:
58
+ id-token: write # Trusted Publishing: PyPI checks this workflow's identity
59
+ steps:
60
+ - uses: actions/download-artifact@v4
61
+ with:
62
+ name: dist
63
+ path: dist/
64
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,18 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .venv/
5
+ venv/
6
+ build/
7
+ dist/
8
+ .pytest_cache/
9
+ .ruff_cache/
10
+ .python-version
11
+ .DS_Store
12
+ # device manual PDFs kept beside the checkout (makers' copyright; never committed)
13
+ docs/manuals/
14
+ # a local configuration
15
+ fourier.toml
16
+ # local overrides
17
+ /local/
18
+ /CLAUDE.local.md
@@ -0,0 +1,13 @@
1
+ # Instructions for coding agents
2
+
3
+ Helping someone *use* Fourier Samples (their library, a build, a device)? Read
4
+ [skills/fourier/SKILL.md](skills/fourier/SKILL.md). Changing its code? Read on.
5
+
6
+ - Read [CLAUDE.md](CLAUDE.md) first: the ground rules (the library's files are never changed,
7
+ device facts come from the manuals, no one's library in the code), the architecture, and the
8
+ checks to run before a change is done.
9
+ - Adding or changing a device profile: [docs/device-profiles.md](docs/device-profiles.md).
10
+ - Contributing (tests, the CLI snapshot, what a pull request needs):
11
+ [CONTRIBUTING.md](CONTRIBUTING.md).
12
+ - Ratings don't need Ableton Live: `fourier review rate` and `fourier review import` write the
13
+ same store Live's tag harvest writes (packs/ratings.py).
@@ -0,0 +1,125 @@
1
+ # Changelog
2
+
3
+ All notable changes to Fourier Samples are recorded here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow
5
+ [Semantic Versioning](https://semver.org/spec/v2.0.0.html) (before 1.0, a minor version may
6
+ change the CLI or the config).
7
+
8
+ ## [0.1.0] - 2026-10-04
9
+
10
+ The first public release.
11
+
12
+ ### Added
13
+
14
+ - **The command line, for people new to it.** `fourier --help` lists seventeen commands in
15
+ sections (Get started, Build and load, Releases, Look inside, Settings, Listen and rate,
16
+ Advanced). `fourier setup` is a step-by-step wizard (or `--yes` with flags): the sample
17
+ folders (counted, with a warning for cloud-only files), devices and style ("What do you
18
+ make?"), output folders, the config file, the CLAP model (it installs PyTorch and
19
+ Transformers into the running environment with uv or pip, the CPU-only PyTorch on Linux
20
+ without an NVIDIA GPU, and downloads the pinned model), an optional local LLM through Ollama,
21
+ then doctor's checks and the first build. `fourier build` (every category; or one) scans the
22
+ library and analyzes what's new before it builds (`--no-scan` skips both), with stage headers
23
+ and an estimate up front; on a terminal its Build and Verify stages show progress and a
24
+ summary (what it built, what it left empty, verify's result), with everything else in a log
25
+ in `<home>/logs` (`--verbose` prints it all). The steps underneath are `fourier tools` (`scan`, `analyze`,
26
+ `train`, `audit`, `import-folder`, `dedup`, `resolve`, `db-stats`); `publish --dry-run` and
27
+ `render --dry-run` preview what a release or a render would change.
28
+ - **Curation.** `fourier build` turns a sample library into one master of
29
+ `CATEGORY/family/file.wav` folders: nineteen categories in play order (an overlay can add
30
+ more), families clustered from CLAP embeddings and named for what they sound like, drum kits
31
+ (`KITS`) and slice-ready loops (`SLICE`), and a manifest recording where every file came from
32
+ and why it's there. Builds are deterministic and stable (a rule change moves only the files
33
+ it touches), resumable (`--resume`), and swapped into place only when every category built
34
+ and verify passed. A category the library has too few samples for is left empty and named,
35
+ not a failure.
36
+ - **The master's size follows the library** (`scale = "library"`): a style's budgets are for a
37
+ large library, and a smaller one gets a master in proportion. `fourier doctor`, setup and
38
+ `build --dry-run` say how big it will be. `scale = "off"` and `files = N` keep fixed budgets;
39
+ `size` caps them to fit a device.
40
+ - **Export DSP.** DC offset, phase-flipped stereo, clicks at the end, hot starts, long tails,
41
+ per-folder loudness, and notes a few cents off are fixed on the way into the master.
42
+ - **Any library layout, with or without other apps.** A build's scan reads Sononym's analysis
43
+ and Ableton Live's auto-tags when they're there, and otherwise walks the library folders.
44
+ Neither app is needed: built-in `path` and `audio` classifiers (folder and file names, WAV
45
+ chunks, and the audio itself) stand in, with your own words (`words`), and `vendors = "auto"`
46
+ reads each folder as vendor/pack folders, folders by sound type, an umbrella of packs or a
47
+ flat folder. Files no name rule recognizes are placed by sound: by CLAP's prompts, and by a
48
+ sound model trained on your own library (below). Fourier's own analysis (librosa features,
49
+ CLAP embeddings, sample-chain events, loop trims, pYIN roots and keys) runs with or without
50
+ Sononym and is stored apart from Sononym's; a resolver records which source each value a
51
+ build uses came from. The library's own files are never changed.
52
+ - **A sound model trained on your library** (without Sononym). The first build trains a small
53
+ classifier over each sample's CLAP embedding and Fourier's own measurements, taught by how
54
+ your sample makers named their files and folders and by your ratings (never by Sononym's or
55
+ Live's analysis), once the names label enough samples; it retrains when the library has grown
56
+ a lot, and keeps it only when it's reliable on the packs it didn't learn from; `fourier tools
57
+ train` trains it any time. It places what no name rule recognizes when it's confident, and
58
+ its weights and report stay in your Fourier home. Fourier ships no
59
+ weights: they'd be learned from licensed samples. `fourier setup --no-sound-model` turns it
60
+ off.
61
+ - **Cloud-synced libraries.** `fourier setup` and `fourier doctor` count the files a cloud
62
+ drive keeps only online, the analysis skips them (`fourier tools analyze --download` fetches
63
+ them first), and a build downloads the ones it picks before reading any and stops, writing
64
+ nothing, if one won't download.
65
+ - **A second library** can share the Fourier home: another config (`fourier setup --to
66
+ other.toml`) gets its own master, renders and releases.
67
+ - **Checks and explanations.** `fourier verify` checks the master against every curation rule
68
+ (and, with `--render`, scratch device renders); `fourier why` says where a sample landed and
69
+ which rule put it there, or why it was left out, and `why --unrecognized` lists what no rule
70
+ recognized; `fourier diff` compares two builds.
71
+ - **Releases and devices.** `fourier publish` cuts immutable releases; `fourier render` converts
72
+ the master or a release for a device; a released render locks its paths on that device, so
73
+ later releases never move them. `fourier sync` copies a render onto an SD card without
74
+ macOS's `._` files.
75
+ - **Device profiles** for the Elektron Digitakt 2 and the Dirtywave M8, every value citing a
76
+ page of the device's manual or marked as a convention or unverified, plus `generic_44k`,
77
+ `generic_48k`, `generic_folder` and `generic_sd_card`. `fourier devices new` writes a profile
78
+ for your own device; `fourier devices list|show`.
79
+ - **Configuration.** Style presets (`balanced`, the default and the code's own settings,
80
+ `breaks-acid`, which the other genre styles build on, `hiphop-lofi`, `house-techno`,
81
+ `ambient-cinematic`, `trap`), fifteen knobs (`devices`, `categories`, `tempo`,
82
+ `fold`, `scale`, `files`, `size`, `loudness`, `retune`, `stereo`, `names`, `vendors`, `words`,
83
+ `sets`, `sources`), overlays and `--set KEY=VALUE` layer over the code's defaults; `fourier
84
+ config show|explain` prints what resolved and from where. `fourier doctor` checks what a
85
+ build needs, fails on anything that would stop one, and estimates how long it takes.
86
+ - **The listening loop.** Rate the master's files Keep, Drop or Misfiled with `fourier review
87
+ rate` or `review import` (a CSV file), or with tags in Ableton Live's browser; `fourier
88
+ review` scores a build against the ratings and queues the files whose rating teaches the
89
+ most. Every build follows the ratings.
90
+ - **Search.** `fourier search` by words or a reference file (CLAP), or by duration, tempo and
91
+ descriptors.
92
+ - **`fourier demo`**: the whole pipeline on a generated library, in a sandbox folder with its own
93
+ home and config, with no samples, model or download needed.
94
+ - **Safety guards.** Commands that write or delete refuse a library folder, a release, the
95
+ home folder, a volume root and any folder they didn't make: a build stops on a master holding
96
+ files it didn't make and lists them, renders and the demo replace only folders they marked as
97
+ theirs, and `fourier sync --delete` removes only what it copied to that card. A first run's
98
+ usual errors are one line saying what to run, not a traceback.
99
+ - **Preparer plug-ins** (`fourier.preparers` entry points) for open formats soundfile doesn't
100
+ read.
101
+ - **For people new to Terminal**: a step-by-step getting-started page for the Mac
102
+ (`docs/start.md`); setup takes a folder dragged in from Finder, devices and styles by number
103
+ or name, and asks how big a set (starter, standard, full, with load times for Transfer),
104
+ prepared for hardware or as-is, and which kinds of sound to leave out; `fourier open` shows the
105
+ master, a render, the releases or the logs in your file browser; `fourier config edit` opens
106
+ fourier.toml in a text editor (and runs with a config that doesn't load, which is when it's
107
+ needed); a fourier.toml typed with smart quotes still loads, and a mistake in one names its
108
+ line and the usual fix. `fourier config explain <setting>` says what a setting does in plain
109
+ words, with an example.
110
+ - **A report page**: `fourier open report` opens a page in the web browser with every category
111
+ and family, a play button for each file, where it came from, what the build left out and why,
112
+ and Keep / Drop / Misfiled buttons that export a CSV for `fourier review import`.
113
+ - **Plain answers**: `fourier why` answers in one sentence (`--detail` for every rule, vote and
114
+ score); a command that hits something unexpected says so in one line and keeps the details in
115
+ a file for a bug report; commands with nothing built yet say what to run first.
116
+ - **Loading only what's new**: `fourier render <device> --release v2 --new-only` also copies the
117
+ files v2 adds into a folder of their own, for a loader that can't sync (Elektron Transfer).
118
+ - **Documentation**: the README, a guide to what comes after the first build
119
+ (`docs/guide.md`), the reference (`docs/usage.md`), the curation rules (`docs/curation.md`),
120
+ and a skill for coding assistants (`skills/fourier/SKILL.md`) that teaches one to help with
121
+ Fourier safely.
122
+ - `fourier --version`, and `python -m fourier`.
123
+ - Python 3.11 to 3.13 on macOS and Linux; Windows is experimental.
124
+
125
+ [0.1.0]: https://github.com/bvk7787/fourier-samples/releases/tag/v0.1.0