rushdaw 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 (171) hide show
  1. rushdaw-0.1.0/LICENSE +21 -0
  2. rushdaw-0.1.0/MANIFEST.in +2 -0
  3. rushdaw-0.1.0/PKG-INFO +170 -0
  4. rushdaw-0.1.0/README.md +151 -0
  5. rushdaw-0.1.0/pyproject.toml +37 -0
  6. rushdaw-0.1.0/setup.cfg +4 -0
  7. rushdaw-0.1.0/src/rushdaw/__init__.py +3 -0
  8. rushdaw-0.1.0/src/rushdaw/__main__.py +4 -0
  9. rushdaw-0.1.0/src/rushdaw/cli.py +92 -0
  10. rushdaw-0.1.0/src/rushdaw/export.py +73 -0
  11. rushdaw-0.1.0/src/rushdaw/mcp.py +699 -0
  12. rushdaw-0.1.0/src/rushdaw/model.py +489 -0
  13. rushdaw-0.1.0/src/rushdaw/server.py +292 -0
  14. rushdaw-0.1.0/src/rushdaw/static/app.js +2113 -0
  15. rushdaw-0.1.0/src/rushdaw/static/index.html +124 -0
  16. rushdaw-0.1.0/src/rushdaw/static/samples/NOTICE.txt +26 -0
  17. rushdaw-0.1.0/src/rushdaw/static/samples/acoustic_guitar_steel/40.mp3 +0 -0
  18. rushdaw-0.1.0/src/rushdaw/static/samples/acoustic_guitar_steel/43.mp3 +0 -0
  19. rushdaw-0.1.0/src/rushdaw/static/samples/acoustic_guitar_steel/46.mp3 +0 -0
  20. rushdaw-0.1.0/src/rushdaw/static/samples/acoustic_guitar_steel/49.mp3 +0 -0
  21. rushdaw-0.1.0/src/rushdaw/static/samples/acoustic_guitar_steel/52.mp3 +0 -0
  22. rushdaw-0.1.0/src/rushdaw/static/samples/acoustic_guitar_steel/55.mp3 +0 -0
  23. rushdaw-0.1.0/src/rushdaw/static/samples/acoustic_guitar_steel/58.mp3 +0 -0
  24. rushdaw-0.1.0/src/rushdaw/static/samples/acoustic_guitar_steel/61.mp3 +0 -0
  25. rushdaw-0.1.0/src/rushdaw/static/samples/acoustic_guitar_steel/64.mp3 +0 -0
  26. rushdaw-0.1.0/src/rushdaw/static/samples/acoustic_guitar_steel/67.mp3 +0 -0
  27. rushdaw-0.1.0/src/rushdaw/static/samples/acoustic_guitar_steel/70.mp3 +0 -0
  28. rushdaw-0.1.0/src/rushdaw/static/samples/acoustic_guitar_steel/73.mp3 +0 -0
  29. rushdaw-0.1.0/src/rushdaw/static/samples/acoustic_guitar_steel/76.mp3 +0 -0
  30. rushdaw-0.1.0/src/rushdaw/static/samples/acoustic_guitar_steel/79.mp3 +0 -0
  31. rushdaw-0.1.0/src/rushdaw/static/samples/acoustic_guitar_steel/82.mp3 +0 -0
  32. rushdaw-0.1.0/src/rushdaw/static/samples/acoustic_guitar_steel/85.mp3 +0 -0
  33. rushdaw-0.1.0/src/rushdaw/static/samples/acoustic_guitar_steel/88.mp3 +0 -0
  34. rushdaw-0.1.0/src/rushdaw/static/samples/drums/chh-1-1.mp3 +0 -0
  35. rushdaw-0.1.0/src/rushdaw/static/samples/drums/chh-1-2.mp3 +0 -0
  36. rushdaw-0.1.0/src/rushdaw/static/samples/drums/chh-1-3.mp3 +0 -0
  37. rushdaw-0.1.0/src/rushdaw/static/samples/drums/chh-2-1.mp3 +0 -0
  38. rushdaw-0.1.0/src/rushdaw/static/samples/drums/chh-2-2.mp3 +0 -0
  39. rushdaw-0.1.0/src/rushdaw/static/samples/drums/chh-2-3.mp3 +0 -0
  40. rushdaw-0.1.0/src/rushdaw/static/samples/drums/chh-3-1.mp3 +0 -0
  41. rushdaw-0.1.0/src/rushdaw/static/samples/drums/chh-3-2.mp3 +0 -0
  42. rushdaw-0.1.0/src/rushdaw/static/samples/drums/chh-3-3.mp3 +0 -0
  43. rushdaw-0.1.0/src/rushdaw/static/samples/drums/crash-1-1.mp3 +0 -0
  44. rushdaw-0.1.0/src/rushdaw/static/samples/drums/crash-1-2.mp3 +0 -0
  45. rushdaw-0.1.0/src/rushdaw/static/samples/drums/crash-1-3.mp3 +0 -0
  46. rushdaw-0.1.0/src/rushdaw/static/samples/drums/crash-2-1.mp3 +0 -0
  47. rushdaw-0.1.0/src/rushdaw/static/samples/drums/crash-2-2.mp3 +0 -0
  48. rushdaw-0.1.0/src/rushdaw/static/samples/drums/crash-2-3.mp3 +0 -0
  49. rushdaw-0.1.0/src/rushdaw/static/samples/drums/crash-3-1.mp3 +0 -0
  50. rushdaw-0.1.0/src/rushdaw/static/samples/drums/crash-3-2.mp3 +0 -0
  51. rushdaw-0.1.0/src/rushdaw/static/samples/drums/crash-3-3.mp3 +0 -0
  52. rushdaw-0.1.0/src/rushdaw/static/samples/drums/kick-1-1.mp3 +0 -0
  53. rushdaw-0.1.0/src/rushdaw/static/samples/drums/kick-1-2.mp3 +0 -0
  54. rushdaw-0.1.0/src/rushdaw/static/samples/drums/kick-1-3.mp3 +0 -0
  55. rushdaw-0.1.0/src/rushdaw/static/samples/drums/kick-2-1.mp3 +0 -0
  56. rushdaw-0.1.0/src/rushdaw/static/samples/drums/kick-2-2.mp3 +0 -0
  57. rushdaw-0.1.0/src/rushdaw/static/samples/drums/kick-2-3.mp3 +0 -0
  58. rushdaw-0.1.0/src/rushdaw/static/samples/drums/kick-3-1.mp3 +0 -0
  59. rushdaw-0.1.0/src/rushdaw/static/samples/drums/kick-3-2.mp3 +0 -0
  60. rushdaw-0.1.0/src/rushdaw/static/samples/drums/kick-3-3.mp3 +0 -0
  61. rushdaw-0.1.0/src/rushdaw/static/samples/drums/ohh-1-1.mp3 +0 -0
  62. rushdaw-0.1.0/src/rushdaw/static/samples/drums/ohh-1-2.mp3 +0 -0
  63. rushdaw-0.1.0/src/rushdaw/static/samples/drums/ohh-1-3.mp3 +0 -0
  64. rushdaw-0.1.0/src/rushdaw/static/samples/drums/ohh-2-1.mp3 +0 -0
  65. rushdaw-0.1.0/src/rushdaw/static/samples/drums/ohh-2-2.mp3 +0 -0
  66. rushdaw-0.1.0/src/rushdaw/static/samples/drums/ohh-2-3.mp3 +0 -0
  67. rushdaw-0.1.0/src/rushdaw/static/samples/drums/ohh-3-1.mp3 +0 -0
  68. rushdaw-0.1.0/src/rushdaw/static/samples/drums/ohh-3-2.mp3 +0 -0
  69. rushdaw-0.1.0/src/rushdaw/static/samples/drums/ohh-3-3.mp3 +0 -0
  70. rushdaw-0.1.0/src/rushdaw/static/samples/drums/ride-1-1.mp3 +0 -0
  71. rushdaw-0.1.0/src/rushdaw/static/samples/drums/ride-1-2.mp3 +0 -0
  72. rushdaw-0.1.0/src/rushdaw/static/samples/drums/ride-1-3.mp3 +0 -0
  73. rushdaw-0.1.0/src/rushdaw/static/samples/drums/ride-2-1.mp3 +0 -0
  74. rushdaw-0.1.0/src/rushdaw/static/samples/drums/ride-2-2.mp3 +0 -0
  75. rushdaw-0.1.0/src/rushdaw/static/samples/drums/ride-2-3.mp3 +0 -0
  76. rushdaw-0.1.0/src/rushdaw/static/samples/drums/ride-3-1.mp3 +0 -0
  77. rushdaw-0.1.0/src/rushdaw/static/samples/drums/ride-3-2.mp3 +0 -0
  78. rushdaw-0.1.0/src/rushdaw/static/samples/drums/ride-3-3.mp3 +0 -0
  79. rushdaw-0.1.0/src/rushdaw/static/samples/drums/snare-1-1.mp3 +0 -0
  80. rushdaw-0.1.0/src/rushdaw/static/samples/drums/snare-1-2.mp3 +0 -0
  81. rushdaw-0.1.0/src/rushdaw/static/samples/drums/snare-1-3.mp3 +0 -0
  82. rushdaw-0.1.0/src/rushdaw/static/samples/drums/snare-2-1.mp3 +0 -0
  83. rushdaw-0.1.0/src/rushdaw/static/samples/drums/snare-2-2.mp3 +0 -0
  84. rushdaw-0.1.0/src/rushdaw/static/samples/drums/snare-2-3.mp3 +0 -0
  85. rushdaw-0.1.0/src/rushdaw/static/samples/drums/snare-3-1.mp3 +0 -0
  86. rushdaw-0.1.0/src/rushdaw/static/samples/drums/snare-3-2.mp3 +0 -0
  87. rushdaw-0.1.0/src/rushdaw/static/samples/drums/snare-3-3.mp3 +0 -0
  88. rushdaw-0.1.0/src/rushdaw/static/samples/drums/tomh-1-1.mp3 +0 -0
  89. rushdaw-0.1.0/src/rushdaw/static/samples/drums/tomh-1-2.mp3 +0 -0
  90. rushdaw-0.1.0/src/rushdaw/static/samples/drums/tomh-1-3.mp3 +0 -0
  91. rushdaw-0.1.0/src/rushdaw/static/samples/drums/tomh-2-1.mp3 +0 -0
  92. rushdaw-0.1.0/src/rushdaw/static/samples/drums/tomh-2-2.mp3 +0 -0
  93. rushdaw-0.1.0/src/rushdaw/static/samples/drums/tomh-2-3.mp3 +0 -0
  94. rushdaw-0.1.0/src/rushdaw/static/samples/drums/tomh-3-1.mp3 +0 -0
  95. rushdaw-0.1.0/src/rushdaw/static/samples/drums/tomh-3-2.mp3 +0 -0
  96. rushdaw-0.1.0/src/rushdaw/static/samples/drums/tomh-3-3.mp3 +0 -0
  97. rushdaw-0.1.0/src/rushdaw/static/samples/drums/toml-1-1.mp3 +0 -0
  98. rushdaw-0.1.0/src/rushdaw/static/samples/drums/toml-1-2.mp3 +0 -0
  99. rushdaw-0.1.0/src/rushdaw/static/samples/drums/toml-1-3.mp3 +0 -0
  100. rushdaw-0.1.0/src/rushdaw/static/samples/drums/toml-2-1.mp3 +0 -0
  101. rushdaw-0.1.0/src/rushdaw/static/samples/drums/toml-2-2.mp3 +0 -0
  102. rushdaw-0.1.0/src/rushdaw/static/samples/drums/toml-2-3.mp3 +0 -0
  103. rushdaw-0.1.0/src/rushdaw/static/samples/drums/toml-3-1.mp3 +0 -0
  104. rushdaw-0.1.0/src/rushdaw/static/samples/drums/toml-3-2.mp3 +0 -0
  105. rushdaw-0.1.0/src/rushdaw/static/samples/drums/toml-3-3.mp3 +0 -0
  106. rushdaw-0.1.0/src/rushdaw/static/samples/drums/tomm-1-1.mp3 +0 -0
  107. rushdaw-0.1.0/src/rushdaw/static/samples/drums/tomm-1-2.mp3 +0 -0
  108. rushdaw-0.1.0/src/rushdaw/static/samples/drums/tomm-1-3.mp3 +0 -0
  109. rushdaw-0.1.0/src/rushdaw/static/samples/drums/tomm-2-1.mp3 +0 -0
  110. rushdaw-0.1.0/src/rushdaw/static/samples/drums/tomm-2-2.mp3 +0 -0
  111. rushdaw-0.1.0/src/rushdaw/static/samples/drums/tomm-2-3.mp3 +0 -0
  112. rushdaw-0.1.0/src/rushdaw/static/samples/drums/tomm-3-1.mp3 +0 -0
  113. rushdaw-0.1.0/src/rushdaw/static/samples/drums/tomm-3-2.mp3 +0 -0
  114. rushdaw-0.1.0/src/rushdaw/static/samples/drums/tomm-3-3.mp3 +0 -0
  115. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_finger/28.mp3 +0 -0
  116. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_finger/31.mp3 +0 -0
  117. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_finger/34.mp3 +0 -0
  118. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_finger/37.mp3 +0 -0
  119. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_finger/40.mp3 +0 -0
  120. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_finger/43.mp3 +0 -0
  121. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_finger/46.mp3 +0 -0
  122. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_finger/49.mp3 +0 -0
  123. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_finger/52.mp3 +0 -0
  124. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_finger/55.mp3 +0 -0
  125. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_finger/58.mp3 +0 -0
  126. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_finger/61.mp3 +0 -0
  127. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_finger/64.mp3 +0 -0
  128. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_finger/67.mp3 +0 -0
  129. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_pick/28.mp3 +0 -0
  130. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_pick/31.mp3 +0 -0
  131. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_pick/34.mp3 +0 -0
  132. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_pick/37.mp3 +0 -0
  133. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_pick/40.mp3 +0 -0
  134. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_pick/43.mp3 +0 -0
  135. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_pick/46.mp3 +0 -0
  136. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_pick/49.mp3 +0 -0
  137. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_pick/52.mp3 +0 -0
  138. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_pick/55.mp3 +0 -0
  139. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_pick/58.mp3 +0 -0
  140. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_pick/61.mp3 +0 -0
  141. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_pick/64.mp3 +0 -0
  142. rushdaw-0.1.0/src/rushdaw/static/samples/electric_bass_pick/67.mp3 +0 -0
  143. rushdaw-0.1.0/src/rushdaw/static/samples/electric_guitar_clean/40.mp3 +0 -0
  144. rushdaw-0.1.0/src/rushdaw/static/samples/electric_guitar_clean/43.mp3 +0 -0
  145. rushdaw-0.1.0/src/rushdaw/static/samples/electric_guitar_clean/46.mp3 +0 -0
  146. rushdaw-0.1.0/src/rushdaw/static/samples/electric_guitar_clean/49.mp3 +0 -0
  147. rushdaw-0.1.0/src/rushdaw/static/samples/electric_guitar_clean/52.mp3 +0 -0
  148. rushdaw-0.1.0/src/rushdaw/static/samples/electric_guitar_clean/55.mp3 +0 -0
  149. rushdaw-0.1.0/src/rushdaw/static/samples/electric_guitar_clean/58.mp3 +0 -0
  150. rushdaw-0.1.0/src/rushdaw/static/samples/electric_guitar_clean/61.mp3 +0 -0
  151. rushdaw-0.1.0/src/rushdaw/static/samples/electric_guitar_clean/64.mp3 +0 -0
  152. rushdaw-0.1.0/src/rushdaw/static/samples/electric_guitar_clean/67.mp3 +0 -0
  153. rushdaw-0.1.0/src/rushdaw/static/samples/electric_guitar_clean/70.mp3 +0 -0
  154. rushdaw-0.1.0/src/rushdaw/static/samples/electric_guitar_clean/73.mp3 +0 -0
  155. rushdaw-0.1.0/src/rushdaw/static/samples/electric_guitar_clean/76.mp3 +0 -0
  156. rushdaw-0.1.0/src/rushdaw/static/samples/electric_guitar_clean/79.mp3 +0 -0
  157. rushdaw-0.1.0/src/rushdaw/static/samples/electric_guitar_clean/82.mp3 +0 -0
  158. rushdaw-0.1.0/src/rushdaw/static/samples/electric_guitar_clean/85.mp3 +0 -0
  159. rushdaw-0.1.0/src/rushdaw/static/samples/electric_guitar_clean/88.mp3 +0 -0
  160. rushdaw-0.1.0/src/rushdaw/static/style.css +211 -0
  161. rushdaw-0.1.0/src/rushdaw/storage.py +230 -0
  162. rushdaw-0.1.0/src/rushdaw.egg-info/PKG-INFO +170 -0
  163. rushdaw-0.1.0/src/rushdaw.egg-info/SOURCES.txt +169 -0
  164. rushdaw-0.1.0/src/rushdaw.egg-info/dependency_links.txt +1 -0
  165. rushdaw-0.1.0/src/rushdaw.egg-info/entry_points.txt +2 -0
  166. rushdaw-0.1.0/src/rushdaw.egg-info/requires.txt +1 -0
  167. rushdaw-0.1.0/src/rushdaw.egg-info/top_level.txt +1 -0
  168. rushdaw-0.1.0/tests/test_cli.py +67 -0
  169. rushdaw-0.1.0/tests/test_mcp.py +503 -0
  170. rushdaw-0.1.0/tests/test_server.py +69 -0
  171. rushdaw-0.1.0/tests/test_storage.py +121 -0
