@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 +37 -1
- package/dist/index.js +108 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
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,
|