@onjmin/koe 1.0.5 → 1.0.7

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/dist/index.d.ts CHANGED
@@ -1,16 +1,44 @@
1
1
  /**
2
2
  * UTAU `.frq` frequency-analysis files (FREQ0003). The same format OpenUtau
3
- * reads: an 8-byte header, hop size, then the average fundamental frequency of
4
- * the recording exactly the reference pitch we need for correct resampling.
3
+ * reads: an 8-byte header, hop size, the average fundamental frequency of the
4
+ * whole recording, then a per-frame f0 / amplitude curve.
5
5
  *
6
6
  * Layout:
7
7
  * char[8] "FREQ0003"
8
- * int32 hopSize
9
- * float64 averageF0 the recorded pitch in Hz
8
+ * int32 hopSize ← in ORIGINAL WAV samples
9
+ * float64 averageF0 whole-file average in Hz
10
10
  * byte[16] (blank)
11
11
  * int32 length
12
12
  * { float64 f0, float64 amp } × length
13
13
  */
14
+ interface FrqData {
15
+ /** Analysis hop in samples of the ORIGINAL wav (not the 48 kHz conversion) */
16
+ hopSize: number;
17
+ /** Whole-file average f0 in Hz, as stored in the header */
18
+ averageF0: number;
19
+ /** Per-frame f0 in Hz — 0 where the analyser found no clear pitch */
20
+ f0: Float64Array;
21
+ /** Per-frame amplitude, parallel to {@link f0} */
22
+ amp: Float64Array;
23
+ }
24
+ /** Parse a `.frq` file, including the per-frame curve. Null if not FREQ0003. */
25
+ declare function parseFrq(buffer: ArrayBuffer): FrqData | null;
26
+ /**
27
+ * Average f0 over the voiced frames covering a time span of the recording.
28
+ *
29
+ * The header's whole-file average includes leading silence and unvoiced
30
+ * consonants, so it can sit well away from the pitch actually sounding in the
31
+ * region a note is built from. Playback resamples the whole phoneme by one
32
+ * scalar ratio, so an inaccurate value both detunes the note and makes two
33
+ * crossfading notes drift apart in phase across the overlap — a 1% error at
34
+ * 233 Hz drifts ~25° over a 30 ms overlap and ~84° over 100 ms.
35
+ *
36
+ * @param startMs / endMs span within the ORIGINAL recording, in milliseconds
37
+ * @param sourceRate sample rate of the original WAV the frq describes
38
+ * @returns Hz, or 0 when the span holds no voiced frames
39
+ */
40
+ declare function frqAverageF0InRange(frq: FrqData, startMs: number, endMs: number, sourceRate: number): number;
41
+ /** Whole-file average f0 in Hz from a `.frq` file, or null. */
14
42
  declare function parseFrqAverageF0(buffer: ArrayBuffer): number | null;
15
43
  /** Map a WAV filename to its sibling frq filename: "あ.wav" → "あ_wav.frq". */
16
44
  declare function frqFileName(wavName: string): string;
@@ -77,7 +105,7 @@ declare function parseOto(content: string): OtoEntry[];
77
105
 