rushdaw-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Afrizal F.A
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,2 @@
1
+ include README.md LICENSE
2
+ recursive-include src/rushdaw/static *
rushdaw-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,170 @@
1
+ Metadata-Version: 2.4
2
+ Name: rushdaw
3
+ Version: 0.1.0
4
+ Summary: RushDAW - web based DAW (mixing, recording, mastering, drum machine, synth) inspired by BandLab
5
+ Author-email: HarshXor <afrzlf4@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://pypi.org/project/rushdaw/
8
+ Keywords: daw,audio,mixing,mastering,music,drum machine,web audio
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Mixers
13
+ Classifier: Environment :: Web Environment
14
+ Requires-Python: >=3.8
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: imageio-ffmpeg>=0.4.8
18
+ Dynamic: license-file
19
+
20
+ # RushDAW 🎛️
21
+
22
+ A web-based DAW, inspired by **BandLab**, for writing songs, **mixing**, **recording**, and **mastering**.
23
+ It's a Python package: the Python side runs a small local server, and the studio runs in your browser using the Web Audio API.
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ python3 -m pip install . # from the source folder
29
+ # or, once it's on PyPI:
30
+ python3 -m pip install rushdaw
31
+ ```
32
+
33
+ Start it:
34
+
35
+ ```bash
36
+ rushdaw # opens http://localhost:8765 in your browser
37
+ rushdaw --port 9000 --no-browser
38
+ python3 -m rushdaw # alternative
39
+ ```
40
+
41
+ If port 8765 is busy: when RushDAW is already running there, `rushdaw` just opens it in the browser; when another program uses it, the next free port (8766, 8767, …) is used. With an explicit `--port`, a busy port gives a clear error instead.
42
+
43
+ `ffmpeg` is installed automatically through the `imageio-ffmpeg` dependency, so you don't need to install it yourself to export MP3/MP4.
44
+
45
+ ## Features
46
+
47
+ | Area | Features |
48
+ |---|---|
49
+ | **Mixing** (main focus) | A mixer channel per track: volume fader (dB), pan, mute/solo, 3-band EQ, compressor, Reverb & Delay sends, peak meter |
50
+ | **Drum machine** | 10-part acoustic kit: **Kick, Snare, Closed Hi-Hat, Open Hi-Hat, High Tom, Mid Tom, Floor Tom, Crash Cymbal, Ride Cymbal, Pedal Hi-Hat** (open hi-hat is choked by closed/pedal hi-hat). Default kit tuning is a **rock / metal** tuning (fat ~190 Hz snare, low toms ~170 / 128 / 88 Hz, clicky metal kick). Per-part tuning under each part name, all in **percent** (100% = default): **Tune 25–400%** (−2 … +2 octaves), **Decay 10–400%**, **Volume off–200%**. Light room reverb + velocity variation. Step sequencer 1–8 bars. Presets: Metal Double Bass (default), Metal Gallop, Blast Beat, Hammer Blast, Thrash Skank, Hardcore Breakdown, Hardcore Two-Step, D-Beat, Crust D-Beat, Punk Beat, Punk Polka, Pop-Punk Drive, plus Four on the floor, Rock, Hip-Hop, Trap, Reggaeton. Projects with the older 9-part kit are converted automatically. Default tempo 180 BPM |
51
+ | **Per-track insert FX** | **Effects** tab / `FX…` button on each mixer strip: EQ, Compressor, Distortion, **Guitar Amp** (input gain → drive → cabinet tone → level), Filter (LP/HP/BP), Chorus, Tremolo, Delay (BPM sync), Reverb. Unlimited effects per track, on/off bypass, reordering, chain presets: Vocal, Guitar Amp, Metal Guitar, Drum Bus, Bass, Lo-Fi. Included in exports and saved projects |
52
+ | **Guitar & Bass MIDI** | `+ Guitar` / `+ Bass` tracks play **real recorded instruments** (sampled clean electric guitar, steel acoustic guitar, finger and pick electric bass), not a synth. Defaults: **Clean Guitar** and **Finger Bass**. Presets: Clean / Acoustic / Crunch / Distortion / Power Chord / Palm Mute Guitar (amped presets put a **Guitar Amp** effect into *that layer's* Effects chain – visible and editable there, independent per layer; switching back to a clean preset removes it), Finger / Pick Bass. Small random timing / dynamics / tuning variation like a real player. Long notes: **drag in the piano roll** to draw one note over many steps, drag a note to change its length, click it to delete. Each instrument has a proper note envelope – **Decay** (seconds the pluck takes to fall), **Sustain** (level a held note keeps; the recording has a seamless sustain loop, so long notes keep ringing) and **Release** (fade after the note), with a live envelope graph. Palm Mute uses Sustain 0 (dies away). Controls: Instrument, Decay, Sustain, Release, Tone, Chord (single / power chord). Engine can also be switched to *Modeled string* or *Synth*. Older guitar/bass tracks are upgraded automatically |
53
+ | **Synth / MIDI** | Piano roll C2–C6, presets Keys / Synth Bass / Lead / Pad / Pluck / 808, wave, attack, release, filter cutoff, note length |
54
+ | **Tempo** | BPM 40–240, time signature 3/4, 4/4, 6/4, grid snap |
55
+ | **Recording** | Record from the mic onto an armed track, **metronome on/off**, count-in, latency compensation. **Punch-in replace**: a new take overwrites the old audio on that track exactly where it was recorded (old clips there are deleted / trimmed / split), so only the newest take is heard; takes are numbered Take 1, 2, 3 … |
56
+ | **Import** | Import audio/video files (button or drag & drop onto a track). Clips can be dragged to change their position or move to another track, trimmed, split (S), duplicated (Ctrl/Cmd+D) |
57
+ | **Mastering** | Master chain EQ → Bus Compressor → Make-up → Limiter → Output, presets (Balanced, Loud Pop, Warm, Bass Boost, Bright), dBFS meter |
58
+ | **Export** | WAV, MP3, MP4 (video with waveform visualizer), M4A, AAC, OGG, OPUS, FLAC, with optional normalization |
59
+ | **Project manager** | Start screen listing all projects: create (templates Metal demo / Band / **D-Beat / Crust Punk** / Empty), open, rename, search, delete to Trash + restore, import `.rushdaw` |
60
+ | **Revision history** | Every 💾 Save (Ctrl/Cmd+S) creates a new revision with an optional note; History lets you open any old revision, restore it (non-destructive, becomes a new revision) or download it. Unsaved changes are auto-saved as a revision when you go back to Projects |
61
+
62
+ ### Keyboard shortcuts
63
+ `Space` play/stop · `Ctrl/Cmd+C / X / V` copy / cut / paste the selected clip, track (layer) or drum part · `R` record · `Home`/`Enter` back to start · `S` split · `Ctrl/Cmd+D` duplicate · `Delete` remove clip · `M` metronome toggle · `Shift + drag` on the ruler sets the loop region · double-click a Drum/Synth lane to add a pattern region.
64
+
65
+ **MIDI regions are linear, not loops.** Each MIDI region (drum, guitar, bass, synth) stores its own pads/notes for its whole length: a pad sounds exactly where it is placed and nothing repeats, so deleting one pad deletes only that pad. Extending a region (drag its right edge, *Length* in the Editor, or right-click → *Extend by 1 empty bar*) adds **empty** bars; shortening cuts the end. Copy/paste, `Ctrl/Cmd+D` duplicate and `S` split create independent copies. The Editor shows the whole region with bar numbers (B1, B2, …), *Region n/m · bar x* with ◀ ▶, **+ Empty bar**, **⧉ Duplicate region** and **Apply to all regions**. Older projects are converted automatically (looping regions are written out once, so they sound the same).
66
+
67
+ **Right-click menus.** Right-click an empty spot of a lane: MIDI tracks → *Add empty bar / 2 / 4 bars here*, *Add copy of the selected region here*, *Paste here*; audio tracks → *Add audio here (import file)…*, *Record here*, *Paste here*. Right-click a region/clip → edit, extend / shorten by a bar, duplicate, split at playhead, delete. Double-clicking an empty spot of a MIDI lane also adds an empty bar.
68
+
69
+ ## Data storage
70
+
71
+ All data stays on your computer in `~/.rushdaw/` (change with `rushdaw --home PATH` or `RUSHDAW_HOME`):
72
+
73
+ ```
74
+ ~/.rushdaw/
75
+ ├── db.sql # SQLite – important metadata only
76
+ └── file/
77
+ └── <project_id>/
78
+ ├── rev-0001.rushdaw # full project snapshot (tracks, notes, FX, embedded audio)
79
+ ├── rev-0002.rushdaw
80
+ └── ...
81
+ ```
82
+
83
+ - `db.sql` table `projects`: id, name, bpm, time signature, created/updated time, latest revision, deleted time (soft delete).
84
+ - `db.sql` table `revisions`: project, revision number, note, file name, size, SHA-256, track count, time.
85
+ - Project content (including audio) is **not** stored in the database. It lives in the `.rushdaw` files, written atomically.
86
+ - Deleting a project only moves it to the Trash. Its files and history stay on disk.
87
+
88
+ ## MCP (edit projects from an AI agent)
89
+
90
+ RushDAW includes an **MCP server** (Model Context Protocol) so AI assistants and agents can read and edit projects, tracks (layers), drum pads, notes and effects. The transport is **HTTP + SSE only**.
91
+
92
+ The API key is **optional**. Without `--api-key` MCP is **off** and the DAW works normally. To turn MCP on, start with a key (or set `RUSHDAW_API_KEY`):
93
+
94
+ ```bash
95
+ rushdaw --api-key "my-secret-key"
96
+ # MCP: http://localhost:8765/sse (HTTP+SSE transport, header 'Authorization: Bearer <api key>')
97
+ ```
98
+
99
+ - `GET /sse` opens the event stream. The first event, `endpoint`, gives the URL to post to: `/messages?session_id=...`.
100
+ - `POST /messages?session_id=...` takes JSON-RPC messages and answers `202 Accepted`. The responses arrive as `message` events on the SSE stream.
101
+ - Both endpoints require `Authorization: Bearer <api key>` (otherwise 401).
102
+
103
+ Example client config:
104
+
105
+ ```json
106
+ {
107
+ "mcpServers": {
108
+ "rushdaw": {
109
+ "type": "sse",
110
+ "url": "http://localhost:8765/sse",
111
+ "headers": { "Authorization": "Bearer my-secret-key" }
112
+ }
113
+ }
114
+ }
115
+ ```
116
+
117
+ | Group | Tools |
118
+ |---|---|
119
+ | Info / projects | `get_reference`, `list_projects`, `get_project`, `create_project`, `update_project` (title/BPM/time signature), `list_revisions`, `restore_revision`, `delete_project` (to Trash), `set_master` |
120
+ | Tracks / layers | `add_track` (audio/drum/synth/guitar), `update_track` (name, volume, pan, mute, solo, EQ, compressor, sends), `delete_track`, `duplicate_track`, `move_track` |
121
+ | Effects | `set_track_effects`, `add_effect`, `update_effect` (params / bypass / remove) |
122
+ | Drum pads | `set_drum_pads` (turn steps on/off), `set_drum_pattern` (`"x...x..."` strings), `apply_drum_preset`, `clear_drum_pads`, `set_drum_tuning` (`tune_percent` / `decay_percent` / `volume_percent`, or raw pitch / decay / level / `volume_db`), `set_pattern_length` |
123
+ | Synth / guitar | `set_notes` (e.g. `{"note": "E2", "step": 1, "length": 2}`), `set_synth` |
124
+ | Clips / regions | `add_region` (`empty: true` for a blank bar, `copy_from` another region, or the track pattern written out), `update_clip`, `delete_clip`. Pattern tools (`set_drum_pads`, `set_drum_pattern`, `apply_drum_preset`, `clear_drum_pads`, `set_pattern_length`, `set_notes`) take an optional `clip_id` to edit only that region; without it they edit the whole track (all regions). Regions are linear: `update_clip length_beats` / `set_pattern_length` extend with empty steps or cut the end |
125
+
126
+ Every MCP edit is saved as a **new revision** (`MCP: …` in History), so it can always be restored. An editor open in the browser reloads automatically when MCP changes the project. If you have unsaved changes, it shows a warning instead.
127
+
128
+ ## Uploading to PyPI
129
+
130
+ ```bash
131
+ python3 -m pip install build twine
132
+ python3 -m build # creates dist/rushdaw-0.1.0.tar.gz and .whl
133
+ python3 -m twine check dist/*
134
+ python3 -m twine upload dist/* # requires a PyPI API token
135
+ ```
136
+
137
+ Bump `version` in `pyproject.toml` and `src/rushdaw/__init__.py` for every release. If the name `rushdaw` is already taken on PyPI, change `name` in `pyproject.toml`.
138
+
139
+ ## Development
140
+
141
+ ```bash
142
+ python3 -m pip install -e .
143
+ python3 -m unittest discover -s tests
144
+ ```
145
+
146
+ ## Architecture
147
+ - `src/rushdaw/server.py` – standard-library HTTP server (static UI + `/api/export`, `/api/info`)
148
+ - `src/rushdaw/export.py` – converts the WAV mixdown to other formats with ffmpeg
149
+ - `src/rushdaw/storage.py` – SQLite metadata + `.rushdaw` revision files
150
+ - `src/rushdaw/model.py` – server-side project model (mirrors the web UI data)
151
+ - `src/rushdaw/mcp.py` – MCP tools + JSON-RPC (served over HTTP+SSE by `server.py`, bearer auth)
152
+ - `src/rushdaw/static/` – the DAW UI (HTML/CSS/JS, Web Audio engine, offline rendering for export)
153
+
154
+ ## Third-party sounds
155
+
156
+ The guitar and bass recordings in `src/rushdaw/static/samples/` come from the **FluidR3_GM** SoundFont by Frank Wen (MP3 renders from [midi-js-soundfonts](https://github.com/gleitz/midi-js-soundfonts)), licensed under [Creative Commons Attribution 3.0](https://creativecommons.org/licenses/by/3.0/us/). See `src/rushdaw/static/samples/NOTICE.txt`.
157
+
158
+ ### Vocal MIDI (voice: "hooo" / "haaa")
159
+ Click **+ Vocal MIDI** to add a sung-voice layer played from the piano roll. Drag across pads to make one long, sustained note (same as guitar/bass).
160
+ - Presets: Choir Aah (default), Choir Ooh, Choir Ohh, Solo Hoo, Solo Haa (engine `voice`, formant synthesis).
161
+ - Editor controls: Vowel (Aah / Eeh / Iih / Ohh / Ooh), Voices (1 = solo, 2-6 = choir), Breath, Vibrato, Attack, Decay, Sustain, Release.
162
+ - New track starts with the "Choir Space" effect chain (EQ, Chorus, Reverb), visible and editable in the Effects tab.
163
+ - New effect **Vocal Formant** (vowel a/e/i/o/u, formant shift, mix) turns any layer into an "aah"/"ooh" sound.
164
+ - MCP: `add_track type=vocal`, `set_synth` with `engine: voice`, `vowel`, `voices`, `breath`, `vibrato`; `add_effect type=formant`.
165
+
166
+ ### Natural drums: recorded kit, velocity and feel
167
+ - Drum tracks play a **recorded acoustic kit** (MuldjordKit, CC-BY 4.0): 3 velocity layers x 3 different hits per part, so repeated hits never sound identical. Editor → **Kit**: *Acoustic (recorded)* (default) or *Synth kit*. Tune / Decay / Volume per part still work.
168
+ - **Velocity per pad and note**: choose the velocity for new pads (Accent 100%, Normal 80%, Soft 60%, Ghost 30%); right-click or Alt+click a pad/note to cycle it. Softer pads are drawn dimmer and play a softer recording (ghost notes, accents).
169
+ - **Feel** per layer: Humanize (random timing in ms; drums add a drummer feel: snare slightly behind, hats slightly ahead), Swing, Push / lay back. New drum tracks start with Humanize 6 ms.
170
+ - MCP: `set_drum_pads velocity` (1-127), pattern strings `x` = 100%, `o` = soft, `g` = ghost, `set_notes` note `velocity`, `update_track humanize_ms / swing / push_ms`.
@@ -0,0 +1,151 @@
1
+ # RushDAW 🎛️
2
+
3
+ A web-based DAW, inspired by **BandLab**, for writing songs, **mixing**, **recording**, and **mastering**.
4
+ It's a Python package: the Python side runs a small local server, and the studio runs in your browser using the Web Audio API.
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ python3 -m pip install . # from the source folder
10
+ # or, once it's on PyPI:
11
+ python3 -m pip install rushdaw
12
+ ```
13
+
14
+ Start it:
15
+
16
+ ```bash
17
+ rushdaw # opens http://localhost:8765 in your browser
18
+ rushdaw --port 9000 --no-browser
19
+ python3 -m rushdaw # alternative
20
+ ```
21
+
22
+ If port 8765 is busy: when RushDAW is already running there, `rushdaw` just opens it in the browser; when another program uses it, the next free port (8766, 8767, …) is used. With an explicit `--port`, a busy port gives a clear error instead.
23
+
24
+ `ffmpeg` is installed automatically through the `imageio-ffmpeg` dependency, so you don't need to install it yourself to export MP3/MP4.
25
+
26
+ ## Features
27
+
28
+ | Area | Features |
29
+ |---|---|
30
+ | **Mixing** (main focus) | A mixer channel per track: volume fader (dB), pan, mute/solo, 3-band EQ, compressor, Reverb & Delay sends, peak meter |
31
+ | **Drum machine** | 10-part acoustic kit: **Kick, Snare, Closed Hi-Hat, Open Hi-Hat, High Tom, Mid Tom, Floor Tom, Crash Cymbal, Ride Cymbal, Pedal Hi-Hat** (open hi-hat is choked by closed/pedal hi-hat). Default kit tuning is a **rock / metal** tuning (fat ~190 Hz snare, low toms ~170 / 128 / 88 Hz, clicky metal kick). Per-part tuning under each part name, all in **percent** (100% = default): **Tune 25–400%** (−2 … +2 octaves), **Decay 10–400%**, **Volume off–200%**. Light room reverb + velocity variation. Step sequencer 1–8 bars. Presets: Metal Double Bass (default), Metal Gallop, Blast Beat, Hammer Blast, Thrash Skank, Hardcore Breakdown, Hardcore Two-Step, D-Beat, Crust D-Beat, Punk Beat, Punk Polka, Pop-Punk Drive, plus Four on the floor, Rock, Hip-Hop, Trap, Reggaeton. Projects with the older 9-part kit are converted automatically. Default tempo 180 BPM |
32
+ | **Per-track insert FX** | **Effects** tab / `FX…` button on each mixer strip: EQ, Compressor, Distortion, **Guitar Amp** (input gain → drive → cabinet tone → level), Filter (LP/HP/BP), Chorus, Tremolo, Delay (BPM sync), Reverb. Unlimited effects per track, on/off bypass, reordering, chain presets: Vocal, Guitar Amp, Metal Guitar, Drum Bus, Bass, Lo-Fi. Included in exports and saved projects |
33
+ | **Guitar & Bass MIDI** | `+ Guitar` / `+ Bass` tracks play **real recorded instruments** (sampled clean electric guitar, steel acoustic guitar, finger and pick electric bass), not a synth. Defaults: **Clean Guitar** and **Finger Bass**. Presets: Clean / Acoustic / Crunch / Distortion / Power Chord / Palm Mute Guitar (amped presets put a **Guitar Amp** effect into *that layer's* Effects chain – visible and editable there, independent per layer; switching back to a clean preset removes it), Finger / Pick Bass. Small random timing / dynamics / tuning variation like a real player. Long notes: **drag in the piano roll** to draw one note over many steps, drag a note to change its length, click it to delete. Each instrument has a proper note envelope – **Decay** (seconds the pluck takes to fall), **Sustain** (level a held note keeps; the recording has a seamless sustain loop, so long notes keep ringing) and **Release** (fade after the note), with a live envelope graph. Palm Mute uses Sustain 0 (dies away). Controls: Instrument, Decay, Sustain, Release, Tone, Chord (single / power chord). Engine can also be switched to *Modeled string* or *Synth*. Older guitar/bass tracks are upgraded automatically |
34
+ | **Synth / MIDI** | Piano roll C2–C6, presets Keys / Synth Bass / Lead / Pad / Pluck / 808, wave, attack, release, filter cutoff, note length |
35
+ | **Tempo** | BPM 40–240, time signature 3/4, 4/4, 6/4, grid snap |
36
+ | **Recording** | Record from the mic onto an armed track, **metronome on/off**, count-in, latency compensation. **Punch-in replace**: a new take overwrites the old audio on that track exactly where it was recorded (old clips there are deleted / trimmed / split), so only the newest take is heard; takes are numbered Take 1, 2, 3 … |
37
+ | **Import** | Import audio/video files (button or drag & drop onto a track). Clips can be dragged to change their position or move to another track, trimmed, split (S), duplicated (Ctrl/Cmd+D) |
38
+ | **Mastering** | Master chain EQ → Bus Compressor → Make-up → Limiter → Output, presets (Balanced, Loud Pop, Warm, Bass Boost, Bright), dBFS meter |
39
+ | **Export** | WAV, MP3, MP4 (video with waveform visualizer), M4A, AAC, OGG, OPUS, FLAC, with optional normalization |
40
+ | **Project manager** | Start screen listing all projects: create (templates Metal demo / Band / **D-Beat / Crust Punk** / Empty), open, rename, search, delete to Trash + restore, import `.rushdaw` |
41
+ | **Revision history** | Every 💾 Save (Ctrl/Cmd+S) creates a new revision with an optional note; History lets you open any old revision, restore it (non-destructive, becomes a new revision) or download it. Unsaved changes are auto-saved as a revision when you go back to Projects |
42
+
43
+ ### Keyboard shortcuts
44
+ `Space` play/stop · `Ctrl/Cmd+C / X / V` copy / cut / paste the selected clip, track (layer) or drum part · `R` record · `Home`/`Enter` back to start · `S` split · `Ctrl/Cmd+D` duplicate · `Delete` remove clip · `M` metronome toggle · `Shift + drag` on the ruler sets the loop region · double-click a Drum/Synth lane to add a pattern region.
45
+
46
+ **MIDI regions are linear, not loops.** Each MIDI region (drum, guitar, bass, synth) stores its own pads/notes for its whole length: a pad sounds exactly where it is placed and nothing repeats, so deleting one pad deletes only that pad. Extending a region (drag its right edge, *Length* in the Editor, or right-click → *Extend by 1 empty bar*) adds **empty** bars; shortening cuts the end. Copy/paste, `Ctrl/Cmd+D` duplicate and `S` split create independent copies. The Editor shows the whole region with bar numbers (B1, B2, …), *Region n/m · bar x* with ◀ ▶, **+ Empty bar**, **⧉ Duplicate region** and **Apply to all regions**. Older projects are converted automatically (looping regions are written out once, so they sound the same).
47
+
48
+ **Right-click menus.** Right-click an empty spot of a lane: MIDI tracks → *Add empty bar / 2 / 4 bars here*, *Add copy of the selected region here*, *Paste here*; audio tracks → *Add audio here (import file)…*, *Record here*, *Paste here*. Right-click a region/clip → edit, extend / shorten by a bar, duplicate, split at playhead, delete. Double-clicking an empty spot of a MIDI lane also adds an empty bar.
49
+
50
+ ## Data storage
51
+
52
+ All data stays on your computer in `~/.rushdaw/` (change with `rushdaw --home PATH` or `RUSHDAW_HOME`):
53
+
54
+ ```
55
+ ~/.rushdaw/
56
+ ├── db.sql # SQLite – important metadata only
57
+ └── file/
58
+ └── <project_id>/
59
+ ├── rev-0001.rushdaw # full project snapshot (tracks, notes, FX, embedded audio)
60
+ ├── rev-0002.rushdaw
61
+ └── ...
62
+ ```
63
+
64
+ - `db.sql` table `projects`: id, name, bpm, time signature, created/updated time, latest revision, deleted time (soft delete).
65
+ - `db.sql` table `revisions`: project, revision number, note, file name, size, SHA-256, track count, time.
66
+ - Project content (including audio) is **not** stored in the database. It lives in the `.rushdaw` files, written atomically.
67
+ - Deleting a project only moves it to the Trash. Its files and history stay on disk.
68
+
69
+ ## MCP (edit projects from an AI agent)
70
+
71
+ RushDAW includes an **MCP server** (Model Context Protocol) so AI assistants and agents can read and edit projects, tracks (layers), drum pads, notes and effects. The transport is **HTTP + SSE only**.
72
+
73
+ The API key is **optional**. Without `--api-key` MCP is **off** and the DAW works normally. To turn MCP on, start with a key (or set `RUSHDAW_API_KEY`):
74
+
75
+ ```bash
76
+ rushdaw --api-key "my-secret-key"
77
+ # MCP: http://localhost:8765/sse (HTTP+SSE transport, header 'Authorization: Bearer <api key>')
78
+ ```
79
+
80
+ - `GET /sse` opens the event stream. The first event, `endpoint`, gives the URL to post to: `/messages?session_id=...`.
81
+ - `POST /messages?session_id=...` takes JSON-RPC messages and answers `202 Accepted`. The responses arrive as `message` events on the SSE stream.
82
+ - Both endpoints require `Authorization: Bearer <api key>` (otherwise 401).
83
+
84
+ Example client config:
85
+
86
+ ```json
87
+ {
88
+ "mcpServers": {
89
+ "rushdaw": {
90
+ "type": "sse",
91
+ "url": "http://localhost:8765/sse",
92
+ "headers": { "Authorization": "Bearer my-secret-key" }
93
+ }
94
+ }
95
+ }
96
+ ```
97
+
98
+ | Group | Tools |
99
+ |---|---|
100
+ | Info / projects | `get_reference`, `list_projects`, `get_project`, `create_project`, `update_project` (title/BPM/time signature), `list_revisions`, `restore_revision`, `delete_project` (to Trash), `set_master` |
101
+ | Tracks / layers | `add_track` (audio/drum/synth/guitar), `update_track` (name, volume, pan, mute, solo, EQ, compressor, sends), `delete_track`, `duplicate_track`, `move_track` |
102
+ | Effects | `set_track_effects`, `add_effect`, `update_effect` (params / bypass / remove) |
103
+ | Drum pads | `set_drum_pads` (turn steps on/off), `set_drum_pattern` (`"x...x..."` strings), `apply_drum_preset`, `clear_drum_pads`, `set_drum_tuning` (`tune_percent` / `decay_percent` / `volume_percent`, or raw pitch / decay / level / `volume_db`), `set_pattern_length` |
104
+ | Synth / guitar | `set_notes` (e.g. `{"note": "E2", "step": 1, "length": 2}`), `set_synth` |
105
+ | Clips / regions | `add_region` (`empty: true` for a blank bar, `copy_from` another region, or the track pattern written out), `update_clip`, `delete_clip`. Pattern tools (`set_drum_pads`, `set_drum_pattern`, `apply_drum_preset`, `clear_drum_pads`, `set_pattern_length`, `set_notes`) take an optional `clip_id` to edit only that region; without it they edit the whole track (all regions). Regions are linear: `update_clip length_beats` / `set_pattern_length` extend with empty steps or cut the end |
106
+
107
+ Every MCP edit is saved as a **new revision** (`MCP: …` in History), so it can always be restored. An editor open in the browser reloads automatically when MCP changes the project. If you have unsaved changes, it shows a warning instead.
108
+
109
+ ## Uploading to PyPI
110
+
111
+ ```bash
112
+ python3 -m pip install build twine
113
+ python3 -m build # creates dist/rushdaw-0.1.0.tar.gz and .whl
114
+ python3 -m twine check dist/*
115
+ python3 -m twine upload dist/* # requires a PyPI API token
116
+ ```
117
+
118
+ Bump `version` in `pyproject.toml` and `src/rushdaw/__init__.py` for every release. If the name `rushdaw` is already taken on PyPI, change `name` in `pyproject.toml`.
119
+
120
+ ## Development
121
+
122
+ ```bash
123
+ python3 -m pip install -e .
124
+ python3 -m unittest discover -s tests
125
+ ```
126
+
127
+ ## Architecture
128
+ - `src/rushdaw/server.py` – standard-library HTTP server (static UI + `/api/export`, `/api/info`)
129
+ - `src/rushdaw/export.py` – converts the WAV mixdown to other formats with ffmpeg
130
+ - `src/rushdaw/storage.py` – SQLite metadata + `.rushdaw` revision files
131
+ - `src/rushdaw/model.py` – server-side project model (mirrors the web UI data)
132
+ - `src/rushdaw/mcp.py` – MCP tools + JSON-RPC (served over HTTP+SSE by `server.py`, bearer auth)
133
+ - `src/rushdaw/static/` – the DAW UI (HTML/CSS/JS, Web Audio engine, offline rendering for export)
134
+
135
+ ## Third-party sounds
136
+
137
+ The guitar and bass recordings in `src/rushdaw/static/samples/` come from the **FluidR3_GM** SoundFont by Frank Wen (MP3 renders from [midi-js-soundfonts](https://github.com/gleitz/midi-js-soundfonts)), licensed under [Creative Commons Attribution 3.0](https://creativecommons.org/licenses/by/3.0/us/). See `src/rushdaw/static/samples/NOTICE.txt`.
138
+
139
+ ### Vocal MIDI (voice: "hooo" / "haaa")
140
+ Click **+ Vocal MIDI** to add a sung-voice layer played from the piano roll. Drag across pads to make one long, sustained note (same as guitar/bass).
141
+ - Presets: Choir Aah (default), Choir Ooh, Choir Ohh, Solo Hoo, Solo Haa (engine `voice`, formant synthesis).
142
+ - Editor controls: Vowel (Aah / Eeh / Iih / Ohh / Ooh), Voices (1 = solo, 2-6 = choir), Breath, Vibrato, Attack, Decay, Sustain, Release.
143
+ - New track starts with the "Choir Space" effect chain (EQ, Chorus, Reverb), visible and editable in the Effects tab.
144
+ - New effect **Vocal Formant** (vowel a/e/i/o/u, formant shift, mix) turns any layer into an "aah"/"ooh" sound.
145
+ - MCP: `add_track type=vocal`, `set_synth` with `engine: voice`, `vowel`, `voices`, `breath`, `vibrato`; `add_effect type=formant`.
146
+
147
+ ### Natural drums: recorded kit, velocity and feel
148
+ - Drum tracks play a **recorded acoustic kit** (MuldjordKit, CC-BY 4.0): 3 velocity layers x 3 different hits per part, so repeated hits never sound identical. Editor → **Kit**: *Acoustic (recorded)* (default) or *Synth kit*. Tune / Decay / Volume per part still work.
149
+ - **Velocity per pad and note**: choose the velocity for new pads (Accent 100%, Normal 80%, Soft 60%, Ghost 30%); right-click or Alt+click a pad/note to cycle it. Softer pads are drawn dimmer and play a softer recording (ghost notes, accents).
150
+ - **Feel** per layer: Humanize (random timing in ms; drums add a drummer feel: snare slightly behind, hats slightly ahead), Swing, Push / lay back. New drum tracks start with Humanize 6 ms.
151
+ - MCP: `set_drum_pads velocity` (1-127), pattern strings `x` = 100%, `o` = soft, `g` = ghost, `set_notes` note `velocity`, `update_track humanize_ms / swing / push_ms`.
@@ -0,0 +1,37 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "rushdaw"
7
+ version = "0.1.0"
8
+ description = "RushDAW - web based DAW (mixing, recording, mastering, drum machine, synth) inspired by BandLab"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "HarshXor", email = "afrzlf4@gmail.com" }]
13
+ keywords = ["daw", "audio", "mixing", "mastering", "music", "drum machine", "web audio"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ "Topic :: Multimedia :: Sound/Audio :: Mixers",
19
+ "Environment :: Web Environment",
20
+ ]
21
+ dependencies = ["imageio-ffmpeg>=0.4.8"]
22
+
23
+ [project.scripts]
24
+ rushdaw = "rushdaw.cli:main"
25
+
26
+ [project.urls]
27
+ Homepage = "https://pypi.org/project/rushdaw/"
28
+
29
+ [tool.setuptools]
30
+ package-dir = { "" = "src" }
31
+ include-package-data = true
32
+
33
+ [tool.setuptools.packages.find]
34
+ where = ["src"]
35
+
36
+ [tool.setuptools.package-data]
37
+ rushdaw = ["static/*", "static/samples/*", "static/samples/*/*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """RushDAW - a web based digital audio workstation served by Python."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,4 @@
1
+ from .cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
@@ -0,0 +1,92 @@
1
+ """Command line entry point: ``rushdaw`` / ``python -m rushdaw``."""
2
+ import argparse
3
+ import errno
4
+ import json
5
+ import os
6
+ import sys
7
+ import urllib.request
8
+ import threading
9
+ import webbrowser
10
+
11
+ from . import __version__
12
+ from .server import make_server
13
+
14
+ DEFAULT_PORT = 8765
15
+ PORT_TRIES = 20 # when the default port is busy, try 8766, 8767, ...
16
+
17
+
18
+ def _url(host, port):
19
+ return f"http://{'localhost' if host in ('127.0.0.1', '0.0.0.0') else host}:{port}/"
20
+
21
+
22
+ def _running_rushdaw(host, port):
23
+ """Return /api/info of a RushDAW server already listening on host:port, else None."""
24
+ h = "127.0.0.1" if host in ("0.0.0.0", "") else host
25
+ try:
26
+ with urllib.request.urlopen(f"http://{h}:{port}/api/info", timeout=1.5) as r:
27
+ if not r.headers.get("Server", "").startswith("RushDAW/"):
28
+ return None
29
+ return json.loads(r.read().decode("utf-8"))
30
+ except Exception:
31
+ return None
32
+
33
+
34
+ def _bind(args):
35
+ """Create the server. If the port is busy: reuse an already running RushDAW (same settings),
36
+ otherwise (default port only) move on to the next free port. Returns (httpd, None) or (None, url_of_running)."""
37
+ port = args.port or DEFAULT_PORT
38
+ ports = [port] if args.port else range(port, port + PORT_TRIES)
39
+ for pt in ports:
40
+ try:
41
+ return make_server(args.host, pt, args.home, args.api_key), None
42
+ except OSError as e:
43
+ if e.errno != errno.EADDRINUSE:
44
+ raise
45
+ info = _running_rushdaw(args.host, pt)
46
+ if info is not None and not args.home and not args.api_key:
47
+ return None, _url(args.host, pt) # RushDAW is already running there: just use it
48
+ why = "another RushDAW with different settings" if info is not None else "another program"
49
+ if args.port:
50
+ sys.exit(f"rushdaw: port {pt} is already in use by {why}. Start with a different --port, e.g. --port {pt + 1}.")
51
+ print(f"Port {pt} is in use by {why}, trying {pt + 1}…", flush=True)
52
+ sys.exit(f"rushdaw: no free port between {port} and {port + PORT_TRIES - 1}. Use --port.")
53
+
54
+
55
+ def main(argv=None):
56
+ p = argparse.ArgumentParser(prog="rushdaw", description="RushDAW web based DAW")
57
+ p.add_argument("--host", default="127.0.0.1", help="bind address (default 127.0.0.1)")
58
+ p.add_argument("--port", type=int, default=None,
59
+ help=f"port (default {DEFAULT_PORT}; if busy, the next free port is used automatically)")
60
+ p.add_argument("--no-browser", action="store_true", help="do not open a browser automatically")
61
+ p.add_argument("--home", default=None, help="data folder (default ~/.rushdaw or $RUSHDAW_HOME)")
62
+ p.add_argument("--api-key", default=os.environ.get("RUSHDAW_API_KEY"),
63
+ help="optional: enable the MCP server (HTTP+SSE at /sse) protected by "
64
+ "'Authorization: Bearer <KEY>' (default: $RUSHDAW_API_KEY; MCP is off when not set)")
65
+ p.add_argument("--version", action="version", version=f"rushdaw {__version__}")
66
+ args = p.parse_args(argv)
67
+
68
+ httpd, running = _bind(args)
69
+ if running:
70
+ print(f"RushDAW is already running at {running} – opening it.", flush=True)
71
+ if not args.no_browser:
72
+ webbrowser.open(running)
73
+ return
74
+ url = _url(args.host, httpd.server_address[1])
75
+ print(f"RushDAW {__version__} running at {url} (Ctrl+C to stop)", flush=True)
76
+ print(f"Data: {httpd.RequestHandlerClass.storage.home} (db.sql + file/)", flush=True)
77
+ if args.api_key:
78
+ print(f"MCP: {url}sse (HTTP+SSE transport, header 'Authorization: Bearer <api key>')", flush=True)
79
+ else:
80
+ print("MCP: off (optional – start with --api-key KEY to enable)", flush=True)
81
+ if not args.no_browser:
82
+ threading.Timer(0.8, lambda: webbrowser.open(url)).start()
83
+ try:
84
+ httpd.serve_forever()
85
+ except KeyboardInterrupt:
86
+ print("\nStopping RushDAW.")
87
+ finally:
88
+ httpd.server_close()
89
+
90
+
91
+ if __name__ == "__main__":
92
+ main()
@@ -0,0 +1,73 @@
1
+ """Convert a rendered WAV mixdown to other formats with ffmpeg."""
2
+ import os
3
+ import shutil
4
+ import subprocess
5
+ import tempfile
6
+
7
+ # format -> (extension, mime, ffmpeg output args)
8
+ FORMATS = {
9
+ "wav": ("wav", "audio/wav", None),
10
+ "mp3": ("mp3", "audio/mpeg", ["-codec:a", "libmp3lame", "-b:a", "320k"]),
11
+ "ogg": ("ogg", "audio/ogg", ["-codec:a", "libvorbis", "-q:a", "6"]),
12
+ "flac": ("flac", "audio/flac", ["-codec:a", "flac"]),
13
+ "m4a": ("m4a", "audio/mp4", ["-codec:a", "aac", "-b:a", "256k"]),
14
+ "aac": ("aac", "audio/aac", ["-codec:a", "aac", "-b:a", "256k"]),
15
+ "opus": ("opus", "audio/ogg", ["-codec:a", "libopus", "-b:a", "192k"]),
16
+ "mp4": ("mp4", "video/mp4", "VIDEO"),
17
+ }
18
+
19
+
20
+ def find_ffmpeg():
21
+ """Return path to an ffmpeg binary (system one first, then imageio-ffmpeg)."""
22
+ exe = shutil.which("ffmpeg")
23
+ if exe:
24
+ return exe
25
+ try:
26
+ import imageio_ffmpeg
27
+
28
+ return imageio_ffmpeg.get_ffmpeg_exe()
29
+ except Exception:
30
+ return None
31
+
32
+
33
+ def available_formats():
34
+ if find_ffmpeg():
35
+ return list(FORMATS)
36
+ return ["wav"]
37
+
38
+
39
+ def convert(wav_bytes, fmt, title="RushDAW"):
40
+ """Convert WAV bytes into ``fmt``. Returns (bytes, ext, mime)."""
41
+ fmt = fmt.lower()
42
+ if fmt not in FORMATS:
43
+ raise ValueError(f"unsupported format: {fmt}")
44
+ ext, mime, args = FORMATS[fmt]
45
+ if args is None:
46
+ return wav_bytes, ext, mime
47
+ ffmpeg = find_ffmpeg()
48
+ if not ffmpeg:
49
+ raise RuntimeError("ffmpeg not available; install imageio-ffmpeg or ffmpeg")
50
+ safe_title = "".join(c for c in title if c.isalnum() or c in " -_.")[:80] or "RushDAW"
51
+ with tempfile.TemporaryDirectory(prefix="rushdaw_") as tmp:
52
+ src = os.path.join(tmp, "in.wav")
53
+ dst = os.path.join(tmp, "out." + ext)
54
+ with open(src, "wb") as f:
55
+ f.write(wav_bytes)
56
+ cmd = [ffmpeg, "-y", "-hide_banner", "-loglevel", "error", "-i", src]
57
+ if args == "VIDEO":
58
+ # music video: animated waveform on dark background
59
+ cmd += [
60
+ "-filter_complex",
61
+ "[0:a]showwaves=s=1280x720:mode=cline:rate=30:colors=0x00e5ff|0xffffff,format=yuv420p[v]",
62
+ "-map", "[v]", "-map", "0:a",
63
+ "-c:v", "libx264", "-preset", "veryfast", "-crf", "23",
64
+ "-c:a", "aac", "-b:a", "256k", "-shortest", "-movflags", "+faststart",
65
+ ]
66
+ else:
67
+ cmd += args
68
+ cmd += ["-metadata", f"title={safe_title}", "-metadata", "encoder=RushDAW", dst]
69
+ proc = subprocess.run(cmd, capture_output=True, timeout=900)
70
+ if proc.returncode != 0 or not os.path.exists(dst):
71
+ raise RuntimeError(proc.stderr.decode("utf-8", "replace")[-800:] or "ffmpeg failed")
72
+ with open(dst, "rb") as f:
73
+ return f.read(), ext, mime