jackboyla commited on
Commit
a0f6a1b
·
verified ·
1 Parent(s): 6b4a4b8

Deploy checked Hendrix D26 browser demo

Browse files
.gitattributes CHANGED
@@ -34,3 +34,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  source/docs/assets/choir_bwv407.wav filter=lfs diff=lfs merge=lfs -text
 
 
 
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  source/docs/assets/choir_bwv407.wav filter=lfs diff=lfs merge=lfs -text
37
+ diagnostic.wav filter=lfs diff=lfs merge=lfs -text
38
+ source/docs/assets/slakh_test_Track01876.wav filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -21,8 +21,8 @@ custom_headers:
21
 
22
  # Hendrix
23
 
24
- Choose an audio file, record with your microphone, or try the short Bach
25
- example. Hendrix predicts notes and instrument families, draws a piano roll,
26
  plays the result, and lets you download a MIDI file.
27
 
28
  Audio decoding and model inference run on your device. The app does not upload
@@ -36,9 +36,12 @@ depends on browser permissions. Predictions are a research result and may
36
  miss notes, assign the wrong instrument, or give poor note endings. MIDI
37
  playback uses a simple browser synth.
38
 
39
- The example is a two-second excerpt of our synthetic Bach BWV 407 chorale
40
- rendering, made from a public-domain score. We do not redistribute the local
41
- YouTube diagnostic audio.
 
 
 
42
 
43
  Model: 46.5M-parameter D26, derived from YPTF-MoE+Multi. `models/fp32/manifest.json`
44
  records the source checkpoint and graph hashes. `deployment.json` records the
 
21
 
22
  # Hendrix
23
 
24
+ Choose an audio file, record with your microphone, or try the short demo
25
+ clip. Hendrix predicts notes and instrument families, draws a piano roll,
26
  plays the result, and lets you download a MIDI file.
27
 
28
  Audio decoding and model inference run on your device. The app does not upload
 
36
  miss notes, assign the wrong instrument, or give poor note endings. MIDI
37
  playback uses a simple browser synth.
38
 
