@onjmin/koe 1.0.0
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.
- package/LICENSE +21 -0
- package/README.md +2 -0
- package/dist/index.d.ts +362 -0
- package/dist/index.js +632 -0
- package/dist/index.js.map +1 -0
- package/dist/koe-convert.js +344 -0
- package/dist/koe-worklet.js +1 -0
- package/dist/world/worldline.js +2 -0
- package/dist/world/worldline.wasm +0 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 おんJ民
|
|
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.
|
package/README.md
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One phoneme, already trimmed to its usable oto region.
|
|
3
|
+
* Sample 0 corresponds to the oto `offset` (left blank); everything before it
|
|
4
|
+
* in the source WAV has been removed during conversion. All positions below are
|
|
5
|
+
* therefore relative to the trimmed sample start.
|
|
6
|
+
*/
|
|
7
|
+
interface PhonemeEntry {
|
|
8
|
+
/** Byte offset of this phoneme's PCM within voice.bin */
|
|
9
|
+
offset: number;
|
|
10
|
+
/** Trimmed sample count = oto region [offset, cutoff] (48kHz / 16bit / mono) */
|
|
11
|
+
length: number;
|
|
12
|
+
/** Preutterance in samples (note onset alignment point) */
|
|
13
|
+
pre: number;
|
|
14
|
+
/** Overlap / crossfade length in samples */
|
|
15
|
+
overlap: number;
|
|
16
|
+
/** Consonant (fixed, non-looped) region length in samples */
|
|
17
|
+
consonant: number;
|
|
18
|
+
/**
|
|
19
|
+
* Detected recorded fundamental frequency in Hz (0 if undetectable).
|
|
20
|
+
* Playback resamples by `targetPitch / pitch`, so any source format
|
|
21
|
+
* (multi-pitch, single-pitch with pitch-suffixed aliases, …) tunes correctly.
|
|
22
|
+
*/
|
|
23
|
+
pitch: number;
|
|
24
|
+
}
|
|
25
|
+
interface Manifest {
|
|
26
|
+
sampleRate: 48000;
|
|
27
|
+
/** Reference pitch used when recording the voice bank (Hz) */
|
|
28
|
+
referencePitch: number;
|
|
29
|
+
phonemes: Record<string, PhonemeEntry>;
|
|
30
|
+
}
|
|
31
|
+
interface NoteEvent {
|
|
32
|
+
phoneme: string;
|
|
33
|
+
/** Desired output pitch in Hz */
|
|
34
|
+
pitch: number;
|
|
35
|
+
/** Output duration in samples at 48kHz */
|
|
36
|
+
duration: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Read-only access to a .koe voice bank: its manifest plus per-phoneme PCM,
|
|
41
|
+
* fetched on demand (Blob slice or HTTP Range). The full bank is never held in
|
|
42
|
+
* memory.
|
|
43
|
+
*
|
|
44
|
+
* Pure data — no AudioContext, no AudioWorklet, no DOM. Use this when you only
|
|
45
|
+
* need the source samples (e.g. to feed the {@link Worldline} renderer or any
|
|
46
|
+
* other vocoder). {@link KoeEngine} builds its concatenative playback on top of
|
|
47
|
+
* this same class.
|
|
48
|
+
*
|
|
49
|
+
* const bank = await VoiceBank.load(koeBlobOrUrl);
|
|
50
|
+
* const pcm = await bank.getPcm('a'); // Float64 [-1, 1]
|
|
51
|
+
*/
|
|
52
|
+
declare class VoiceBank {
|
|
53
|
+
/** The voice bank manifest (sample rate, reference pitch, phoneme table). */
|
|
54
|
+
readonly manifest: Manifest;
|
|
55
|
+
private source;
|
|
56
|
+
private constructor();
|
|
57
|
+
/**
|
|
58
|
+
* Parse a .koe archive header + manifest and bind a lazy PCM source.
|
|
59
|
+
* @param koe a Blob/File of the .koe archive, or a URL (served with Range support)
|
|
60
|
+
*/
|
|
61
|
+
static load(koe: Blob | string): Promise<VoiceBank>;
|
|
62
|
+
/** True if the bank contains a phoneme under this alias. */
|
|
63
|
+
has(phoneme: string): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Raw Int16 PCM bytes (48 kHz / mono) for a phoneme, or null if unknown.
|
|
66
|
+
* The returned ArrayBuffer is freshly allocated and safe to transfer to a
|
|
67
|
+
* worker / AudioWorklet.
|
|
68
|
+
*/
|
|
69
|
+
readPcmBytes(phoneme: string): Promise<ArrayBuffer | null>;
|
|
70
|
+
/**
|
|
71
|
+
* A phoneme's PCM as a Float64Array normalised to [-1, 1], or null if unknown.
|
|
72
|
+
* Intended for external analysis / resynthesis such as the WORLD vocoder.
|
|
73
|
+
*/
|
|
74
|
+
getPcm(phoneme: string): Promise<Float64Array | null>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface KoeEngineOptions {
|
|
78
|
+
/** URL to koe-worklet.js. Defaults to './koe-worklet.js'. */
|
|
79
|
+
workletUrl?: string;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Main-thread API for the koe concatenative synthesis engine.
|
|
83
|
+
*
|
|
84
|
+
* Loads a single .koe archive. The full voice bank is never held in memory:
|
|
85
|
+
* phonemes are fetched on demand (via {@link VoiceBank}) and cached in the
|
|
86
|
+
* audio thread.
|
|
87
|
+
*
|
|
88
|
+
* const engine = new KoeEngine();
|
|
89
|
+
* await engine.load(koeBlobOrUrl);
|
|
90
|
+
* await engine.play([{ phoneme: 'a', pitch: 440, duration: 48000 }]);
|
|
91
|
+
*
|
|
92
|
+
* For analysis / resynthesis (e.g. {@link Worldline}) you usually only need the
|
|
93
|
+
* raw samples — use {@link VoiceBank} directly instead, which needs no
|
|
94
|
+
* AudioContext or worklet.
|
|
95
|
+
*/
|
|
96
|
+
declare class KoeEngine {
|
|
97
|
+
private ctx;
|
|
98
|
+
private workletUrl;
|
|
99
|
+
private node;
|
|
100
|
+
private bank;
|
|
101
|
+
private delivered;
|
|
102
|
+
private pending;
|
|
103
|
+
constructor(options?: KoeEngineOptions);
|
|
104
|
+
get audioContext(): AudioContext;
|
|
105
|
+
get manifest(): Manifest | null;
|
|
106
|
+
/** The underlying voice bank (manifest + on-demand PCM), or null before load(). */
|
|
107
|
+
get voiceBank(): VoiceBank | null;
|
|
108
|
+
/**
|
|
109
|
+
* Register the worklet and bind a .koe voice bank.
|
|
110
|
+
* @param koe a Blob/File of the .koe archive, or a URL (served with Range support)
|
|
111
|
+
*/
|
|
112
|
+
load(koe: Blob | string): Promise<void>;
|
|
113
|
+
/** Fetch one phoneme's PCM and deliver it to the worklet (deduped, cached). */
|
|
114
|
+
private ensurePhoneme;
|
|
115
|
+
/** Stop current playback, preload the phonemes for `notes`, then queue them. */
|
|
116
|
+
play(notes: NoteEvent[]): Promise<void>;
|
|
117
|
+
/** Stop playback and clear the queue. */
|
|
118
|
+
stop(): void;
|
|
119
|
+
/** Resume the AudioContext if suspended (e.g. after autoplay block). */
|
|
120
|
+
resume(): Promise<void>;
|
|
121
|
+
/**
|
|
122
|
+
* Read a phoneme's raw PCM and return it as a Float64Array normalised to
|
|
123
|
+
* [-1, 1]. Convenience that forwards to the underlying {@link VoiceBank}.
|
|
124
|
+
* Intended for external analysis such as the WORLD vocoder.
|
|
125
|
+
*/
|
|
126
|
+
getPcm(phoneme: string): Promise<Float64Array | null>;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Output sample rate of the worldline synthesizer. */
|
|
130
|
+
declare const WORLDLINE_SAMPLE_RATE = 48000;
|
|
131
|
+
/**
|
|
132
|
+
* WORLD needs roughly 85 ms of audio for stable F0 analysis. Phonemes with
|
|
133
|
+
* fewer samples than this are rejected (renderNote returns null).
|
|
134
|
+
*/
|
|
135
|
+
declare const MIN_WORLDLINE_SAMPLES = 4096;
|
|
136
|
+
/**
|
|
137
|
+
* The subset of the Emscripten module surface that we call. worldline.js is an
|
|
138
|
+
* MODULARIZE=1 / EXPORT_NAME=WorldlineModule build of OpenUtau's worldline.
|
|
139
|
+
*/
|
|
140
|
+
interface WorldlineWasm {
|
|
141
|
+
_PhraseSynthNew(): number;
|
|
142
|
+
_PhraseSynthDelete(ps: number): void;
|
|
143
|
+
_PhraseSynthAddRequest(ps: number, req: number, posMs: number, skipMs: number, lengthMs: number, fadeInMs: number, fadeOutMs: number, flag: number): void;
|
|
144
|
+
_PhraseSynthSetCurves(ps: number, f0: number, gender: number, tension: number, breathiness: number, voicing: number, length: number, frameMs: number): void;
|
|
145
|
+
_PhraseSynthSynth(ps: number, yPtrPtr: number, flag: number): number;
|
|
146
|
+
_malloc(size: number): number;
|
|
147
|
+
_free(ptr: number): void;
|
|
148
|
+
setValue(ptr: number, value: number, type: string): void;
|
|
149
|
+
getValue(ptr: number, type: string): number;
|
|
150
|
+
HEAPF32: Float32Array;
|
|
151
|
+
HEAPF64: Float64Array;
|
|
152
|
+
}
|
|
153
|
+
type WorldlineFactory = (opts?: {
|
|
154
|
+
locateFile?: (path: string) => string;
|
|
155
|
+
}) => Promise<WorldlineWasm>;
|
|
156
|
+
declare global {
|
|
157
|
+
var WorldlineModule: WorldlineFactory | undefined;
|
|
158
|
+
}
|
|
159
|
+
interface WorldlineLoadOptions {
|
|
160
|
+
/**
|
|
161
|
+
* URL of `worldline.js` (the Emscripten loader). The matching
|
|
162
|
+
* `worldline.wasm` must sit next to it — it is resolved relative to this URL.
|
|
163
|
+
*
|
|
164
|
+
* When consuming koe from npm + a CDN this is typically e.g.
|
|
165
|
+
* `https://cdn.jsdelivr.net/npm/@onjmin/koe/dist/world/worldline.js`, or your
|
|
166
|
+
* own hosted copy of `dist/world/`.
|
|
167
|
+
*/
|
|
168
|
+
scriptUrl: string;
|
|
169
|
+
}
|
|
170
|
+
interface RenderNoteParams {
|
|
171
|
+
/**
|
|
172
|
+
* Source phoneme PCM normalised to [-1, 1] (e.g. from
|
|
173
|
+
* `VoiceBank.getPcm()` / `KoeEngine.getPcm()`).
|
|
174
|
+
*/
|
|
175
|
+
pcm: Float64Array;
|
|
176
|
+
/** Target output pitch in Hz. */
|
|
177
|
+
pitch: number;
|
|
178
|
+
/** Sustain / vowel duration in ms (the lead-in below is rendered on top). */
|
|
179
|
+
durationMs: number;
|
|
180
|
+
/** Preutterance / lead-in in ms — convert from {@link PhonemeEntry.pre}. */
|
|
181
|
+
preMs: number;
|
|
182
|
+
/** Consonant length in ms — convert from {@link PhonemeEntry.consonant}. */
|
|
183
|
+
consonantMs: number;
|
|
184
|
+
/** Reference tempo in BPM for worldline's internal timing. Default 120. */
|
|
185
|
+
tempo?: number;
|
|
186
|
+
}
|
|
187
|
+
/** Convert a sample count at 48 kHz to milliseconds. */
|
|
188
|
+
declare const samplesToMs: (samples: number) => number;
|
|
189
|
+
/**
|
|
190
|
+
* Derive {@link RenderNoteParams} lead-in / consonant fields from a manifest
|
|
191
|
+
* entry, so callers don't repeat the sample→ms conversion.
|
|
192
|
+
*
|
|
193
|
+
* const params = { pcm, pitch, durationMs, ...leadInFromEntry(entry) };
|
|
194
|
+
*/
|
|
195
|
+
declare function leadInFromEntry(entry: PhonemeEntry): {
|
|
196
|
+
preMs: number;
|
|
197
|
+
consonantMs: number;
|
|
198
|
+
};
|
|
199
|
+
/**
|
|
200
|
+
* High-quality WORLD-vocoder note renderer (OpenUtau's worldline via WASM).
|
|
201
|
+
*
|
|
202
|
+
* Pure synthesis: PCM in → PCM out. No AudioContext, no scheduling — the caller
|
|
203
|
+
* owns playback (schedule the returned buffer on its own timeline). Pair it with
|
|
204
|
+
* {@link VoiceBank} for the source samples:
|
|
205
|
+
*
|
|
206
|
+
* const bank = await VoiceBank.load(koeUrl);
|
|
207
|
+
* const wl = await Worldline.load({ scriptUrl: '.../world/worldline.js' });
|
|
208
|
+
*
|
|
209
|
+
* const entry = bank.manifest.phonemes[alias];
|
|
210
|
+
* const pcm = await bank.getPcm(alias);
|
|
211
|
+
* const audio = wl.renderNote({
|
|
212
|
+
* pcm, pitch: 440, durationMs: 500, ...leadInFromEntry(entry),
|
|
213
|
+
* });
|
|
214
|
+
* // audio: Float32 @ 48 kHz, layout [lead-in/consonant ≈ preMs][vowel ≈ durationMs]
|
|
215
|
+
*/
|
|
216
|
+
declare class Worldline {
|
|
217
|
+
private wasm;
|
|
218
|
+
readonly sampleRate = 48000;
|
|
219
|
+
private constructor();
|
|
220
|
+
/** Load + instantiate the worldline WASM module (deduped per scriptUrl). */
|
|
221
|
+
static load(options: WorldlineLoadOptions): Promise<Worldline>;
|
|
222
|
+
/**
|
|
223
|
+
* Render one note to Float32 PCM at 48 kHz.
|
|
224
|
+
*
|
|
225
|
+
* The output buffer is laid out as [lead-in/consonant ≈ preMs][vowel ≈
|
|
226
|
+
* durationMs], rendered from sample offset 0 (no leading silence). The vowel
|
|
227
|
+
* onset (the "beat") sits at ≈ preMs into the buffer, so a sequencer should
|
|
228
|
+
* place the buffer at `beatTime − preMs` and may trim/crossfade the lead-in.
|
|
229
|
+
*
|
|
230
|
+
* No internal crossfade is applied — apply fades externally.
|
|
231
|
+
*
|
|
232
|
+
* @returns Float32 PCM, or null when `pcm` is shorter than
|
|
233
|
+
* {@link MIN_WORLDLINE_SAMPLES} (too short for stable F0 analysis).
|
|
234
|
+
*/
|
|
235
|
+
renderNote(params: RenderNoteParams): Float32Array | null;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
interface OtoEntry {
|
|
239
|
+
/** Source WAV filename */
|
|
240
|
+
wav: string;
|
|
241
|
+
/** Phoneme alias */
|
|
242
|
+
alias: string;
|
|
243
|
+
/** Left blank — offset from WAV start (ms) */
|
|
244
|
+
offset: number;
|
|
245
|
+
/** Consonant portion end from offset (ms) */
|
|
246
|
+
consonant: number;
|
|
247
|
+
/** Right blank — negative = from WAV end, positive = from offset (ms) */
|
|
248
|
+
cutoff: number;
|
|
249
|
+
/** Preutterance from offset (ms) */
|
|
250
|
+
pre: number;
|
|
251
|
+
/** Overlap / crossfade region (ms) */
|
|
252
|
+
overlap: number;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Parse oto.ini content (already decoded to UTF-8 string).
|
|
256
|
+
* Silently skips malformed lines.
|
|
257
|
+
*/
|
|
258
|
+
declare function parseOto(content: string): OtoEntry[];
|
|
259
|
+
|
|
260
|
+
interface WavData {
|
|
261
|
+
sampleRate: number;
|
|
262
|
+
channels: number;
|
|
263
|
+
/** Normalized samples in [-1, 1], interleaved if multi-channel */
|
|
264
|
+
samples: Float32Array;
|
|
265
|
+
}
|
|
266
|
+
/** Parse a WAV file from an ArrayBuffer. Supports PCM 8/16/24-bit and IEEE float 32-bit. */
|
|
267
|
+
declare function parseWav(buf: ArrayBuffer): WavData;
|
|
268
|
+
/** Mix down to mono by averaging all channels. */
|
|
269
|
+
declare function toMono(wav: WavData): WavData;
|
|
270
|
+
/** Linear interpolation resample to targetRate. Expects mono input. */
|
|
271
|
+
declare function resample(wav: WavData, targetRate: number): WavData;
|
|
272
|
+
/** Convert Float32 [-1,1] samples to Int16 PCM. */
|
|
273
|
+
declare function toInt16(samples: Float32Array): Int16Array;
|
|
274
|
+
/** Normalize then convert a WAV to 48kHz/16bit/mono Int16 PCM. */
|
|
275
|
+
declare function normalizePcm(buf: ArrayBuffer): Int16Array;
|
|
276
|
+
|
|
277
|
+
interface PackInput {
|
|
278
|
+
oto: OtoEntry;
|
|
279
|
+
/** Full normalized PCM of the source WAV (48kHz / 16bit / mono) */
|
|
280
|
+
pcm: Int16Array;
|
|
281
|
+
/** Known recorded pitch in Hz (e.g. from the .frq file). 0/undefined → auto-detect. */
|
|
282
|
+
recordedPitch?: number;
|
|
283
|
+
}
|
|
284
|
+
interface PackOutput {
|
|
285
|
+
manifest: Manifest;
|
|
286
|
+
/** Raw PCM blob — Int16 / 48kHz / mono */
|
|
287
|
+
bin: ArrayBuffer;
|
|
288
|
+
}
|
|
289
|
+
interface TrimmedPhoneme {
|
|
290
|
+
/** PCM trimmed to the oto region [offset, cutoff] */
|
|
291
|
+
pcm: Int16Array;
|
|
292
|
+
/** Manifest params relative to the trimmed start (sample 0 = oto offset) */
|
|
293
|
+
entry: Omit<PhonemeEntry, 'offset'>;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Cut the full WAV PCM down to its usable oto region and recompute parameters
|
|
297
|
+
* relative to the trimmed start.
|
|
298
|
+
*
|
|
299
|
+
* UTAU oto.ini values are all in ms and measured from `offset` (the left blank),
|
|
300
|
+
* except `cutoff` (right blank):
|
|
301
|
+
* - cutoff >= 0 : measured from the END of the file
|
|
302
|
+
* - cutoff < 0 : region length from offset = |cutoff|
|
|
303
|
+
*
|
|
304
|
+
* After trimming, sample 0 == oto offset, so pre/overlap/consonant carry over
|
|
305
|
+
* unchanged (just converted to samples), and the slice length is the region end.
|
|
306
|
+
*/
|
|
307
|
+
declare function trimToOto(pcm: Int16Array, oto: OtoEntry, recordedPitch?: number): TrimmedPhoneme;
|
|
308
|
+
/**
|
|
309
|
+
* Pack normalized PCM phonemes into voice.bin + manifest.json.
|
|
310
|
+
* Each phoneme is trimmed to its oto region first.
|
|
311
|
+
* Duplicate aliases are silently overwritten by the later entry.
|
|
312
|
+
*/
|
|
313
|
+
declare function pack(inputs: PackInput[], referencePitch?: number): PackOutput;
|
|
314
|
+
|
|
315
|
+
/** Parse a note name like "E4", "G#4", "Db5" → frequency in Hz (null if invalid). */
|
|
316
|
+
declare function noteNameToHz(name: string): number | null;
|
|
317
|
+
/** Recorded pitch encoded in a multi-pitch alias suffix: "a い_E4" → 329.63 Hz. */
|
|
318
|
+
declare function pitchFromAliasSuffix(alias: string): number | null;
|
|
319
|
+
/**
|
|
320
|
+
* Estimate the fundamental frequency (Hz) of a voiced region by normalized
|
|
321
|
+
* autocorrelation. Returns 0 when no clear pitch is found (unvoiced consonant,
|
|
322
|
+
* silence, or a region too short to analyse).
|
|
323
|
+
*
|
|
324
|
+
* The signal is decimated to a lower analysis rate for speed; f0 below ~700 Hz
|
|
325
|
+
* is well within the resulting Nyquist limit. A parabolic interpolation around
|
|
326
|
+
* the best lag gives sub-sample (sub-semitone) accuracy.
|
|
327
|
+
*/
|
|
328
|
+
declare function detectF0(pcm: Int16Array, start: number, end: number): number;
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* UTAU `.frq` frequency-analysis files (FREQ0003). The same format OpenUtau
|
|
332
|
+
* reads: an 8-byte header, hop size, then the average fundamental frequency of
|
|
333
|
+
* the recording — exactly the reference pitch we need for correct resampling.
|
|
334
|
+
*
|
|
335
|
+
* Layout:
|
|
336
|
+
* char[8] "FREQ0003"
|
|
337
|
+
* int32 hopSize
|
|
338
|
+
* float64 averageF0 ← the recorded pitch in Hz
|
|
339
|
+
* byte[16] (blank)
|
|
340
|
+
* int32 length
|
|
341
|
+
* { float64 f0, float64 amp } × length
|
|
342
|
+
*/
|
|
343
|
+
declare function parseFrqAverageF0(buffer: ArrayBuffer): number | null;
|
|
344
|
+
/** Map a WAV filename to its sibling frq filename: "あ.wav" → "あ_wav.frq". */
|
|
345
|
+
declare function frqFileName(wavName: string): string;
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Koe Archive Format (.koe)
|
|
349
|
+
* [4B] magic 'KOE\0' (big-endian)
|
|
350
|
+
* [4B] JSON length (little-endian)
|
|
351
|
+
* [N ] manifest JSON (UTF-8)
|
|
352
|
+
* [M ] raw PCM (Int16 / 48kHz / mono); phoneme offsets are relative to here
|
|
353
|
+
*/
|
|
354
|
+
declare function packKoe(manifest: Manifest, pcmParts: BlobPart[]): Blob;
|
|
355
|
+
/** Read the 8-byte header → JSON length. Throws on bad magic. */
|
|
356
|
+
declare function parseKoeHeader(headerBytes: ArrayBuffer): {
|
|
357
|
+
jsonLength: number;
|
|
358
|
+
};
|
|
359
|
+
/** Byte offset where PCM data begins, given the JSON length. */
|
|
360
|
+
declare const pcmBase: (jsonLength: number) => number;
|
|
361
|
+
|
|
362
|
+
export { KoeEngine, type KoeEngineOptions, MIN_WORLDLINE_SAMPLES, type Manifest, type NoteEvent, type OtoEntry, type PackInput, type PackOutput, type PhonemeEntry, type RenderNoteParams, type TrimmedPhoneme, VoiceBank, WORLDLINE_SAMPLE_RATE, type WavData, Worldline, type WorldlineLoadOptions, detectF0, frqFileName, leadInFromEntry, normalizePcm, noteNameToHz, pack, packKoe, parseFrqAverageF0, parseKoeHeader, parseOto, parseWav, pcmBase, pitchFromAliasSuffix, resample, samplesToMs, toInt16, toMono, trimToOto };
|