takeloom 0.5.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 (149) hide show
  1. takeloom-0.5.0/.github/workflows/daily.yml +90 -0
  2. takeloom-0.5.0/.github/workflows/release.yml +107 -0
  3. takeloom-0.5.0/.gitignore +20 -0
  4. takeloom-0.5.0/CLAUDE.md +24 -0
  5. takeloom-0.5.0/LICENSE +21 -0
  6. takeloom-0.5.0/PKG-INFO +21 -0
  7. takeloom-0.5.0/README.md +201 -0
  8. takeloom-0.5.0/camera_app/android/app/build.gradle.kts +32 -0
  9. takeloom-0.5.0/camera_app/android/app/src/main/AndroidManifest.xml +26 -0
  10. takeloom-0.5.0/camera_app/android/app/src/main/java/com/priestc/takeloomcamera/CameraEngine.kt +471 -0
  11. takeloom-0.5.0/camera_app/android/app/src/main/java/com/priestc/takeloomcamera/MainActivity.kt +402 -0
  12. takeloom-0.5.0/camera_app/android/app/src/main/java/com/priestc/takeloomcamera/ServerLink.kt +208 -0
  13. takeloom-0.5.0/camera_app/android/app/src/main/java/com/priestc/takeloomcamera/Settings.kt +48 -0
  14. takeloom-0.5.0/camera_app/android/app/src/main/java/com/priestc/takeloomcamera/Uploads.kt +190 -0
  15. takeloom-0.5.0/camera_app/android/app/src/main/java/com/priestc/takeloomcamera/Wire.kt +142 -0
  16. takeloom-0.5.0/camera_app/android/app/src/main/res/drawable-nodpi/ic_launcher_foreground.png +0 -0
  17. takeloom-0.5.0/camera_app/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +6 -0
  18. takeloom-0.5.0/camera_app/android/app/src/main/res/values/ic_launcher_background.xml +5 -0
  19. takeloom-0.5.0/camera_app/android/app/src/main/res/values/strings.xml +4 -0
  20. takeloom-0.5.0/camera_app/android/build.gradle.kts +4 -0
  21. takeloom-0.5.0/camera_app/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  22. takeloom-0.5.0/camera_app/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  23. takeloom-0.5.0/camera_app/android/gradle.properties +4 -0
  24. takeloom-0.5.0/camera_app/android/gradlew +249 -0
  25. takeloom-0.5.0/camera_app/android/gradlew.bat +92 -0
  26. takeloom-0.5.0/camera_app/android/settings.gradle.kts +16 -0
  27. takeloom-0.5.0/camera_app/ios/TakeloomCamera/AppModel.swift +190 -0
  28. takeloom-0.5.0/camera_app/ios/TakeloomCamera/Assets.xcassets/AppIcon.appiconset/Contents.json +11 -0
  29. takeloom-0.5.0/camera_app/ios/TakeloomCamera/Assets.xcassets/AppIcon.appiconset/icon-1024.png +0 -0
  30. takeloom-0.5.0/camera_app/ios/TakeloomCamera/Assets.xcassets/Contents.json +3 -0
  31. takeloom-0.5.0/camera_app/ios/TakeloomCamera/CameraPipeline.swift +268 -0
  32. takeloom-0.5.0/camera_app/ios/TakeloomCamera/H264Encoder.swift +155 -0
  33. takeloom-0.5.0/camera_app/ios/TakeloomCamera/Info.plist +53 -0
  34. takeloom-0.5.0/camera_app/ios/TakeloomCamera/ServerLink.swift +231 -0
  35. takeloom-0.5.0/camera_app/ios/TakeloomCamera/SessionRecorder.swift +113 -0
  36. takeloom-0.5.0/camera_app/ios/TakeloomCamera/Settings.swift +74 -0
  37. takeloom-0.5.0/camera_app/ios/TakeloomCamera/Uploads.swift +174 -0
  38. takeloom-0.5.0/camera_app/ios/TakeloomCamera/Views.swift +212 -0
  39. takeloom-0.5.0/camera_app/ios/TakeloomCamera/Wire.swift +157 -0
  40. takeloom-0.5.0/camera_app/ios/project.yml +23 -0
  41. takeloom-0.5.0/docs/desktop-audio-capture.md +66 -0
  42. takeloom-0.5.0/docs/inspiration-server-autocomplete-api.md +196 -0
  43. takeloom-0.5.0/docs/prerequisites.md +78 -0
  44. takeloom-0.5.0/docs/releasing.md +68 -0
  45. takeloom-0.5.0/docs/takeloom-camera.md +104 -0
  46. takeloom-0.5.0/docs/youtube-streaming-setup.md +89 -0
  47. takeloom-0.5.0/pyproject.toml +43 -0
  48. takeloom-0.5.0/scripts/build_macos_dmg.py +17 -0
  49. takeloom-0.5.0/scripts/make_icon.py +276 -0
  50. takeloom-0.5.0/takeloom/__init__.py +1 -0
  51. takeloom-0.5.0/takeloom/__main__.py +1457 -0
  52. takeloom-0.5.0/takeloom/audio/__init__.py +0 -0
  53. takeloom-0.5.0/takeloom/audio/benchmark.py +93 -0
  54. takeloom-0.5.0/takeloom/audio/coreaudio_devices.py +134 -0
  55. takeloom-0.5.0/takeloom/audio/devices.py +18 -0
  56. takeloom-0.5.0/takeloom/audio/engine.py +393 -0
  57. takeloom-0.5.0/takeloom/audio/filters.py +130 -0
  58. takeloom-0.5.0/takeloom/audio/formats.py +81 -0
  59. takeloom-0.5.0/takeloom/audio/instrument_classifier.py +726 -0
  60. takeloom-0.5.0/takeloom/audio/metronome.py +45 -0
  61. takeloom-0.5.0/takeloom/audio/midi_input.py +323 -0
  62. takeloom-0.5.0/takeloom/audio/midi_log.py +203 -0
  63. takeloom-0.5.0/takeloom/audio/mixer.py +226 -0
  64. takeloom-0.5.0/takeloom/audio/pitch.py +220 -0
  65. takeloom-0.5.0/takeloom/audio/pitch_shift.py +282 -0
  66. takeloom-0.5.0/takeloom/audio/recorder.py +80 -0
  67. takeloom-0.5.0/takeloom/audio/scarlett2_direct_monitor.py +235 -0
  68. takeloom-0.5.0/takeloom/audio/soundfont.py +68 -0
  69. takeloom-0.5.0/takeloom/audio/synth.py +456 -0
  70. takeloom-0.5.0/takeloom/backend.py +6174 -0
  71. takeloom-0.5.0/takeloom/config.py +418 -0
  72. takeloom-0.5.0/takeloom/data/__init__.py +0 -0
  73. takeloom-0.5.0/takeloom/data/icon.png +0 -0
  74. takeloom-0.5.0/takeloom/data/measure_latency.wav +0 -0
  75. takeloom-0.5.0/takeloom/device_check.py +65 -0
  76. takeloom-0.5.0/takeloom/ffmpeg_bin.py +27 -0
  77. takeloom-0.5.0/takeloom/inspiration.py +331 -0
  78. takeloom-0.5.0/takeloom/instrument_colors.py +55 -0
  79. takeloom-0.5.0/takeloom/keyboard_drivers/__init__.py +106 -0
  80. takeloom-0.5.0/takeloom/keyboard_drivers/alesis_qx25.py +32 -0
  81. takeloom-0.5.0/takeloom/keyboard_drivers/m_audio_keystation_61es.py +19 -0
  82. takeloom-0.5.0/takeloom/launcher.py +198 -0
  83. takeloom-0.5.0/takeloom/net.py +24 -0
  84. takeloom-0.5.0/takeloom/phone_camera/__init__.py +2 -0
  85. takeloom-0.5.0/takeloom/phone_camera/hub.py +577 -0
  86. takeloom-0.5.0/takeloom/phone_camera/protocol.py +141 -0
  87. takeloom-0.5.0/takeloom/phone_camera/recorder.py +234 -0
  88. takeloom-0.5.0/takeloom/phone_camera/sync.py +343 -0
  89. takeloom-0.5.0/takeloom/processing/__init__.py +0 -0
  90. takeloom-0.5.0/takeloom/processing/splicer.py +547 -0
  91. takeloom-0.5.0/takeloom/project.py +308 -0
  92. takeloom-0.5.0/takeloom/recording_driver.py +621 -0
  93. takeloom-0.5.0/takeloom/remote/__init__.py +7 -0
  94. takeloom-0.5.0/takeloom/remote/backend.py +592 -0
  95. takeloom-0.5.0/takeloom/remote/client.py +190 -0
  96. takeloom-0.5.0/takeloom/remote/protocol.py +193 -0
  97. takeloom-0.5.0/takeloom/remote/server.py +414 -0
  98. takeloom-0.5.0/takeloom/rig_watcher.py +188 -0
  99. takeloom-0.5.0/takeloom/service.py +166 -0
  100. takeloom-0.5.0/takeloom/sleep_guard.py +129 -0
  101. takeloom-0.5.0/takeloom/streamdeck_controller.py +1119 -0
  102. takeloom-0.5.0/takeloom/streaming.py +169 -0
  103. takeloom-0.5.0/takeloom/take_video.py +110 -0
  104. takeloom-0.5.0/takeloom/ui/__init__.py +0 -0
  105. takeloom-0.5.0/takeloom/ui/add_to_setlist_chooser.py +65 -0
  106. takeloom-0.5.0/takeloom/ui/add_to_setlist_dialog.py +468 -0
  107. takeloom-0.5.0/takeloom/ui/app.py +339 -0
  108. takeloom-0.5.0/takeloom/ui/app_state.py +85 -0
  109. takeloom-0.5.0/takeloom/ui/audio_player.py +411 -0
  110. takeloom-0.5.0/takeloom/ui/backing_trim_editor.py +238 -0
  111. takeloom-0.5.0/takeloom/ui/completed_takes.py +467 -0
  112. takeloom-0.5.0/takeloom/ui/desktop_entry.py +77 -0
  113. takeloom-0.5.0/takeloom/ui/detect_test.py +380 -0
  114. takeloom-0.5.0/takeloom/ui/filter_fields.py +157 -0
  115. takeloom-0.5.0/takeloom/ui/instrument_colors.py +39 -0
  116. takeloom-0.5.0/takeloom/ui/latency.py +363 -0
  117. takeloom-0.5.0/takeloom/ui/level_meter.py +49 -0
  118. takeloom-0.5.0/takeloom/ui/new_project_dialog.py +78 -0
  119. takeloom-0.5.0/takeloom/ui/platform_style.py +69 -0
  120. takeloom-0.5.0/takeloom/ui/record.py +1764 -0
  121. takeloom-0.5.0/takeloom/ui/recording_devices.py +402 -0
  122. takeloom-0.5.0/takeloom/ui/remote.py +441 -0
  123. takeloom-0.5.0/takeloom/ui/sessions.py +387 -0
  124. takeloom-0.5.0/takeloom/ui/setlist_row.py +119 -0
  125. takeloom-0.5.0/takeloom/ui/song_mixer.py +428 -0
  126. takeloom-0.5.0/takeloom/ui/song_set_dialog.py +329 -0
  127. takeloom-0.5.0/takeloom/ui/streamdeck_emulator.py +150 -0
  128. takeloom-0.5.0/takeloom/ui/streaming.py +345 -0
  129. takeloom-0.5.0/takeloom/ui/studio_setup.py +1061 -0
  130. takeloom-0.5.0/takeloom/ui/tuner_meter.py +116 -0
  131. takeloom-0.5.0/takeloom/ui/video_check_dialog.py +61 -0
  132. takeloom-0.5.0/takeloom/update_check.py +187 -0
  133. takeloom-0.5.0/takeloom/utils.py +131 -0
  134. takeloom-0.5.0/takeloom/vault.py +322 -0
  135. takeloom-0.5.0/takeloom/vault_client.py +257 -0
  136. takeloom-0.5.0/takeloom/video/__init__.py +0 -0
  137. takeloom-0.5.0/takeloom/video/capture.py +390 -0
  138. takeloom-0.5.0/takeloom/video/devices.py +80 -0
  139. takeloom-0.5.0/takeloom/youtube.py +101 -0
  140. takeloom-0.5.0/takeloom/youtube_api.py +331 -0
  141. takeloom-0.5.0/vault_server/README.md +75 -0
  142. takeloom-0.5.0/vault_server/pyproject.toml +19 -0
  143. takeloom-0.5.0/vault_server/takeloom_vault/__init__.py +5 -0
  144. takeloom-0.5.0/vault_server/takeloom_vault/__main__.py +131 -0
  145. takeloom-0.5.0/vault_server/takeloom_vault/auth.py +202 -0
  146. takeloom-0.5.0/vault_server/takeloom_vault/server.py +369 -0
  147. takeloom-0.5.0/vault_server/takeloom_vault/store.py +173 -0
  148. takeloom-0.5.0/vault_server/takeloom_vault/takes.py +158 -0
  149. takeloom-0.5.0/vault_server/takeloom_vault/web.py +244 -0