39
+ The example is a two-second excerpt (6–8 seconds of the repo's preview) from
40
+ Slakh2100 test Track01876, cropped from the existing project demo. Slakh2100
41
+ is by Ethan Manilow, Gordon Wichern, Prem Seetharaman, and Jonathan Le Roux,
42
+ available on [Zenodo](https://zenodo.org/records/4599666) under
43
+ [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). We do not
44
+ redistribute the local YouTube diagnostic audio.
45
 
46
  Model: 46.5M-parameter D26, derived from YPTF-MoE+Multi. `models/fp32/manifest.json`
47
  records the source checkpoint and graph hashes. `deployment.json` records the
assets/local-DNpA26UR.js ADDED
The diff for this file is too large to render. See raw diff
 
deployment.json CHANGED
@@ -5,10 +5,16 @@
5
  "source_dirty": true,
6
  "build_command": "VITE_HENDRIX_SPACE=1 npx --yes pnpm@10.20.0 --dir demo/web build --manifest",
7
  "checkpoint_sha256": "3df08bb38dce7ae30be23090524b63755c565d1a3242860aef6c9401fc125bbe",
 
 
 
 
 
 
8
  "files": {
9
  "README.md": {
10
- "bytes": 2435,
11
- "sha256": "bc7674dddae42f8ac5e6e998dcc901b42ed74b1e5246cabf8364e053b04b57e3"
12
  },
13
  "assets/PianoRollCanvas-xSq9nagc.js": {
14
  "bytes": 3055,
@@ -18,9 +24,9 @@
18
  "bytes": 4149,
19
  "sha256": "dfab143da9fda372f21e3513d3882baffdc7c40f4df51ad86f7ad3c01b34ef3d"
20
  },
21
- "assets/local-DblFrGk8.js": {
22
- "bytes": 133988,
23
- "sha256": "215ca24985995907cf196d12e0d63ba585fc25e4fd0ff5893629ba63f7be3d17"
24
  },
25
  "assets/ort-wasm-simd-threaded.asyncify-CvvOzbbq.wasm": {
26
  "bytes": 24254953,
@@ -39,8 +45,8 @@
39
  "sha256": "1be18fcb99a202c31bfe5ab4154afda3aaeb95af45706ae534653910c0b914dc"
40
  },
41
  "diagnostic.wav": {
42
- "bytes": 65644,
43
- "sha256": "1d2f1b3974848ea5a714c393b53e85a0d5c9d0d34cac846040ef293dbc961b8a"
44
  },
45
  "favicon.png": {
46
  "bytes": 4671,
@@ -48,11 +54,11 @@
48
  },
49
  "index.html": {
50
  "bytes": 770,
51
- "sha256": "ae09e25f5a38cb3af09085e6a4fd3379d8ed4b66d372289be41707ea40495f57"
52
  },
53
  "local.html": {
54
  "bytes": 770,
55
- "sha256": "ae09e25f5a38cb3af09085e6a4fd3379d8ed4b66d372289be41707ea40495f57"
56
  },
57
  "models/fp32/cross_cache.onnx": {
58
  "bytes": 12648891,
@@ -195,8 +201,8 @@
195
  "sha256": "461a7c37240940c440694d7a65cc18c47090b8429bbeb1daa5a6175440c75c99"
196
  },
197
  "source/demo/web/src/local.tsx": {
198
- "bytes": 15344,
199
- "sha256": "e0ad3fe81c9272536e3f0dd0eea05365b10cdc49329d5f8f50646933d18446cb"
200
  },
201
  "source/demo/web/src/main.tsx": {
202
  "bytes": 381,
@@ -231,20 +237,20 @@
231
  "sha256": "2e3e6765cca9de53a0f23e42a52f4f152b9789b36e630345c9e2e6fb3f1b8475"
232
  },
233
  "source/deploy/huggingface/README.md": {
234
- "bytes": 2435,
235
- "sha256": "bc7674dddae42f8ac5e6e998dcc901b42ed74b1e5246cabf8364e053b04b57e3"
236
  },
237
- "source/docs/assets/choir_bwv407.wav": {
238
- "bytes": 960044,
239
- "sha256": "2640f4fe1b68b6d5f8392c688d5a919ac0e89a6b017d3c3f41ebad5bb72d6e0f"
240
  },
241
  "source/scripts/check_huggingface_space.mjs": {
242
- "bytes": 4649,
243
- "sha256": "81b018c40a7f236afc1f2ed229304a9b4dc0337847529a15798d79b0ac62bc54"
244
  },
245
  "source/scripts/deploy_huggingface_space.py": {
246
- "bytes": 7881,
247
- "sha256": "cdd1e2d2e5bb2f13aa4673f1356ba1d8bc81a89c9c0cb79103697366ebb65737"
248
  }
249
  }
250
  }
 
5
  "source_dirty": true,
6
  "build_command": "VITE_HENDRIX_SPACE=1 npx --yes pnpm@10.20.0 --dir demo/web build --manifest",
7
  "checkpoint_sha256": "3df08bb38dce7ae30be23090524b63755c565d1a3242860aef6c9401fc125bbe",
8
+ "example": {
9
+ "source": "Slakh2100 Track01876",
10
+ "clip_offset_seconds": 6,
11
+ "duration_seconds": 2,
12
+ "license": "CC-BY-4.0"
13
+ },
14
  "files": {
15
  "README.md": {
16
+ "bytes": 2670,
17
+ "sha256": "6e3f1d73e894ae2b0c221cf67a249539c1f474a22dfb2485fde1c46fe7ab308b"
18
  },
19
  "assets/PianoRollCanvas-xSq9nagc.js": {
20
  "bytes": 3055,
 
24
  "bytes": 4149,
25
  "sha256": "dfab143da9fda372f21e3513d3882baffdc7c40f4df51ad86f7ad3c01b34ef3d"
26
  },
27
+ "assets/local-DNpA26UR.js": {
28
+ "bytes": 133985,
29
+ "sha256": "065a69aa447b366c140d4b15cc3390269182840da10ac416804456e0996ef163"
30
  },
31
  "assets/ort-wasm-simd-threaded.asyncify-CvvOzbbq.wasm": {
32
  "bytes": 24254953,
 
45
  "sha256": "1be18fcb99a202c31bfe5ab4154afda3aaeb95af45706ae534653910c0b914dc"
46
  },
47
  "diagnostic.wav": {
48
+ "bytes": 128044,
49
+ "sha256": "87586b57be103e74950daa9035b2459a000a57b19a320e1faf0649d678fbcfc4"
50
  },
51
  "favicon.png": {
52
  "bytes": 4671,
 
54
  },
55
  "index.html": {
56
  "bytes": 770,
57
+ "sha256": "40e2ccc066efb40ca142438b0d3e0bdbaf8479c74e660cb803e1d9af9f1e951c"
58
  },
59
  "local.html": {
60
  "bytes": 770,
61
+ "sha256": "40e2ccc066efb40ca142438b0d3e0bdbaf8479c74e660cb803e1d9af9f1e951c"
62
  },
63
  "models/fp32/cross_cache.onnx": {
64
  "bytes": 12648891,
 
201
  "sha256": "461a7c37240940c440694d7a65cc18c47090b8429bbeb1daa5a6175440c75c99"
202
  },
203
  "source/demo/web/src/local.tsx": {
204
+ "bytes": 15341,
205
+ "sha256": "c5e348b230534646320555cdb770047b38b035fee4bf49383a1c1f30c903f5c1"
206
  },
207
  "source/demo/web/src/main.tsx": {
208
  "bytes": 381,
 
237
  "sha256": "2e3e6765cca9de53a0f23e42a52f4f152b9789b36e630345c9e2e6fb3f1b8475"
238
  },
239
  "source/deploy/huggingface/README.md": {
240
+ "bytes": 2670,
241
+ "sha256": "6e3f1d73e894ae2b0c221cf67a249539c1f474a22dfb2485fde1c46fe7ab308b"
242
  },
243
+ "source/docs/assets/slakh_test_Track01876.wav": {
244
+ "bytes": 1920044,
245
+ "sha256": "6b514aae5163051da66d320b12957f7f90ab0aaf120b3b4fae7164b883f823ec"
246
  },
247
  "source/scripts/check_huggingface_space.mjs": {
248
+ "bytes": 4643,
249
+ "sha256": "38356c955cdf3f8c3592e10d56470f37e9ca8f12b0829312bb2ebf47ed8aa6c5"
250
  },
251
  "source/scripts/deploy_huggingface_space.py": {
252
+ "bytes": 8153,
253
+ "sha256": "a370c0ce64da666b22af4ca5d2d36dba628f5ea00cad1a646b65abaa7f0f7d85"
254
  }
255
  }
256
  }
