fuze-eletrik commited on
Commit
0752ee5
·
verified ·
1 Parent(s): d03188b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +71 -1
app.py CHANGED
@@ -83,6 +83,30 @@ async () => {
83
  let previousUrls = [];
84
  function setLog(message) { $("sam-log").textContent = message; }
85
  function setStatus(message) { $("sam-status").textContent = message; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  function errorDetails(error) {
87
  const values = [];
88
  for (const key of ["name", "message", "code", "stack"]) {
@@ -187,6 +211,9 @@ async () => {
187
  runtime.env.wasm.numThreads = 0; // automatic, subject to browser support
188
  }
189
  runtimeProvider = provider;
 
 
 
190
  }
191
  return runtime;
192
  }
@@ -332,7 +359,14 @@ async () => {
332
  maskDims: [1, mask.length]
333
  };
334
  }
 
 
 
 
 
 
335
  async function runDit(noisy, timeValue, audioFeatures, text, sequenceLength) {
 
336
  const batch = 1;
337
  const zerosInt = new BigInt64Array(sequenceLength);
338
  const mask = new Uint8Array(sequenceLength).fill(1);
@@ -350,9 +384,11 @@ async () => {
350
  };
351
  const outputs = await sessions.dit.run(feeds);
352
  const tensor = outputs.velocity || outputs[sessions.dit.outputNames[0]];
 
353
  return new Float32Array(tensor.data);
354
  }
355
  async function runOde(initial, audioFeatures, text, steps, sequenceLength) {
 
356
  let x = new Float32Array(initial);
357
  const dt = 1 / steps;
358
  for (let i = 0; i < steps; i++) {
@@ -368,9 +404,11 @@ async () => {
368
  // Yield so status/progress updates can paint between long CPU steps.
369
  await new Promise(resolve => setTimeout(resolve, 0));
370
  }
 
371
  return x;
372
  }
373
  async function decodeLatent(latent, latentChannels, timeSteps) {
 
374
  const session = sessions.decoder;
375
  const chunkSize = 25;
376
  const hopLength = 1920;
@@ -396,6 +434,7 @@ async () => {
396
  const output = new Float32Array(totalLength);
397
  let offset = 0;
398
  for (const chunk of chunks) { output.set(chunk, offset); offset += chunk.length; }
 
399
  return output;
400
  }
401
  function wavBlob(samples, sampleRate) {
@@ -433,20 +472,28 @@ async () => {
433
  const expectedSamples = audio.samples.length;
434
  if (expectedSamples > 48000 * 20) throw new Error("For this first prototype, use audio no longer than 20 seconds.");
435
  setStatus("Loading models for " + deviceLabel + "…");
 
 
436
  if (provider === "wasm") {
437
  setLog("CPU mode selected. ONNX Runtime will use WebAssembly in a background worker where supported, keeping the page responsive. This may take longer than GPU inference.");
438
  }
439
  await loadModels(provider);
 
 
440
  const startedAt = performance.now();
441
  setStatus("Encoding audio on " + deviceLabel + "…");
 
442
  const encoded = await encodeAudio(audio.samples);
 
443
  if (encoded.dims.length !== 3) throw new Error("Unexpected encoder output dimensions: " + JSON.stringify(encoded.dims));
444
  const [batch, latentChannels, timeSteps] = encoded.dims;
445
  if (batch !== 1 || latentChannels !== 128) {
446
  throw new Error("Unexpected audio latent shape " + JSON.stringify(encoded.dims) + "; expected [1, 128, time].");
447
  }
448
  setStatus("Encoding text…");
 
449
  const text = await encodeText(prompt);
 
450
  const mixture = new Float32Array(timeSteps * 256);
451
  for (let t = 0; t < timeSteps; t++) {
452
  for (let c = 0; c < latentChannels; c++) {
@@ -474,21 +521,44 @@ async () => {
474
  }
475
  }
476
  setStatus("Decoding target…");
 
477
  const target = await decodeLatent(targetLatent, latentChannels, timeSteps);
 
478
  setStatus("Decoding residual…");
 
479
  const residual = await decodeLatent(residualLatent, latentChannels, timeSteps);
 
480
  if (audio.samples.length !== expectedSamples) {
481
  throw new Error("Internal audio buffer was unexpectedly detached or resized: expected " + expectedSamples + " samples, got " + audio.samples.length + ".");
482
  }
483
  showOutput("sam-target-player", "sam-target-download", target.slice(0, expectedSamples), 48000, "target.wav");
484
  showOutput("sam-residual-player", "sam-residual-download", residual.slice(0, expectedSamples), 48000, "residual.wav");
485
  setStatus("Finished");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
486
  setLog("Finished browser inference.\nInput: " + file.name + "\nPrompt: " + prompt +
487
- "\nDevice: " + deviceLabel + "\nProcessing time: " + ((performance.now() - startedAt) / 1000).toFixed(2) + " s" +
488
  "\nODE steps: " + steps + "\nInput samples: " + expectedSamples +
489
  "\nLatent shape: " + JSON.stringify(encoded.dims) +
490
  "\nTarget samples: " + Math.min(target.length, expectedSamples) +
491
  "\nResidual samples: " + Math.min(residual.length, expectedSamples) +
 
492
  "\n\nThis is an experimental port. Compare quality and tensor shapes against the Python reference before relying on results.");
493
  }
494
  $("sam-clear-cache").addEventListener("click", async () => {
 
83
  let previousUrls = [];
84
  function setLog(message) { $("sam-log").textContent = message; }
85
  function setStatus(message) { $("sam-status").textContent = message; }
86
+ function diagnosticEnvironment(provider) {
87
+ const runtime = ort;
88
+ const isolated = window.crossOriginIsolated === true;
89
+ const sharedArrayBuffer = typeof SharedArrayBuffer !== "undefined";
90
+ const requestedThreads = runtime?.env?.wasm?.numThreads ?? "not initialized";
91
+ const proxy = runtime?.env?.wasm?.proxy ?? "not initialized";
92
+ const report = {
93
+ browser: navigator.userAgent,
94
+ hardwareConcurrency: navigator.hardwareConcurrency ?? "unknown",
95
+ crossOriginIsolated: isolated,
96
+ sharedArrayBufferAvailable: sharedArrayBuffer,
97
+ wasmMultithreadingLikelyAvailable: isolated && sharedArrayBuffer,
98
+ provider,
99
+ wasmThreadsRequested: requestedThreads,
100
+ wasmProxyWorker: proxy,
101
+ note: isolated && sharedArrayBuffer
102
+ ? "WASM multithreading prerequisites appear available; actual thread use depends on ONNX Runtime/browser support."
103
+ : "WASM multithreading is likely unavailable because cross-origin isolation or SharedArrayBuffer is missing."
104
+ };
105
+ return report;
106
+ }
107
+ function logDiagnostic(label, details) {
108
+ console.info("[SAM Audio diagnostics] " + label, details);
109
+ }
110
  function errorDetails(error) {
111
  const values = [];
112
  for (const key of ["name", "message", "code", "stack"]) {
 
211
  runtime.env.wasm.numThreads = 0; // automatic, subject to browser support
212
  }
213
  runtimeProvider = provider;
214
+ const diagnostics = diagnosticEnvironment(provider);
215
+ logDiagnostic("Runtime configured", diagnostics);
216
+ setLog("Runtime diagnostics\n" + JSON.stringify(diagnostics, null, 2));
217
  }
218
  return runtime;
219
  }
 
359
  maskDims: [1, mask.length]
360
  };
361
  }
362
+ const stageMetrics = {};
363
+ function addMetric(name, milliseconds) {
364
+ if (!stageMetrics[name]) stageMetrics[name] = {totalMs: 0, calls: 0};
365
+ stageMetrics[name].totalMs += milliseconds;
366
+ stageMetrics[name].calls += 1;
367
+ }
368
  async function runDit(noisy, timeValue, audioFeatures, text, sequenceLength) {
369
+ const started = performance.now();
370
  const batch = 1;
371
  const zerosInt = new BigInt64Array(sequenceLength);
372
  const mask = new Uint8Array(sequenceLength).fill(1);
 
384
  };
385
  const outputs = await sessions.dit.run(feeds);
386
  const tensor = outputs.velocity || outputs[sessions.dit.outputNames[0]];
387
+ addMetric("DiT session.run", performance.now() - started);
388
  return new Float32Array(tensor.data);
389
  }
390
  async function runOde(initial, audioFeatures, text, steps, sequenceLength) {
391
+ const started = performance.now();
392
  let x = new Float32Array(initial);
393
  const dt = 1 / steps;
394
  for (let i = 0; i < steps; i++) {
 
404
  // Yield so status/progress updates can paint between long CPU steps.
405
  await new Promise(resolve => setTimeout(resolve, 0));
406
  }
407
+ addMetric("ODE denoising total", performance.now() - started);
408
  return x;
409
  }
410
  async function decodeLatent(latent, latentChannels, timeSteps) {
411
+ const started = performance.now();
412
  const session = sessions.decoder;
413
  const chunkSize = 25;
414
  const hopLength = 1920;
 
434
  const output = new Float32Array(totalLength);
435
  let offset = 0;
436
  for (const chunk of chunks) { output.set(chunk, offset); offset += chunk.length; }
437
+ addMetric("Decoder total", performance.now() - started);
438
  return output;
439
  }
440
  function wavBlob(samples, sampleRate) {
 
472
  const expectedSamples = audio.samples.length;
473
  if (expectedSamples > 48000 * 20) throw new Error("For this first prototype, use audio no longer than 20 seconds.");
474
  setStatus("Loading models for " + deviceLabel + "…");
475
+ const totalStartedAt = performance.now();
476
+ const modelLoadStartedAt = performance.now();
477
  if (provider === "wasm") {
478
  setLog("CPU mode selected. ONNX Runtime will use WebAssembly in a background worker where supported, keeping the page responsive. This may take longer than GPU inference.");
479
  }
480
  await loadModels(provider);
481
+ const modelLoadMs = performance.now() - modelLoadStartedAt;
482
+ for (const key of Object.keys(stageMetrics)) delete stageMetrics[key];
483
  const startedAt = performance.now();
484
  setStatus("Encoding audio on " + deviceLabel + "…");
485
+ const encoderStartedAt = performance.now();
486
  const encoded = await encodeAudio(audio.samples);
487
+ const encoderMs = performance.now() - encoderStartedAt;
488
  if (encoded.dims.length !== 3) throw new Error("Unexpected encoder output dimensions: " + JSON.stringify(encoded.dims));
489
  const [batch, latentChannels, timeSteps] = encoded.dims;
490
  if (batch !== 1 || latentChannels !== 128) {
491
  throw new Error("Unexpected audio latent shape " + JSON.stringify(encoded.dims) + "; expected [1, 128, time].");
492
  }
493
  setStatus("Encoding text…");
494
+ const textStartedAt = performance.now();
495
  const text = await encodeText(prompt);
496
+ const textMs = performance.now() - textStartedAt;
497
  const mixture = new Float32Array(timeSteps * 256);
498
  for (let t = 0; t < timeSteps; t++) {
499
  for (let c = 0; c < latentChannels; c++) {
 
521
  }
522
  }
523
  setStatus("Decoding target…");
524
+ const targetStartedAt = performance.now();
525
  const target = await decodeLatent(targetLatent, latentChannels, timeSteps);
526
+ const targetDecodeMs = performance.now() - targetStartedAt;
527
  setStatus("Decoding residual…");
528
+ const residualStartedAt = performance.now();
529
  const residual = await decodeLatent(residualLatent, latentChannels, timeSteps);
530
+ const residualDecodeMs = performance.now() - residualStartedAt;
531
  if (audio.samples.length !== expectedSamples) {
532
  throw new Error("Internal audio buffer was unexpectedly detached or resized: expected " + expectedSamples + " samples, got " + audio.samples.length + ".");
533
  }
534
  showOutput("sam-target-player", "sam-target-download", target.slice(0, expectedSamples), 48000, "target.wav");
535
  showOutput("sam-residual-player", "sam-residual-download", residual.slice(0, expectedSamples), 48000, "residual.wav");
536
  setStatus("Finished");
537
+ const runtimeDiagnostics = diagnosticEnvironment(provider);
538
+ const stageTimings = {
539
+ modelLoadingSeconds: +(modelLoadMs / 1000).toFixed(2),
540
+ audioEncoderSeconds: +(encoderMs / 1000).toFixed(2),
541
+ textEncoderIncludingTokenizerSeconds: +(textMs / 1000).toFixed(2),
542
+ denoisingTotalSeconds: +((stageMetrics["ODE denoising total"]?.totalMs || 0) / 1000).toFixed(2),
543
+ ditSessionRunSeconds: +((stageMetrics["DiT session.run"]?.totalMs || 0) / 1000).toFixed(2),
544
+ ditCalls: stageMetrics["DiT session.run"]?.calls || 0,
545
+ averageDitCallSeconds: stageMetrics["DiT session.run"]?.calls
546
+ ? +((stageMetrics["DiT session.run"].totalMs / stageMetrics["DiT session.run"].calls) / 1000).toFixed(3) : 0,
547
+ targetDecoderSeconds: +(targetDecodeMs / 1000).toFixed(2),
548
+ residualDecoderSeconds: +(residualDecodeMs / 1000).toFixed(2),
549
+ inferenceAfterModelsSeconds: +((performance.now() - startedAt) / 1000).toFixed(2),
550
+ totalSecondsIncludingModelLoad: +((performance.now() - totalStartedAt) / 1000).toFixed(2)
551
+ };
552
+ const diagnosticsText = "Runtime diagnostics\n" + JSON.stringify(runtimeDiagnostics, null, 2) +
553
+ "\n\nStage timings\n" + JSON.stringify(stageTimings, null, 2);
554
+ logDiagnostic("Inference completed", {runtime: runtimeDiagnostics, timings: stageTimings});
555
  setLog("Finished browser inference.\nInput: " + file.name + "\nPrompt: " + prompt +
556
+ "\nDevice: " + deviceLabel + "\nProcessing time after model load: " + stageTimings.inferenceAfterModelsSeconds + " s" +
557
  "\nODE steps: " + steps + "\nInput samples: " + expectedSamples +
558
  "\nLatent shape: " + JSON.stringify(encoded.dims) +
559
  "\nTarget samples: " + Math.min(target.length, expectedSamples) +
560
  "\nResidual samples: " + Math.min(residual.length, expectedSamples) +
561
+ "\n\n" + diagnosticsText +
562
  "\n\nThis is an experimental port. Compare quality and tensor shapes against the Python reference before relying on results.");
563
  }
564
  $("sam-clear-cache").addEventListener("click", async () => {