@onjmin/koe 1.0.7 → 1.0.8

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
@@ -830,4 +830,40 @@ declare function formatOto(entries: readonly OtoEntry[]): string;
830
830
  /** Render entries as Shift-JIS oto.ini bytes, ready to write to disk. */
831
831
  declare function encodeOto(entries: readonly OtoEntry[]): Uint8Array;
832
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 };
833
+ interface UtauTTSUnit {
834
+ position: number;
835
+ mora: string;
836
+ alias: string;
837
+ oto_path: string;
838
+ note_start_ms: number;
839
+ duration_ms: number;
840
+ offset_ms: number;
841
+ consonant_ms: number;
842
+ cutoff_ms: number;
843
+ preutterance_ms: number;
844
+ overlap_ms: number;
845
+ }
846
+ interface UtauTTSPlan {
847
+ duration_ms: number;
848
+ units: UtauTTSUnit[];
849
+ pitch_cents?: number[];
850
+ pitch_frame_ms?: number;
851
+ }
852
+ declare class UtauTTSAdapter {
853
+ private worldline;
854
+ constructor(worldline: Worldline);
855
+ /**
856
+ * Wasmモジュールを初期化します。
857
+ */
858
+ static initializeWasm(wasmUrl?: string): Promise<void>;
859
+ /**
860
+ * koe の VoiceBank から、UtauTTS Wasm に渡すための仮想 oto.ini 辞書を構築します。
861
+ */
862
+ private buildOtoEntriesFromKoe;
863
+ /**
864
+ * 指定したテキストを合成するための Plan (発話計画) と音声を生成します。
865
+ */
866
+ synthesizeText(bank: VoiceBank, text: string, tone?: string): Promise<Float32Array | null>;
867
+ }
868
+
869
+ 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, UtauTTSAdapter, type UtauTTSPlan, type UtauTTSUnit, 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 };
package/dist/index.js CHANGED
@@ -3146,9 +3146,117 @@ function formatOto(entries) {
3146
3146
  function encodeOto(entries) {
3147
3147
  return encodeShiftJis(formatOto(entries));
3148
3148
  }
3149
+
3150
+ // src/utautts/UtauTTSAdapter.ts
3151
+ var UtauTTSAdapter = class {
3152
+ worldline;
3153
+ constructor(worldline) {
3154
+ this.worldline = worldline;
3155
+ }
3156
+ /**
3157
+ * Wasmモジュールを初期化します。
3158
+ */
3159
+ static async initializeWasm(wasmUrl = "utautts.wasm") {
3160
+ if (typeof Go === "undefined") {
3161
+ throw new Error("wasm_exec.js must be loaded before calling initializeWasm.");
3162
+ }
3163
+ const go = new Go();
3164
+ const response = await fetch(wasmUrl);
3165
+ const buffer = await response.arrayBuffer();
3166
+ const result = await WebAssembly.instantiate(buffer, go.importObject);
3167
+ go.run(result.instance);
3168
+ await new Promise((resolve) => setTimeout(resolve, 50));
3169
+ if (typeof utautts_plan !== "function") {
3170
+ throw new Error("utautts_plan failed to initialize in global scope.");
3171
+ }
3172
+ }
3173
+ /**
3174
+ * koe の VoiceBank から、UtauTTS Wasm に渡すための仮想 oto.ini 辞書を構築します。
3175
+ */
3176
+ buildOtoEntriesFromKoe(bank) {
3177
+ const entries = {};
3178
+ const manifest = bank.manifest;
3179
+ for (const [alias, phoneme] of Object.entries(manifest.phonemes)) {
3180
+ entries[alias] = [{
3181
+ Filename: `v_${alias}.wav`,
3182
+ // Wasm内での識別用ダミーファイル名
3183
+ Alias: alias,
3184
+ Offset: 0,
3185
+ // koe は事前にトリミング済みのため常に0
3186
+ Fixed: phoneme.consonant / 48,
3187
+ Preutterance: phoneme.pre / 48,
3188
+ Overlap: phoneme.overlap / 48,
3189
+ SourceGroup: "koe-bank"
3190
+ }];
3191
+ }
3192
+ return entries;
3193
+ }
3194
+ /**
3195
+ * 指定したテキストを合成するための Plan (発話計画) と音声を生成します。
3196
+ */
3197
+ async synthesizeText(bank, text, tone = "C4") {
3198
+ const otoEntries = this.buildOtoEntriesFromKoe(bank);
3199
+ const request = {
3200
+ text,
3201
+ oto_entries: otoEntries,
3202
+ tone,
3203
+ duration_ms: 0
3204
+ // UtauTTS側に自動計算させる
3205
+ };
3206
+ const response = utautts_plan(JSON.stringify(request));
3207
+ if (!response.success) {
3208
+ throw new Error(`UtauTTS Error: ${response.error}`);
3209
+ }
3210
+ const plan = JSON.parse(response.plan);
3211
+ console.log("Generated TTS Plan:", plan);
3212
+ const phraseUnits = [];
3213
+ let currentPosMs = 0;
3214
+ for (const unit of plan.units) {
3215
+ const pcm = await bank.getPcm(unit.alias);
3216
+ if (!pcm) {
3217
+ console.warn(`PCM not found for alias: ${unit.alias}`);
3218
+ continue;
3219
+ }
3220
+ phraseUnits.push({
3221
+ pcm,
3222
+ posMs: currentPosMs,
3223
+ skipMs: unit.offset_ms,
3224
+ lengthMs: unit.duration_ms,
3225
+ fadeInMs: unit.overlap_ms,
3226
+ fadeOutMs: unit.overlap_ms,
3227
+ // 次ノートとのクロスフェードとして設定
3228
+ consonantMs: unit.consonant_ms
3229
+ });
3230
+ currentPosMs += unit.duration_ms;
3231
+ }
3232
+ const baseHz = 261.63;
3233
+ let pitchInput = baseHz;
3234
+ if (plan.pitch_cents && plan.pitch_frame_ms) {
3235
+ const frameMs = plan.pitch_frame_ms;
3236
+ const centsArr = plan.pitch_cents;
3237
+ pitchInput = (tMs, totalMs) => {
3238
+ const frameIndex = Math.floor(tMs / frameMs);
3239
+ if (frameIndex < 0 || frameIndex >= centsArr.length) return baseHz;
3240
+ const centsOffset = centsArr[frameIndex];
3241
+ return baseHz * Math.pow(2, centsOffset / 1200);
3242
+ };
3243
+ }
3244
+ const outPcm = this.worldline.renderPhrase({
3245
+ units: phraseUnits,
3246
+ pitch: pitchInput
3247
+ });
3248
+ if (outPcm) {
3249
+ console.log(`Successfully generated audio: ${outPcm.length} samples.`);
3250
+ } else {
3251
+ console.error("Audio generation failed.");
3252
+ }
3253
+ return outPcm;
3254
+ }
3255
+ };
3149
3256
  export {
3150
3257
  KoeEngine,
3151
3258
  MIN_WORLDLINE_SAMPLES,
3259
+ UtauTTSAdapter,
3152
3260
  VoiceBank,
3153
3261
  WORLDLINE_SAMPLE_RATE,
3154
3262
  Worldline,