diagnostic.wav CHANGED
Binary files a/diagnostic.wav and b/diagnostic.wav differ
 
index.html CHANGED
@@ -6,7 +6,7 @@
6
  <meta name="theme-color" content="#090a0d" />
7
  <link rel="icon" href="/favicon.png" />
8
  <title>Hendrix — on-device transcription</title>
9
- <script type="module" crossorigin src="/assets/local-DblFrGk8.js"></script>
10
  <link rel="modulepreload" crossorigin href="/assets/style-CdJLhYve.js">
11
  <link rel="modulepreload" crossorigin href="/assets/PianoRollCanvas-xSq9nagc.js">
12
  <link rel="stylesheet" crossorigin href="/assets/style-BAFoxh21.css">
 
6
  <meta name="theme-color" content="#090a0d" />
7
  <link rel="icon" href="/favicon.png" />
8
  <title>Hendrix — on-device transcription</title>
9
+ <script type="module" crossorigin src="/assets/local-DNpA26UR.js"></script>
10
  <link rel="modulepreload" crossorigin href="/assets/style-CdJLhYve.js">
11
  <link rel="modulepreload" crossorigin href="/assets/PianoRollCanvas-xSq9nagc.js">
12
  <link rel="stylesheet" crossorigin href="/assets/style-BAFoxh21.css">