78
106
  interface PackInput {
79
107
  oto: OtoEntry;
80
- /** Full normalized PCM of the source WAV (48kHz / 16bit / mono) */
108
+ /** Full PCM of the source WAV (48kHz / 16bit / mono) */
81
109
  pcm: Int16Array;
82
110
  /** Known recorded pitch in Hz (e.g. from the .frq file). 0/undefined → auto-detect. */
83
111
  recordedPitch?: number;
@@ -93,6 +121,16 @@ interface TrimmedPhoneme {
93
121
  /** Manifest params relative to the trimmed start (sample 0 = oto offset) */
94
122
  entry: Omit<PhonemeEntry, "offset">;
95
123
  }
124
+ /**
125
+ * Resolve an oto entry's usable region within the full 48 kHz PCM.
126
+ *
127
+ * Shared with the converter CLI, which needs the same bounds to look up the
128
+ * region's local f0 in a `.frq` curve.
129
+ */
130
+ declare function otoRegion(pcmLength: number, oto: OtoEntry): {
131
+ start: number;
132
+ end: number;
133
+ };
96
134
  /**
97
135
  * Cut the full WAV PCM down to its usable oto region and recompute parameters
98
136
  * relative to the trimmed start.
@@ -107,8 +145,8 @@ interface TrimmedPhoneme {
107
145
  */
108
146
  declare function trimToOto(pcm: Int16Array, oto: OtoEntry, recordedPitch?: number): TrimmedPhoneme;
109
147
  /**
110
- * Pack normalized PCM phonemes into voice.bin + manifest.json.
111
- * Each phoneme is trimmed to its oto region first.
148
+ * Pack phonemes into voice.bin + manifest.json.
149
+ * Each phoneme is trimmed to its oto region and DC-centred first.
112
150
  * Duplicate aliases are silently overwritten by the later entry.
113
151
  */
114
152
  declare function pack(inputs: PackInput[], referencePitch?: number): PackOutput;
@@ -142,7 +180,27 @@ declare function toMono(wav: WavData): WavData;
142
180
  declare function resample(wav: WavData, targetRate: number): WavData;
143
181
  /** Convert Float32 [-1,1] samples to Int16 PCM. */
144
182
  declare function toInt16(samples: Float32Array): Int16Array;
145
- /** Normalize then convert a WAV to 48kHz/16bit/mono Int16 PCM. */
183
+ /**
184
+ * Decode a WAV to 48kHz/16bit/mono Int16 PCM, reporting the source sample rate.
185
+ *
186
+ * The rate is needed to index a sibling `.frq` file, whose analysis hop is
187
+ * counted in ORIGINAL samples — see {@link frqAverageF0InRange}.
188
+ *
189
+ * Note this does not touch amplitude: peak levels are carried through
190
+ * unchanged, so a bank's own relative loudness between phonemes is preserved
191
+ * (UTAU's engine instead normalises each region to −6 dBFS, which is why its
192
+ * イ/エ段 and 語尾 samples come out louder than recorded).
193
+ */
194
+ declare function readWavPcm48k(buf: ArrayBuffer): {
195
+ pcm: Int16Array;
196
+ sourceRate: number;
197
+ };
198
+ /**
199
+ * Convert a WAV to 48kHz/16bit/mono Int16 PCM.
200
+ *
201
+ * @deprecated Misnomer — this never normalised amplitude. Use
202
+ * {@link readWavPcm48k}, which also reports the source sample rate.
203
+ */
146
204
  declare function normalizePcm(buf: ArrayBuffer): Int16Array;
147
205
 
148
206
  /** Minimal file-like handle so zip entries can stand in for `File` objects. */
@@ -159,6 +217,14 @@ interface ZipFile {
159
217
  * original bytes and re-decode with the right encoding per entry.
160
218
  */
161
219
  declare function unzipToFileMap(data: ArrayBuffer): Promise<Record<string, ZipFile>>;
220
+ /**
221
+ * Pack a path → bytes map into a zip Blob.
222
+ *
223
+ * Entry names are written as UTF-8 with the language flag set, which is what
224
+ * every current unzip tool reads — including Windows Explorer, so a bank whose
225
+ * folders are named in Japanese comes back out intact.
226
+ */
227
+ declare function zipFiles(files: Record<string, Uint8Array>): Blob;
162
228
 
163
229
  /**
164
230
  * Read-only access to a .koe voice bank: its manifest plus per-phoneme PCM,
@@ -203,6 +269,21 @@ interface KoeEngineOptions {
203
269
  /** URL to koe-worklet.js. Defaults to './koe-worklet.js'. */
204
270
  workletUrl?: string;
205
271
  }
272
+ interface PlayOptions {
273
+ /**
274
+ * Play the first note's lead-in (its consonant / preutterance region) instead
275
+ * of skipping straight to the vowel.
276
+ *
277
+ * Every note but the first gets its lead-in from the crossfade with the note
278
+ * before it. The first note has no predecessor, so the lead-in has to come
279
+ * from somewhere: with `leadIn` the phrase starts one preutterance EARLIER
280
+ * relative to its beats, and {@link KoeEngine.play} returns that offset in
281
+ * samples so a sequencer can schedule around it. Left off (the default), the
282
+ * first note keeps its beat exactly but opens on its vowel, dropping the
283
+ * consonant.
284
+ */
285
+ leadIn?: boolean;
286
+ }
206
287
  /**
207
288
  * Main-thread API for the koe concatenative synthesis engine.
208
289
  *
@@ -237,8 +318,18 @@ declare class KoeEngine {
237
318
  load(koe: Blob | string): Promise<void>;
238
319
  /** Fetch one phoneme's PCM and deliver it to the worklet (deduped, cached). */
239
320
  private ensurePhoneme;
240
- /** Stop current playback, preload the phonemes for `notes`, then queue them. */
241
- play(notes: NoteEvent[]): Promise<void>;
321
+ /**
322
+ * Stop current playback, preload the phonemes for `notes`, then queue them.
323
+ *
324
+ * @returns the lead-in offset in samples — how far the first note's audio
325
+ * starts ahead of its beat. 0 unless {@link PlayOptions.leadIn}.
326
+ */
327
+ play(notes: NoteEvent[], options?: PlayOptions): Promise<number>;
328
+ /**
329
+ * Output samples the first note's lead-in occupies ahead of its beat —
330
+ * mirrors what the worklet does with `leadIn`, so callers can compensate.
331
+ */
332
+ private leadInSamples;
242
333
  /** Stop playback and clear the queue. */
243
334
  stop(): void;
244
335
  /** Resume the AudioContext if suspended (e.g. after autoplay block). */
@@ -310,6 +401,25 @@ interface WorldlineLoadOptions {
310
401
  * function you give it once per frame.
311
402
  */
312
403
  type CurveInput = number | ((tMs: number, totalMs: number) => number);
404
+ interface PhraseUnit {
405
+ pcm: Float64Array;
406
+ posMs: number;
407
+ skipMs: number;
408
+ lengthMs: number;
409
+ fadeInMs: number;
410
+ fadeOutMs: number;
411
+ consonantMs: number;
412
+ cutMs?: number;
413
+ }
414
+ interface RenderPhraseParams {
415
+ units: PhraseUnit[];
416
+ pitch: CurveInput;
417
+ gender?: CurveInput;
418
+ tension?: CurveInput;
419
+ breathiness?: CurveInput;
420
+ voicing?: CurveInput;
421
+ tempo?: number;
422
+ }
313
423
  interface RenderNoteParams {
314
424
  /**
315
425
  * Source phoneme PCM normalised to [-1, 1] (e.g. from
@@ -402,6 +512,7 @@ declare class Worldline {
402
512
  * @returns Float32 PCM, or null when `pcm` is shorter than
403
513
  * {@link MIN_WORLDLINE_SAMPLES} (too short for stable F0 analysis).
404
514
  */
515
+ renderPhrase(params: RenderPhraseParams): Float32Array | null;
405
516
  renderNote(params: RenderNoteParams): Float32Array | null;
406
517
  }
407
518
 
@@ -420,4 +531,303 @@ declare function parseKoeHeader(headerBytes: ArrayBuffer): {
420
531
  /** Byte offset where PCM data begins, given the JSON length. */
421
532
  declare const pcmBase: (jsonLength: number) => number;
422
533
 
423
- 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, type ZipFile, detectF0, frqFileName, leadInFromEntry, normalizePcm, noteNameToHz, pack, packKoe, parseFrqAverageF0, parseKoeHeader, parseOto, parseWav, pcmBase, pitchFromAliasSuffix, resample, samplesToMs, toInt16, toMono, trimToOto, unzipToFileMap };
534
+ /**
535
+ * Frame-level acoustic features used to locate mora boundaries.
536
+ *
537
+ * Everything downstream reasons in *frames* at a fixed 2 ms hop, so a frame
538
+ * index doubles as a millisecond timestamp once multiplied by {@link HOP_MS}.
539
+ * The analysis runs at 16 kHz regardless of the source rate: that is well past
540
+ * the 4–8 kHz band where fricative noise lives, and keeps a 512-point FFT to
541
+ * 32 ms — short enough to see a plosive burst, long enough to resolve F1.
542
+ */
543
+
544
+ interface Frames {
545
+ /** Number of frames. */
546
+ n: number;
547
+ /** Source duration in milliseconds. */
548
+ durationMs: number;
549
+ /** Short-time level in dBFS, −120 for digital silence. */
550
+ rmsDb: Float32Array;
551
+ /** {@link rmsDb} smoothed over ~30 ms, for threshold crossings. */
552
+ smoothDb: Float32Array;
553
+ /** Normalised autocorrelation peak, 0–1. Above ~0.5 reads as voiced. */
554
+ voiced: Float32Array;
555
+ /** Share of spectral energy above 4 kHz — high for /s/, /sh/, /ch/. */
556
+ highRatio: Float32Array;
557
+ /** Level of the >4 kHz band alone, in dB. Frication shows here first. */
558
+ highDb: Float32Array;
559
+ /** Noise floor of {@link highDb}, in dB. */
560
+ highFloorDb: number;
561
+ /** Positive spectral flux, normalised so its own median is 1. */
562
+ flux: Float32Array;
563
+ /** Median f0 of the voiced portion, in Hz (0 when nothing is voiced). */
564
+ f0: number;
565
+ /** Noise floor in dBFS, estimated from the quietest tenth of the file. */
566
+ floorDb: number;
567
+ /**
568
+ * Level of the quietest 50 ms in the file — a true noise floor, unlike
569
+ * {@link floorDb}, which a short recording's own voice can drag upwards.
570
+ * Used to trace an attack back past the point where it is merely audible.
571
+ */
572
+ quietDb: number;
573
+ /** Loudest smoothed frame, in dBFS. */
574
+ peakDb: number;
575
+ }
576
+ /** Extract every feature the mora estimator needs from one mono signal. */
577
+ declare function analyze(wav: WavData): Frames;
578
+ /** Convenience wrapper: decode a WAV buffer and analyse it. */
579
+ declare function analyzeWav(buf: ArrayBuffer): Frames;
580
+
581
+ /**
582
+ * Kana → phoneme tables for oto.ini generation.
583
+ *
584
+ * A UTAU recording's filename *is* its phonetic transcript: `か.wav` holds one
585
+ * mora, `_ああいあうえあ.wav` holds seven. Everything the estimator does — where
586
+ * to look for the vowel, how wide to make the crossfade, whether the overlap
587
+ * goes negative — follows from which consonant a mora starts with, so the kana
588
+ * has to be resolved into (consonant, vowel) before any audio is touched.
589
+ */
590
+ /**
591
+ * Articulation class of a mora's initial consonant. The estimator branches on
592
+ * this to pick where the preutterance lands and what overlap the mora gets;
593
+ * see `ARTICULATION` in `estimate.ts`.
594
+ */
595
+ type ConsonantClass =
596
+ /** あ/い/う/え/お — no consonant at all. */
597
+ "vowel"
598
+ /** ん — a syllabic nasal that is its own nucleus. */
599
+ | "nasalN"
600
+ /** な/ま行 — voiced throughout, vowel starts at the nasal release. */
601
+ | "nasal"
602
+ /** ら行 — a flap: brief closure, then the vowel. */
603
+ | "liquid"
604
+ /** や/わ行 and vowel glides (いぇ, うぉ) — barely a consonant at all. */
605
+ | "semivowel"
606
+ /** さ/は行 — voiceless noise, so voicing onset *is* the vowel onset. */
607
+ | "fricativeVoiceless"
608
+ /** ざ行, ヴ — voiced noise. */
609
+ | "fricativeVoiced"
610
+ /** つ/ち — a stop released into friction; behaves like a plosive. */
611
+ | "affricate"
612
+ /** か/た/ぱ行 — a silent closure precedes the burst. */
613
+ | "plosiveVoiceless"
614
+ /** が/だ/ば行 — may prevoice through the closure. */
615
+ | "plosiveVoiced";
616
+ interface Syllable {
617
+ /** The kana as written, e.g. "きゃ". Used verbatim in the alias. */
618
+ kana: string;
619
+ /** Romanised onset, e.g. "ky". Empty for bare vowels and ん. */
620
+ consonant: string;
621
+ /** Romanised nucleus: a/i/u/e/o, or "n" for ん. */
622
+ vowel: string;
623
+ cls: ConsonantClass;
624
+ }
625
+ /**
626
+ * Katakana → hiragana, so ヴァ and ゔぁ resolve identically. Only the kana
627
+ * block is folded; ー and everything else is left alone for the caller to
628
+ * reject.
629
+ */
630
+ declare function toHiragana(s: string): string;
631
+ /**
632
+ * Split a kana string into moras.
633
+ *
634
+ * Returns null if any character is not kana we can resolve — that is the
635
+ * signal to skip the file entirely, which is what keeps a bank's karaoke
636
+ * tracks and readme audio out of the generated oto.ini.
637
+ */
638
+ declare function splitKana(text: string): Syllable[] | null;
639
+
640
+ /**
641
+ * Parameter estimation for oto.ini entries.
642
+ *
643
+ * The rules encoded here follow the UTAU音源制作wiki's 原音設定 articles
644
+ * (https://w.atwiki.jp/vbmaker/pages/17.html and its 単独音 / 連続音 sequels):
645
+ *
646
+ * - オフセット sits just before the consonant, keeping a little room so the
647
+ * attack is never clipped.
648
+ * - 先行発声 marks where the *vowel* begins — the voicing onset for a voiceless
649
+ * consonant, the release for a nasal or a flap, the midpoint of the glide for
650
+ * や/わ行.
651
+ * - 子音部 runs from the offset through the vowel's onset until the spectrum
652
+ * settles, so a long note stretches only steady-state vowel.
653
+ * - オーバーラップ is ~20 ms for さ/な/ま/ら行, ~30 ms for や/わ行, and goes
654
+ * *negative* for 破裂音 to reproduce the silent closure of か/た/ぱ行.
655
+ * - 右ブランク lands just before the note starts to decay.
656
+ */
657
+
658
+ /** Frame positions the oto parameters are built from. */
659
+ interface MoraPosition {
660
+ /** Frame where the consonant (or vowel, if there is none) begins. */
661
+ consStart: number;
662
+ /** Frame where the vowel begins — the 先行発声 anchor. */
663
+ vowelOnset: number;
664
+ /** Frame past which the vowel is steady. */
665
+ stable: number;
666
+ /** True when a voiced stop prevoiced into its burst. */
667
+ prevoiced: boolean;
668
+ }
669
+ /**
670
+ * Locate one mora inside `[from, to)`, given what consonant it starts with.
671
+ */
672
+ declare function locateMora(f: Frames, cls: ConsonantClass, from: number, to: number): MoraPosition;
673
+ /**
674
+ * 単独音: one mora per file, one entry per alias.
675
+ *
676
+ * `aliases` lets a caller emit the usual family for a file — the bare kana plus
677
+ * a `- か` head variant — all sharing the same measurements.
678
+ */
679
+ declare function estimateSolo(wav: string, f: Frames, syl: Syllable, aliases: string[]): OtoEntry[];
680
+ /**
681
+ * A 母音結合 entry (`* あ`): a mid-phrase vowel taken from the steady part of
682
+ * the note, with the long symmetric crossfade those aliases are used with.
683
+ */
684
+ declare function estimateVowelJoin(wav: string, f: Frames, syl: Syllable, alias: string): OtoEntry | null;
685
+ /** A 連続音 recording's rhythmic grid. */
686
+ interface Grid {
687
+ /** Frame of the first mora's onset. */
688
+ start: number;
689
+ /** Frames between successive moras. */
690
+ interval: number;
691
+ /** Per-mora onset frames, snapped to the strongest nearby transition. */
692
+ onsets: number[];
693
+ }
694
+ /**
695
+ * Fit `count` evenly spaced moras to the recording.
696
+ *
697
+ * 連続音 lists are sung to a guide BGM, so the moras land on a metronomic grid —
698
+ * which is exactly why the wiki treats 連続音 oto as something you *generate*
699
+ * and then touch up. Fitting a global tempo first, and only then snapping each
700
+ * mora to the nearest real transition, keeps one mis-detected onset from
701
+ * dragging the rest of the file out of alignment.
702
+ */
703
+ declare function detectGrid(f: Frames, count: number): Grid | null;
704
+ /**
705
+ * 連続音: every mora in the file gets an entry, aliased against the vowel it
706
+ * follows (`a か`), with the first written as a phrase head (`- あ`).
707
+ *
708
+ * The template — 先行発声 at half the mora interval, オーバーラップ at a third of
709
+ * that, 固定範囲 half again as long, 右ブランク two thirds of an interval past the
710
+ * note — is the one the established 連続音 banks ship, and it survives a mora
711
+ * whose consonant is longer than average because half an interval is far more
712
+ * room than any Japanese onset needs.
713
+ */
714
+ declare function estimateSequence(wav: string, f: Frames, syllables: Syllable[], opts?: {
715
+ suffix?: string;
716
+ prefix?: string;
717
+ trailingRest?: boolean;
718
+ }): OtoEntry[];
719
+
720
+ /**
721
+ * Folder → oto.ini, with no manual step in between.
722
+ *
723
+ * The filename carries the transcript, so the recording style falls out of it:
724
+ * one mora per file is 単独音, several is 連続音. Everything else — which
725
+ * aliases to emit, where the phrase head goes, whether a trailing R belongs on
726
+ * the end — follows from the same parse.
727
+ */
728
+
729
+ interface GenerateOptions {
730
+ /**
731
+ * Appended to every alias — the usual home for a multi-pitch or expression
732
+ * marker (`_G4`, `強`). Defaults to the folder's own note name when it has
733
+ * one; pass `""` to suppress that.
734
+ */
735
+ suffix?: string;
736
+ /** Emit `- か` phrase-head aliases alongside the bare kana. Default true. */
737
+ headAliases?: boolean;
738
+ /** Emit `* あ` 母音結合 aliases for vowel-only files. Default true. */
739
+ vowelJoinAliases?: boolean;
740
+ }
741
+ /** One file that could not be transcribed, and why. */
742
+ interface SkippedFile {
743
+ wav: string;
744
+ reason: string;
745
+ }
746
+ interface GenerateResult {
747
+ entries: OtoEntry[];
748
+ skipped: SkippedFile[];
749
+ /** Recording style inferred from the filenames. */
750
+ style: "solo" | "sequence" | "mixed" | "empty";
751
+ }
752
+ /** What one recording produced. */
753
+ interface FileResult {
754
+ entries: OtoEntry[];
755
+ /** Set instead of entries when the file could not be set up. */
756
+ skipped: SkippedFile | null;
757
+ /** Style this one file was read as, or null if it was skipped. */
758
+ style: "solo" | "sequence" | null;
759
+ }
760
+ interface WavInput {
761
+ /** Filename as it appears in oto.ini, e.g. `_ああいあう.wav`. */
762
+ name: string;
763
+ data: ArrayBuffer;
764
+ }
765
+ interface Transcript {
766
+ syllables: Syllable[];
767
+ trailingRest: boolean;
768
+ /** Non-kana marker before the kana, e.g. the `x` of `_xか.wav`. */
769
+ prefix: string;
770
+ /** Non-kana marker after the kana, e.g. the `b` of `_あb.wav`. */
771
+ mark: string;
772
+ }
773
+ /**
774
+ * Read the transcript out of a filename.
775
+ *
776
+ * The leading `_` that marks a recording-list file, an extension, and a
777
+ * trailing pitch tag are all noise; what is left has to be kana end to end, or
778
+ * the file is not a voice sample we can set up (a karaoke track, a sample song,
779
+ * a readme recording).
780
+ */
781
+ declare function transcribe(filename: string): Transcript | null;
782
+ /**
783
+ * Alias suffix implied by a folder's name.
784
+ *
785
+ * A multi-pitch bank keeps one folder per pitch and merges every oto.ini into
786
+ * one alias namespace, so without the pitch tag each folder's `- あ` would
787
+ * overwrite the last. The tag is taken either from a folder named for nothing
788
+ * but the pitch (`G4`) or from an explicit `_G4` token inside a longer name
789
+ * (`多音階03:_G4(連続音)`). A bare `G4` buried in a name is left alone — in
790
+ * `表情音01:強(G4歌連続音)` it describes the take, and the suffix the bank
791
+ * actually uses there is `強`, which no filename carries.
792
+ */
793
+ declare function suffixFromFolderName(folder: string): string;
794
+ /**
795
+ * Estimate oto.ini entries for every WAV in one folder.
796
+ *
797
+ * Decoding and analysis are per-file and independent, so a bad WAV is reported
798
+ * and skipped rather than failing the folder.
799
+ */
800
+ declare function generateOtoForFile(file: WavInput, options?: GenerateOptions): FileResult;
801
+ /**
802
+ * Estimate oto.ini entries for every WAV in one folder.
803
+ *
804
+ * Decoding and analysis are per-file and independent, so a bad WAV is reported
805
+ * and skipped rather than failing the folder. A caller that needs to stay
806
+ * responsive — a browser UI, say — should drive {@link generateOtoForFile}
807
+ * itself and yield between files.
808
+ */
809
+ declare function generateOto(files: readonly WavInput[], options?: GenerateOptions): GenerateResult;
810
+ /** Fold per-file styles into the one label that describes the folder. */
811
+ declare function summarise(solo: number, sequence: number): GenerateResult["style"];
812
+
813
+ /**
814
+ * oto.ini serialisation.
815
+ *
816
+ * UTAU reads oto.ini as Shift-JIS, and OpenUtau follows a bank's declared
817
+ * encoding, so writing UTF-8 would leave every kana alias mojibake in the
818
+ * original editor. There is no Shift-JIS *encoder* in the platform — only a
819
+ * decoder — so the table is built by decoding every legal byte pair once and
820
+ * inverting the result.
821
+ */
822
+
823
+ /**
824
+ * Encode text as Shift-JIS. Characters with no Shift-JIS form become `?`,
825
+ * matching what UTAU's own tools do rather than corrupting the line.
826
+ */
827
+ declare function encodeShiftJis(text: string): Uint8Array;
828
+ /** Render entries as oto.ini text (CRLF, as UTAU writes it). */
829
+ declare function formatOto(entries: readonly OtoEntry[]): string;
830
+ /** Render entries as Shift-JIS oto.ini bytes, ready to write to disk. */
831
+ declare function encodeOto(entries: readonly OtoEntry[]): Uint8Array;
832
+
833
+ export { type ConsonantClass, type FileResult, type Frames, type FrqData, type GenerateOptions, type GenerateResult, type Grid, KoeEngine, type KoeEngineOptions, MIN_WORLDLINE_SAMPLES, type Manifest, type MoraPosition, type NoteEvent, type OtoEntry, type PackInput, type PackOutput, type PhonemeEntry, type PlayOptions, type RenderNoteParams, type SkippedFile, type Syllable, type TrimmedPhoneme, VoiceBank, WORLDLINE_SAMPLE_RATE, type WavData, type WavInput, Worldline, type WorldlineLoadOptions, type ZipFile, analyze, analyzeWav, detectF0, detectGrid, encodeOto, encodeShiftJis, estimateSequence, estimateSolo, estimateVowelJoin, formatOto, frqAverageF0InRange, frqFileName, generateOto, generateOtoForFile, leadInFromEntry, locateMora, normalizePcm, noteNameToHz, otoRegion, pack, packKoe, parseFrq, parseFrqAverageF0, parseKoeHeader, parseOto, parseWav, pcmBase, pitchFromAliasSuffix, readWavPcm48k, resample, samplesToMs, splitKana, suffixFromFolderName, summarise, toHiragana, toInt16, toMono, transcribe, trimToOto, unzipToFileMap, zipFiles };