@@ -0,0 +1,90 @@
1
+ # Daily build: once a day, if anything was committed since the last build
2
+ # (daily or release), build takeloom and publish it as a GitHub pre-release
3
+ # tagged daily-YYYY-MM-DD. Never sent to PyPI/Homebrew/apt — that's
4
+ # release.yml. See docs/releasing.md.
5
+ name: Daily build
6
+
7
+ on:
8
+ schedule:
9
+ # 07:00 UTC: midnight in Denver in winter (MST), 1am in summer (MDT) —
10
+ # always after the day being built has ended.
11
+ - cron: "0 7 * * *"
12
+ workflow_dispatch:
13
+
14
+ permissions:
15
+ contents: write
16
+
17
+ concurrency: daily-build
18
+
19
+ jobs:
20
+ daily:
21
+ runs-on: ubuntu-latest
22
+ env:
23
+ GH_TOKEN: ${{ github.token }}
24
+ steps:
25
+ - uses: actions/checkout@v4
26
+ with:
27
+ fetch-depth: 0 # tags + history: the version comes from them
28
+
29
+ - name: Anything new since the last build?
30
+ id: check
31
+ run: |
32
+ if [ -n "$(git tag --points-at HEAD --list 'daily-*' 'v[0-9]*')" ]; then
33
+ echo "Nothing committed since the last build — no daily build today."
34
+ echo "skip=true" >> "$GITHUB_OUTPUT"
35
+ exit 0
36
+ fi
37
+ # Named for the (Denver) day the newest commit was made.
38
+ day=$(TZ=America/Denver git log -1 --format=%cd --date=format-local:%Y-%m-%d)
39
+ previous=$(git describe --tags --abbrev=0 --match 'daily-*' --match 'v[0-9]*' 2>/dev/null || true)
40
+ echo "skip=false" >> "$GITHUB_OUTPUT"
41
+ echo "tag=daily-$day" >> "$GITHUB_OUTPUT"
42
+ echo "day=$day" >> "$GITHUB_OUTPUT"
43
+ echo "previous=$previous" >> "$GITHUB_OUTPUT"
44
+
45
+ - uses: actions/setup-python@v5
46
+ if: steps.check.outputs.skip == 'false'
47
+ with:
48
+ python-version: "3.12"
49
+
50
+ - name: Build
51
+ if: steps.check.outputs.skip == 'false'
52
+ run: |
53
+ python -m pip install --quiet build
54
+ python -m build
55
+ ls dist
56
+
57
+ - name: Smoke test
58
+ if: steps.check.outputs.skip == 'false'
59
+ run: |
60
+ sudo apt-get update -qq && sudo apt-get install -y -qq libportaudio2 libsndfile1
61
+ python -m pip install --quiet dist/*.whl
62
+ takeloom --help
63
+
64
+ - name: Publish pre-release
65
+ if: steps.check.outputs.skip == 'false'
66
+ run: |
67
+ tag="${{ steps.check.outputs.tag }}"
68
+ previous="${{ steps.check.outputs.previous }}"
69
+ version=$(ls dist/*.whl | sed -E 's/.*takeloom-([^-]+)-.*/\1/')
70
+ {
71
+ echo "Daily build of \`main\` for ${{ steps.check.outputs.day }} — version $version."
72
+ echo
73
+ echo "Not a release: for testing. Install the wheel with \`pipx install <url of the .whl>\`."
74
+ echo
75
+ echo "### Changes since ${previous:-the beginning}"
76
+ git log --format='- %s (%h)' ${previous:+$previous..}HEAD
77
+ } > notes.md
78
+ # A second build on the same day (manual run) replaces the first.
79
+ gh release delete "$tag" --cleanup-tag --yes 2>/dev/null || true
80
+ gh release create "$tag" dist/* --prerelease --target "$GITHUB_SHA" \
81
+ --title "Daily build ${{ steps.check.outputs.day }} ($version)" --notes-file notes.md
82
+
83
+ - name: Keep only the last 14 daily builds
84
+ if: steps.check.outputs.skip == 'false'
85
+ run: |
86
+ gh release list --limit 200 --json tagName,createdAt \
87
+ -q '[.[] | select(.tagName | startswith("daily-"))] | sort_by(.createdAt) | reverse | .[14:] | .[].tagName' |
88
+ while read -r old; do
89
+ gh release delete "$old" --cleanup-tag --yes
90
+ done
@@ -0,0 +1,107 @@
1
+ # Release: run by hand (Actions tab -> Release -> Run workflow, or
2
+ # `gh workflow run release.yml -f bump=minor`). Tags the current main as
3
+ # the next version, builds it, and publishes it as a GitHub release. The
4
+ # package-manager channels (PyPI, Homebrew, apt, ...) hang off the end of
5
+ # this as they're added. See docs/releasing.md.
6
+ name: Release
7
+
8
+ on:
9
+ workflow_dispatch:
10
+ inputs:
11
+ bump:
12
+ description: Which part of the version to bump
13
+ type: choice
14
+ options: [minor, patch, major]
15
+ default: minor
16
+
17
+ permissions:
18
+ contents: write
19
+
20
+ concurrency: release
21
+
22
+ jobs:
23
+ release:
24
+ runs-on: ubuntu-latest
25
+ env:
26
+ GH_TOKEN: ${{ github.token }}
27
+ outputs:
28
+ version: ${{ steps.version.outputs.version }}
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+ with:
32
+ fetch-depth: 0
33
+
34
+ - name: Next version
35
+ id: version
36
+ run: |
37
+ previous=$(git describe --tags --abbrev=0 --match 'v[0-9]*')
38
+ if [ -n "$(git tag --points-at HEAD --list 'v[0-9]*')" ]; then
39
+ echo "::error::main is already released as $previous — nothing new to release."
40
+ exit 1
41
+ fi
42
+ IFS=. read -r major minor patch <<< "${previous#v}"
43
+ case "${{ inputs.bump }}" in
44
+ major) version="$((major + 1)).0.0" ;;
45
+ minor) version="$major.$((minor + 1)).0" ;;
46
+ patch) version="$major.$minor.$((patch + 1))" ;;
47
+ esac
48
+ echo "Releasing $version (previous: $previous)"
49
+ echo "version=$version" >> "$GITHUB_OUTPUT"
50
+ echo "previous=$previous" >> "$GITHUB_OUTPUT"
51
+
52
+ - name: Tag
53
+ run: |
54
+ git config user.name "github-actions[bot]"
55
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
56
+ git tag -a "v${{ steps.version.outputs.version }}" -m "takeloom ${{ steps.version.outputs.version }}"
57
+
58
+ - uses: actions/setup-python@v5
59
+ with:
60
+ python-version: "3.12"
61
+
62
+ - name: Build
63
+ run: |
64
+ python -m pip install --quiet build
65
+ python -m build
66
+ ls dist
67
+ # The tag must have produced exactly this version, not a .dev one.
68
+ test -f "dist/takeloom-${{ steps.version.outputs.version }}-py3-none-any.whl"
69
+
70
+ - name: Smoke test
71
+ run: |
72
+ sudo apt-get update -qq && sudo apt-get install -y -qq libportaudio2 libsndfile1
73
+ python -m pip install --quiet dist/*.whl
74
+ takeloom --help
75
+
76
+ - name: Push tag and publish release
77
+ run: |
78
+ version="${{ steps.version.outputs.version }}"
79
+ previous="${{ steps.version.outputs.previous }}"
80
+ git push origin "v$version"
81
+ {
82
+ echo "### Changes since $previous"
83
+ git log --format='- %s (%h)' "$previous..HEAD"
84
+ } > notes.md
85
+ gh release create "v$version" dist/* --latest --title "takeloom $version" --notes-file notes.md
86
+
87
+ - uses: actions/upload-artifact@v4
88
+ with:
89
+ name: dist
90
+ path: dist/
91
+
92
+ pypi:
93
+ # Trusted publishing: PyPI's takeloom project trusts this repo's
94
+ # release.yml in the "pypi" environment, so no token is stored anywhere.
95
+ needs: release
96
+ runs-on: ubuntu-latest
97
+ environment:
98
+ name: pypi
99
+ url: https://pypi.org/project/takeloom/${{ needs.release.outputs.version }}/
100
+ permissions:
101
+ id-token: write
102
+ steps:
103
+ - uses: actions/download-artifact@v4
104
+ with:
105
+ name: dist
106
+ path: dist/
107
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,20 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ *.egg
7
+ .eggs/
8
+ *.flac
9
+ *.wav
10
+ !takeloom/data/measure_latency.wav
11
+ *.mp3
12
+ *.m4a
13
+ .env
14
+
15
+ # Takeloom Camera app build output (camera_app/)
16
+ camera_app/ios/TakeloomCamera.xcodeproj/
17
+ camera_app/android/.gradle/
18
+ camera_app/android/local.properties
19
+ camera_app/android/app/build/
20
+ .DS_Store
@@ -0,0 +1,24 @@
1
+ # Deployment
2
+
3
+ Always push every commit to `origin` (`main`) right after making it — no need to ask first. This is a standing authorization for `git push` specifically in this repo; it doesn't extend to other destructive/shared-state git operations (force-push, reset --hard, etc.), which still need confirmation as usual.
4
+
5
+ There are two machines running `takeloom`, installed differently:
6
+
7
+ - The laptop ("Framework"), reachable via `ssh framework` (non-interactive/key-auth works). Installed via `pipx` directly from this GitHub repo (`takeloom @ git+https://github.com/priestc/takeloom.git`) — not an editable clone, so picking up new code requires a reinstall, not a `git pull`.
8
+ - The Mac Mini — the studio hub machine with the Scarlett 4i4 attached (see `studio_hardware_scarlett_4i4` memory), and normally the machine Claude Code itself is already running on (`hostname` reports `Mac.local`). Installed via `pipx install --editable ~/Documents/GitHub/takeloom` — an editable install pointing straight at this working copy (confirm with `cat ~/.local/pipx/venvs/takeloom/lib/python*/site-packages/takeloom*.dist-info/direct_url.json`, should show `"editable": true`). Any commit — or even an uncommitted local edit — takes effect the next time `takeloom` is launched there; no reinstall step needed. (If a dependency is ever added/changed in `pyproject.toml`, that *does* still need `pipx install --editable --force ~/Documents/GitHub/takeloom` to pick up the new package.)
9
+
10
+ Any change **except code that only affects server mode** (i.e. changes confined to the headless `takeloom server` path — see `takeloom/__main__.py`'s `server_command` and anything only reachable from it) should be automatically deployed once committed:
11
+
12
+ 1. Push to `origin` (`main`). That's the only step to take here — do not `ssh framework` to reinstall.
13
+ 2. The laptop no longer gets deployed to manually. `takeloom ui` checks GitHub's `main` on launch and self-updates (reinstalls + restarts) if it's behind — see `takeloom/update_check.py`. The user's habit is to close and reopen the app on the laptop before testing, which is what actually picks up the new commit; nothing needs to happen on the laptop from this side beyond the push.
14
+ 3. Nothing further needed for the Mac Mini — just make sure `takeloom` (or `takeloom server`) is restarted there to pick up the change.
15
+
16
+ Manual laptop reinstall (reference only — for troubleshooting if auto-update ever fails or misbehaves, not part of the normal flow above):
17
+ `ssh framework "pipx install --force git+https://github.com/priestc/takeloom.git"`
18
+
19
+ # Testing setup
20
+
21
+ - All actual hardware — audio interfaces (the Scarlett 4i4) and the Stream Deck — is attached to the **Mac Mini**, and only the Mac Mini ever runs `takeloom server` or performs an actual recording. Nothing is ever plugged into the laptop.
22
+ - The **laptop ("Framework")** only ever runs `takeloom` in remote mode — connecting to the Mac Mini's `takeloom server` via the Remote tab / `--remote=IP` — to control a session running on the Mac Mini. It never has a device attached and a recording is never started or written locally on it, so the CLI recording paths (`start-session`, `inspiration`) are effectively unused/untested there in practice; treat bugs reported "on the laptop" as remote-mode bugs first.
23
+ - The laptop sleeps whenever it's unplugged, which is its normal state (it's rarely plugged in). Keep this in mind for anything that assumes it stays awake/reachable — e.g. it can drop off the network mid-session, and any "don't sleep while recording" behavior only matters there for the remote-viewing UI, not a local recording session.
24
+ - On the Mac Mini, the Scarlett 4i4 and Stream Deck are usually powered off between sessions. You're allowed to launch `takeloom ui` there to visually verify pure-UI changes (screenshot it, check it looks right, close it) — no need to ask first. It's expected and fine for audio devices/instrument meters/the Stream Deck connection to show empty or disconnected in that state; that's not a bug, just means the hardware happens to be off right now. Don't read anything into it unless the change you're testing is specifically about device detection/connection.
takeloom-0.5.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Chris Priest
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.5
2
+ Name: takeloom
3
+ Version: 0.5.0
4
+ Summary: Music Recording Session Manager
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.9
8
+ Requires-Dist: click>=8.0
9
+ Requires-Dist: mido>=1.3.0
10
+ Requires-Dist: numpy>=1.24.0
11
+ Requires-Dist: opencv-python>=4.8.0
12
+ Requires-Dist: pillow>=9.0.0
13
+ Requires-Dist: pyfluidsynth>=1.3.2
14
+ Requires-Dist: python-rtmidi>=1.5.0; sys_platform == 'darwin'
15
+ Requires-Dist: pyusb>=1.2.1
16
+ Requires-Dist: sounddevice>=0.4.6
17
+ Requires-Dist: soundfile>=0.12.1
18
+ Requires-Dist: static-ffmpeg>=2.5
19
+ Requires-Dist: streamdeck>=0.9.0
20
+ Requires-Dist: tkinterdnd2>=0.3.0
21
+ Requires-Dist: wakepy>=0.7.0
@@ -0,0 +1,201 @@
1
+ # Takeloom
2
+
3
+ A CLI application for musicians to record instruments over backing tracks, manage songs/albums, and track completed takes.
4
+
5
+ Takeloom handles continuous audio recording, playback mixing, session logging, and per-take file management. Record one instrument at a time — each new session plays back your previous takes alongside the backing track so you can layer parts.
6
+
7
+ ## Requirements
8
+
9
+ - Python 3.9+
10
+ - PortAudio, libsndfile, ffmpeg (system libraries)
11
+ - yt-dlp (optional, system binary) — only needed to add YouTube URLs as backing tracks from the New Project dialog
12
+
13
+ See [Prerequisites](docs/prerequisites.md) for install instructions for each operating system.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ pipx install takeloom
19
+ takeloom install-launcher
20
+ ```
21
+
22
+ That's the latest release from PyPI. To follow every commit on `main`
23
+ instead (and auto-update to it on each launch):
24
+ `pipx install git+https://github.com/priestc/takeloom.git`
25
+
26
+ `takeloom install-launcher` adds Takeloom to the Applications folder
27
+ (macOS) or to the applications menu and desktop (Linux), so the UI opens
28
+ like any other app. Updates still go through pipx, automatically, each
29
+ time the UI opens.
30
+
31
+ **macOS, drag-to-install:** `python3 scripts/build_macos_dmg.py` builds
32
+ `dist/Takeloom.dmg`. Drag Takeloom into Applications. On first open the app
33
+ installs takeloom with pipx (Homebrew's `pipx` and `python-tk` must already
34
+ be installed). An app that came from a download is blocked the first time,
35
+ since it isn't signed by Apple: right-click it and choose Open.
36
+
37
+ ## Usage
38
+
39
+ ### Studio Setup
40
+
41
+ Configure your studio in three steps. Each command reads and updates `~/studio_config.json` independently, so you can re-run one without redoing the others.
42
+
43
+ ```bash
44
+ takeloom setup-studio # studio name, location, musician, remote vault server
45
+ takeloom setup-recording-devices # sample rate, buffer size, output device, input labels, camera
46
+ takeloom setup-instruments # assign instruments to input channels
47
+ ```
48
+
49
+ `setup-recording-devices` also lets you pick a camera. If one is configured, every session
50
+ records video alongside the audio (see [Recording Session](#recording-session)).
51
+
52
+ `setup-studio` also configures the **Studio Session Vault** — shared storage for everything
53
+ a project used to keep to itself: recorded sessions, backing tracks, and completed takes
54
+ (see [Project Structure](#project-structure)). A project is just a setlist file now. Three
55
+ vault modes: local only (default, under `~/Documents`), remote only (uploaded to the remote
56
+ vault server and removed locally once every file's checksum is verified), or both (uploaded
57
+ but also kept locally). Starting a session downloads whatever that project's setlist needs
58
+ from the remote first, in remote-only mode.
59
+
60
+ The remote vault is a separate small server, `takeloom-vault` (this repo's
61
+ [`vault_server/`](vault_server/README.md) folder), running on the storage machine. It also
62
+ serves a web page for browsing sessions and watching their videos. Takeloom pairs with it
63
+ the same way Remote mode pairs: click **Pair...** next to "Remote vault server" in Studio
64
+ Setup (or run `takeloom vault pair tank2`), then approve the code shown on the vault's web
65
+ page. `takeloom vault status` checks the connection. If you have projects from before the vault existed, run
66
+ `takeloom migrate-sessions-to-vault` once to move everything in — safe to re-run,
67
+ already-migrated projects are skipped.
68
+
69
+ ### Graphical Interface
70
+
71
+ ```bash
72
+ takeloom ui
73
+ ```
74
+
75
+ Opens a desktop window (built with tkinter). Currently implements the Studio Setup screen —
76
+ the same fields as `takeloom setup-studio` — with more screens to follow.
77
+
78
+ ### Creating a Project
79
+
80
+ ```bash
81
+ takeloom new-project
82
+ ```
83
+
84
+ Enter a project name (e.g. "My Album"). Creates `~/Takeloom Projects/My Album.json` — an
85
+ empty setlist.
86
+
87
+ ### Updating the Setlist
88
+
89
+ Copy audio files (FLAC, WAV, MP3, M4A) into the vault's `backing_tracks/` directory, then:
90
+
91
+ ```bash
92
+ takeloom update-setlist "My Album"
93
+ ```
94
+
95
+ Adds any of the vault's backing tracks not yet in this project's setlist (omit the project
96
+ name to use the last-used project). Each track in the setlist includes a `volume` field
97
+ (default 100%) that you can edit manually to adjust backing track playback level.
98
+
99
+ ### Recording Session
100
+
101
+ ```bash
102
+ takeloom start-session guitar "My Album"
103
+ ```
104
+
105
+ The session plays the backing track through your speakers, monitors your instrument input in real-time, and records your take. Controls:
106
+
107
+ | Key | Action | When |
108
+ |-----|--------|------|
109
+ | `r` | Start recording (plays backing track) | Waiting |
110
+ | `b` | Back to start (restart the take) | Playing |
111
+ | `e` | Mark song end (complete the take) | Playing |
112
+ | `n` | Move to next track (auto-starts recording) | Between tracks |
113
+ | `l` | Lower backing track volume by 5% | Any time |
114
+ | `u` | Raise backing track volume by 5% | Any time |
115
+ | `q` | End session | Any time |
116
+
117
+ **Completing a take:** Press `r` to start, then `e` when the song finishes. The take is saved to the vault's `completed_takes/` and set as the preferred take in the setlist.
118
+
119
+ **Restarting a take:** Press `b` to loop back to the beginning. The backing track restarts and a new take file begins.
120
+
121
+ **Volume adjustments** are saved back to the setlist file at the end of the session.
122
+
123
+ **Video:** if a camera is configured (`takeloom setup-recording-devices`), the whole session is
124
+ also recorded on video, saved as `session_video.mp4` alongside `session.flac` once the session
125
+ ends. The video has two audio tracks: a compressed (AAC) mix of the backing track + your
126
+ instrument for easy playback, and a lossless (FLAC) track of just your instrument alone.
127
+
128
+ ### Multi-Instrument Layering
129
+
130
+ Start a new session with a different instrument (e.g. "bass"). The backing track plays mixed with your previously recorded preferred takes, so you hear everything together while recording the new part.
131
+
132
+ ### Desktop Audio Capture
133
+
134
+ To record virtual instruments, software synths, or system audio, you need a virtual audio loopback device. See [Desktop Audio Capture Setup](docs/desktop-audio-capture.md) for instructions.
135
+
136
+ ### Live Streaming
137
+
138
+ The GUI's Streaming tab can push every session live to YouTube over RTMP — just paste a
139
+ stream key from YouTube Studio and enable it. Connecting a YouTube account is optional
140
+ and only needed if you also want each session's stream auto-titled with the studio,
141
+ musician, project, and date; that requires creating your own free Google Cloud OAuth
142
+ credentials. See [YouTube Streaming Setup](docs/youtube-streaming-setup.md) for the
143
+ steps, and why it can't just be a one-click sign-in.
144
+
145
+ ## Project Structure
146
+
147
+ A project is just a setlist file — `<projects_dir>/My Album.json`. Everything else lives in
148
+ the shared Studio Session Vault (see [Studio Setup](#studio-setup)) instead of belonging to
149
+ one project, so two different projects can reference the same backing track or completed
150
+ take (most commonly a song pulled in from the Inspiration library — see
151
+ [Cross-Project Take Reuse](#cross-project-take-reuse)) without either downloading or
152
+ recording it twice:
153
+
154
+ ```
155
+ <projects_dir>/
156
+ ├── My Album.json
157
+ └── Side Project.json
158
+
159
+ <vault>/
160
+ ├── backing_tracks/
161
+ │ ├── song1.mp3
162
+ │ └── a1b2c3d4_song2.flac
163
+ ├── completed_takes/
164
+ │ ├── song1 - guitar - take1 [upload:a1b2c3d4].flac
165
+ │ ├── song1 - bass - take1 [upload:a1b2c3d4].flac
166
+ │ └── song2 - guitar - take2 [youtube:dQw4w9WgXcQ].flac
167
+ ├── inspiration_takes.json
168
+ └── sessions/
169
+ └── 2025-01-15_14-30-00_guitar_My Album/
170
+ ├── session.flac
171
+ ├── session_video.mp4
172
+ └── session_log.json
173
+ ```
174
+
175
+ - `backing_tracks/` and `completed_takes/` are shared across every project. A locally
176
+ uploaded file gets a short random prefix added only if its plain name would otherwise
177
+ collide with an unrelated project's file; a file that already has a globally unique name
178
+ (a YouTube download, or one pulled from the Inspiration library) keeps it as-is.
179
+ - A completed take's filename tags the exact backing track it was recorded against —
180
+ `[upload:<id>]`, `[youtube:<video id>]`, or `[inspiration:<track id>]` — since backing
181
+ tracks are shared vault-wide now and two entries can share a display name while pointing
182
+ at different audio.
183
+ - Existing take files are never deleted. New takes increment the take number and replace the preferred take in the setlist.
184
+ - `inspiration_takes.json` — the cross-project take index (see below).
185
+ - `session.flac` — the continuous raw recording spanning the full session
186
+ - `session_log.json` — musician, studio, and event data; what post-session processing replays to find completed takes and copy them into the vault's `completed_takes/`
187
+ - `session_video.mp4` is only present when a camera is configured
188
+
189
+ ### Cross-Project Take Reuse
190
+
191
+ A song pulled from the Inspiration library carries a stable ID from that library, so a take
192
+ recorded on it from *any* project is recorded into `inspiration_takes.json` too, keyed by
193
+ that ID. This is what lets an "inspiration filter" setlist slot (a standing "draw a random
194
+ song from this filter each session" entry — added from the GUI's Streaming/Setlist screens)
195
+ prefer redrawing a song other instruments already have a take on, and what lets
196
+ `ensure_setlist_files_local` (session start, in remote vault mode) pull down another
197
+ project's take on a shared song before it's needed.
198
+
199
+ ## License
200
+
201
+ MIT — see [LICENSE](LICENSE). Builds and releases: [docs/releasing.md](docs/releasing.md).
@@ -0,0 +1,32 @@
1
+ plugins {
2
+ id("com.android.application")
3
+ id("org.jetbrains.kotlin.android")
4
+ }
5
+
6
+ android {
7
+ namespace = "com.priestc.takeloomcamera"
8
+ compileSdk = 34
9
+
10
+ defaultConfig {
11
+ applicationId = "com.priestc.takeloomcamera"
12
+ // 29: MediaFormat.KEY_MAX_B_FRAMES, and AudioRecord timestamps on
13
+ // the boot-time clock camera sensors often use.
14
+ minSdk = 29
15
+ targetSdk = 34
16
+ versionCode = 1
17
+ versionName = "1.0"
18
+ }
19
+
20
+ buildTypes {
21
+ release {
22
+ isMinifyEnabled = false
23
+ }
24
+ }
25
+ compileOptions {
26
+ sourceCompatibility = JavaVersion.VERSION_17
27
+ targetCompatibility = JavaVersion.VERSION_17
28
+ }
29
+ kotlinOptions {
30
+ jvmTarget = "17"
31
+ }
32
+ }
@@ -0,0 +1,26 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
3
+
4
+ <uses-feature android:name="android.hardware.camera.any" android:required="true" />
5
+ <uses-permission android:name="android.permission.CAMERA" />
6
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
7
+ <uses-permission android:name="android.permission.INTERNET" />
8
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
9
+
10
+ <application
11
+ android:allowBackup="false"
12
+ android:icon="@mipmap/ic_launcher"
13
+ android:label="@string/app_name"
14
+ android:theme="@android:style/Theme.Material.NoActionBar.Fullscreen">
15
+ <activity
16
+ android:name=".MainActivity"
17
+ android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
18
+ android:exported="true"
19
+ android:screenOrientation="sensorLandscape">
20
+ <intent-filter>
21
+ <action android:name="android.intent.action.MAIN" />
22
+ <category android:name="android.intent.category.LAUNCHER" />
23
+ </intent-filter>
24
+ </activity>
25
+ </application>
26
+ </manifest>