local.html CHANGED
@@ -6,7 +6,7 @@
6
  <meta name="theme-color" content="#090a0d" />
7
  <link rel="icon" href="/favicon.png" />
8
  <title>Hendrix — on-device transcription</title>
9
- <script type="module" crossorigin src="/assets/local-DblFrGk8.js"></script>
10
  <link rel="modulepreload" crossorigin href="/assets/style-CdJLhYve.js">
11
  <link rel="modulepreload" crossorigin href="/assets/PianoRollCanvas-xSq9nagc.js">
12
  <link rel="stylesheet" crossorigin href="/assets/style-BAFoxh21.css">
 
6
  <meta name="theme-color" content="#090a0d" />
7
  <link rel="icon" href="/favicon.png" />
8
  <title>Hendrix — on-device transcription</title>
9
+ <script type="module" crossorigin src="/assets/local-DNpA26UR.js"></script>
10
  <link rel="modulepreload" crossorigin href="/assets/style-CdJLhYve.js">
11
  <link rel="modulepreload" crossorigin href="/assets/PianoRollCanvas-xSq9nagc.js">
12
  <link rel="stylesheet" crossorigin href="/assets/style-BAFoxh21.css">
source/demo/web/src/local.tsx CHANGED
@@ -272,7 +272,7 @@ function LocalApp() {
272
  if (selected) chooseFile(selected);
273
  }} />
274
  </label>
275
- <button className="secondary-button" disabled={inputLocked} onClick={() => prepareInput(loadDiagnosticClip)}>{isSpace ? "Try Bach example" : "Known check clip"}</button>
276
  {!recording ? (
277
  <button className="secondary-button" onClick={() => prepareInput(startRecording)} disabled={!capability?.supported || inputLocked}>Record here</button>
278
  ) : (
 
272
  if (selected) chooseFile(selected);
273
  }} />
274
  </label>
