shreyask commited on
Commit
cc4d90f
·
verified ·
1 Parent(s): 4e8059a

speed slider now scales pred_dur (was playback-rate only)

Browse files

Re-exported v0.2 ONNX with speed as a third dynamic input
(float32[1]); KModel.forward_with_tokens divides
duration_proj output by speed before rounding, so 0.85x actually
allocates +18% frames per phoneme — consonants get room to breathe
instead of being crammed and played back slow.

Wiring:
- model.ts: synthesize(ids, refS, speed) passes a Tensor("float32",
[speed], [1]) as the 'speed' ONNX input.
- main.ts: pulls speedEl.value into the synthesize call.
audioEl.playbackRate stays at 1.0 — the buffer is already at the
requested pacing, so per-word timings line up with playback.

Side effect: re-export with torch 2.6 produced ~22% longer audio at
speed=1.0 than the previous TORCH26 export (Pearson r 0.992 vs old —
same audio shape, slight numerical drift from the InstanceNorm-train
warning). Net: at speed=0.85, perceived pacing comparable to old
slider=0.85 setup.

assets/{index-C7URAddU.js → index--nWWJ2d_.js} RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:3138cdd545845a8eb98e2a0af9d038d1332dd311c5e455d09683cbc08cb3de82
3
- size 19065620
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:453cab6ad3dfbb505afe87d21bd0fb4f905e8cf2295e5e17d7b4f1e2ae25a71b
3
+ size 19065680
index.html CHANGED
@@ -17,7 +17,7 @@
17
  rel="stylesheet"
18
  />
19
 
20
- <script type="module" crossorigin src="/assets/index-C7URAddU.js"></script>
21
  <link rel="stylesheet" crossorigin href="/assets/index-BdhRcMoO.css">
22
  </head>
23
  <body>
 
17
  rel="stylesheet"
18
  />
19
 
20
+ <script type="module" crossorigin src="/assets/index--nWWJ2d_.js"></script>
21
  <link rel="stylesheet" crossorigin href="/assets/index-BdhRcMoO.css">
22
  </head>
23
  <body>
src/main.ts CHANGED
@@ -499,7 +499,7 @@ async function onSynth() {
499
  const idx = Math.min(tok.inputIds.length - 1, 509);
500
  const refS = voicepack.slice(idx * 256, (idx + 1) * 256);
501
 
502
- const result = await session.synthesize(tok.inputIds, refS);
503
  sampleRate = result.sampleRate;
504
  const PAD_SAMPLES = Math.floor(sampleRate * INTER_CHUNK_PAD_SEC);
505
  const { phonemes, words } = buildTimings(tok, result.predDur, sampleRate);
@@ -568,7 +568,11 @@ async function onSynth() {
568
  currentAudioURL = URL.createObjectURL(wavBlob);
569
  audioEl.src = currentAudioURL;
570
  audioEl.preservesPitch = true;
571
- audioEl.playbackRate = speed;
 
 
 
 
572
 
573
  const phonemes = allPhonemes;
574
  const words = allWords;
 
499
  const idx = Math.min(tok.inputIds.length - 1, 509);
500
  const refS = voicepack.slice(idx * 256, (idx + 1) * 256);
501
 
502
+ const result = await session.synthesize(tok.inputIds, refS, speed);
503
  sampleRate = result.sampleRate;
504
  const PAD_SAMPLES = Math.floor(sampleRate * INTER_CHUNK_PAD_SEC);
505
  const { phonemes, words } = buildTimings(tok, result.predDur, sampleRate);
 
568
  currentAudioURL = URL.createObjectURL(wavBlob);
569
  audioEl.src = currentAudioURL;
570
  audioEl.preservesPitch = true;
571
+ // Speed is now applied at the model level (passed to ONNX as a float32[1]
572
+ // input that scales the predictor's per-phoneme duration before rounding).
573
+ // The audio buffer is already at the requested pacing — playbackRate stays
574
+ // at 1.0 so per-word timings line up with what the user hears.
575
+ audioEl.playbackRate = 1.0;
576
 
577
  const phonemes = allPhonemes;
578
  const words = allWords;
src/model.ts CHANGED
@@ -78,12 +78,13 @@ export class KokoroSession {
78
  return new KokoroSession(model, hasWebGpu);
79
  }
80
 
81
- async synthesize(inputIds: number[], refS: Float32Array): Promise<SynthesizeResult> {
82
  const ids = BigInt64Array.from(inputIds.map(v => BigInt(v)));
83
- const idTensor = new Tensor("int64", ids, [1, ids.length]);
84
- const refTensor = new Tensor("float32", refS, [1, 256]);
 
85
 
86
- const out = await this.model({ input_ids: idTensor, ref_s: refTensor });
87
 
88
  // transformers.js wraps ort outputs as Tensor objects; unwrap .data
89
  const audio = out.audio.data as Float32Array;
 
78
  return new KokoroSession(model, hasWebGpu);
79
  }
80
 
81
+ async synthesize(inputIds: number[], refS: Float32Array, speed: number = 1.0): Promise<SynthesizeResult> {
82
  const ids = BigInt64Array.from(inputIds.map(v => BigInt(v)));
83
+ const idTensor = new Tensor("int64", ids, [1, ids.length]);
84
+ const refTensor = new Tensor("float32", refS, [1, 256]);
85
+ const speedTensor = new Tensor("float32", new Float32Array([speed]), [1]);
86
 
87
+ const out = await this.model({ input_ids: idTensor, ref_s: refTensor, speed: speedTensor });
88
 
89
  // transformers.js wraps ort outputs as Tensor objects; unwrap .data
90
  const audio = out.audio.data as Float32Array;