275
+ <button className="secondary-button" disabled={inputLocked} onClick={() => prepareInput(loadDiagnosticClip)}>{isSpace ? "Try demo clip" : "Known check clip"}</button>
276
  {!recording ? (
277
  <button className="secondary-button" onClick={() => prepareInput(startRecording)} disabled={!capability?.supported || inputLocked}>Record here</button>
278
  ) : (
source/deploy/huggingface/README.md CHANGED
@@ -21,8 +21,8 @@ custom_headers:
21
 
22
  # Hendrix
23
 
24
- Choose an audio file, record with your microphone, or try the short Bach
25
- example. Hendrix predicts notes and instrument families, draws a piano roll,
26
  plays the result, and lets you download a MIDI file.
27
 
28
  Audio decoding and model inference run on your device. The app does not upload
@@ -36,9 +36,12 @@ depends on browser permissions. Predictions are a research result and may
36
  miss notes, assign the wrong instrument, or give poor note endings. MIDI
37
  playback uses a simple browser synth.
38
 
39
- The example is a two-second excerpt of our synthetic Bach BWV 407 chorale
40
- rendering, made from a public-domain score. We do not redistribute the local
41
- YouTube diagnostic audio.
 
 
 
42
 
43
  Model: 46.5M-parameter D26, derived from YPTF-MoE+Multi. `models/fp32/manifest.json`
44
  records the source checkpoint and graph hashes. `deployment.json` records the
 
21
 
22
  # Hendrix
23
 
24
+ Choose an audio file, record with your microphone, or try the short demo
25
+ clip. Hendrix predicts notes and instrument families, draws a piano roll,
26
  plays the result, and lets you download a MIDI file.
27
 
28
  Audio decoding and model inference run on your device. The app does not upload
 
36
  miss notes, assign the wrong instrument, or give poor note endings. MIDI
37
  playback uses a simple browser synth.
38
 
39
+ The example is a two-second excerpt (6–8 seconds of the repo's preview) from
40
+ Slakh2100 test Track01876, cropped from the existing project demo. Slakh2100
41
+ is by Ethan Manilow, Gordon Wichern, Prem Seetharaman, and Jonathan Le Roux,
42
+ available on [Zenodo](https://zenodo.org/records/4599666) under
43
+ [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). We do not
44
+ redistribute the local YouTube diagnostic audio.
45
 
46
  Model: 46.5M-parameter D26, derived from YPTF-MoE+Multi. `models/fp32/manifest.json`
47
  records the source checkpoint and graph hashes. `deployment.json` records the
source/docs/assets/slakh_test_Track01876.wav ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6b514aae5163051da66d320b12957f7f90ab0aaf120b3b4fae7164b883f823ec
3
+ size 1920044
source/scripts/check_huggingface_space.mjs CHANGED
@@ -37,7 +37,7 @@ async function check(page, embedded) {
37
  await page.goto(`${base}/local.html?backend=wasm`, { waitUntil: "domcontentloaded", timeout: 60_000 });
38
  app = page.mainFrame();
39
  }
40
- await app.getByRole("button", { name: "Try Bach example" }).waitFor({ timeout: 60_000 });
41
  await app.waitForFunction(() => document.querySelector(".capability-card")?.textContent.includes("WebAssembly CPU"));
42
  const isolation = await app.evaluate(() => ({
43
  isolated: crossOriginIsolated, secure: isSecureContext,
@@ -46,7 +46,7 @@ async function check(page, embedded) {
46
  }));
47
  assert.equal(isolation.secure, true);
48
  if (!embedded) assert.equal(isolation.isolated, true, "direct app must enable cross-origin isolation");
49
- await app.getByRole("button", { name: "Try Bach example" }).click();
50
  const started = Date.now();
51
  await app.getByRole("button", { name: "Run on this device" }).click();
52
  await app.waitForFunction(() => {
 
37
  await page.goto(`${base}/local.html?backend=wasm`, { waitUntil: "domcontentloaded", timeout: 60_000 });
38
  app = page.mainFrame();
39
  }
40
+ await app.getByRole("button", { name: "Try demo clip" }).waitFor({ timeout: 60_000 });
41
  await app.waitForFunction(() => document.querySelector(".capability-card")?.textContent.includes("WebAssembly CPU"));
42
  const isolation = await app.evaluate(() => ({
43
  isolated: crossOriginIsolated, secure: isSecureContext,
 
46
  }));
47
  assert.equal(isolation.secure, true);
48
  if (!embedded) assert.equal(isolation.isolated, true, "direct app must enable cross-origin isolation");
49
+ await app.getByRole("button", { name: "Try demo clip" }).click();
50
  const started = Date.now();
51
  await app.getByRole("button", { name: "Run on this device" }).click();
52
  await app.waitForFunction(() => {
source/scripts/deploy_huggingface_space.py CHANGED
@@ -81,11 +81,13 @@ def package_space(web_dir: Path, graph_dir: Path, ort_dir: Path, output: Path) -
81
  for filename in ORT_FILES:
82
  copy_file(ort_dir / filename, output / "ort" / filename)
83
 
84
- # A public-domain synthetic example replaces the private jazz diagnostic.
85
- with wave.open(str(REPO / "docs/assets/choir_bwv407.wav"), "rb") as source:
 
86
  with wave.open(str(output / "diagnostic.wav"), "wb") as target:
87
  target.setparams(source.getparams())
88
- target.writeframes(source.readframes(round(2.05 * source.getframerate())))
 
89
 
90
  copy_file(REPO / "deploy/huggingface/README.md", output / "README.md")
91
  # Keep the exact app source next to the build for review and reproduction.
@@ -96,7 +98,7 @@ def package_space(web_dir: Path, graph_dir: Path, ort_dir: Path, output: Path) -
96
  copy_file(Path(__file__), output / "source/scripts/deploy_huggingface_space.py")
97
  copy_file(REPO / "scripts/check_huggingface_space.mjs", output / "source/scripts/check_huggingface_space.mjs")
98
  copy_file(REPO / "deploy/huggingface/README.md", output / "source/deploy/huggingface/README.md")
99
- copy_file(REPO / "docs/assets/choir_bwv407.wav", output / "source/docs/assets/choir_bwv407.wav")
100
 
101
  def git(*args: str) -> str:
102
  return subprocess.check_output(["git", *args], cwd=REPO, text=True).strip()
@@ -108,6 +110,7 @@ def package_space(web_dir: Path, graph_dir: Path, ort_dir: Path, output: Path) -
108
  "source_dirty": bool(git("status", "--porcelain")),
109
  "build_command": "VITE_HENDRIX_SPACE=1 npx --yes pnpm@10.20.0 --dir demo/web build --manifest",
110
  "checkpoint_sha256": manifest["checkpoint"]["sha256"],
 
111
  "files": {
112
  path.relative_to(output).as_posix(): {"bytes": path.stat().st_size, "sha256": sha256(path)}
113
  for path in sorted(output.rglob("*")) if path.is_file()
 
81
  for filename in ORT_FILES:
82
  copy_file(ort_dir / filename, output / "ort" / filename)
83
 
84
+ # Use the public CC-BY demo mix, skipping its silent opening. Never copy
85
+ # the private YouTube jazz diagnostic or the local examples directory.
86
+ with wave.open(str(REPO / "docs/assets/slakh_test_Track01876.wav"), "rb") as source:
87
  with wave.open(str(output / "diagnostic.wav"), "wb") as target:
88
  target.setparams(source.getparams())
89
+ source.setpos(6 * source.getframerate())
90
+ target.writeframes(source.readframes(2 * source.getframerate()))
91
 
92
  copy_file(REPO / "deploy/huggingface/README.md", output / "README.md")
93
  # Keep the exact app source next to the build for review and reproduction.
 
98
  copy_file(Path(__file__), output / "source/scripts/deploy_huggingface_space.py")
99
  copy_file(REPO / "scripts/check_huggingface_space.mjs", output / "source/scripts/check_huggingface_space.mjs")
100
  copy_file(REPO / "deploy/huggingface/README.md", output / "source/deploy/huggingface/README.md")
101
+ copy_file(REPO / "docs/assets/slakh_test_Track01876.wav", output / "source/docs/assets/slakh_test_Track01876.wav")
102
 
103
  def git(*args: str) -> str:
104
  return subprocess.check_output(["git", *args], cwd=REPO, text=True).strip()
 
110
  "source_dirty": bool(git("status", "--porcelain")),
111
  "build_command": "VITE_HENDRIX_SPACE=1 npx --yes pnpm@10.20.0 --dir demo/web build --manifest",
112
  "checkpoint_sha256": manifest["checkpoint"]["sha256"],
113
+ "example": {"source": "Slakh2100 Track01876", "clip_offset_seconds": 6, "duration_seconds": 2, "license": "CC-BY-4.0"},
114
  "files": {
115
  path.relative_to(output).as_posix(): {"bytes": path.stat().st_size, "sha256": sha256(path)}
116
  for path in sorted(output.rglob("*")) if path.is_file()