@onjmin/dtm 2.1.12 → 2.1.14
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/README.md +26 -0
- package/dist/index.d.mts +363 -110
- package/dist/index.d.ts +363 -110
- package/dist/index.js +2622 -486
- package/dist/index.mjs +2610 -486
- package/dist/voice-worker.js +547 -5
- package/package.json +4 -3
package/dist/index.mjs
CHANGED
|
@@ -2657,7 +2657,7 @@ var buildChordPlacements = (options) => {
|
|
|
2657
2657
|
return placements;
|
|
2658
2658
|
};
|
|
2659
2659
|
|
|
2660
|
-
// node_modules/.pnpm/@onjmin+koe@1.0.
|
|
2660
|
+
// node_modules/.pnpm/@onjmin+koe@1.0.10/node_modules/@onjmin/koe/dist/index.js
|
|
2661
2661
|
var u8 = Uint8Array;
|
|
2662
2662
|
var u16 = Uint16Array;
|
|
2663
2663
|
var i32 = Int32Array;
|
|
@@ -3045,6 +3045,130 @@ var Worldline = class _Worldline {
|
|
|
3045
3045
|
* @returns Float32 PCM, or null when `pcm` is shorter than
|
|
3046
3046
|
* {@link MIN_WORLDLINE_SAMPLES} (too short for stable F0 analysis).
|
|
3047
3047
|
*/
|
|
3048
|
+
renderPhrase(params) {
|
|
3049
|
+
const {
|
|
3050
|
+
units: units2,
|
|
3051
|
+
pitch,
|
|
3052
|
+
gender = 0.5,
|
|
3053
|
+
tension = 0.5,
|
|
3054
|
+
breathiness = 0.5,
|
|
3055
|
+
voicing = 1,
|
|
3056
|
+
tempo = 120
|
|
3057
|
+
} = params;
|
|
3058
|
+
if (units2.length === 0) return null;
|
|
3059
|
+
const WL = this.wasm;
|
|
3060
|
+
const FS2 = WORLDLINE_SAMPLE_RATE;
|
|
3061
|
+
let totalMs = 0;
|
|
3062
|
+
for (const u of units2) {
|
|
3063
|
+
const endMs = u.posMs + u.lengthMs;
|
|
3064
|
+
if (endMs > totalMs) totalMs = endMs;
|
|
3065
|
+
}
|
|
3066
|
+
const ps = WL._PhraseSynthNew();
|
|
3067
|
+
if (!ps) return null;
|
|
3068
|
+
const pointersToFree = [];
|
|
3069
|
+
for (const u of units2) {
|
|
3070
|
+
if (!u.pcm || u.pcm.length < MIN_WORLDLINE_SAMPLES) continue;
|
|
3071
|
+
const reqPtr = WL._malloc(SYNTH_REQ_SIZE);
|
|
3072
|
+
if (!reqPtr) continue;
|
|
3073
|
+
pointersToFree.push(reqPtr);
|
|
3074
|
+
const samplePtr = WL._malloc(u.pcm.length * 8);
|
|
3075
|
+
if (!samplePtr) continue;
|
|
3076
|
+
pointersToFree.push(samplePtr);
|
|
3077
|
+
WL.HEAPF64.set(u.pcm, samplePtr >> 3);
|
|
3078
|
+
const sv = (off, val, type) => WL.setValue(reqPtr + off, val, type);
|
|
3079
|
+
sv(0, FS2, "i32");
|
|
3080
|
+
sv(4, u.pcm.length, "i32");
|
|
3081
|
+
sv(8, samplePtr, "*");
|
|
3082
|
+
sv(12, 0, "i32");
|
|
3083
|
+
sv(16, 0, "*");
|
|
3084
|
+
sv(20, u.tone ?? 69, "i32");
|
|
3085
|
+
sv(24, 100, "double");
|
|
3086
|
+
sv(32, 0, "double");
|
|
3087
|
+
sv(40, u.requiredLengthMs ?? u.lengthMs, "double");
|
|
3088
|
+
sv(48, u.consonantMs, "double");
|
|
3089
|
+
const cutMs = u.cutMs ?? WL_FRAME_MS * 2;
|
|
3090
|
+
sv(56, cutMs, "double");
|
|
3091
|
+
sv(64, u.volume ?? 100, "double");
|
|
3092
|
+
sv(72, 0, "double");
|
|
3093
|
+
sv(80, tempo, "double");
|
|
3094
|
+
sv(88, 0, "i32");
|
|
3095
|
+
sv(92, 0, "*");
|
|
3096
|
+
sv(96, 0, "i32");
|
|
3097
|
+
sv(100, 0, "i32");
|
|
3098
|
+
sv(104, 100, "i32");
|
|
3099
|
+
sv(108, 0, "i32");
|
|
3100
|
+
sv(112, 0, "i32");
|
|
3101
|
+
sv(116, 100, "i32");
|
|
3102
|
+
WL._PhraseSynthAddRequest(
|
|
3103
|
+
ps,
|
|
3104
|
+
reqPtr,
|
|
3105
|
+
u.posMs,
|
|
3106
|
+
u.skipMs,
|
|
3107
|
+
u.lengthMs,
|
|
3108
|
+
u.fadeInMs,
|
|
3109
|
+
u.fadeOutMs,
|
|
3110
|
+
0
|
|
3111
|
+
);
|
|
3112
|
+
}
|
|
3113
|
+
totalMs += WL_FRAME_MS * 2;
|
|
3114
|
+
const nFrames = Math.ceil(totalMs / WL_FRAME_MS) + 4;
|
|
3115
|
+
const f0Arr = new Float64Array(nFrames);
|
|
3116
|
+
const gArr = new Float64Array(nFrames);
|
|
3117
|
+
const tArr = new Float64Array(nFrames);
|
|
3118
|
+
const bArr = new Float64Array(nFrames);
|
|
3119
|
+
const vArr = new Float64Array(nFrames);
|
|
3120
|
+
for (let i2 = 0; i2 < nFrames; i2++) {
|
|
3121
|
+
const tMs = i2 * WL_FRAME_MS;
|
|
3122
|
+
f0Arr[i2] = sampleCurve(tMs, pitch, totalMs);
|
|
3123
|
+
gArr[i2] = sampleCurve(tMs, gender, totalMs);
|
|
3124
|
+
tArr[i2] = sampleCurve(tMs, tension, totalMs);
|
|
3125
|
+
bArr[i2] = sampleCurve(tMs, breathiness, totalMs);
|
|
3126
|
+
vArr[i2] = sampleCurve(tMs, voicing, totalMs);
|
|
3127
|
+
}
|
|
3128
|
+
const f0Ptr = WL._malloc(nFrames * 8);
|
|
3129
|
+
const gPtr = WL._malloc(nFrames * 8);
|
|
3130
|
+
const tPtr = WL._malloc(nFrames * 8);
|
|
3131
|
+
const bPtr = WL._malloc(nFrames * 8);
|
|
3132
|
+
const vPtr = WL._malloc(nFrames * 8);
|
|
3133
|
+
if (f0Ptr && gPtr && tPtr && bPtr && vPtr) {
|
|
3134
|
+
WL.HEAPF64.set(f0Arr, f0Ptr >> 3);
|
|
3135
|
+
WL.HEAPF64.set(gArr, gPtr >> 3);
|
|
3136
|
+
WL.HEAPF64.set(tArr, tPtr >> 3);
|
|
3137
|
+
WL.HEAPF64.set(bArr, bPtr >> 3);
|
|
3138
|
+
WL.HEAPF64.set(vArr, vPtr >> 3);
|
|
3139
|
+
WL._PhraseSynthSetCurves(
|
|
3140
|
+
ps,
|
|
3141
|
+
f0Ptr,
|
|
3142
|
+
gPtr,
|
|
3143
|
+
tPtr,
|
|
3144
|
+
bPtr,
|
|
3145
|
+
vPtr,
|
|
3146
|
+
nFrames,
|
|
3147
|
+
WL_FRAME_MS
|
|
3148
|
+
);
|
|
3149
|
+
}
|
|
3150
|
+
if (f0Ptr) WL._free(f0Ptr);
|
|
3151
|
+
if (gPtr) WL._free(gPtr);
|
|
3152
|
+
if (tPtr) WL._free(tPtr);
|
|
3153
|
+
if (bPtr) WL._free(bPtr);
|
|
3154
|
+
if (vPtr) WL._free(vPtr);
|
|
3155
|
+
const yPtrPtr = WL._malloc(4);
|
|
3156
|
+
let audio = null;
|
|
3157
|
+
if (yPtrPtr) {
|
|
3158
|
+
const outLen = WL._PhraseSynthSynth(ps, yPtrPtr, 0);
|
|
3159
|
+
const yPtr = WL.getValue(yPtrPtr, "*");
|
|
3160
|
+
if (outLen > 0 && yPtr) {
|
|
3161
|
+
audio = new Float32Array(WL.HEAPF32.buffer, yPtr, outLen).slice();
|
|
3162
|
+
WL._free(yPtr);
|
|
3163
|
+
}
|
|
3164
|
+
WL._free(yPtrPtr);
|
|
3165
|
+
}
|
|
3166
|
+
for (const ptr of pointersToFree) {
|
|
3167
|
+
WL._free(ptr);
|
|
3168
|
+
}
|
|
3169
|
+
WL._PhraseSynthDelete(ps);
|
|
3170
|
+
return audio;
|
|
3171
|
+
}
|
|
3048
3172
|
renderNote(params) {
|
|
3049
3173
|
const {
|
|
3050
3174
|
pcm,
|
|
@@ -3060,8 +3184,12 @@ var Worldline = class _Worldline {
|
|
|
3060
3184
|
} = params;
|
|
3061
3185
|
if (!pcm || pcm.length < MIN_WORLDLINE_SAMPLES) return null;
|
|
3062
3186
|
const WL = this.wasm;
|
|
3063
|
-
const
|
|
3064
|
-
const basePitch = sampleCurve(
|
|
3187
|
+
const FS2 = WORLDLINE_SAMPLE_RATE;
|
|
3188
|
+
const basePitch = sampleCurve(
|
|
3189
|
+
preMs + durationMs / 2,
|
|
3190
|
+
pitch,
|
|
3191
|
+
preMs + durationMs
|
|
3192
|
+
);
|
|
3065
3193
|
const midiNote2 = Math.round(69 + 12 * Math.log2(basePitch / 440));
|
|
3066
3194
|
const posMs = 0;
|
|
3067
3195
|
const reqLen = preMs + durationMs;
|
|
@@ -3081,7 +3209,7 @@ var Worldline = class _Worldline {
|
|
|
3081
3209
|
}
|
|
3082
3210
|
WL.HEAPF64.set(pcm, samplePtr >> 3);
|
|
3083
3211
|
const sv = (off, val, type) => WL.setValue(reqPtr + off, val, type);
|
|
3084
|
-
sv(0,
|
|
3212
|
+
sv(0, FS2, "i32");
|
|
3085
3213
|
sv(4, pcm.length, "i32");
|
|
3086
3214
|
sv(8, samplePtr, "*");
|
|
3087
3215
|
sv(12, 0, "i32");
|
|
@@ -3169,6 +3297,836 @@ var Worldline = class _Worldline {
|
|
|
3169
3297
|
return audio;
|
|
3170
3298
|
}
|
|
3171
3299
|
};
|
|
3300
|
+
var RATE = 16e3;
|
|
3301
|
+
var HOP_MS = 2;
|
|
3302
|
+
var HOP = RATE * HOP_MS / 1e3;
|
|
3303
|
+
var FFT_SIZE = 512;
|
|
3304
|
+
var BINS = FFT_SIZE / 2;
|
|
3305
|
+
var PITCH_DECIM = 2;
|
|
3306
|
+
var PITCH_RATE = RATE / PITCH_DECIM;
|
|
3307
|
+
var WINDOW_CENTRE_MS = FFT_SIZE / 2 / RATE * 1e3;
|
|
3308
|
+
var DEFAULT_CACHE = "koe-tts-assets-v1";
|
|
3309
|
+
async function openCache(name) {
|
|
3310
|
+
if (name === null) return null;
|
|
3311
|
+
try {
|
|
3312
|
+
if (typeof caches === "undefined") return null;
|
|
3313
|
+
return await caches.open(name ?? DEFAULT_CACHE);
|
|
3314
|
+
} catch {
|
|
3315
|
+
return null;
|
|
3316
|
+
}
|
|
3317
|
+
}
|
|
3318
|
+
async function cachedCopyIsCurrent(url2, cached, signal) {
|
|
3319
|
+
let head;
|
|
3320
|
+
try {
|
|
3321
|
+
head = await fetch(url2, { method: "HEAD", signal, cache: "no-cache" });
|
|
3322
|
+
} catch {
|
|
3323
|
+
return true;
|
|
3324
|
+
}
|
|
3325
|
+
if (!head.ok) return true;
|
|
3326
|
+
for (const name of ["etag", "last-modified", "content-length"]) {
|
|
3327
|
+
const remote = head.headers.get(name);
|
|
3328
|
+
const local = cached.headers.get(name);
|
|
3329
|
+
if (remote && local) return remote === local;
|
|
3330
|
+
}
|
|
3331
|
+
return true;
|
|
3332
|
+
}
|
|
3333
|
+
async function fetchAsset(url2, options = {}) {
|
|
3334
|
+
const { onProgress, signal, revalidate = true } = options;
|
|
3335
|
+
const cache2 = await openCache(options.cacheName);
|
|
3336
|
+
if (cache2) {
|
|
3337
|
+
try {
|
|
3338
|
+
const hit = await cache2.match(url2);
|
|
3339
|
+
if (hit && (!revalidate || await cachedCopyIsCurrent(url2, hit, signal))) {
|
|
3340
|
+
const total2 = Number(hit.headers.get("content-length")) || 0;
|
|
3341
|
+
onProgress?.({ url: url2, loaded: total2, total: total2, fromCache: true });
|
|
3342
|
+
return hit;
|
|
3343
|
+
}
|
|
3344
|
+
} catch {
|
|
3345
|
+
}
|
|
3346
|
+
}
|
|
3347
|
+
const response = await fetch(url2, { signal });
|
|
3348
|
+
if (!response.ok) {
|
|
3349
|
+
throw new Error(`fetch ${url2}: HTTP ${response.status}`);
|
|
3350
|
+
}
|
|
3351
|
+
const total = Number(response.headers.get("content-length")) || 0;
|
|
3352
|
+
let body;
|
|
3353
|
+
if (response.body && onProgress) {
|
|
3354
|
+
const reader = response.body.getReader();
|
|
3355
|
+
const parts = [];
|
|
3356
|
+
let loaded = 0;
|
|
3357
|
+
onProgress({ url: url2, loaded, total, fromCache: false });
|
|
3358
|
+
for (; ; ) {
|
|
3359
|
+
const { done, value } = await reader.read();
|
|
3360
|
+
if (done) break;
|
|
3361
|
+
parts.push(value);
|
|
3362
|
+
loaded += value.byteLength;
|
|
3363
|
+
onProgress({ url: url2, loaded, total, fromCache: false });
|
|
3364
|
+
}
|
|
3365
|
+
body = new Uint8Array(new ArrayBuffer(loaded));
|
|
3366
|
+
let offset = 0;
|
|
3367
|
+
for (const part of parts) {
|
|
3368
|
+
body.set(part, offset);
|
|
3369
|
+
offset += part.byteLength;
|
|
3370
|
+
}
|
|
3371
|
+
} else {
|
|
3372
|
+
body = new Uint8Array(await response.arrayBuffer());
|
|
3373
|
+
onProgress?.({
|
|
3374
|
+
url: url2,
|
|
3375
|
+
loaded: body.byteLength,
|
|
3376
|
+
total: total || body.byteLength,
|
|
3377
|
+
fromCache: false
|
|
3378
|
+
});
|
|
3379
|
+
}
|
|
3380
|
+
const headers = new Headers();
|
|
3381
|
+
for (const name of ["content-type", "etag", "last-modified"]) {
|
|
3382
|
+
const value = response.headers.get(name);
|
|
3383
|
+
if (value) headers.set(name, value);
|
|
3384
|
+
}
|
|
3385
|
+
headers.set("content-length", String(body.byteLength));
|
|
3386
|
+
const buffered = new Response(body, { status: 200, headers });
|
|
3387
|
+
if (cache2) {
|
|
3388
|
+
try {
|
|
3389
|
+
await cache2.put(url2, buffered.clone());
|
|
3390
|
+
} catch {
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
return buffered;
|
|
3394
|
+
}
|
|
3395
|
+
async function fetchAssetBytes(url2, options) {
|
|
3396
|
+
return new Uint8Array(await (await fetchAsset(url2, options)).arrayBuffer());
|
|
3397
|
+
}
|
|
3398
|
+
async function fetchAssetText(url2, options) {
|
|
3399
|
+
return (await fetchAsset(url2, options)).text();
|
|
3400
|
+
}
|
|
3401
|
+
var NAIST_JDIC_FILES = [
|
|
3402
|
+
"metadata.json",
|
|
3403
|
+
"char_def.bin",
|
|
3404
|
+
"matrix.mtx",
|
|
3405
|
+
"dict.da",
|
|
3406
|
+
"dict.vals",
|
|
3407
|
+
"unk.bin",
|
|
3408
|
+
"dict.wordsidx",
|
|
3409
|
+
"dict.words"
|
|
3410
|
+
];
|
|
3411
|
+
async function inflate(bytes) {
|
|
3412
|
+
if (typeof DecompressionStream === "undefined") {
|
|
3413
|
+
throw new Error(
|
|
3414
|
+
"DecompressionStream is not available; serve the dictionary uncompressed"
|
|
3415
|
+
);
|
|
3416
|
+
}
|
|
3417
|
+
const stream = new Blob([bytes]).stream().pipeThrough(new DecompressionStream("gzip"));
|
|
3418
|
+
return new Uint8Array(await new Response(stream).arrayBuffer());
|
|
3419
|
+
}
|
|
3420
|
+
async function loadNaistJdic(baseUrl, options = {}) {
|
|
3421
|
+
const { compressed = true, ...fetchOptions } = options;
|
|
3422
|
+
const base = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
3423
|
+
const entries = await Promise.all(
|
|
3424
|
+
NAIST_JDIC_FILES.map(async (name) => {
|
|
3425
|
+
const raw = await fetchAssetBytes(
|
|
3426
|
+
`${base}${name}${compressed ? ".gz" : ""}`,
|
|
3427
|
+
fetchOptions
|
|
3428
|
+
);
|
|
3429
|
+
return [name, compressed ? await inflate(raw) : raw];
|
|
3430
|
+
})
|
|
3431
|
+
);
|
|
3432
|
+
return Object.fromEntries(entries);
|
|
3433
|
+
}
|
|
3434
|
+
function initJpreprocessDictionary(module, data) {
|
|
3435
|
+
module.init_dictionary(
|
|
3436
|
+
data["metadata.json"],
|
|
3437
|
+
data["char_def.bin"],
|
|
3438
|
+
data["matrix.mtx"],
|
|
3439
|
+
data["dict.da"],
|
|
3440
|
+
data["dict.vals"],
|
|
3441
|
+
data["unk.bin"],
|
|
3442
|
+
data["dict.wordsidx"],
|
|
3443
|
+
data["dict.words"]
|
|
3444
|
+
);
|
|
3445
|
+
}
|
|
3446
|
+
var NUCLEUS = /* @__PURE__ */ new Set([
|
|
3447
|
+
"a",
|
|
3448
|
+
"i",
|
|
3449
|
+
"u",
|
|
3450
|
+
"e",
|
|
3451
|
+
"o",
|
|
3452
|
+
"A",
|
|
3453
|
+
"I",
|
|
3454
|
+
"U",
|
|
3455
|
+
"E",
|
|
3456
|
+
"O",
|
|
3457
|
+
"N",
|
|
3458
|
+
"cl"
|
|
3459
|
+
]);
|
|
3460
|
+
function softLimit(value, knee, max2) {
|
|
3461
|
+
const magnitude = Math.abs(value);
|
|
3462
|
+
const limited = magnitude <= knee ? magnitude : knee + (magnitude - knee) * 0.5;
|
|
3463
|
+
return Math.sign(value) * Math.min(max2, limited);
|
|
3464
|
+
}
|
|
3465
|
+
var DEVOICED = /* @__PURE__ */ new Set(["A", "I", "U", "E", "O"]);
|
|
3466
|
+
function segmentPhonemes(phonemes) {
|
|
3467
|
+
const segments = [];
|
|
3468
|
+
let openStart = null;
|
|
3469
|
+
for (let i2 = 0; i2 < phonemes.length; i2++) {
|
|
3470
|
+
const { phone, start_ms, duration_ms } = phonemes[i2];
|
|
3471
|
+
if (phone === "sil" || phone === "pau") {
|
|
3472
|
+
if (openStart !== null) {
|
|
3473
|
+
segments.push({
|
|
3474
|
+
pause: false,
|
|
3475
|
+
startMs: openStart,
|
|
3476
|
+
durationMs: start_ms - openStart,
|
|
3477
|
+
devoiced: false
|
|
3478
|
+
});
|
|
3479
|
+
openStart = null;
|
|
3480
|
+
}
|
|
3481
|
+
if (i2 === 0) continue;
|
|
3482
|
+
segments.push({
|
|
3483
|
+
pause: true,
|
|
3484
|
+
startMs: start_ms,
|
|
3485
|
+
durationMs: duration_ms,
|
|
3486
|
+
devoiced: false
|
|
3487
|
+
});
|
|
3488
|
+
continue;
|
|
3489
|
+
}
|
|
3490
|
+
if (openStart === null) openStart = start_ms;
|
|
3491
|
+
if (NUCLEUS.has(phone)) {
|
|
3492
|
+
segments.push({
|
|
3493
|
+
pause: false,
|
|
3494
|
+
startMs: openStart,
|
|
3495
|
+
durationMs: start_ms + duration_ms - openStart,
|
|
3496
|
+
devoiced: DEVOICED.has(phone)
|
|
3497
|
+
});
|
|
3498
|
+
openStart = null;
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
if (openStart !== null) {
|
|
3502
|
+
const last = phonemes[phonemes.length - 1];
|
|
3503
|
+
segments.push({
|
|
3504
|
+
pause: false,
|
|
3505
|
+
startMs: openStart,
|
|
3506
|
+
durationMs: last.start_ms + last.duration_ms - openStart,
|
|
3507
|
+
devoiced: false
|
|
3508
|
+
});
|
|
3509
|
+
}
|
|
3510
|
+
return segments;
|
|
3511
|
+
}
|
|
3512
|
+
function alignHtsProsody(frames, features, options = {}) {
|
|
3513
|
+
const {
|
|
3514
|
+
intonationStrength = 1,
|
|
3515
|
+
frameMs = 10,
|
|
3516
|
+
fallbackPauseMs = 150,
|
|
3517
|
+
kneeCents = 400,
|
|
3518
|
+
maxCents = 700
|
|
3519
|
+
} = options;
|
|
3520
|
+
const segments = segmentPhonemes(frames.phonemes);
|
|
3521
|
+
const durations = new Array(features.length).fill(0);
|
|
3522
|
+
const htsStart = new Array(features.length).fill(null);
|
|
3523
|
+
const devoiced = new Array(features.length).fill(false);
|
|
3524
|
+
let s = 0;
|
|
3525
|
+
for (let f2 = 0; f2 < features.length; f2++) {
|
|
3526
|
+
const feature = features[f2];
|
|
3527
|
+
while (s < segments.length && segments[s].pause && !feature.pause) s++;
|
|
3528
|
+
const segment = segments[s];
|
|
3529
|
+
if (feature.pause) {
|
|
3530
|
+
if (segment?.pause) {
|
|
3531
|
+
durations[f2] = segment.durationMs;
|
|
3532
|
+
htsStart[f2] = segment.startMs;
|
|
3533
|
+
s++;
|
|
3534
|
+
} else {
|
|
3535
|
+
durations[f2] = fallbackPauseMs;
|
|
3536
|
+
}
|
|
3537
|
+
continue;
|
|
3538
|
+
}
|
|
3539
|
+
if (!segment) return null;
|
|
3540
|
+
durations[f2] = segment.durationMs;
|
|
3541
|
+
htsStart[f2] = segment.startMs;
|
|
3542
|
+
devoiced[f2] = segment.devoiced;
|
|
3543
|
+
s++;
|
|
3544
|
+
}
|
|
3545
|
+
if (segments.slice(s).some((segment) => !segment.pause)) return null;
|
|
3546
|
+
const f0 = frames.f0_hz;
|
|
3547
|
+
const logF0 = new Float64Array(f0.length);
|
|
3548
|
+
const voiced = [];
|
|
3549
|
+
for (let i2 = 0; i2 < f0.length; i2++) if (f0[i2] > 0) voiced.push(i2);
|
|
3550
|
+
if (voiced.length === 0) return null;
|
|
3551
|
+
const sorted = voiced.map((i2) => f0[i2]).sort((a, b) => a - b);
|
|
3552
|
+
const medianHz = sorted[Math.floor(sorted.length / 2)];
|
|
3553
|
+
for (let i2 = 0, v = 0; i2 < f0.length; i2++) {
|
|
3554
|
+
if (f0[i2] > 0) {
|
|
3555
|
+
logF0[i2] = Math.log2(f0[i2]);
|
|
3556
|
+
continue;
|
|
3557
|
+
}
|
|
3558
|
+
while (v < voiced.length && voiced[v] < i2) v++;
|
|
3559
|
+
const right = voiced[v];
|
|
3560
|
+
const left = v > 0 ? voiced[v - 1] : void 0;
|
|
3561
|
+
if (left === void 0) logF0[i2] = Math.log2(f0[right]);
|
|
3562
|
+
else if (right === void 0) logF0[i2] = Math.log2(f0[left]);
|
|
3563
|
+
else {
|
|
3564
|
+
const t = (i2 - left) / (right - left);
|
|
3565
|
+
logF0[i2] = Math.log2(f0[left]) * (1 - t) + Math.log2(f0[right]) * t;
|
|
3566
|
+
}
|
|
3567
|
+
}
|
|
3568
|
+
const logMedian = Math.log2(medianHz);
|
|
3569
|
+
const htsCentsAt = (tMs) => {
|
|
3570
|
+
const position = Math.max(0, tMs) / frames.frame_ms;
|
|
3571
|
+
const left = Math.min(logF0.length - 1, Math.floor(position));
|
|
3572
|
+
const right = Math.min(logF0.length - 1, left + 1);
|
|
3573
|
+
const t = position - Math.floor(position);
|
|
3574
|
+
return (logF0[left] * (1 - t) + logF0[right] * t - logMedian) * 1200;
|
|
3575
|
+
};
|
|
3576
|
+
const planStart = [];
|
|
3577
|
+
let cursor = 0;
|
|
3578
|
+
for (let f2 = 0; f2 < features.length; f2++) {
|
|
3579
|
+
planStart.push(cursor);
|
|
3580
|
+
cursor += durations[f2];
|
|
3581
|
+
}
|
|
3582
|
+
const durationMs = cursor;
|
|
3583
|
+
const count = Math.max(2, Math.ceil(durationMs / frameMs) + 2);
|
|
3584
|
+
const cents = new Array(count);
|
|
3585
|
+
let f = 0;
|
|
3586
|
+
for (let frame = 0; frame < count; frame++) {
|
|
3587
|
+
const tMs = frame * frameMs;
|
|
3588
|
+
while (f + 1 < features.length && tMs >= planStart[f + 1]) f++;
|
|
3589
|
+
const start = htsStart[f];
|
|
3590
|
+
let htsMs;
|
|
3591
|
+
if (start !== null) htsMs = start + (tMs - planStart[f]);
|
|
3592
|
+
else {
|
|
3593
|
+
const next = htsStart.slice(f + 1).find((value) => value !== null);
|
|
3594
|
+
htsMs = next ?? frames.phonemes.at(-1)?.start_ms ?? 0;
|
|
3595
|
+
}
|
|
3596
|
+
cents[frame] = softLimit(
|
|
3597
|
+
htsCentsAt(htsMs) * intonationStrength,
|
|
3598
|
+
kneeCents,
|
|
3599
|
+
maxCents
|
|
3600
|
+
);
|
|
3601
|
+
}
|
|
3602
|
+
return {
|
|
3603
|
+
moraDurationsMs: durations,
|
|
3604
|
+
pitchCurve: { frame_ms: frameMs, cents },
|
|
3605
|
+
medianHz,
|
|
3606
|
+
durationMs,
|
|
3607
|
+
devoiced
|
|
3608
|
+
};
|
|
3609
|
+
}
|
|
3610
|
+
var PUNCTUATION = /* @__PURE__ */ new Set(["\u3001", "\u3002", "\uFF1F", "\uFF01", ",", ".", "?", "!"]);
|
|
3611
|
+
var SMALL_KANA = /* @__PURE__ */ new Set(["\u3041", "\u3043", "\u3045", "\u3047", "\u3049", "\u3083", "\u3085", "\u3087", "\u308E", "\u3095", "\u3096"]);
|
|
3612
|
+
function toHiragana2(character) {
|
|
3613
|
+
const code = character.charCodeAt(0);
|
|
3614
|
+
if (12449 <= code && code <= 12534) {
|
|
3615
|
+
return String.fromCharCode(code - 96);
|
|
3616
|
+
}
|
|
3617
|
+
return character;
|
|
3618
|
+
}
|
|
3619
|
+
function splitMorae(reading) {
|
|
3620
|
+
const result = [];
|
|
3621
|
+
const normalized = reading.normalize("NFC").replace(/'/g, "").replace(/’/g, "");
|
|
3622
|
+
for (const character of normalized) {
|
|
3623
|
+
if (/\s/.test(character) || PUNCTUATION.has(character)) {
|
|
3624
|
+
if (result.length > 0 && !result[result.length - 1].pause) {
|
|
3625
|
+
result.push({ mora: "", pause: true });
|
|
3626
|
+
}
|
|
3627
|
+
continue;
|
|
3628
|
+
}
|
|
3629
|
+
const mora = toHiragana2(character);
|
|
3630
|
+
if (SMALL_KANA.has(mora) && result.length > 0 && !result[result.length - 1].pause) {
|
|
3631
|
+
result[result.length - 1].mora += mora;
|
|
3632
|
+
} else {
|
|
3633
|
+
result.push({ mora, pause: false });
|
|
3634
|
+
}
|
|
3635
|
+
}
|
|
3636
|
+
return result;
|
|
3637
|
+
}
|
|
3638
|
+
function isHigh(position, accent) {
|
|
3639
|
+
if (accent === 1) return position === 1;
|
|
3640
|
+
if (accent > 1) return 2 <= position && position <= accent;
|
|
3641
|
+
return position >= 2;
|
|
3642
|
+
}
|
|
3643
|
+
function analyze2(nodes) {
|
|
3644
|
+
const reading_parts = [];
|
|
3645
|
+
const result = [];
|
|
3646
|
+
let index = 0;
|
|
3647
|
+
while (index < nodes.length) {
|
|
3648
|
+
const node = nodes[index];
|
|
3649
|
+
const pronunciation = (node.pron || "").replace(/'/g, "").replace(/’/g, "");
|
|
3650
|
+
if (node.mora_size === 0 || PUNCTUATION.has(node.string)) {
|
|
3651
|
+
reading_parts.push(node.string || "\u3001");
|
|
3652
|
+
if (result.length > 0 && !result[result.length - 1].pause) {
|
|
3653
|
+
result.push({ mora: "", pause: true });
|
|
3654
|
+
}
|
|
3655
|
+
index++;
|
|
3656
|
+
continue;
|
|
3657
|
+
}
|
|
3658
|
+
const phrase_nodes = [];
|
|
3659
|
+
while (index < nodes.length) {
|
|
3660
|
+
const current = nodes[index];
|
|
3661
|
+
if (current.mora_size === 0 || PUNCTUATION.has(current.string)) {
|
|
3662
|
+
break;
|
|
3663
|
+
}
|
|
3664
|
+
if (phrase_nodes.length > 0 && current.chain_flag !== 1) {
|
|
3665
|
+
break;
|
|
3666
|
+
}
|
|
3667
|
+
const current_pronunciation = (current.pron || "").replace(/'/g, "").replace(/’/g, "");
|
|
3668
|
+
const morae = splitMorae(current_pronunciation).filter((item) => !item.pause);
|
|
3669
|
+
phrase_nodes.push({ current, morae });
|
|
3670
|
+
reading_parts.push(current_pronunciation);
|
|
3671
|
+
index++;
|
|
3672
|
+
}
|
|
3673
|
+
const phrase_length = phrase_nodes.reduce((sum, item) => sum + item.morae.length, 0);
|
|
3674
|
+
const accent = phrase_nodes[0].current.acc || 0;
|
|
3675
|
+
let phrase_position = 0;
|
|
3676
|
+
for (const { current, morae } of phrase_nodes) {
|
|
3677
|
+
for (let word_position = 1; word_position <= morae.length; word_position++) {
|
|
3678
|
+
phrase_position++;
|
|
3679
|
+
result.push({
|
|
3680
|
+
mora: morae[word_position - 1].mora,
|
|
3681
|
+
pause: false,
|
|
3682
|
+
accent_phrase_position: phrase_position,
|
|
3683
|
+
accent_phrase_length: phrase_length,
|
|
3684
|
+
accent_nucleus: accent,
|
|
3685
|
+
accent_high: isHigh(phrase_position, accent),
|
|
3686
|
+
accent_phrase_start: phrase_position === 1,
|
|
3687
|
+
accent_phrase_end: phrase_position === phrase_length,
|
|
3688
|
+
word_start: word_position === 1,
|
|
3689
|
+
word_end: word_position === morae.length,
|
|
3690
|
+
pos: current.pos || "*",
|
|
3691
|
+
pos_group1: current.pos_group1 || "*"
|
|
3692
|
+
});
|
|
3693
|
+
}
|
|
3694
|
+
}
|
|
3695
|
+
}
|
|
3696
|
+
return { reading: reading_parts.join(""), features: result };
|
|
3697
|
+
}
|
|
3698
|
+
function sparse_features(token) {
|
|
3699
|
+
if (token.pause || token.accent_phrase_position === void 0 || token.accent_phrase_length === void 0 || token.accent_nucleus === void 0) {
|
|
3700
|
+
return {};
|
|
3701
|
+
}
|
|
3702
|
+
const phrase_length = Math.max(1, token.accent_phrase_length);
|
|
3703
|
+
const phrase_position = token.accent_phrase_position;
|
|
3704
|
+
const nucleus = token.accent_nucleus;
|
|
3705
|
+
const result = {
|
|
3706
|
+
"accent_position": phrase_position / phrase_length,
|
|
3707
|
+
"accent_from_end": (phrase_length - phrase_position) / phrase_length,
|
|
3708
|
+
"accent_nucleus_position": nucleus / phrase_length,
|
|
3709
|
+
"accent_high": token.accent_high ? 1 : 0,
|
|
3710
|
+
"accent_phrase_start": token.accent_phrase_start ? 1 : 0,
|
|
3711
|
+
"accent_phrase_end": token.accent_phrase_end ? 1 : 0,
|
|
3712
|
+
"word_start": token.word_start ? 1 : 0,
|
|
3713
|
+
"word_end": token.word_end ? 1 : 0
|
|
3714
|
+
};
|
|
3715
|
+
result[`pos=${token.pos || "*"}`] = 1;
|
|
3716
|
+
result[`pos_group1=${token.pos_group1 || "*"}`] = 1;
|
|
3717
|
+
if (nucleus === 0) {
|
|
3718
|
+
result["accent_type=heiban"] = 1;
|
|
3719
|
+
} else if (phrase_position < nucleus) {
|
|
3720
|
+
result["accent_type=before"] = 1;
|
|
3721
|
+
} else if (phrase_position === nucleus) {
|
|
3722
|
+
result["accent_type=nucleus"] = 1;
|
|
3723
|
+
} else {
|
|
3724
|
+
result["accent_type=after"] = 1;
|
|
3725
|
+
}
|
|
3726
|
+
return result;
|
|
3727
|
+
}
|
|
3728
|
+
var TRAILING = /[\s」』))〕】>>"'”’]+$/u;
|
|
3729
|
+
function isQuestion(text) {
|
|
3730
|
+
return /[??]$/u.test(text.replace(TRAILING, ""));
|
|
3731
|
+
}
|
|
3732
|
+
function smoothstep(t) {
|
|
3733
|
+
const x2 = Math.min(1, Math.max(0, t));
|
|
3734
|
+
return x2 * x2 * (3 - 2 * x2);
|
|
3735
|
+
}
|
|
3736
|
+
function shapeProsody(prosody, features, options = {}) {
|
|
3737
|
+
const {
|
|
3738
|
+
question = false,
|
|
3739
|
+
questionRiseCents = 350,
|
|
3740
|
+
energyDbPerSemitone = 0.5,
|
|
3741
|
+
devoicedGain = 0.5,
|
|
3742
|
+
minGain = 0.35,
|
|
3743
|
+
maxGain = 1.8
|
|
3744
|
+
} = options;
|
|
3745
|
+
const { frame_ms: frameMs } = prosody.pitchCurve;
|
|
3746
|
+
const cents = prosody.pitchCurve.cents.slice();
|
|
3747
|
+
const count = Math.min(features.length, prosody.moraDurationsMs.length);
|
|
3748
|
+
const startFrame = [];
|
|
3749
|
+
const endFrame = [];
|
|
3750
|
+
let cursor = 0;
|
|
3751
|
+
for (let f = 0; f < count; f++) {
|
|
3752
|
+
startFrame.push(cursor / frameMs);
|
|
3753
|
+
cursor += prosody.moraDurationsMs[f];
|
|
3754
|
+
endFrame.push(cursor / frameMs);
|
|
3755
|
+
}
|
|
3756
|
+
const frameRange = (f) => [
|
|
3757
|
+
Math.max(0, Math.min(cents.length, Math.round(startFrame[f]))),
|
|
3758
|
+
Math.max(0, Math.min(cents.length, Math.round(endFrame[f])))
|
|
3759
|
+
];
|
|
3760
|
+
if (question && questionRiseCents > 0) {
|
|
3761
|
+
let last = -1;
|
|
3762
|
+
for (let f = count - 1; f >= 0; f--) {
|
|
3763
|
+
if (!features[f].pause) {
|
|
3764
|
+
last = f;
|
|
3765
|
+
break;
|
|
3766
|
+
}
|
|
3767
|
+
}
|
|
3768
|
+
if (last >= 0) {
|
|
3769
|
+
const [from, to] = frameRange(last);
|
|
3770
|
+
if (to > from) {
|
|
3771
|
+
const existing = cents[to - 1] - cents[from];
|
|
3772
|
+
const shortfall = questionRiseCents - existing;
|
|
3773
|
+
if (shortfall > 0) {
|
|
3774
|
+
for (let frame = from; frame < cents.length; frame++) {
|
|
3775
|
+
const t = (frame - from) / (to - from);
|
|
3776
|
+
cents[frame] += shortfall * smoothstep((t - 0.25) / 0.75);
|
|
3777
|
+
}
|
|
3778
|
+
}
|
|
3779
|
+
}
|
|
3780
|
+
}
|
|
3781
|
+
}
|
|
3782
|
+
const moraGains = new Array(features.length).fill(1);
|
|
3783
|
+
for (let f = 0; f < count; f++) {
|
|
3784
|
+
if (features[f].pause) continue;
|
|
3785
|
+
let gainDb = 0;
|
|
3786
|
+
if (energyDbPerSemitone !== 0) {
|
|
3787
|
+
const [from, to] = frameRange(f);
|
|
3788
|
+
if (to > from) {
|
|
3789
|
+
let sum = 0;
|
|
3790
|
+
for (let frame = from; frame < to; frame++) sum += cents[frame];
|
|
3791
|
+
gainDb += sum / (to - from) / 100 * energyDbPerSemitone;
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
let gain = 10 ** (gainDb / 20);
|
|
3795
|
+
if (prosody.devoiced[f]) gain *= devoicedGain;
|
|
3796
|
+
moraGains[f] = Math.min(maxGain, Math.max(minGain, gain));
|
|
3797
|
+
}
|
|
3798
|
+
return {
|
|
3799
|
+
...prosody,
|
|
3800
|
+
pitchCurve: { frame_ms: frameMs, cents },
|
|
3801
|
+
moraGains
|
|
3802
|
+
};
|
|
3803
|
+
}
|
|
3804
|
+
function readingFromFeatures(features) {
|
|
3805
|
+
return features.map((frame) => frame.pause ? "\u3001" : frame.mora).join("");
|
|
3806
|
+
}
|
|
3807
|
+
var FS = WORLDLINE_SAMPLE_RATE;
|
|
3808
|
+
var msToSamples2 = (ms) => Math.round(ms / 1e3 * FS);
|
|
3809
|
+
function f0At(timeline, tMs) {
|
|
3810
|
+
const curve = timeline.f0_curve;
|
|
3811
|
+
if (curve.length === 0) return timeline.reference_hz || 220;
|
|
3812
|
+
const position = Math.max(0, tMs) / timeline.frame_ms;
|
|
3813
|
+
const left = Math.floor(position);
|
|
3814
|
+
if (left >= curve.length - 1) return curve[curve.length - 1];
|
|
3815
|
+
const progress = position - left;
|
|
3816
|
+
return curve[left] * (1 - progress) + curve[left + 1] * progress;
|
|
3817
|
+
}
|
|
3818
|
+
function applyMoraGains(plan, gains) {
|
|
3819
|
+
const morae = plan.morae ?? [];
|
|
3820
|
+
const timings = plan.mora_timings ?? [];
|
|
3821
|
+
const byIndex = morae.length === gains.length;
|
|
3822
|
+
const moraAt = (unit) => {
|
|
3823
|
+
if (byIndex) return unit.position;
|
|
3824
|
+
let found = -1;
|
|
3825
|
+
for (let i2 = 0; i2 < timings.length && i2 < gains.length; i2++) {
|
|
3826
|
+
if (timings[i2].StartMS <= unit.note_start_ms + 1e-6) found = i2;
|
|
3827
|
+
else break;
|
|
3828
|
+
}
|
|
3829
|
+
return found;
|
|
3830
|
+
};
|
|
3831
|
+
for (const unit of plan.timeline.units) {
|
|
3832
|
+
const index = moraAt(unit);
|
|
3833
|
+
const gain = gains[index];
|
|
3834
|
+
if (index < 0 || gain === void 0 || !Number.isFinite(gain)) continue;
|
|
3835
|
+
unit.volume *= gain;
|
|
3836
|
+
}
|
|
3837
|
+
}
|
|
3838
|
+
function planChunks(units2, firstChunkUnits, chunkUnits) {
|
|
3839
|
+
const ranges = [];
|
|
3840
|
+
if (units2.length === 0) return ranges;
|
|
3841
|
+
let start = 0;
|
|
3842
|
+
let coveredEndMs = units2[0].position_ms + units2[0].length_ms;
|
|
3843
|
+
let headSeam = false;
|
|
3844
|
+
for (let i2 = 1; i2 <= units2.length; i2++) {
|
|
3845
|
+
const limit = ranges.length === 0 ? firstChunkUnits : chunkUnits;
|
|
3846
|
+
const atEnd = i2 === units2.length;
|
|
3847
|
+
const clean = !atEnd && units2[i2].position_ms >= coveredEndMs - 1e-6;
|
|
3848
|
+
const full = !atEnd && i2 - start >= limit;
|
|
3849
|
+
if (atEnd || clean || full) {
|
|
3850
|
+
const tailSeam = !atEnd && !clean;
|
|
3851
|
+
ranges.push({ start, end: i2, headSeam, tailSeam });
|
|
3852
|
+
start = i2;
|
|
3853
|
+
headSeam = tailSeam;
|
|
3854
|
+
if (!atEnd) coveredEndMs = units2[i2].position_ms + units2[i2].length_ms;
|
|
3855
|
+
continue;
|
|
3856
|
+
}
|
|
3857
|
+
coveredEndMs = Math.max(
|
|
3858
|
+
coveredEndMs,
|
|
3859
|
+
units2[i2].position_ms + units2[i2].length_ms
|
|
3860
|
+
);
|
|
3861
|
+
}
|
|
3862
|
+
return ranges;
|
|
3863
|
+
}
|
|
3864
|
+
function seamTimeMs(next, crossfadeMs) {
|
|
3865
|
+
const earliest = next.position_ms + next.fade_in_ms + crossfadeMs / 2;
|
|
3866
|
+
const latest = next.position_ms + next.length_ms - crossfadeMs / 2;
|
|
3867
|
+
return Math.min(
|
|
3868
|
+
earliest,
|
|
3869
|
+
Math.max(next.position_ms + crossfadeMs / 2, latest)
|
|
3870
|
+
);
|
|
3871
|
+
}
|
|
3872
|
+
function applyFade(pcm, fromSample, samples, fadeIn) {
|
|
3873
|
+
const n = Math.max(1, Math.min(samples, pcm.length - fromSample));
|
|
3874
|
+
for (let k = 0; k < n; k++) {
|
|
3875
|
+
const t = (k + 0.5) / n;
|
|
3876
|
+
const gain = fadeIn ? Math.sin(Math.PI / 2 * t) : Math.cos(Math.PI / 2 * t);
|
|
3877
|
+
pcm[fromSample + k] *= gain;
|
|
3878
|
+
}
|
|
3879
|
+
}
|
|
3880
|
+
var UtauTTSAdapter = class _UtauTTSAdapter {
|
|
3881
|
+
constructor(worldline) {
|
|
3882
|
+
this.worldline = worldline;
|
|
3883
|
+
}
|
|
3884
|
+
worldline;
|
|
3885
|
+
static modelId = null;
|
|
3886
|
+
bankAliases = /* @__PURE__ */ new WeakMap();
|
|
3887
|
+
currentBank = null;
|
|
3888
|
+
pcmCache = /* @__PURE__ */ new Map();
|
|
3889
|
+
/**
|
|
3890
|
+
* Load the UtauTTS Go wasm. `wasm_exec.js` must already be on the page.
|
|
3891
|
+
* Pass `fetch` (e.g. koe's `fetchAsset`) to stream from the Cache API.
|
|
3892
|
+
*/
|
|
3893
|
+
static async initializeWasm(wasmUrl = "utautts.wasm", options = {}) {
|
|
3894
|
+
if (typeof utautts_plan === "function") return;
|
|
3895
|
+
if (typeof Go === "undefined") {
|
|
3896
|
+
throw new Error(
|
|
3897
|
+
"wasm_exec.js must be loaded before calling initializeWasm."
|
|
3898
|
+
);
|
|
3899
|
+
}
|
|
3900
|
+
const go = new Go();
|
|
3901
|
+
const responsePromise = (options.fetch ?? fetch)(wasmUrl);
|
|
3902
|
+
let instance;
|
|
3903
|
+
try {
|
|
3904
|
+
instance = (await WebAssembly.instantiateStreaming(responsePromise, go.importObject)).instance;
|
|
3905
|
+
} catch {
|
|
3906
|
+
const bytes = await (await (options.fetch ?? fetch)(wasmUrl)).arrayBuffer();
|
|
3907
|
+
instance = (await WebAssembly.instantiate(bytes, go.importObject)).instance;
|
|
3908
|
+
}
|
|
3909
|
+
void go.run(instance);
|
|
3910
|
+
for (let attempt = 0; attempt < 100 && typeof utautts_plan !== "function"; attempt++) {
|
|
3911
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
3912
|
+
}
|
|
3913
|
+
if (typeof utautts_plan !== "function") {
|
|
3914
|
+
throw new Error("utautts_plan failed to initialize in global scope.");
|
|
3915
|
+
}
|
|
3916
|
+
}
|
|
3917
|
+
static get ready() {
|
|
3918
|
+
return typeof utautts_plan === "function";
|
|
3919
|
+
}
|
|
3920
|
+
/** Parse and cache a prosody model (e.g. `frame-intonation-v8.json`) inside the wasm. */
|
|
3921
|
+
static setModel(modelJSON) {
|
|
3922
|
+
_UtauTTSAdapter.assertReady();
|
|
3923
|
+
const result = utautts_set_model(modelJSON);
|
|
3924
|
+
if (!result.success)
|
|
3925
|
+
throw new Error(`UtauTTS model error: ${result.error}`);
|
|
3926
|
+
_UtauTTSAdapter.modelId = result.id ?? null;
|
|
3927
|
+
return _UtauTTSAdapter.modelId;
|
|
3928
|
+
}
|
|
3929
|
+
static get currentModelId() {
|
|
3930
|
+
return _UtauTTSAdapter.modelId;
|
|
3931
|
+
}
|
|
3932
|
+
static assertReady() {
|
|
3933
|
+
if (typeof utautts_plan !== "function") {
|
|
3934
|
+
throw new Error(
|
|
3935
|
+
"UtauTTS wasm is not initialized; call UtauTTSAdapter.initializeWasm() first."
|
|
3936
|
+
);
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3939
|
+
/**
|
|
3940
|
+
* Register a koe voice bank with the planner: the manifest becomes a virtual
|
|
3941
|
+
* oto.ini (koe PCM is pre-trimmed, so offset is 0) plus each sample's
|
|
3942
|
+
* recorded pitch. Called automatically by {@link plan}; cheap when unchanged.
|
|
3943
|
+
*/
|
|
3944
|
+
setBank(bank) {
|
|
3945
|
+
_UtauTTSAdapter.assertReady();
|
|
3946
|
+
if (this.currentBank === bank && this.bankAliases.has(bank)) return;
|
|
3947
|
+
const entries = {};
|
|
3948
|
+
const pitch = {};
|
|
3949
|
+
for (const [alias, phoneme] of Object.entries(bank.manifest.phonemes)) {
|
|
3950
|
+
entries[alias] = [
|
|
3951
|
+
{
|
|
3952
|
+
Filename: `koe:${alias}`,
|
|
3953
|
+
Alias: alias,
|
|
3954
|
+
Offset: 0,
|
|
3955
|
+
Fixed: phoneme.consonant / 48,
|
|
3956
|
+
Blank: 0,
|
|
3957
|
+
Preutterance: phoneme.pre / 48,
|
|
3958
|
+
Overlap: phoneme.overlap / 48,
|
|
3959
|
+
SourceGroup: "koe"
|
|
3960
|
+
}
|
|
3961
|
+
];
|
|
3962
|
+
if (phoneme.pitch > 0) pitch[alias] = phoneme.pitch;
|
|
3963
|
+
}
|
|
3964
|
+
const result = utautts_set_bank(
|
|
3965
|
+
JSON.stringify({
|
|
3966
|
+
name: "koe",
|
|
3967
|
+
oto_entries: entries,
|
|
3968
|
+
source_pitch_hz: pitch
|
|
3969
|
+
})
|
|
3970
|
+
);
|
|
3971
|
+
if (!result.success) throw new Error(`UtauTTS bank error: ${result.error}`);
|
|
3972
|
+
this.bankAliases.set(bank, result.aliases ?? 0);
|
|
3973
|
+
this.currentBank = bank;
|
|
3974
|
+
this.pcmCache.clear();
|
|
3975
|
+
}
|
|
3976
|
+
/**
|
|
3977
|
+
* Plan an utterance: unit selection, timing, pitch contour and worldline
|
|
3978
|
+
* placement. `features` are the mora-level frames from `openjtalkAnalyze`
|
|
3979
|
+
* (pauses included); the kana reading is derived from them.
|
|
3980
|
+
*/
|
|
3981
|
+
plan(bank, text, features, options = {}) {
|
|
3982
|
+
this.setBank(bank);
|
|
3983
|
+
const request = {
|
|
3984
|
+
text,
|
|
3985
|
+
reading: readingFromFeatures(features),
|
|
3986
|
+
frames: features.map((frame) => sparse_features(frame)),
|
|
3987
|
+
tone: options.tone ?? "C4",
|
|
3988
|
+
mora_duration_ms: options.moraDurationMs ?? 0,
|
|
3989
|
+
pause_duration_ms: options.pauseDurationMs ?? 0,
|
|
3990
|
+
release_ms: options.releaseMs ?? 20,
|
|
3991
|
+
leading_preutterance_ms: options.leadingPreutteranceMs ?? 0,
|
|
3992
|
+
apply_pitch: options.applyPitch ?? true,
|
|
3993
|
+
intonation_strength: options.intonationStrength ?? 1,
|
|
3994
|
+
speech_timing: options.speechTiming ?? false,
|
|
3995
|
+
word_boundary_envelope: options.wordBoundaryEnvelope ?? false,
|
|
3996
|
+
mora_durations_ms: options.prosody?.moraDurationsMs,
|
|
3997
|
+
pitch_curve: options.prosody?.pitchCurve
|
|
3998
|
+
};
|
|
3999
|
+
const response = utautts_plan(JSON.stringify(request));
|
|
4000
|
+
if (!response.success || !response.plan) {
|
|
4001
|
+
throw new Error(`UtauTTS error: ${response.error ?? "no plan"}`);
|
|
4002
|
+
}
|
|
4003
|
+
const plan = JSON.parse(response.plan);
|
|
4004
|
+
const gains = options.prosody?.moraGains;
|
|
4005
|
+
if (gains) applyMoraGains(plan, gains);
|
|
4006
|
+
return plan;
|
|
4007
|
+
}
|
|
4008
|
+
getPcm(bank, alias) {
|
|
4009
|
+
let cached = this.pcmCache.get(alias);
|
|
4010
|
+
if (!cached) {
|
|
4011
|
+
cached = bank.getPcm(alias);
|
|
4012
|
+
this.pcmCache.set(alias, cached);
|
|
4013
|
+
}
|
|
4014
|
+
return cached;
|
|
4015
|
+
}
|
|
4016
|
+
/**
|
|
4017
|
+
* Render a plan chunk by chunk. Each chunk is independent audio positioned
|
|
4018
|
+
* at `startMs`; schedule them as they arrive (see the demo) or sum them.
|
|
4019
|
+
*
|
|
4020
|
+
* Chunk breaks fall on pauses when possible. Inside a phrase a break renders
|
|
4021
|
+
* one neighbouring unit of context on each side so the unit crossfade stays
|
|
4022
|
+
* WORLD's spectral one, then the two renders are joined with a short
|
|
4023
|
+
* equal-power crossfade in the following vowel.
|
|
4024
|
+
*/
|
|
4025
|
+
async *renderChunks(bank, plan, options = {}) {
|
|
4026
|
+
const {
|
|
4027
|
+
firstChunkUnits = 3,
|
|
4028
|
+
chunkUnits = 6,
|
|
4029
|
+
seamCrossfadeMs = 20,
|
|
4030
|
+
signal
|
|
4031
|
+
} = options;
|
|
4032
|
+
const timeline = plan.timeline;
|
|
4033
|
+
const units2 = timeline.units.filter((unit) => unit.length_ms > 0);
|
|
4034
|
+
const ranges = planChunks(
|
|
4035
|
+
units2,
|
|
4036
|
+
Math.max(1, firstChunkUnits),
|
|
4037
|
+
Math.max(1, chunkUnits)
|
|
4038
|
+
);
|
|
4039
|
+
const crossfadeSamples = Math.max(2, msToSamples2(seamCrossfadeMs));
|
|
4040
|
+
for (let index = 0; index < ranges.length; index++) {
|
|
4041
|
+
if (signal?.aborted) return;
|
|
4042
|
+
const range = ranges[index];
|
|
4043
|
+
const renderStart = range.headSeam ? range.start - 1 : range.start;
|
|
4044
|
+
const renderEnd = range.tailSeam ? range.end + 1 : range.end;
|
|
4045
|
+
const rendered = units2.slice(renderStart, renderEnd);
|
|
4046
|
+
const baseMs = Math.min(...rendered.map((unit) => unit.position_ms));
|
|
4047
|
+
const phraseUnits = [];
|
|
4048
|
+
for (const unit of rendered) {
|
|
4049
|
+
const pcm2 = await this.getPcm(bank, unit.alias);
|
|
4050
|
+
if (!pcm2 || pcm2.length < MIN_WORLDLINE_SAMPLES) {
|
|
4051
|
+
console.warn(
|
|
4052
|
+
`[utautts] no usable PCM for alias "${unit.alias}"; skipped`
|
|
4053
|
+
);
|
|
4054
|
+
continue;
|
|
4055
|
+
}
|
|
4056
|
+
phraseUnits.push({
|
|
4057
|
+
pcm: pcm2,
|
|
4058
|
+
posMs: unit.position_ms - baseMs,
|
|
4059
|
+
skipMs: unit.skip_ms,
|
|
4060
|
+
lengthMs: unit.length_ms,
|
|
4061
|
+
fadeInMs: unit.fade_in_ms,
|
|
4062
|
+
fadeOutMs: unit.fade_out_ms,
|
|
4063
|
+
consonantMs: unit.consonant_ms,
|
|
4064
|
+
requiredLengthMs: unit.required_length_ms,
|
|
4065
|
+
volume: unit.volume,
|
|
4066
|
+
tone: unit.tone
|
|
4067
|
+
});
|
|
4068
|
+
}
|
|
4069
|
+
if (signal?.aborted) return;
|
|
4070
|
+
if (phraseUnits.length === 0) continue;
|
|
4071
|
+
const audio = this.worldline.renderPhrase({
|
|
4072
|
+
units: phraseUnits,
|
|
4073
|
+
pitch: (tMs) => f0At(timeline, baseMs + tMs),
|
|
4074
|
+
gender: options.gender,
|
|
4075
|
+
tension: options.tension,
|
|
4076
|
+
breathiness: options.breathiness,
|
|
4077
|
+
voicing: options.voicing
|
|
4078
|
+
});
|
|
4079
|
+
if (!audio || audio.length === 0) continue;
|
|
4080
|
+
let fromSample = 0;
|
|
4081
|
+
let toSample = audio.length;
|
|
4082
|
+
let startMs = baseMs;
|
|
4083
|
+
if (range.headSeam) {
|
|
4084
|
+
const seamMs = seamTimeMs(units2[range.start], seamCrossfadeMs);
|
|
4085
|
+
fromSample = Math.max(
|
|
4086
|
+
0,
|
|
4087
|
+
msToSamples2(seamMs - baseMs) - crossfadeSamples / 2
|
|
4088
|
+
);
|
|
4089
|
+
startMs = baseMs + fromSample / FS * 1e3;
|
|
4090
|
+
}
|
|
4091
|
+
if (range.tailSeam) {
|
|
4092
|
+
const seamMs = seamTimeMs(units2[range.end], seamCrossfadeMs);
|
|
4093
|
+
toSample = Math.min(
|
|
4094
|
+
audio.length,
|
|
4095
|
+
msToSamples2(seamMs - baseMs) + crossfadeSamples / 2
|
|
4096
|
+
);
|
|
4097
|
+
}
|
|
4098
|
+
if (toSample <= fromSample) continue;
|
|
4099
|
+
const pcm = audio.slice(fromSample, toSample);
|
|
4100
|
+
if (range.headSeam) applyFade(pcm, 0, crossfadeSamples, true);
|
|
4101
|
+
if (range.tailSeam)
|
|
4102
|
+
applyFade(
|
|
4103
|
+
pcm,
|
|
4104
|
+
Math.max(0, pcm.length - crossfadeSamples),
|
|
4105
|
+
crossfadeSamples,
|
|
4106
|
+
false
|
|
4107
|
+
);
|
|
4108
|
+
yield { pcm, startMs, index, units: units2.slice(range.start, range.end) };
|
|
4109
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
4110
|
+
}
|
|
4111
|
+
}
|
|
4112
|
+
/**
|
|
4113
|
+
* Plan + render an utterance into one buffer (Float32, 48 kHz) covering the
|
|
4114
|
+
* whole timeline. Use {@link plan} + {@link renderChunks} for streaming.
|
|
4115
|
+
*/
|
|
4116
|
+
async synthesizeText(bank, text, features, options = {}) {
|
|
4117
|
+
const plan = this.plan(bank, text, features, options);
|
|
4118
|
+
const total = msToSamples2(plan.timeline.duration_ms) + msToSamples2(200);
|
|
4119
|
+
const out = new Float32Array(total);
|
|
4120
|
+
let any = false;
|
|
4121
|
+
for await (const chunk of this.renderChunks(bank, plan, options)) {
|
|
4122
|
+
any = true;
|
|
4123
|
+
const offset = msToSamples2(chunk.startMs);
|
|
4124
|
+
const n = Math.min(chunk.pcm.length, out.length - offset);
|
|
4125
|
+
for (let k = 0; k < n; k++) out[offset + k] += chunk.pcm[k];
|
|
4126
|
+
}
|
|
4127
|
+
return any ? out : null;
|
|
4128
|
+
}
|
|
4129
|
+
};
|
|
3172
4130
|
|
|
3173
4131
|
// src/vibrato.ts
|
|
3174
4132
|
var VIBRATO_MIN_SEC = 0.35;
|
|
@@ -3190,7 +4148,7 @@ var MAX_PORTAMENTO_MS = 400;
|
|
|
3190
4148
|
var MIN_GLIDE_MS = 4;
|
|
3191
4149
|
var MAX_GLIDE_SPAN_RATIO = 0.9;
|
|
3192
4150
|
var unitsToHz2 = (units2) => 440 * 2 ** ((units2 - 2139) / 372);
|
|
3193
|
-
var
|
|
4151
|
+
var smoothstep2 = (u) => u * u * (3 - 2 * u);
|
|
3194
4152
|
var glideMsForSegments = (segments, totalMs) => segments.map((seg, i2) => {
|
|
3195
4153
|
const atMs = seg.atSec * 1e3;
|
|
3196
4154
|
const prevMs = i2 === 0 ? 0 : segments[i2 - 1].atSec * 1e3;
|
|
@@ -3224,7 +4182,7 @@ var pitchCurveFor = (baseHz, segments, preMs, vibrato, totalMs) => {
|
|
|
3224
4182
|
continue;
|
|
3225
4183
|
}
|
|
3226
4184
|
if (sinceOnset <= k.startMs) break;
|
|
3227
|
-
const u =
|
|
4185
|
+
const u = smoothstep2((sinceOnset - k.startMs) / k.glideMs);
|
|
3228
4186
|
hz = hz * (k.hz / hz) ** u;
|
|
3229
4187
|
break;
|
|
3230
4188
|
}
|
|
@@ -3240,6 +4198,177 @@ var segmentsCacheKey = (segments) => segments?.length ? `|s${segments.map(
|
|
|
3240
4198
|
(s) => `${s.pitch}@${Math.round(s.atSec * 100)}${s.portamento ? "~" : ""}`
|
|
3241
4199
|
).join(",")}` : "";
|
|
3242
4200
|
|
|
4201
|
+
// src/speech.ts
|
|
4202
|
+
var DEFAULT_TTS_BASE_URL = "https://onjmin.github.io/koe/demo/utautts/";
|
|
4203
|
+
var joinUrl = (base, path) => `${base.endsWith("/") ? base : `${base}/`}${path}`;
|
|
4204
|
+
var loadGoRuntime = (url2) => new Promise((resolve, reject) => {
|
|
4205
|
+
if (typeof globalThis.Go !== "undefined") {
|
|
4206
|
+
resolve();
|
|
4207
|
+
return;
|
|
4208
|
+
}
|
|
4209
|
+
if (typeof document === "undefined") {
|
|
4210
|
+
reject(new Error("wasm_exec.js needs a document to load into"));
|
|
4211
|
+
return;
|
|
4212
|
+
}
|
|
4213
|
+
const script = document.createElement("script");
|
|
4214
|
+
script.src = url2;
|
|
4215
|
+
script.async = true;
|
|
4216
|
+
script.onload = () => resolve();
|
|
4217
|
+
script.onerror = () => reject(new Error(`failed to load ${url2}`));
|
|
4218
|
+
document.head.appendChild(script);
|
|
4219
|
+
});
|
|
4220
|
+
var importJpreprocess = (url2) => import(
|
|
4221
|
+
/* @vite-ignore */
|
|
4222
|
+
/* webpackIgnore: true */
|
|
4223
|
+
url2
|
|
4224
|
+
);
|
|
4225
|
+
var shared = null;
|
|
4226
|
+
var getSpeechPlanner = (options = {}) => {
|
|
4227
|
+
const baseUrl = options.baseUrl ?? DEFAULT_TTS_BASE_URL;
|
|
4228
|
+
if (shared) {
|
|
4229
|
+
if (shared.baseUrl !== baseUrl) {
|
|
4230
|
+
console.warn(
|
|
4231
|
+
`[dtm] TTS assets are already loaded from ${shared.baseUrl}; ignoring ${baseUrl}`
|
|
4232
|
+
);
|
|
4233
|
+
}
|
|
4234
|
+
return shared.planner;
|
|
4235
|
+
}
|
|
4236
|
+
const planner = createSpeechPlanner({ ...options, baseUrl });
|
|
4237
|
+
shared = { baseUrl, planner };
|
|
4238
|
+
return planner;
|
|
4239
|
+
};
|
|
4240
|
+
var createSpeechPlanner = (options = {}) => {
|
|
4241
|
+
const baseUrl = options.baseUrl ?? DEFAULT_TTS_BASE_URL;
|
|
4242
|
+
let jp = null;
|
|
4243
|
+
let adapter = null;
|
|
4244
|
+
let readyPromise = null;
|
|
4245
|
+
let isReady = false;
|
|
4246
|
+
const load2 = async () => {
|
|
4247
|
+
const progress = /* @__PURE__ */ new Map();
|
|
4248
|
+
const onProgress = (p) => {
|
|
4249
|
+
if (!options.onProgress) return;
|
|
4250
|
+
progress.set(p.url, p);
|
|
4251
|
+
let loaded = 0;
|
|
4252
|
+
let total = 0;
|
|
4253
|
+
for (const item of progress.values()) {
|
|
4254
|
+
loaded += item.loaded;
|
|
4255
|
+
total += item.total;
|
|
4256
|
+
}
|
|
4257
|
+
options.onProgress(loaded, total);
|
|
4258
|
+
};
|
|
4259
|
+
const fetchWithProgress = (url2) => fetchAsset(url2, { onProgress });
|
|
4260
|
+
const jpreprocessReady = (async () => {
|
|
4261
|
+
const glue2 = await importJpreprocess(
|
|
4262
|
+
joinUrl(baseUrl, "jpreprocess_wasm/jpreprocess_wasm.js")
|
|
4263
|
+
);
|
|
4264
|
+
await glue2.default({
|
|
4265
|
+
module_or_path: fetchWithProgress(
|
|
4266
|
+
joinUrl(baseUrl, "jpreprocess_wasm/jpreprocess_wasm_bg.wasm")
|
|
4267
|
+
)
|
|
4268
|
+
});
|
|
4269
|
+
if (!glue2.is_ready()) {
|
|
4270
|
+
const dict = await loadNaistJdic(
|
|
4271
|
+
joinUrl(baseUrl, "jpreprocess_wasm/naist-jdic"),
|
|
4272
|
+
{ onProgress }
|
|
4273
|
+
);
|
|
4274
|
+
initJpreprocessDictionary(glue2, dict);
|
|
4275
|
+
}
|
|
4276
|
+
return glue2;
|
|
4277
|
+
})();
|
|
4278
|
+
const utauttsReady = (async () => {
|
|
4279
|
+
await loadGoRuntime(joinUrl(baseUrl, "wasm_exec.js"));
|
|
4280
|
+
await UtauTTSAdapter.initializeWasm(joinUrl(baseUrl, "utautts.wasm"), {
|
|
4281
|
+
fetch: fetchWithProgress
|
|
4282
|
+
});
|
|
4283
|
+
UtauTTSAdapter.setModel(
|
|
4284
|
+
await fetchAssetText(joinUrl(baseUrl, "frame-intonation-v8.json"), {
|
|
4285
|
+
onProgress
|
|
4286
|
+
})
|
|
4287
|
+
);
|
|
4288
|
+
})();
|
|
4289
|
+
const htsVoiceReady = fetchAssetBytes(
|
|
4290
|
+
joinUrl(baseUrl, "hts/tohoku-f01-neutral.htsvoice"),
|
|
4291
|
+
{ onProgress }
|
|
4292
|
+
);
|
|
4293
|
+
const [glue, , htsVoice] = await Promise.all([
|
|
4294
|
+
jpreprocessReady,
|
|
4295
|
+
utauttsReady,
|
|
4296
|
+
htsVoiceReady
|
|
4297
|
+
]);
|
|
4298
|
+
if (!glue.is_voice_ready()) glue.init_voice(htsVoice);
|
|
4299
|
+
jp = glue;
|
|
4300
|
+
adapter = new UtauTTSAdapter(null);
|
|
4301
|
+
isReady = true;
|
|
4302
|
+
};
|
|
4303
|
+
const ready = () => {
|
|
4304
|
+
if (!readyPromise) {
|
|
4305
|
+
readyPromise = load2().catch((err2) => {
|
|
4306
|
+
readyPromise = null;
|
|
4307
|
+
throw err2;
|
|
4308
|
+
});
|
|
4309
|
+
}
|
|
4310
|
+
return readyPromise;
|
|
4311
|
+
};
|
|
4312
|
+
const plan = (bank, text, o = {}) => {
|
|
4313
|
+
if (!jp || !adapter) {
|
|
4314
|
+
throw new Error("speech planner is not ready; await ready() first");
|
|
4315
|
+
}
|
|
4316
|
+
const intonationStrength = o.intonationStrength ?? 1;
|
|
4317
|
+
const nodes = JSON.parse(jp.analyze_text(text));
|
|
4318
|
+
const { features } = analyze2(nodes);
|
|
4319
|
+
if (features.length === 0) {
|
|
4320
|
+
throw new Error(`no readable morae in "${text}"`);
|
|
4321
|
+
}
|
|
4322
|
+
let prosody = null;
|
|
4323
|
+
if (jp.is_voice_ready()) {
|
|
4324
|
+
const frames = JSON.parse(jp.analyze_prosody(text, 1));
|
|
4325
|
+
prosody = alignHtsProsody(frames, features, { intonationStrength });
|
|
4326
|
+
if (prosody) {
|
|
4327
|
+
prosody = shapeProsody(prosody, features, {
|
|
4328
|
+
question: isQuestion(text)
|
|
4329
|
+
});
|
|
4330
|
+
}
|
|
4331
|
+
}
|
|
4332
|
+
return adapter.plan(bank, text, features, {
|
|
4333
|
+
tone: o.tone ?? "C4",
|
|
4334
|
+
intonationStrength,
|
|
4335
|
+
prosody: prosody ?? void 0
|
|
4336
|
+
});
|
|
4337
|
+
};
|
|
4338
|
+
return { ready, isReady: () => isReady, plan };
|
|
4339
|
+
};
|
|
4340
|
+
var prepareSpeechPlan = (plan, ratio, toReal = (a) => a) => {
|
|
4341
|
+
const timeline = plan.timeline;
|
|
4342
|
+
return {
|
|
4343
|
+
...plan,
|
|
4344
|
+
timeline: {
|
|
4345
|
+
...timeline,
|
|
4346
|
+
f0_curve: timeline.f0_curve.map((hz) => hz * ratio),
|
|
4347
|
+
units: timeline.units.map((u) => ({
|
|
4348
|
+
...u,
|
|
4349
|
+
alias: toReal(u.alias),
|
|
4350
|
+
target_f0_hz: u.target_f0_hz * ratio
|
|
4351
|
+
}))
|
|
4352
|
+
}
|
|
4353
|
+
};
|
|
4354
|
+
};
|
|
4355
|
+
var speechPlanLeadingSec = (plan) => Math.max(0, plan.timeline.leading_ms || 0) / 1e3;
|
|
4356
|
+
var speechPlanDurationSec = (plan) => {
|
|
4357
|
+
const timeline = plan.timeline;
|
|
4358
|
+
let endMs = 0;
|
|
4359
|
+
for (const u of timeline.units) {
|
|
4360
|
+
endMs = Math.max(endMs, u.position_ms + u.length_ms);
|
|
4361
|
+
}
|
|
4362
|
+
if (endMs <= 0) endMs = timeline.duration_ms;
|
|
4363
|
+
return Math.max(0, endMs / 1e3 - speechPlanLeadingSec(plan));
|
|
4364
|
+
};
|
|
4365
|
+
var medianRecordedPitchHz = (phonemes) => {
|
|
4366
|
+
const pitches = Object.values(phonemes).map((p) => p.pitch).filter((hz) => hz > 0).sort((a, b) => a - b);
|
|
4367
|
+
if (pitches.length === 0) return void 0;
|
|
4368
|
+
const mid = pitches.length >> 1;
|
|
4369
|
+
return pitches.length % 2 ? pitches[mid] : (pitches[mid - 1] + pitches[mid]) / 2;
|
|
4370
|
+
};
|
|
4371
|
+
|
|
3243
4372
|
// src/types.ts
|
|
3244
4373
|
var DEFAULT_VOCAL_VOLUME = 200;
|
|
3245
4374
|
var DEFAULT_BPM = 120;
|
|
@@ -3338,7 +4467,7 @@ var kanaTable = {
|
|
|
3338
4467
|
\u307D: ["p", "o"],
|
|
3339
4468
|
\u3093: ["N", "N"]
|
|
3340
4469
|
};
|
|
3341
|
-
var
|
|
4470
|
+
var SMALL_KANA2 = "\u3041\u3043\u3045\u3047\u3049\u3083\u3085\u3087";
|
|
3342
4471
|
var VOWEL_KANA = {
|
|
3343
4472
|
a: "\u3042",
|
|
3344
4473
|
i: "\u3044",
|
|
@@ -3353,13 +4482,43 @@ var REST_MARK = "_";
|
|
|
3353
4482
|
var BREATH_MARK = "\u3001";
|
|
3354
4483
|
var FADE_OUT_MARK = "\u2193";
|
|
3355
4484
|
var FADE_IN_MARK = "\u2191";
|
|
4485
|
+
var SPEAK_OPEN = "\u300C";
|
|
4486
|
+
var SPEAK_CLOSE = "\u300D";
|
|
4487
|
+
var splitSpeech = (text) => {
|
|
4488
|
+
const pieces = [];
|
|
4489
|
+
let sung = "";
|
|
4490
|
+
let i2 = 0;
|
|
4491
|
+
while (i2 < text.length) {
|
|
4492
|
+
const ch = text[i2];
|
|
4493
|
+
if (ch === SPEAK_OPEN || ch === "\uFF62") {
|
|
4494
|
+
let end = -1;
|
|
4495
|
+
for (let j = i2 + 1; j < text.length; j++) {
|
|
4496
|
+
if (text[j] === SPEAK_CLOSE || text[j] === "\uFF63") {
|
|
4497
|
+
end = j;
|
|
4498
|
+
break;
|
|
4499
|
+
}
|
|
4500
|
+
}
|
|
4501
|
+
if (sung) pieces.push({ sung });
|
|
4502
|
+
sung = "";
|
|
4503
|
+
const body = end < 0 ? text.slice(i2 + 1) : text.slice(i2 + 1, end);
|
|
4504
|
+
pieces.push({ speak: body });
|
|
4505
|
+
i2 = end < 0 ? text.length : end + 1;
|
|
4506
|
+
continue;
|
|
4507
|
+
}
|
|
4508
|
+
sung += ch;
|
|
4509
|
+
i2++;
|
|
4510
|
+
}
|
|
4511
|
+
if (sung) pieces.push({ sung });
|
|
4512
|
+
return pieces;
|
|
4513
|
+
};
|
|
4514
|
+
var sanitizeSpeech = (text) => text.replace(/[\r\n;]+/g, " ").replace(/\s+/g, " ").trim();
|
|
3356
4515
|
var sanitizeText = (text) => text.normalize("NFKC").replace(/[ァ-ヶ]/g, (c) => String.fromCharCode(c.charCodeAt(0) - 96)).replace(/~/g, PORTAMENTO_MARK).replace(/,/g, BREATH_MARK).replace(/[↡⇩⬇🡇]/gu, FADE_OUT_MARK).replace(/[↟⇧⬆🡅]/gu, FADE_IN_MARK).replace(/[^ぁ-ゖー〜_、↓↑]/g, "");
|
|
3357
4516
|
var splitSyllables = (text) => {
|
|
3358
4517
|
const MARKS = `${TIE_MARK}${PORTAMENTO_MARK}${REST_MARK}${BREATH_MARK}${FADE_OUT_MARK}${FADE_IN_MARK}`;
|
|
3359
4518
|
const result = [];
|
|
3360
4519
|
for (const ch of text) {
|
|
3361
4520
|
const prev = result[result.length - 1];
|
|
3362
|
-
if (prev !== void 0 &&
|
|
4521
|
+
if (prev !== void 0 && SMALL_KANA2.includes(ch) && !MARKS.includes(prev[prev.length - 1])) {
|
|
3363
4522
|
result[result.length - 1] += ch;
|
|
3364
4523
|
} else {
|
|
3365
4524
|
result.push(ch);
|
|
@@ -3408,42 +4567,52 @@ var analyzeSyllable = (syllable) => {
|
|
|
3408
4567
|
var normalizeLyrics = (text) => {
|
|
3409
4568
|
const result = [];
|
|
3410
4569
|
let prevVowel = "";
|
|
3411
|
-
for (const
|
|
3412
|
-
if (
|
|
3413
|
-
const
|
|
3414
|
-
|
|
4570
|
+
for (const piece of splitSpeech(text)) {
|
|
4571
|
+
if ("speak" in piece) {
|
|
4572
|
+
const body = sanitizeSpeech(piece.speak);
|
|
4573
|
+
result.push(
|
|
4574
|
+
body ? { kana: body, consonant: "", vowel: "", kind: "speak", text: body } : { kana: REST_MARK, consonant: "", vowel: "", kind: "rest" }
|
|
4575
|
+
);
|
|
3415
4576
|
prevVowel = "";
|
|
3416
4577
|
continue;
|
|
3417
4578
|
}
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
if (
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
if (
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
4579
|
+
for (const mark of splitSyllables(sanitizeText(piece.sung))) {
|
|
4580
|
+
if (mark === BREATH_MARK) {
|
|
4581
|
+
const last = result[result.length - 1];
|
|
4582
|
+
if (last) last.breathAfter = true;
|
|
4583
|
+
prevVowel = "";
|
|
4584
|
+
continue;
|
|
4585
|
+
}
|
|
4586
|
+
if (mark === FADE_OUT_MARK) {
|
|
4587
|
+
const last = result[result.length - 1];
|
|
4588
|
+
if (last) last.fadeOut = true;
|
|
4589
|
+
continue;
|
|
4590
|
+
}
|
|
4591
|
+
if (mark === FADE_IN_MARK) {
|
|
4592
|
+
const last = result[result.length - 1];
|
|
4593
|
+
if (last) last.fadeIn = true;
|
|
4594
|
+
continue;
|
|
4595
|
+
}
|
|
4596
|
+
const syl = analyzeSyllable(mark);
|
|
4597
|
+
if (syl.kind === "tie") {
|
|
4598
|
+
if (!prevVowel) continue;
|
|
4599
|
+
result.push({
|
|
4600
|
+
...syl,
|
|
4601
|
+
kana: prevVowel === "N" ? "\u3093" : VOWEL_KANA[prevVowel] ?? syl.kana,
|
|
4602
|
+
consonant: "",
|
|
4603
|
+
vowel: prevVowel
|
|
4604
|
+
});
|
|
4605
|
+
continue;
|
|
4606
|
+
}
|
|
4607
|
+
if (syl.kind === "rest") prevVowel = "";
|
|
4608
|
+
else if (syl.vowel) prevVowel = syl.vowel;
|
|
4609
|
+
result.push(syl);
|
|
3438
4610
|
}
|
|
3439
|
-
if (syl.kind === "rest") prevVowel = "";
|
|
3440
|
-
else if (syl.vowel) prevVowel = syl.vowel;
|
|
3441
|
-
result.push(syl);
|
|
3442
4611
|
}
|
|
3443
4612
|
return result;
|
|
3444
4613
|
};
|
|
3445
4614
|
var displayKana = (syl) => {
|
|
3446
|
-
const head = syl.kind === "tie" ? syl.portamento ? PORTAMENTO_MARK : TIE_MARK : syl.kind === "rest" ? REST_MARK : syl.kana;
|
|
4615
|
+
const head = syl.kind === "tie" ? syl.portamento ? PORTAMENTO_MARK : TIE_MARK : syl.kind === "rest" ? REST_MARK : syl.kind === "speak" ? `${SPEAK_OPEN}${syl.text ?? syl.kana}${SPEAK_CLOSE}` : syl.kana;
|
|
3447
4616
|
let out = syl.breathAfter ? head + BREATH_MARK : head;
|
|
3448
4617
|
if (syl.fadeIn) out += FADE_IN_MARK;
|
|
3449
4618
|
if (syl.fadeOut) out += FADE_OUT_MARK;
|
|
@@ -3932,6 +5101,40 @@ var collectPitchTokens = (aliases) => {
|
|
|
3932
5101
|
}
|
|
3933
5102
|
return [...seen].map(([token, midi]) => ({ token, midi }));
|
|
3934
5103
|
};
|
|
5104
|
+
var nearestPitchToken = (pitchTokens, noteNum) => {
|
|
5105
|
+
let best = null;
|
|
5106
|
+
for (const t of pitchTokens) {
|
|
5107
|
+
if (!best || Math.abs(t.midi - noteNum) < Math.abs(best.midi - noteNum))
|
|
5108
|
+
best = t;
|
|
5109
|
+
}
|
|
5110
|
+
return best;
|
|
5111
|
+
};
|
|
5112
|
+
var createSpeechToneView = (phonemes, token) => {
|
|
5113
|
+
if (!token) {
|
|
5114
|
+
return {
|
|
5115
|
+
view: { manifest: { phonemes } },
|
|
5116
|
+
toReal: (a) => a,
|
|
5117
|
+
referenceHz: medianRecordedPitchHz(phonemes)
|
|
5118
|
+
};
|
|
5119
|
+
}
|
|
5120
|
+
const suffix = `_${token}`;
|
|
5121
|
+
const stripped = {};
|
|
5122
|
+
const real = /* @__PURE__ */ new Map();
|
|
5123
|
+
for (const [alias, entry] of Object.entries(phonemes)) {
|
|
5124
|
+
if (alias.endsWith(suffix)) {
|
|
5125
|
+
const bare = alias.slice(0, -suffix.length);
|
|
5126
|
+
stripped[bare] = entry;
|
|
5127
|
+
real.set(bare, alias);
|
|
5128
|
+
} else if (!PITCH_SUFFIX.test(alias) && !(alias in stripped)) {
|
|
5129
|
+
stripped[alias] = entry;
|
|
5130
|
+
}
|
|
5131
|
+
}
|
|
5132
|
+
return {
|
|
5133
|
+
view: { manifest: { phonemes: stripped } },
|
|
5134
|
+
toReal: (a) => real.get(a) ?? a,
|
|
5135
|
+
referenceHz: medianRecordedPitchHz(stripped)
|
|
5136
|
+
};
|
|
5137
|
+
};
|
|
3935
5138
|
var resolveKoeAlias = (hasAlias, pitchTokens, syl, prevVowel, noteNum) => {
|
|
3936
5139
|
const kana = syl.kana;
|
|
3937
5140
|
const cons = syl.consonant === "N" ? "n" : syl.consonant;
|
|
@@ -4087,10 +5290,29 @@ var createLocalBackend = async (options) => {
|
|
|
4087
5290
|
rate
|
|
4088
5291
|
};
|
|
4089
5292
|
};
|
|
5293
|
+
let speechAdapter = null;
|
|
5294
|
+
const renderSpeech = async (plan, expr, onChunk, signal) => {
|
|
5295
|
+
if (!worldline) return;
|
|
5296
|
+
speechAdapter ??= new UtauTTSAdapter(worldline);
|
|
5297
|
+
try {
|
|
5298
|
+
for await (const chunk of speechAdapter.renderChunks(bank, plan, {
|
|
5299
|
+
signal,
|
|
5300
|
+
gender: expr?.gender,
|
|
5301
|
+
breathiness: expr?.breathiness,
|
|
5302
|
+
tension: expr?.tension
|
|
5303
|
+
})) {
|
|
5304
|
+
onChunk({ pcm: chunk.pcm, startMs: chunk.startMs, index: chunk.index });
|
|
5305
|
+
}
|
|
5306
|
+
} catch (err2) {
|
|
5307
|
+
console.warn("[dtm] speech synthesis failed", err2);
|
|
5308
|
+
}
|
|
5309
|
+
};
|
|
4090
5310
|
return {
|
|
4091
5311
|
hasAlias: (a) => bank.has(a),
|
|
4092
5312
|
pitchTokens: collectPitchTokens(Object.keys(bank.manifest.phonemes)),
|
|
4093
5313
|
renderAlias,
|
|
5314
|
+
phonemes: bank.manifest.phonemes,
|
|
5315
|
+
renderSpeech,
|
|
4094
5316
|
dispose: () => {
|
|
4095
5317
|
}
|
|
4096
5318
|
};
|
|
@@ -4106,7 +5328,9 @@ var spawnVoiceWorker = async (url2) => {
|
|
|
4106
5328
|
var createWorkerBackend = async (workerUrl, options) => {
|
|
4107
5329
|
const worker2 = await spawnVoiceWorker(workerUrl);
|
|
4108
5330
|
const aliasSet = /* @__PURE__ */ new Set();
|
|
5331
|
+
let phonemes = {};
|
|
4109
5332
|
const pending = /* @__PURE__ */ new Map();
|
|
5333
|
+
const speechPending = /* @__PURE__ */ new Map();
|
|
4110
5334
|
let reqId = 0;
|
|
4111
5335
|
let onReady = null;
|
|
4112
5336
|
let onFail = null;
|
|
@@ -4114,6 +5338,7 @@ var createWorkerBackend = async (workerUrl, options) => {
|
|
|
4114
5338
|
const m = ev.data;
|
|
4115
5339
|
if (m.type === "ready") {
|
|
4116
5340
|
for (const a of m.aliases) aliasSet.add(a);
|
|
5341
|
+
phonemes = m.phonemes ?? {};
|
|
4117
5342
|
onReady?.();
|
|
4118
5343
|
} else if (m.type === "error") {
|
|
4119
5344
|
onFail?.(new Error(m.message));
|
|
@@ -4123,6 +5348,15 @@ var createWorkerBackend = async (workerUrl, options) => {
|
|
|
4123
5348
|
pending.delete(m.id);
|
|
4124
5349
|
cb(m);
|
|
4125
5350
|
}
|
|
5351
|
+
} else if (m.type === "speech-chunk") {
|
|
5352
|
+
speechPending.get(m.id)?.onChunk({ pcm: m.pcm, startMs: m.startMs, index: m.index });
|
|
5353
|
+
} else if (m.type === "speech-end") {
|
|
5354
|
+
const s = speechPending.get(m.id);
|
|
5355
|
+
if (s) {
|
|
5356
|
+
speechPending.delete(m.id);
|
|
5357
|
+
if (m.error) console.warn("[dtm] speech synthesis failed", m.error);
|
|
5358
|
+
s.done();
|
|
5359
|
+
}
|
|
4126
5360
|
}
|
|
4127
5361
|
};
|
|
4128
5362
|
worker2.onerror = (e) => {
|
|
@@ -4162,10 +5396,32 @@ var createWorkerBackend = async (workerUrl, options) => {
|
|
|
4162
5396
|
pitchSegments
|
|
4163
5397
|
});
|
|
4164
5398
|
});
|
|
5399
|
+
const renderSpeech = (plan, expr, onChunk, signal) => new Promise((resolve) => {
|
|
5400
|
+
const id = ++reqId;
|
|
5401
|
+
speechPending.set(id, { onChunk, done: resolve });
|
|
5402
|
+
signal?.addEventListener("abort", () => {
|
|
5403
|
+
worker2.postMessage({
|
|
5404
|
+
type: "speak-abort",
|
|
5405
|
+
id
|
|
5406
|
+
});
|
|
5407
|
+
});
|
|
5408
|
+
worker2.postMessage({
|
|
5409
|
+
type: "speak",
|
|
5410
|
+
id,
|
|
5411
|
+
plan,
|
|
5412
|
+
gender: expr?.gender,
|
|
5413
|
+
breathiness: expr?.breathiness,
|
|
5414
|
+
tension: expr?.tension
|
|
5415
|
+
});
|
|
5416
|
+
});
|
|
4165
5417
|
return {
|
|
4166
5418
|
hasAlias: (a) => aliasSet.has(a),
|
|
4167
5419
|
pitchTokens: collectPitchTokens(aliasSet),
|
|
4168
5420
|
renderAlias,
|
|
5421
|
+
get phonemes() {
|
|
5422
|
+
return phonemes;
|
|
5423
|
+
},
|
|
5424
|
+
renderSpeech,
|
|
4169
5425
|
dispose: () => worker2.terminate()
|
|
4170
5426
|
};
|
|
4171
5427
|
};
|
|
@@ -4353,6 +5609,206 @@ var createKoeVoice = async (ctx, destination, options) => {
|
|
|
4353
5609
|
durationSec
|
|
4354
5610
|
);
|
|
4355
5611
|
};
|
|
5612
|
+
const planner = getSpeechPlanner({
|
|
5613
|
+
baseUrl: options.ttsBaseUrl
|
|
5614
|
+
});
|
|
5615
|
+
const toneViews = /* @__PURE__ */ new Map();
|
|
5616
|
+
const toneViewFor = (token) => {
|
|
5617
|
+
const k = token ?? "";
|
|
5618
|
+
let v = toneViews.get(k);
|
|
5619
|
+
if (!v) {
|
|
5620
|
+
v = createSpeechToneView(backend.phonemes, token);
|
|
5621
|
+
toneViews.set(k, v);
|
|
5622
|
+
}
|
|
5623
|
+
return v;
|
|
5624
|
+
};
|
|
5625
|
+
const toneFor = (pitch) => nearestPitchToken(backend.pitchTokens, unitsToMidiFloat(pitch))?.token ?? null;
|
|
5626
|
+
const plans = /* @__PURE__ */ new Map();
|
|
5627
|
+
const planFor = (text, token) => {
|
|
5628
|
+
const k = `${token ?? ""}|${text}`;
|
|
5629
|
+
let p = plans.get(k);
|
|
5630
|
+
if (!p) {
|
|
5631
|
+
p = (async () => {
|
|
5632
|
+
try {
|
|
5633
|
+
await planner.ready();
|
|
5634
|
+
return planner.plan(toneViewFor(token).view, text, {
|
|
5635
|
+
tone: token ?? "C4"
|
|
5636
|
+
});
|
|
5637
|
+
} catch (err2) {
|
|
5638
|
+
console.warn(`[dtm] speech plan failed for "${text}"`, err2);
|
|
5639
|
+
return null;
|
|
5640
|
+
}
|
|
5641
|
+
})();
|
|
5642
|
+
plans.set(k, p);
|
|
5643
|
+
}
|
|
5644
|
+
return p;
|
|
5645
|
+
};
|
|
5646
|
+
const speechCache = /* @__PURE__ */ new Map();
|
|
5647
|
+
const speechKeyOf = (text, pitch, expr) => `speak|${text}|${Math.round(pitch)}${expr?.gender !== void 0 ? `|g${Math.round(expr.gender * 100)}` : ""}${expr?.breathiness !== void 0 ? `|h${Math.round(expr.breathiness * 100)}` : ""}${expr?.tension !== void 0 ? `|t${Math.round(expr.tension * 100)}` : ""}`;
|
|
5648
|
+
const SPEECH_PITCH_RATIO_MAX = 4;
|
|
5649
|
+
const speechEntryFor = (text, pitch, expr) => {
|
|
5650
|
+
const key = speechKeyOf(text, pitch, expr);
|
|
5651
|
+
let entry = speechCache.get(key);
|
|
5652
|
+
if (entry) return entry;
|
|
5653
|
+
const abort = new AbortController();
|
|
5654
|
+
let resolvePlanned;
|
|
5655
|
+
const planned = new Promise((r) => {
|
|
5656
|
+
resolvePlanned = r;
|
|
5657
|
+
});
|
|
5658
|
+
const e = {
|
|
5659
|
+
planned,
|
|
5660
|
+
rendered: Promise.resolve(),
|
|
5661
|
+
durationSec: 0,
|
|
5662
|
+
chunks: [],
|
|
5663
|
+
done: false,
|
|
5664
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
5665
|
+
abort
|
|
5666
|
+
};
|
|
5667
|
+
e.rendered = (async () => {
|
|
5668
|
+
const token = toneFor(pitch);
|
|
5669
|
+
const plan = await planFor(text, token);
|
|
5670
|
+
if (!plan) {
|
|
5671
|
+
e.done = true;
|
|
5672
|
+
resolvePlanned(null);
|
|
5673
|
+
return;
|
|
5674
|
+
}
|
|
5675
|
+
const view = toneViewFor(token);
|
|
5676
|
+
const reference = plan.timeline.reference_hz || view.referenceHz || 0;
|
|
5677
|
+
const ratio = reference > 0 ? Math.min(
|
|
5678
|
+
SPEECH_PITCH_RATIO_MAX,
|
|
5679
|
+
Math.max(
|
|
5680
|
+
1 / SPEECH_PITCH_RATIO_MAX,
|
|
5681
|
+
unitsToFreq(pitch) / reference
|
|
5682
|
+
)
|
|
5683
|
+
) : 1;
|
|
5684
|
+
const prepared = prepareSpeechPlan(plan, ratio, view.toReal);
|
|
5685
|
+
e.durationSec = speechPlanDurationSec(prepared);
|
|
5686
|
+
const leadingSec = speechPlanLeadingSec(prepared);
|
|
5687
|
+
resolvePlanned(e.durationSec);
|
|
5688
|
+
await backend.renderSpeech(
|
|
5689
|
+
prepared,
|
|
5690
|
+
expr,
|
|
5691
|
+
(chunk) => {
|
|
5692
|
+
const buf = ctx.createBuffer(1, chunk.pcm.length, KOE_SAMPLE_RATE);
|
|
5693
|
+
buf.copyToChannel(chunk.pcm, 0);
|
|
5694
|
+
const r = {
|
|
5695
|
+
audio: buf,
|
|
5696
|
+
startSec: chunk.startMs / 1e3 - leadingSec
|
|
5697
|
+
};
|
|
5698
|
+
e.chunks.push(r);
|
|
5699
|
+
for (const l of e.listeners) l(r);
|
|
5700
|
+
},
|
|
5701
|
+
abort.signal
|
|
5702
|
+
);
|
|
5703
|
+
e.done = true;
|
|
5704
|
+
e.listeners.clear();
|
|
5705
|
+
})();
|
|
5706
|
+
entry = e;
|
|
5707
|
+
speechCache.set(key, entry);
|
|
5708
|
+
return entry;
|
|
5709
|
+
};
|
|
5710
|
+
const placeSpeechChunk = (r, t0, peak, pan, reverbSend = 0, delaySend = 0, destOverride) => {
|
|
5711
|
+
const dest = destOverride ?? destination;
|
|
5712
|
+
let startAt = t0 + r.startSec;
|
|
5713
|
+
let offset = 0;
|
|
5714
|
+
const earliest = ctx.currentTime + 0.01;
|
|
5715
|
+
if (startAt < earliest) {
|
|
5716
|
+
offset = earliest - startAt;
|
|
5717
|
+
startAt = earliest;
|
|
5718
|
+
}
|
|
5719
|
+
if (offset >= r.audio.duration - 5e-3) return;
|
|
5720
|
+
let out = dest;
|
|
5721
|
+
let panner = null;
|
|
5722
|
+
if (typeof ctx.createStereoPanner === "function") {
|
|
5723
|
+
panner = ctx.createStereoPanner();
|
|
5724
|
+
panner.pan.value = Math.max(-1, Math.min(1, pan));
|
|
5725
|
+
panner.connect(dest);
|
|
5726
|
+
out = panner;
|
|
5727
|
+
}
|
|
5728
|
+
let reverbSendGain = null;
|
|
5729
|
+
if (options.reverbBus && reverbSend > 0 && panner) {
|
|
5730
|
+
reverbSendGain = ctx.createGain();
|
|
5731
|
+
reverbSendGain.gain.value = Math.max(0, Math.min(1, reverbSend));
|
|
5732
|
+
panner.connect(reverbSendGain).connect(options.reverbBus);
|
|
5733
|
+
}
|
|
5734
|
+
let delaySendGain = null;
|
|
5735
|
+
if (options.delayBus && delaySend > 0 && panner) {
|
|
5736
|
+
delaySendGain = ctx.createGain();
|
|
5737
|
+
delaySendGain.gain.value = Math.max(0, Math.min(1, delaySend));
|
|
5738
|
+
panner.connect(delaySendGain).connect(options.delayBus);
|
|
5739
|
+
}
|
|
5740
|
+
const env = ctx.createGain();
|
|
5741
|
+
if (offset > 0) {
|
|
5742
|
+
env.gain.setValueAtTime(1e-4, startAt);
|
|
5743
|
+
env.gain.exponentialRampToValueAtTime(peak, startAt + 5e-3);
|
|
5744
|
+
} else {
|
|
5745
|
+
env.gain.setValueAtTime(peak, startAt);
|
|
5746
|
+
}
|
|
5747
|
+
const src = ctx.createBufferSource();
|
|
5748
|
+
src.buffer = r.audio;
|
|
5749
|
+
src.connect(env).connect(out);
|
|
5750
|
+
src.start(startAt, offset);
|
|
5751
|
+
active.add(src);
|
|
5752
|
+
src.onended = () => {
|
|
5753
|
+
active.delete(src);
|
|
5754
|
+
src.disconnect();
|
|
5755
|
+
env.disconnect();
|
|
5756
|
+
panner?.disconnect();
|
|
5757
|
+
reverbSendGain?.disconnect();
|
|
5758
|
+
delaySendGain?.disconnect();
|
|
5759
|
+
};
|
|
5760
|
+
};
|
|
5761
|
+
const speechListeners = /* @__PURE__ */ new Set();
|
|
5762
|
+
model.speakToCache = async (text, pitch, expr, awaitRender) => {
|
|
5763
|
+
const entry = speechEntryFor(text, pitch, expr);
|
|
5764
|
+
const duration = await entry.planned;
|
|
5765
|
+
if (duration === null) return null;
|
|
5766
|
+
if (awaitRender) await entry.rendered;
|
|
5767
|
+
return speechKeyOf(text, pitch, expr);
|
|
5768
|
+
};
|
|
5769
|
+
model.scheduleSpeech = (key, t0, peak, pan, reverbSend, delaySend, dest) => {
|
|
5770
|
+
const entry = speechCache.get(key);
|
|
5771
|
+
if (!entry) return;
|
|
5772
|
+
const place = (r) => placeSpeechChunk(r, t0, peak, pan, reverbSend, delaySend, dest);
|
|
5773
|
+
for (const r of entry.chunks) place(r);
|
|
5774
|
+
if (!entry.done) {
|
|
5775
|
+
entry.listeners.add(place);
|
|
5776
|
+
const reg = { entry, listener: place };
|
|
5777
|
+
speechListeners.add(reg);
|
|
5778
|
+
void entry.rendered.then(() => speechListeners.delete(reg));
|
|
5779
|
+
}
|
|
5780
|
+
};
|
|
5781
|
+
model.speechDurationSec = (key) => {
|
|
5782
|
+
const entry = speechCache.get(key);
|
|
5783
|
+
return entry && entry.durationSec > 0 ? entry.durationSec : void 0;
|
|
5784
|
+
};
|
|
5785
|
+
const previewDurations = /* @__PURE__ */ new Map();
|
|
5786
|
+
model.planSpeech = async (text) => {
|
|
5787
|
+
const cached = previewDurations.get(text);
|
|
5788
|
+
if (cached !== void 0) return cached;
|
|
5789
|
+
const token = backend.pitchTokens.length > 0 ? nearestPitchToken(
|
|
5790
|
+
backend.pitchTokens,
|
|
5791
|
+
unitsToMidiFloat(model.speechReferenceUnits?.() ?? 0)
|
|
5792
|
+
)?.token ?? null : null;
|
|
5793
|
+
const plan = await planFor(text, token);
|
|
5794
|
+
if (!plan) return null;
|
|
5795
|
+
const d = speechPlanDurationSec(plan);
|
|
5796
|
+
previewDurations.set(text, d);
|
|
5797
|
+
return d;
|
|
5798
|
+
};
|
|
5799
|
+
model.peekSpeechDurationSec = (text) => previewDurations.get(text);
|
|
5800
|
+
model.speechReferenceUnits = () => {
|
|
5801
|
+
const tokens = backend.pitchTokens;
|
|
5802
|
+
let hz;
|
|
5803
|
+
if (tokens.length > 0) {
|
|
5804
|
+
const sorted = tokens.slice().sort((a, b) => a.midi - b.midi);
|
|
5805
|
+
hz = toneViewFor(sorted[sorted.length >> 1].token).referenceHz;
|
|
5806
|
+
} else {
|
|
5807
|
+
hz = toneViewFor(null).referenceHz;
|
|
5808
|
+
}
|
|
5809
|
+
if (!hz || hz <= 0) return void 0;
|
|
5810
|
+
return Math.round(2139 + 372 * Math.log2(hz / 440));
|
|
5811
|
+
};
|
|
4356
5812
|
model.stopAll = () => {
|
|
4357
5813
|
for (const src of active) {
|
|
4358
5814
|
try {
|
|
@@ -4362,6 +5818,9 @@ var createKoeVoice = async (ctx, destination, options) => {
|
|
|
4362
5818
|
src.disconnect();
|
|
4363
5819
|
}
|
|
4364
5820
|
active.clear();
|
|
5821
|
+
for (const { entry, listener } of speechListeners)
|
|
5822
|
+
entry.listeners.delete(listener);
|
|
5823
|
+
speechListeners.clear();
|
|
4365
5824
|
};
|
|
4366
5825
|
model.reset = () => {
|
|
4367
5826
|
prevVowel = "";
|
|
@@ -4540,7 +5999,8 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
4540
5999
|
lightweight: options.lightweight,
|
|
4541
6000
|
voiceWorkerUrl: options.voiceWorkerUrl,
|
|
4542
6001
|
reverbBus: options.reverbBus,
|
|
4543
|
-
delayBus: options.delayBus
|
|
6002
|
+
delayBus: options.delayBus,
|
|
6003
|
+
ttsBaseUrl: options.ttsBaseUrl
|
|
4544
6004
|
})
|
|
4545
6005
|
))().then((v) => {
|
|
4546
6006
|
loaded.set(m, v);
|
|
@@ -4575,6 +6035,11 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
4575
6035
|
prevVowel = "";
|
|
4576
6036
|
continue;
|
|
4577
6037
|
}
|
|
6038
|
+
if (syl.kind === "speak") {
|
|
6039
|
+
fn(note, "");
|
|
6040
|
+
prevVowel = "";
|
|
6041
|
+
continue;
|
|
6042
|
+
}
|
|
4578
6043
|
if (syl.consonant === "Q" || syl.vowel === "") continue;
|
|
4579
6044
|
fn(note, prevVowel);
|
|
4580
6045
|
if (syl.vowel && syl.vowel !== "N") prevVowel = syl.vowel;
|
|
@@ -4595,6 +6060,17 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
4595
6060
|
forEachSungNote(track, (note, prevVowel) => {
|
|
4596
6061
|
if (n >= count && note.startSec >= STREAM_LOOKAHEAD_SEC) return;
|
|
4597
6062
|
n++;
|
|
6063
|
+
if (note.syllable.kind === "speak") {
|
|
6064
|
+
tasks.push({
|
|
6065
|
+
model: m,
|
|
6066
|
+
note,
|
|
6067
|
+
prevVowel: "",
|
|
6068
|
+
pitch: note.pitch,
|
|
6069
|
+
expr,
|
|
6070
|
+
speak: note.syllable.text ?? ""
|
|
6071
|
+
});
|
|
6072
|
+
return;
|
|
6073
|
+
}
|
|
4598
6074
|
tasks.push({
|
|
4599
6075
|
model: m,
|
|
4600
6076
|
note,
|
|
@@ -4625,15 +6101,24 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
4625
6101
|
let done = 0;
|
|
4626
6102
|
onProgress?.(done, total);
|
|
4627
6103
|
const promises = tasks.map(async (task) => {
|
|
4628
|
-
|
|
4629
|
-
task.
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
6104
|
+
if (task.speak !== void 0) {
|
|
6105
|
+
await (task.model.speakToCache?.(
|
|
6106
|
+
task.speak,
|
|
6107
|
+
task.pitch,
|
|
6108
|
+
task.expr,
|
|
6109
|
+
true
|
|
6110
|
+
) ?? Promise.resolve(null));
|
|
6111
|
+
} else {
|
|
6112
|
+
await (task.model.renderToCache?.(
|
|
6113
|
+
task.note.syllable,
|
|
6114
|
+
task.prevVowel,
|
|
6115
|
+
task.pitch,
|
|
6116
|
+
task.note.durationSec * 1e3,
|
|
6117
|
+
task.vibrato,
|
|
6118
|
+
task.expr,
|
|
6119
|
+
task.pitchSegments
|
|
6120
|
+
) ?? Promise.resolve(null));
|
|
6121
|
+
}
|
|
4637
6122
|
done++;
|
|
4638
6123
|
onProgress?.(done, total);
|
|
4639
6124
|
});
|
|
@@ -4670,6 +6155,49 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
4670
6155
|
}
|
|
4671
6156
|
if (opts?.isAudible && !opts.isAudible(track)) continue;
|
|
4672
6157
|
const t0 = anchorTime + startSec;
|
|
6158
|
+
if (note.syllable.kind === "speak") {
|
|
6159
|
+
const text = note.syllable.text ?? "";
|
|
6160
|
+
if (text && model.speakToCache && model.scheduleSpeech) {
|
|
6161
|
+
const speakToCache = model.speakToCache;
|
|
6162
|
+
const scheduleSpeech = model.scheduleSpeech;
|
|
6163
|
+
const dest = options.getTrackDestination?.(track.id ?? "");
|
|
6164
|
+
const effPeak = dest ? peak * masterVolumeScalar : peak;
|
|
6165
|
+
void (async () => {
|
|
6166
|
+
const key = await speakToCache(text, note.pitch, {
|
|
6167
|
+
gender: track.gender,
|
|
6168
|
+
breathiness: track.breathiness,
|
|
6169
|
+
tension: track.tension
|
|
6170
|
+
});
|
|
6171
|
+
if (session !== streamSession || !key) return;
|
|
6172
|
+
const delay = ctx.currentTime - t0;
|
|
6173
|
+
if (delay < 1) {
|
|
6174
|
+
scheduleSpeech(
|
|
6175
|
+
key,
|
|
6176
|
+
t0,
|
|
6177
|
+
effPeak,
|
|
6178
|
+
track.pan,
|
|
6179
|
+
track.reverbSend,
|
|
6180
|
+
track.delaySend,
|
|
6181
|
+
dest
|
|
6182
|
+
);
|
|
6183
|
+
opts?.onScheduled?.(
|
|
6184
|
+
track,
|
|
6185
|
+
{
|
|
6186
|
+
...note,
|
|
6187
|
+
durationSec: model.speechDurationSec?.(key) ?? note.durationSec
|
|
6188
|
+
},
|
|
6189
|
+
t0
|
|
6190
|
+
);
|
|
6191
|
+
} else {
|
|
6192
|
+
console.warn(
|
|
6193
|
+
`[dtm] Speech late skip: \u300C${text}\u300D at ${startSec}s (delayed by ${delay.toFixed(3)}s)`
|
|
6194
|
+
);
|
|
6195
|
+
opts?.onLateSkip?.(note, delay);
|
|
6196
|
+
}
|
|
6197
|
+
})();
|
|
6198
|
+
}
|
|
6199
|
+
continue;
|
|
6200
|
+
}
|
|
4673
6201
|
const dispatchNote = (offsetUnits, peakScale) => {
|
|
4674
6202
|
const pitch = units(note.pitch + offsetUnits);
|
|
4675
6203
|
const pitchSegments = transposeSegments(
|
|
@@ -4796,6 +6324,12 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
4796
6324
|
masterGain.gain.value = g;
|
|
4797
6325
|
masterVolumeScalar = g;
|
|
4798
6326
|
};
|
|
6327
|
+
const planSpeech = async (model, text) => {
|
|
6328
|
+
const v = await load2(model);
|
|
6329
|
+
return v?.planSpeech ? v.planSpeech(text) : null;
|
|
6330
|
+
};
|
|
6331
|
+
const peekSpeechDurationSec = (model, text) => loaded.get(model.toLowerCase())?.peekSpeechDurationSec?.(text);
|
|
6332
|
+
const getSpeechReferenceUnits = (model) => loaded.get(model.toLowerCase())?.speechReferenceUnits?.();
|
|
4799
6333
|
return {
|
|
4800
6334
|
loadModels,
|
|
4801
6335
|
registerVoicebanks,
|
|
@@ -4803,7 +6337,10 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
4803
6337
|
startStream,
|
|
4804
6338
|
stopStream,
|
|
4805
6339
|
reset,
|
|
4806
|
-
setVolume
|
|
6340
|
+
setVolume,
|
|
6341
|
+
planSpeech,
|
|
6342
|
+
peekSpeechDurationSec,
|
|
6343
|
+
getSpeechReferenceUnits
|
|
4807
6344
|
};
|
|
4808
6345
|
};
|
|
4809
6346
|
var createVoiceRegistry = (models = {}, fallback = "klatt") => {
|
|
@@ -4824,7 +6361,7 @@ var NATURAL_STEPS2 = {
|
|
|
4824
6361
|
};
|
|
4825
6362
|
var isNatural = (ch) => Object.hasOwn(NATURAL_STEPS2[12], ch);
|
|
4826
6363
|
var clamp3 = (value, lo, hi) => Math.min(hi, Math.max(lo, value));
|
|
4827
|
-
var META_DIRECTIVE = /#(inst|drum|drumfont|volume|drumvolume|reverb|reverbdecay|reverbpredelay|delay|delaydiv|mastercomp|fadein|fadeout|mode|edo|loop)=([\w
|
|
6364
|
+
var META_DIRECTIVE = /#(ver|seed|compose|inst|drum|drumfont|volume|drumvolume|reverb|reverbdecay|reverbpredelay|delay|delaydiv|mastercomp|fadein|fadeout|mode|edo|loop)=([\w:.-]+)/gi;
|
|
4828
6365
|
var AUDIO_URL_DIRECTIVE = /#audio=([^\s#;\r\n]+)/gi;
|
|
4829
6366
|
var AUDIO_NUM_DIRECTIVE = /#audio(start|end|offset|at|vol)=(-?\d+(?:\.\d+)?)/gi;
|
|
4830
6367
|
var TRACK_INST_DIRECTIVE = /#t(\d+)inst=([^#;\r\n]+)/gi;
|
|
@@ -4841,6 +6378,10 @@ var parseMmlMeta = (mml) => {
|
|
|
4841
6378
|
for (const m of mml.matchAll(META_DIRECTIVE)) {
|
|
4842
6379
|
const key = m[1].toLowerCase();
|
|
4843
6380
|
if (key === "ver") meta.version = m[2];
|
|
6381
|
+
else if (key === "seed") {
|
|
6382
|
+
const n = Number.parseInt(m[2], 10);
|
|
6383
|
+
if (Number.isFinite(n) && n >= 0) meta.seed = n;
|
|
6384
|
+
} else if (key === "compose") meta.compose = m[2];
|
|
4844
6385
|
else if (key === "inst") meta.instrument = m[2];
|
|
4845
6386
|
else if (key === "drum") meta.drum = m[2];
|
|
4846
6387
|
else if (key === "drumfont") meta.drumFont = m[2];
|
|
@@ -5008,6 +6549,8 @@ var formatMmlMeta = (meta, space = "") => {
|
|
|
5008
6549
|
if (meta.mode) parts.push(`#mode=${meta.mode}`);
|
|
5009
6550
|
if (meta.edo !== void 0 && meta.edo !== 12) parts.push(`#edo=${meta.edo}`);
|
|
5010
6551
|
if (meta.loop) parts.push("#loop=on");
|
|
6552
|
+
if (meta.seed !== void 0) parts.push(`#seed=${meta.seed}`);
|
|
6553
|
+
if (meta.compose) parts.push(`#compose=${meta.compose}`);
|
|
5011
6554
|
if (meta.audio) {
|
|
5012
6555
|
parts.push(`#audio=${meta.audio}`);
|
|
5013
6556
|
if (meta.audioStart) parts.push(`#audiostart=${round3(meta.audioStart)}`);
|
|
@@ -8438,6 +9981,78 @@ var DAW_CSS = `
|
|
|
8438
9981
|
.dtm-daw *::before,
|
|
8439
9982
|
.dtm-daw *::after { box-sizing: border-box; }
|
|
8440
9983
|
|
|
9984
|
+
/* \u2500\u2500\u2500 \u30EC\u30A4\u30A2\u30A6\u30C8\uFF08\u7DE8\u96C6\u30D8\u30C3\u30C9\u306E\u8CBC\u308A\u4ED8\u3051\uFF09 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
9985
|
+
\u30D1\u30CD\u30EB\u306F\u7E26\u306B\u7A4D\u3080\u3057\u304B\u306A\u3044\u306E\u3067\u3001\u958B\u304F\u307B\u3069\u30D4\u30A2\u30CE\u30ED\u30FC\u30EB\u304C\u753B\u9762\u5916\u3078\u6D41\u308C\u3066\u3044\u304F\u3002
|
|
9986
|
+
\u30D8\u30C3\u30C9\uFF08\u30C8\u30E9\u30F3\u30B9\u30DD\u30FC\u30C8\u30FB\u30C4\u30FC\u30EB\u30FB\u30ED\u30FC\u30EB\uFF09\u3092 sticky \u3067\u753B\u9762\u4E0A\u90E8\u306B\u7559\u3081\u308C\u3070\u3001
|
|
9987
|
+
\u4F55\u679A\u958B\u3044\u3066\u3082\u30ED\u30FC\u30EB\u3068\u306E\u8DDD\u96E2\u306F0\u306E\u307E\u307E\u306B\u306A\u308B\u3002
|
|
9988
|
+
|
|
9989
|
+
\u305F\u3060\u30571\u30AB\u30E9\u30E0\u306E\u3042\u3044\u3060\u3001\u8CBC\u3063\u305F\u30D8\u30C3\u30C9\u306F\u305D\u306E\u9AD8\u3055\u3076\u3093\u753B\u9762\u3092\u5360\u6709\u3057\u7D9A\u3051\u308B\u3002
|
|
9990
|
+
\u30D8\u30C3\u30C9\u306E\u9AD8\u3055\u306F\u300C\u56FA\u5B9A\u90E8\u304A\u3088\u305D194px \uFF0B \u30ED\u30FC\u30EB32vh\u300D\u306A\u306E\u3067\u3001\u753B\u9762\u304C\u4F4E\u3044\u307B\u3069
|
|
9991
|
+
\u5360\u6709\u7387\u304C\u4E0A\u304C\u308B\uFF08844px\u306755%\u3001667px\u306764%\uFF09\u3002\u5360\u6709\u7387\u304C6\u5272\u3092\u5207\u308B\u7E26760px\u4EE5\u4E0A\u3067
|
|
9992
|
+
\u3060\u3051\u8CBC\u308A\u3001\u305D\u308C\u672A\u6E80\u306F\u5F93\u6765\u3069\u304A\u308A\u30C8\u30E9\u30F3\u30B9\u30DD\u30FC\u30C8\u3060\u3051\u3092\u8CBC\u308B\u3002
|
|
9993
|
+
\uFF08\u6A2A\u306B\u5272\u3063\u3066\u7E26\u306E\u53D6\u308A\u5408\u3044\u3092\u7121\u304F\u30592\u30AB\u30E9\u30E0\u6848\u306F\u3001\u30C7\u30E2\u306E #app \u304C max-w-4xl \u306E
|
|
9994
|
+
\u305F\u3081\u4ECA\u306E\u307E\u307E\u3060\u3068\u30ED\u30FC\u30EB\u304C\u7D30\u304F\u306A\u308B\u306E\u3067\u898B\u9001\u3063\u3066\u3044\u308B\u3002\uFF09 */
|
|
9995
|
+
.dtm-daw-panels {
|
|
9996
|
+
display: flex;
|
|
9997
|
+
flex-direction: column;
|
|
9998
|
+
gap: var(--dtm-gap);
|
|
9999
|
+
min-width: 0;
|
|
10000
|
+
}
|
|
10001
|
+
/* \u8CBC\u3089\u306A\u3044\u753B\u9762\u3067\u306F\u7BB1\u3054\u3068\u7573\u3080\u3002display:contents \u306A\u3089\u4E2D\u8EAB\u304C .dtm-daw \u306E\u76F4\u63A5\u306E
|
|
10002
|
+
\u5B50\u306B\u623B\u308B\u306E\u3067\u3001\u4F59\u767D\u3082\u30C8\u30E9\u30F3\u30B9\u30DD\u30FC\u30C8\u306E sticky \u57FA\u6E96\u3082\u5F93\u6765\u306E\u307E\u307E\u5909\u308F\u3089\u306A\u3044\u3002 */
|
|
10003
|
+
.dtm-daw-head { display: contents; }
|
|
10004
|
+
.dtm-topbar {
|
|
10005
|
+
position: sticky;
|
|
10006
|
+
top: 0;
|
|
10007
|
+
z-index: 20;
|
|
10008
|
+
}
|
|
10009
|
+
@media (min-height: 760px) {
|
|
10010
|
+
.dtm-daw-head {
|
|
10011
|
+
display: flex;
|
|
10012
|
+
flex-direction: column;
|
|
10013
|
+
gap: var(--dtm-gap);
|
|
10014
|
+
min-width: 0;
|
|
10015
|
+
position: sticky;
|
|
10016
|
+
top: 0;
|
|
10017
|
+
z-index: 20;
|
|
10018
|
+
/* \u4E0B\u3092\u304F\u3050\u308B\u30D1\u30CD\u30EB\u3092\u900F\u304B\u3055\u306A\u3044\u305F\u3081\u306E\u4E0D\u900F\u660E\u306A\u4E0B\u6577\u304D\u3002
|
|
10019
|
+
.dtm-daw \u306E gap \u3076\u3093\u3092 padding \u3067\u57CB\u3081\u3001\u9699\u9593\u304B\u3089\u4E2D\u8EAB\u3092\u898B\u305B\u306A\u3044\u3002 */
|
|
10020
|
+
background: var(--dtm-bg);
|
|
10021
|
+
padding-bottom: var(--dtm-gap);
|
|
10022
|
+
}
|
|
10023
|
+
}
|
|
10024
|
+
|
|
10025
|
+
/* \u30A8\u30C7\u30A3\u30BF\u81EA\u8EAB\u304C\u5341\u5206\u306B\u5E83\u3044\u3068\u304D\u306F\u3001\u30D8\u30C3\u30C9\u3068\u30D1\u30CD\u30EB\u3092\u5DE6\u53F3\u306B\u4E26\u3079\u3066\u3001\u7E26\u306E
|
|
10026
|
+
\u7A4D\u307F\u4E0A\u304C\u308A\u3092\u6A2A\u3078\u9003\u304C\u3059\u3002\u3053\u3046\u306A\u308B\u3068\u30D8\u30C3\u30C9\u306F\u30D1\u30CD\u30EB\u304B\u3089\u7E26\u3092\u596A\u308F\u306A\u3044\u306E\u3067\u3001
|
|
10027
|
+
\u753B\u9762\u306E\u9AD8\u3055\u306B\u95A2\u4FC2\u306A\u304F\u8CBC\u308C\u308B\u3057\u3001\u30ED\u30FC\u30EB\u3082\u5E83\u304F\u4F7F\u3048\u308B
|
|
10028
|
+
\uFF08.dtm-daw--wide \u306F daw.ts \u306E ResizeObserver \u304C\u4ED8\u3051\u308B\uFF09\u3002 */
|
|
10029
|
+
.dtm-daw--wide {
|
|
10030
|
+
flex-direction: row;
|
|
10031
|
+
align-items: flex-start;
|
|
10032
|
+
}
|
|
10033
|
+
.dtm-daw--wide .dtm-daw-head {
|
|
10034
|
+
display: flex;
|
|
10035
|
+
flex-direction: column;
|
|
10036
|
+
gap: var(--dtm-gap);
|
|
10037
|
+
min-width: 0;
|
|
10038
|
+
flex: 1 1 auto;
|
|
10039
|
+
position: sticky;
|
|
10040
|
+
top: 0;
|
|
10041
|
+
z-index: 20;
|
|
10042
|
+
background: var(--dtm-bg);
|
|
10043
|
+
padding-bottom: 0;
|
|
10044
|
+
}
|
|
10045
|
+
.dtm-daw--wide .dtm-daw-panels {
|
|
10046
|
+
flex: 0 0 360px;
|
|
10047
|
+
}
|
|
10048
|
+
/* 768px \u6642\u70B9\u306E height:420px \u306F max-height:32vh \u306B\u982D\u6253\u3061\u306B\u3055\u308C\u3066\u3044\u305F\u3002
|
|
10049
|
+
2\u30AB\u30E9\u30E0\u3067\u306F\u7E26\u3092\u53D6\u308A\u5408\u308F\u306A\u3044\u306E\u3067\u3001\u305D\u306E\u610F\u56F3\u3069\u304A\u308A\u306E\u9AD8\u3055\u3092\u89E3\u7981\u3059\u308B
|
|
10050
|
+
\uFF08\u4F4E\u3044\u753B\u9762\u3067\u306F 45vh \u3067\u982D\u6253\u3061\u306B\u3057\u3066\u753B\u9762\u3092\u98DF\u3044\u6F70\u3055\u306A\u3044\u3088\u3046\u306B\u3059\u308B\uFF09\u3002 */
|
|
10051
|
+
.dtm-daw--wide .dtm-roll {
|
|
10052
|
+
height: min(420px, 45vh);
|
|
10053
|
+
max-height: none;
|
|
10054
|
+
}
|
|
10055
|
+
|
|
8441
10056
|
/* \u2500\u2500\u2500 \u30B2\u30FC\u30E0\u30A6\u30A3\u30F3\u30C9\u30A6\u5171\u901A\u67A0 \u2500\u2500\u2500 */
|
|
8442
10057
|
/* \u5916\u67A0(\u9ED22px) \u2192 \u8272\u4ED8\u304D2px border \u2192 \u5185\u67A0(\u9ED2inset2px) \u306E3\u91CD\u69CB\u9020 */
|
|
8443
10058
|
.dtm-win {
|
|
@@ -8500,9 +10115,6 @@ var DAW_CSS = `
|
|
|
8500
10115
|
|
|
8501
10116
|
/* \u2500\u2500\u2500 \u30C8\u30E9\u30F3\u30B9\u30DD\u30FC\u30C8\u30D0\u30FC\uFF08HUD\u30B9\u30BF\u30A4\u30EB\uFF09 \u2500\u2500\u2500 */
|
|
8502
10117
|
.dtm-topbar {
|
|
8503
|
-
position: sticky;
|
|
8504
|
-
top: 0;
|
|
8505
|
-
z-index: 20;
|
|
8506
10118
|
display: flex;
|
|
8507
10119
|
flex-wrap: wrap;
|
|
8508
10120
|
align-items: center;
|
|
@@ -8947,19 +10559,17 @@ var DAW_CSS = `
|
|
|
8947
10559
|
.dtm-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
|
|
8948
10560
|
.dtm-track-body { display: flex; flex-direction: column; gap: 10px; }
|
|
8949
10561
|
|
|
8950
|
-
/* \u2500\u2500\u2500 \u81EA\u52D5\u4F5C\u66F2\
|
|
8951
|
-
.dtm-compose
|
|
8952
|
-
background: var(--dtm-deep);
|
|
8953
|
-
border: 2px solid var(--c-black);
|
|
10562
|
+
/* \u2500\u2500\u2500 \u81EA\u52D5\u4F5C\u66F2\u30D1\u30CD\u30EB\uFF08\u770B\u677F\u6A5F\u80FD\u306A\u306E\u3067\u67A0\u3068\u898B\u51FA\u3057\u3092 success \u8272\u3067\u5DEE\u5225\u5316\u3059\u308B\uFF09 \u2500\u2500\u2500 */
|
|
10563
|
+
.dtm-panel--compose {
|
|
8954
10564
|
box-shadow:
|
|
8955
|
-
inset 0 0 0
|
|
8956
|
-
0 0 0
|
|
8957
|
-
|
|
8958
|
-
|
|
8959
|
-
|
|
8960
|
-
|
|
8961
|
-
|
|
8962
|
-
|
|
10565
|
+
inset 0 0 0 2px var(--c-black),
|
|
10566
|
+
0 0 0 2px var(--dtm-success),
|
|
10567
|
+
4px 4px 0 var(--c-black);
|
|
10568
|
+
}
|
|
10569
|
+
.dtm-panel--compose > summary { color: var(--dtm-success); }
|
|
10570
|
+
.dtm-panel--compose > summary::before,
|
|
10571
|
+
.dtm-panel--compose[open] > summary::before {
|
|
10572
|
+
background: var(--dtm-success);
|
|
8963
10573
|
}
|
|
8964
10574
|
|
|
8965
10575
|
/* \u2500\u2500\u2500 \u30A2\u30AF\u30C6\u30A3\u30D6\u30C8\u30E9\u30C3\u30AF\u8272\uFF08\u500B\u5225\u30C8\u30E9\u30C3\u30AF\u8A2D\u5B9A\u30D1\u30CD\u30EB\u306E\u5DE6\u7AEF\u30E9\u30A4\u30F3\uFF09 \u2500\u2500\u2500 */
|
|
@@ -10072,6 +11682,8 @@ var DAW_CSS = `
|
|
|
10072
11682
|
/* \u4FC3\u97F3\uFF08\u3063\uFF09\u30FB\u4F11\u7B26\uFF08_\uFF09\u306F\u6B4C\u308F\u306A\u3044\u30CE\u30FC\u30C8\u3002\u3055\u3089\u306B\u843D\u3068\u3057\u3066\u7121\u97F3\u3067\u3042\u308B\u3053\u3068\u3092\u793A\u3059 */
|
|
10073
11683
|
.dtm-tk--lyric-stop,
|
|
10074
11684
|
.dtm-tk--lyric-rest { color: var(--dtm-border2); }
|
|
11685
|
+
/* \u8A9E\u308A\uFF08\u300C\u2026\u300D\uFF09\u306F\u6B4C\u308F\u305A\u306B\u8AAD\u307F\u4E0A\u3052\u308B 1 \u30CE\u30FC\u30C8\u3002\u6B4C\u8A5E\u3068\u533A\u5225\u3067\u304D\u308B\u3088\u3046\u5F37\u8ABF\u8272\u306B\u3059\u308B */
|
|
11686
|
+
.dtm-tk--lyric-speak { color: var(--dtm-primary); letter-spacing: 0; }
|
|
10075
11687
|
.dtm-tk--break { color: var(--dtm-muted); opacity: 0.7; margin: 0 2px; }
|
|
10076
11688
|
.dtm-tk--meta { color: var(--dtm-border2); margin-right: 4px; }
|
|
10077
11689
|
.dtm-tk.is-active {
|
|
@@ -21765,6 +23377,16 @@ var BASS_SKELETONS = [
|
|
|
21765
23377
|
"pedal"
|
|
21766
23378
|
];
|
|
21767
23379
|
var pick = (items, rnd) => items[Math.floor(rnd() * items.length)];
|
|
23380
|
+
var seededRandom = (seed) => {
|
|
23381
|
+
let a = seed >>> 0;
|
|
23382
|
+
return () => {
|
|
23383
|
+
a = a + 1831565813 >>> 0;
|
|
23384
|
+
let t = a;
|
|
23385
|
+
t = Math.imul(t ^ t >>> 15, t | 1);
|
|
23386
|
+
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
23387
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
23388
|
+
};
|
|
23389
|
+
};
|
|
21768
23390
|
var durationEntropy = (durations) => {
|
|
21769
23391
|
if (durations.length === 0) return 0;
|
|
21770
23392
|
const counts = /* @__PURE__ */ new Map();
|
|
@@ -24708,6 +26330,10 @@ var buildUI = (target, options) => {
|
|
|
24708
26330
|
).join("");
|
|
24709
26331
|
target.innerHTML = `
|
|
24710
26332
|
<div class="dtm-daw" data-dtm="root">
|
|
26333
|
+
<!-- \u7DE8\u96C6\u30D8\u30C3\u30C9\u3002\u30C8\u30E9\u30F3\u30B9\u30DD\u30FC\u30C8\u30FB\u30C4\u30FC\u30EB\u30FB\u30D4\u30A2\u30CE\u30ED\u30FC\u30EB\u3092\u3072\u3068\u307E\u3068\u3081\u306B\u3057\u3066
|
|
26334
|
+
\u753B\u9762\u4E0A\u90E8\u3078\u8CBC\u308A\u4ED8\u3051\u308B\uFF08.dtm-daw-head \u304C position:sticky\uFF09\u3002\u30D1\u30CD\u30EB\u3092
|
|
26335
|
+
\u3044\u304F\u3064\u958B\u3044\u3066\u3082\u30ED\u30FC\u30EB\u304C\u8996\u754C\u304B\u3089\u6D88\u3048\u306A\u3044\u3088\u3046\u306B\u3059\u308B\u305F\u3081\u306E\u7BB1\u3002 -->
|
|
26336
|
+
<div class="dtm-daw-head">
|
|
24711
26337
|
<div class="dtm-topbar" data-dtm="transport">
|
|
24712
26338
|
<div class="dtm-topbar-row1">
|
|
24713
26339
|
<button class="dtm-iconbtn" data-dtm="prev-bar" title="1\u5C0F\u7BC0\u524D">${icon("chevronLeft")}</button>
|
|
@@ -24752,6 +26378,10 @@ var buildUI = (target, options) => {
|
|
|
24752
26378
|
<div class="dtm-vscroll" data-dtm="vscroll"><div class="dtm-vscroll-thumb" data-dtm="vscroll-thumb"></div></div>
|
|
24753
26379
|
</div>
|
|
24754
26380
|
<div class="dtm-hscroll" data-dtm="hscroll"><div class="dtm-hscroll-thumb" data-dtm="hscroll-thumb"></div></div>
|
|
26381
|
+
</div>
|
|
26382
|
+
|
|
26383
|
+
<!-- \u8A2D\u5B9A\u30D1\u30CD\u30EB\u7FA4\u3002\u5E83\u3044\u753B\u9762\u3067\u306F\u30D8\u30C3\u30C9\u306E\u53F3\u96A3\u306B\u7ACB\u3064\u72EC\u7ACB\u3057\u305F\u5217\u306B\u306A\u308B\u3002 -->
|
|
26384
|
+
<div class="dtm-daw-panels">
|
|
24755
26385
|
|
|
24756
26386
|
<details class="dtm-panel" data-dtm-acc="track" open>
|
|
24757
26387
|
<summary>\u500B\u5225\u30C8\u30E9\u30C3\u30AF\u8A2D\u5B9A</summary>
|
|
@@ -24950,7 +26580,7 @@ var buildUI = (target, options) => {
|
|
|
24950
26580
|
</details>
|
|
24951
26581
|
|
|
24952
26582
|
<details class="dtm-panel ${showMidi ? "" : "dtm-hidden"}" data-dtm="midi-panel" data-dtm-acc="io-in">
|
|
24953
|
-
<summary>MIDI / UST / MML \u5165\u529B</summary>
|
|
26583
|
+
<summary>MIDI / MusicXML / UST / MML \u5165\u529B</summary>
|
|
24954
26584
|
<div class="dtm-panel-body">
|
|
24955
26585
|
<div class="dtm-row" style="flex-wrap:nowrap">
|
|
24956
26586
|
<div style="display: inline-flex; flex-direction: column; align-items: center; gap: 4px; justify-content: center; flex-shrink:0;">
|
|
@@ -24961,6 +26591,16 @@ var buildUI = (target, options) => {
|
|
|
24961
26591
|
<button class="dtm-btn dtm-btn--success" data-dtm="midi-load" style="flex-shrink:0">\u8AAD\u8FBC</button>
|
|
24962
26592
|
</div>
|
|
24963
26593
|
<div class="dtm-row dtm-hidden" data-dtm="midi-track-selection"></div>
|
|
26594
|
+
<div class="dtm-row" style="flex-wrap:nowrap">
|
|
26595
|
+
<div style="display: inline-flex; flex-direction: column; align-items: center; gap: 4px; justify-content: center; flex-shrink:0;">
|
|
26596
|
+
<span class="dtm-label" style="line-height: 1;">MusicXML</span>
|
|
26597
|
+
<button class="dtm-infobtn" data-dtm="musicxml-info" title="MusicXML\u306E\u8AAD\u307F\u8FBC\u307F\u89E3\u8AAC">${icon("info", 12)}</button>
|
|
26598
|
+
</div>
|
|
26599
|
+
<input type="file" class="dtm-input dtm-grow" accept=".musicxml,.xml" data-dtm="musicxml-input" style="min-width:0">
|
|
26600
|
+
<button class="dtm-btn dtm-btn--success" data-dtm="musicxml-load" style="flex-shrink:0">\u8AAD\u8FBC</button>
|
|
26601
|
+
</div>
|
|
26602
|
+
<div class="dtm-row dtm-hidden" data-dtm="musicxml-part-selection"></div>
|
|
26603
|
+
<p class="dtm-load-note dtm-hidden" data-dtm="musicxml-load-note"></p>
|
|
24964
26604
|
<div class="dtm-row" style="flex-wrap:nowrap">
|
|
24965
26605
|
<div style="display: inline-flex; flex-direction: column; align-items: center; gap: 4px; justify-content: center; flex-shrink:0;">
|
|
24966
26606
|
<span class="dtm-label" style="line-height: 1;">UST</span>
|
|
@@ -24990,129 +26630,135 @@ var buildUI = (target, options) => {
|
|
|
24990
26630
|
</div>
|
|
24991
26631
|
</details>
|
|
24992
26632
|
|
|
24993
|
-
<details class="dtm-panel" data-dtm-acc="
|
|
24994
|
-
<summary>\
|
|
26633
|
+
<details class="dtm-panel dtm-panel--compose ${showCompose ? "" : "dtm-hidden"}" data-dtm-acc="compose">
|
|
26634
|
+
<summary>\u81EA\u52D5\u4F5C\u66F2</summary>
|
|
24995
26635
|
<div class="dtm-panel-body">
|
|
24996
|
-
<div class="dtm-
|
|
24997
|
-
<
|
|
24998
|
-
|
|
24999
|
-
|
|
25000
|
-
|
|
25001
|
-
|
|
25002
|
-
|
|
25003
|
-
|
|
25004
|
-
|
|
25005
|
-
|
|
25006
|
-
|
|
25007
|
-
|
|
25008
|
-
|
|
25009
|
-
|
|
25010
|
-
|
|
25011
|
-
|
|
25012
|
-
|
|
25013
|
-
|
|
25014
|
-
|
|
25015
|
-
|
|
25016
|
-
|
|
25017
|
-
|
|
25018
|
-
|
|
25019
|
-
|
|
25020
|
-
</
|
|
25021
|
-
</
|
|
25022
|
-
|
|
25023
|
-
|
|
25024
|
-
|
|
25025
|
-
|
|
25026
|
-
|
|
25027
|
-
|
|
25028
|
-
|
|
25029
|
-
|
|
25030
|
-
|
|
25031
|
-
|
|
25032
|
-
|
|
25033
|
-
</
|
|
25034
|
-
<span class="dtm-grow"></span>
|
|
25035
|
-
<span class="dtm-hint" data-dtm="compose-sections-len"></span>
|
|
25036
|
-
</div>
|
|
25037
|
-
<div class="dtm-row" data-dtm="compose-key-row">
|
|
25038
|
-
<span class="dtm-label">\u30D9\u30FC\u30B9\u8ABF</span>
|
|
25039
|
-
<select class="dtm-select" data-dtm="compose-key" title="\u81EA\u52D5\u4F5C\u66F2\u306E\u30D9\u30FC\u30B9\u3068\u306A\u308B\u8ABF\u3084\u96F0\u56F2\u6C17\u3092\u9078\u3073\u307E\u3059">
|
|
25040
|
-
<option value="any" title="\u516824\u8ABF\u304B\u3089\u30E9\u30F3\u30C0\u30E0\u306B\u6C7A\u5B9A\u3057\u307E\u3059">\u5E0C\u671B\u306A\u3057</option>
|
|
25041
|
-
<optgroup label="\u57FA\u672C">
|
|
25042
|
-
<option value="major" title="12\u306E\u9577\u8ABF\u306E\u4E2D\u304B\u3089\u30E9\u30F3\u30C0\u30E0\u306B\u62BD\u9078\u3057\u307E\u3059">\u9577\u8ABF</option>
|
|
25043
|
-
<option value="minor" title="12\u306E\u77ED\u8ABF\u306E\u4E2D\u304B\u3089\u30E9\u30F3\u30C0\u30E0\u306B\u62BD\u9078\u3057\u307E\u3059">\u77ED\u8ABF</option>
|
|
25044
|
-
</optgroup>
|
|
25045
|
-
<optgroup label="\u96F0\u56F2\u6C17\u304B\u3089\u9078\u3076\uFF08\u62BD\u9078\uFF09">
|
|
25046
|
-
<option value="mood_happy" title="\u30CF\u9577\u8ABF\u30FB\u30A4\u9577\u8ABF\u30FB\u5909\u30ED\u9577\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u7121\u57A2\u306B\u559C\u3070\u3057\u3044\u3001\u7267\u6B4C\u7684\u3001\u967D\u6C17\uFF09">\u559C\u3070\u3057\u3044\u30FB\u967D\u6C17\u306A\u66F2</option>
|
|
25047
|
-
<option value="mood_triumphant" title="\u30CB\u9577\u8ABF\u30FB\u5909\u30C8\u9577\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u610F\u6C17\u63DA\u3005\u3001\u52DD\u5229\u306E\u558A\u58F0\u3001\u56F0\u96E3\u6253\u7834\u3001\u5B89\u5835\uFF09">\u52DD\u5229\u30FB\u529B\u5F37\u3044\u66F2</option>
|
|
25048
|
-
<option value="mood_fierce" title="\u30DB\u9577\u8ABF\u30FB\u30D8\u9577\u8ABF\u30FB\u30ED\u9577\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u3051\u3093\u304B\u3063\u65E9\u3044\u3001\u6012\u308A\u72C2\u3063\u305F\u8352\u3005\u3057\u3055\u3001\u3069\u304E\u3064\u304F\u731B\u70C8\uFF09">\u6FC0\u3057\u3044\u30FB\u8352\u3005\u3057\u3044\u66F2</option>
|
|
25049
|
-
<option value="mood_solemn" title="\u30C8\u9577\u8ABF\u30FB\u30CB\u77ED\u8ABF\u30FB\u30A4\u77ED\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u53B3\u7C9B\u3001\u5D07\u9AD8\u3001\u5E7B\u60F3\u3001\u656C\u8654\u3001\u601D\u7D22\u7684\uFF09">\u53B3\u7C9B\u30FB\u5E7B\u60F3\u7684\u306A\u66F2</option>
|
|
25050
|
-
<option value="mood_plaintive" title="\u30CF\u77ED\u8ABF\u30FB\u30DB\u77ED\u8ABF\u30FB\u30D8\u77ED\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u7D14\u7C8B\u306B\u60B2\u3057\u3052\u3001\u604B\u308F\u305A\u3089\u3044\u3001\u843D\u3061\u7740\u304D\u306E\u306A\u3044\u3001\u7269\u60B2\u3057\u3044\u54C0\u6101\uFF09">\u7269\u60B2\u3057\u3044\u30FB\u54C0\u6101\u306E\u66F2</option>
|
|
25051
|
-
<option value="mood_melancholy" title="\u5909\u30CB\u9577\u8ABF\u30FB\u30ED\u77ED\u8ABF\u30FB\u5B30\u30CF\u77ED\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u60B2\u3057\u307F\u3001\u6182\u9B31\u3001\u5B64\u72EC\u3001\u5FCD\u8010\u3001\u843D\u80C6\u3001\u60B2\u6D99\uFF09">\u6182\u9B31\u30FB\u5B64\u72EC\u306A\u66F2</option>
|
|
25052
|
-
<option value="mood_anxious" title="\u30C8\u77ED\u8ABF\u30FB\u5909\u30DB\u77ED\u8ABF\u30FB\u5B30\u30D8\u77ED\u8ABF\u30FB\u5909\u30A4\u77ED\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u4E0D\u6E80\u3001\u4E0D\u5B89\u3001\u6DF1\u3044\u82E6\u60A9\u3001\u9670\u6C17\u306A\u61A4\u308A\uFF09">\u4E0D\u5B89\u30FB\u82E6\u60A9\u306A\u66F2</option>
|
|
25053
|
-
<option value="mood_dark" title="\u5909\u30DB\u9577\u8ABF\u30FB\u5909\u30A4\u9577\u8ABF\u30FB\u5909\u30ED\u77ED\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u53B3\u3057\u3044\u611B\u3001\u6B7B\u3001\u6C38\u9060\u3001\u88C1\u304D\u3001\u6697\u95C7\u3001\u6050\u308D\u3057\u3044\u5632\u308A\uFF09">\u6697\u95C7\u30FB\u91CD\u539A\u306A\u66F2</option>
|
|
25054
|
-
</optgroup>
|
|
25055
|
-
<optgroup label="\u9577\u8ABF\uFF08\u500B\u5225\u6307\u5B9A\uFF09">
|
|
25056
|
-
<option value="key_C" title="\u7121\u57A2\u306B\u559C\u3070\u3057\u3044\u3001\u7D14\u7C8B\u3001\u7D20\u6734\u3001\u51FA\u767A">\u30CF\u9577\u8ABF (C)</option>
|
|
25057
|
-
<option value="key_Db" title="\u60B2\u3057\u307F\u3001\u6182\u9B31\u306A\u3001\u7518\u7F8E\u306A\u611F\u50B7">\u5909\u30CB\u9577\u8ABF (D\u266D)</option>
|
|
25058
|
-
<option value="key_D" title="\u610F\u6C17\u63DA\u3005\u3068\u3057\u305F\u3001\u52DD\u5229\u306E\u3001\u558A\u58F0">\u30CB\u9577\u8ABF (D)</option>
|
|
25059
|
-
<option value="key_Eb" title="\u53B3\u3057\u3044\u3001\u304D\u3064\u3044\u3001\u305D\u308C\u3067\u3044\u3066\u611B\u306B\u6E80\u3061\u305F">\u5909\u30DB\u9577\u8ABF (E\u266D)</option>
|
|
25060
|
-
<option value="key_E" title="\u3051\u3093\u304B\u3063\u65E9\u3044\u3001\u8352\u3005\u3057\u3044\u3001\u8F1D\u304B\u3057\u3044\u60C5\u71B1">\u30DB\u9577\u8ABF (E)</option>
|
|
25061
|
-
<option value="key_F" title="\u6012\u308A\u72C2\u3063\u305F\u3001\u6C17\u6027\u306E\u8352\u3044\u3001\u4E00\u6642\u7684\u306A\u60B2\u5606">\u30D8\u9577\u8ABF (F)</option>
|
|
25062
|
-
<option value="key_Gb" title="\u56F0\u96E3\u306E\u6253\u7834\u3001\u5B89\u5835\u306E\u305F\u3081\u606F\u3001\u51F1\u65CB">\u5909\u30C8\u9577\u8ABF (G\u266D)</option>
|
|
25063
|
-
<option value="key_G" title="\u53B3\u7C9B\u306A\u3001\u5D07\u9AD8\u306A\u3001\u5E7B\u60F3\u3001\u8AA0\u5B9F">\u30C8\u9577\u8ABF (G)</option>
|
|
25064
|
-
<option value="key_Ab" title="\u6B7B\u3001\u6C38\u9060\u3001\u88C1\u304D\u3001\u6DF1\u9060\u306A\u7791\u60F3">\u5909\u30A4\u9577\u8ABF (A\u266D)</option>
|
|
25065
|
-
<option value="key_A" title="\u3046\u308C\u3057\u3044\u3001\u7267\u6B4C\u7684\u306A\u3001\u611B\u306E\u544A\u767D">\u30A4\u9577\u8ABF (A)</option>
|
|
25066
|
-
<option value="key_Bb" title="\u559C\u3070\u3057\u3044\u3001\u98A8\u5909\u308F\u308A\u306A\u3001\u967D\u6C17\u306A\u3001\u8EFD\u5FEB">\u5909\u30ED\u9577\u8ABF (B\u266D)</option>
|
|
25067
|
-
<option value="key_B" title="\u3069\u304E\u3064\u3044\u3001\u5F37\u70C8\u306A\u3001\u8352\u3063\u307D\u3044\u3001\u731B\u70C8">\u30ED\u9577\u8ABF (B)</option>
|
|
25068
|
-
</optgroup>
|
|
25069
|
-
<optgroup label="\u77ED\u8ABF\uFF08\u500B\u5225\u6307\u5B9A\uFF09">
|
|
25070
|
-
<option value="key_Am" title="\u67D4\u3089\u304B\u306A\u3001\u7269\u60B2\u3057\u3044\u3001\u656C\u8654\u306A\u3001\u7D20\u6734\u306A\u54C0\u6101">\u30A4\u77ED\u8ABF (Am)</option>
|
|
25071
|
-
<option value="key_Bbm" title="\u6050\u308D\u3057\u3044\u3001\u6697\u95C7\u3001\u5632\u308B\u3088\u3046\u306A\u3001\u4E0D\u6C17\u5473">\u5909\u30ED\u77ED\u8ABF (B\u266Dm)</option>
|
|
25072
|
-
<option value="key_Bm" title="\u5B64\u72EC\u306A\u3001\u6182\u9B31\u306A\u3001\u5FCD\u8010\u3001\u9759\u304B\u306A\u8AE6\u5FF5">\u30ED\u77ED\u8ABF (Bm)</option>
|
|
25073
|
-
<option value="key_Cm" title="\u7D14\u7C8B\u306B\u60B2\u3057\u3052\u306A\u3001\u604B\u308F\u305A\u3089\u3044\u306E\u3001\u60B2\u5287\u7684">\u30CF\u77ED\u8ABF (Cm)</option>
|
|
25074
|
-
<option value="key_Csm" title="\u843D\u80C6\u3001\u6CE3\u304D\u53EB\u3093\u3060\u3001\u60B2\u6D99\u306E\u3001\u6DF1\u3044\u5606\u304D">\u5B30\u30CF\u77ED\u8ABF (C\u266Fm)</option>
|
|
25075
|
-
<option value="key_Dm" title="\u53B3\u7C9B\u306A\u3001\u656C\u8654\u306A\u3001\u601D\u7D22\u7684\u306A\u3001\u91CD\u539A\u306A\u7948\u308A">\u30CB\u77ED\u8ABF (Dm)</option>
|
|
25076
|
-
<option value="key_Ebm" title="\u6DF1\u3044\u82E6\u60A9\u3001\u5B9F\u5B58\u7684\u306A\u4E0D\u5B89\u3001\u6226\u6144">\u5909\u30DB\u77ED\u8ABF (E\u266Dm)</option>
|
|
25077
|
-
<option value="key_Em" title="\u5F31\u3005\u3057\u3044\u3001\u306A\u307E\u3081\u304B\u3057\u3044\u3001\u843D\u3061\u7740\u304D\u306E\u306A\u3044">\u30DB\u77ED\u8ABF (Em)</option>
|
|
25078
|
-
<option value="key_Fm" title="\u307C\u3093\u3084\u308A\u3057\u305F\u3001\u7269\u60B2\u3057\u3044\u3001\u3057\u3081\u3084\u304B\u306A\u3001\u846C\u9001">\u30D8\u77ED\u8ABF (Fm)</option>
|
|
25079
|
-
<option value="key_Fsm" title="\u9670\u6C17\u306A\u3001\u6FC0\u3057\u3044\u61A4\u308A\u3001\u6697\u3044\u60C5\u5FF5">\u5B30\u30D8\u77ED\u8ABF (F\u266Fm)</option>
|
|
25080
|
-
<option value="key_Gm" title="\u4E0D\u6E80\u3001\u4E0D\u5B89\u3001\u3084\u308B\u305B\u306A\u3055\u3001\u60B2\u75DB\u306A\u53EB\u3073">\u30C8\u77ED\u8ABF (Gm)</option>
|
|
25081
|
-
<option value="key_Abm" title="\u4E0D\u670D\u306A\u3001\u5606\u304D\u306E\u3001\u6CE3\u304D\u53EB\u3093\u3060">\u5909\u30A4\u77ED\u8ABF (A\u266Dm)</option>
|
|
25082
|
-
</optgroup>
|
|
25083
|
-
</select>
|
|
25084
|
-
<span class="dtm-grow"></span>
|
|
25085
|
-
<span class="dtm-hint" data-dtm="compose-key-hint"></span>
|
|
25086
|
-
</div>
|
|
25087
|
-
<div class="dtm-row" data-dtm="compose-scale-row">
|
|
25088
|
-
<span class="dtm-label">\u97F3\u968E</span>
|
|
25089
|
-
<select class="dtm-select" data-dtm="compose-scale" title="\u65CB\u5F8B\u304C\u4F7F\u3046\u97F3\u968E\u3092\u9078\u3073\u307E\u3059\u3002\u30D9\u30FC\u30B9\u8ABF\uFF08\u4E3B\u97F3\u306E\u9AD8\u3055\uFF09\u3068\u306F\u72EC\u7ACB\u3057\u305F\u8A2D\u5B9A\u3067\u3059">
|
|
25090
|
-
<option value="auto" title="\u30D9\u30FC\u30B9\u8ABF\u306E\u9577\u77ED\u306B\u5408\u308F\u305B\u3066\u3001\u967D\u97F3\u968E\uFF08\u9577\u8ABF\uFF09\u304B\u6C11\u8B21\u97F3\u968E\uFF08\u77ED\u8ABF\uFF09\u3092\u4F7F\u3044\u307E\u3059">\u304A\u307E\u304B\u305B\uFF08\u5F93\u6765\u3069\u304A\u308A\uFF09</option>
|
|
25091
|
-
<option value="any" title="9\u3064\u306E\u97F3\u968E\u304B\u3089\u30E9\u30F3\u30C0\u30E0\u306B\u62BD\u9078\u3057\u307E\u3059">\u5E0C\u671B\u306A\u3057\uFF08\u5168\u97F3\u968E\u304B\u3089\u62BD\u9078\uFF09</option>
|
|
25092
|
-
<optgroup label="\u30DA\u30F3\u30BF\u30C8\u30CB\u30C3\u30AF\uFF085\u97F3\u97F3\u968E\uFF09">
|
|
25093
|
-
<option value="yo" title="J-POP\u306E\u6A19\u6E96\u3002\u660E\u308B\u304F\u7D20\u76F4\u3067\u6B4C\u3044\u3084\u3059\u3044\u3002\u5F93\u6765\u306E\u9577\u8ABF\u3068\u540C\u3058">\u967D\u97F3\u968E\uFF08\u9577\u8ABF\u30DA\u30F3\u30BF\uFF09</option>
|
|
25094
|
-
<option value="minyo" title="\u308F\u3089\u3079\u6B4C\u30FB\u6C11\u8B21\u306E\u97F3\u968E\u3002\u7FF3\u308A\u304C\u3042\u308B\u304C\u6697\u3059\u304E\u306A\u3044\u3002\u5F93\u6765\u306E\u77ED\u8ABF\u3068\u540C\u3058">\u6C11\u8B21\u97F3\u968E\uFF08\u77ED\u8ABF\u30DA\u30F3\u30BF\uFF09</option>
|
|
25095
|
-
<option value="ryukyu" title="\u6C96\u7E04\u97F3\u968E\u3002\u30EC\u3068\u30E9\u3092\u629C\u304D\u3001\u30D5\u30A1\u3068\u30B7\u3092\u67F1\u306B\u3059\u308B\u3002\u660E\u308B\u304F\u8DF3\u306D\u308B">\u7409\u7403\u97F3\u968E\uFF08\u6C96\u7E04\uFF09</option>
|
|
25096
|
-
<option value="miyakobushi" title="\u300E\u3055\u304F\u3089\u3055\u304F\u3089\u300F\u306E\u97F3\u968E\u3002\u4E3B\u97F3\u306E\u3059\u3050\u4E0A\u304C\u534A\u97F3\u3067\u3001\u7FF3\u308A\u304C\u6FC3\u3044">\u90FD\u7BC0\u97F3\u968E\uFF08\u9670\u97F3\u968E\uFF09</option>
|
|
25097
|
-
<option value="ritsu" title="\u96C5\u697D\u30FB\u58F0\u660E\u306E\u97F3\u968E\u3002\u534A\u97F3\u3092\u542B\u307E\u305A\u3001\u5E73\u3089\u3067\u8358\u91CD\u306B\u6D41\u308C\u308B">\u5F8B\u97F3\u968E\uFF08\u96C5\u697D\uFF09</option>
|
|
25098
|
-
</optgroup>
|
|
25099
|
-
<optgroup label="\u30C1\u30E3\u30FC\u30C1\u30E2\u30FC\u30C9\uFF087\u97F3\u97F3\u968E\uFF09">
|
|
25100
|
-
<option value="dorian" title="\u77ED\u8ABF\u3060\u304C6\u5EA6\u304C\u660E\u308B\u3044\u3002\u30B1\u30EB\u30C8\u30FB\u30ED\u30C3\u30AF\u30FB\u30B7\u30C6\u30A3\u30DD\u30C3\u30D7">\u30C9\u30EA\u30A2\u30F3</option>
|
|
25101
|
-
<option value="phrygian" title="\u4E3B\u97F3\u306E\u4E0A\u304C\u534A\u97F3\u3002\u30B9\u30D1\u30CB\u30C3\u30B7\u30E5\uFF0F\u30E1\u30BF\u30EB\u306E\u7DCA\u8FEB\u3057\u305F\u97FF\u304D">\u30D5\u30EA\u30B8\u30A2\u30F3</option>
|
|
25102
|
-
<option value="lydian" title="4\u5EA6\u304C\u9AD8\u304F\u3001\u6D6E\u904A\u3057\u3066\u5E83\u304C\u308B\u3002\u6620\u753B\u97F3\u697D\u30FB\u30B2\u30FC\u30E0\u306E\u7A7A\u306E\u8272">\u30EA\u30C7\u30A3\u30A2\u30F3</option>
|
|
25103
|
-
<option value="mixolydian" title="\u9577\u8ABF\u3060\u304C7\u5EA6\u304C\u4F4E\u3044\u3002\u30D6\u30EB\u30FC\u30B9\u30ED\u30C3\u30AF\u30FB\u6C11\u65CF\u97F3\u697D\u306E\u571F\u304F\u3055\u3055">\u30DF\u30AF\u30BD\u30EA\u30C7\u30A3\u30A2\u30F3</option>
|
|
25104
|
-
</optgroup>
|
|
25105
|
-
<optgroup label="\u7279\u6B8A\u97F3\u968E\uFF08\u97F3\u7A0B\u96C6\u5408\u3054\u3068\u5165\u308C\u66FF\u308F\u308B\uFF09">
|
|
25106
|
-
<option value="harmonic_minor" title="\u5C0E\u97F3\u30BD\u266F\u3092\u6301\u3064\u77ED\u8ABF\u3002\u58972\u5EA6\u304C\u6CE3\u304D\u3092\u4F5C\u308B\u3002\u30AF\u30E9\u30B7\u30C3\u30AF\u30FBV\u7CFB\u30FB\u5287\u4F34">\u548C\u58F0\u7684\u77ED\u97F3\u968E</option>
|
|
25107
|
-
<option value="hijaz" title="\u4E3B\u97F3\u306E\u4E0A\u304C\u534A\u97F3\u3001\u4E3B\u548C\u97F3\u306F\u9577\u4E09\u548C\u97F3\u3002\u4E2D\u6771\u30FB\u30B9\u30D1\u30CB\u30C3\u30B7\u30E5\u30FB\u30E1\u30BF\u30EB">\u30D2\u30B8\u30E3\u30FC\u30BA\uFF08\u30D5\u30EA\u30B8\u30A2\u30F3\u30FB\u30C9\u30DF\u30CA\u30F3\u30C8\uFF09</option>
|
|
25108
|
-
<option value="hungarian" title="\u58972\u5EA6\u304C2\u304B\u6240\u3002\u97F3\u968E\u306E\u4E2D\u3067\u3044\u3061\u3070\u3093\u8DF3\u306D\u305F\u3001\u7570\u56FD\u3081\u3044\u305F\u97FF\u304D">\u30CF\u30F3\u30AC\u30EA\u30A2\u30F3\u30FB\u30DE\u30A4\u30CA\u30FC\uFF08\u30B8\u30D7\u30B7\u30FC\uFF09</option>
|
|
25109
|
-
<option value="blues" title="\u30D6\u30EB\u30FC\u30CE\u30FC\u30C8\u5165\u308A\u306E6\u97F3\u97F3\u968E\u3002\u77ED3\u5EA6\u3067\u6B4C\u3044\u3001\u4F34\u594F\u306F\u95773\u5EA6\u3067\u9CF4\u308B">\u30D6\u30EB\u30FC\u30B9\u97F3\u968E</option>
|
|
25110
|
-
</optgroup>
|
|
25111
|
-
</select>
|
|
25112
|
-
<span class="dtm-grow"></span>
|
|
25113
|
-
<span class="dtm-hint" data-dtm="compose-scale-hint"></span>
|
|
26636
|
+
<div class="dtm-row" data-dtm="compose-row">
|
|
26637
|
+
<button class="dtm-btn dtm-btn--success" data-dtm="macro-compose" title="\u30B3\u30FC\u30C9\u9032\u884C\u30FB\u30E1\u30ED\u30C7\u30A3\u30FB\u30B5\u30D6\u30E1\u30ED\u30FB\u30D9\u30FC\u30B9\u30FB\u4F34\u594F\u30FB\u30C9\u30E9\u30E0\u3092\u81EA\u52D5\u3067\u4F5C\u308A\u307E\u3059">\u4F5C\u66F2</button>
|
|
26638
|
+
<button class="dtm-btn dtm-btn--success" data-dtm="macro-compose-vocal" title="\u4F5C\u66F2\u3057\u305F\u3046\u3048\u3067\u3001\u30E1\u30ED\u30C7\u30A3\u306B\u6B4C\u8A5E\u3092\u4ED8\u3051\u3066\u6B4C\u308F\u305B\u307E\u3059">\u6B4C\u5165\u308A\u4F5C\u66F2</button>
|
|
26639
|
+
<button class="dtm-infobtn" data-dtm="macro-compose-info" title="\u4F5C\u66F2\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
|
|
26640
|
+
<!--
|
|
26641
|
+
**\u30AD\u30FC\u30D7\u67A0\u306F1\u3064\u3060\u3051\u3002** \u81EA\u52D5\u4F5C\u66F2\u306F\u6C17\u306B\u5165\u308B\u307E\u3067\u5F15\u304D\u76F4\u3059\u4F7F\u3044\u65B9\u306B\u306A\u308B\u304C\u3001
|
|
26642
|
+
\u300C\u5F15\u304D\u76F4\u3059\u3068\u4ECA\u306E\u304C\u6D88\u3048\u308B\u300D\u3068\u601D\u3046\u3068\u5F15\u304D\u76F4\u305B\u306A\u304F\u306A\u308B\u3002\u53D6\u3063\u3066\u304A\u3051\u308B\u5834\u6240\u304C
|
|
26643
|
+
1\u3064\u3042\u308C\u3070\u30012\u3064\u3092\u6BD4\u3079\u3066\u9078\u3076\u3053\u3068\u306F\u6210\u7ACB\u3059\u308B\u3002\u5019\u88DC\u3092\u4E26\u3079\u308BUI\u306F\u30B9\u30DE\u30DB\u3067\u306F
|
|
26644
|
+
\u6210\u7ACB\u3057\u306A\u3044\uFF08\u8A66\u8074\u6642\u9593\u30FB\u753B\u9762\u30FB\u751F\u6210\u30B3\u30B9\u30C8\u306E\u3069\u308C\u3082\u8DB3\u308A\u306A\u3044\uFF09\u3002
|
|
26645
|
+
\u300C\u5165\u308C\u66FF\u3048\u300D\u306F\u4ECA\u306E\u66F2\u3068\u30AD\u30FC\u30D7\u3092\u4EA4\u63DB\u3059\u308B\u3002\u547C\u3073\u51FA\u3059\u3060\u3051\u3060\u3068\u4ECA\u306E\u66F2\u304C\u6D88\u3048\u3066
|
|
26646
|
+
2\u66F2\u3092\u884C\u304D\u6765\u3067\u304D\u306A\u3044\u305F\u3081\u3002\u67A0\u306F\u30EA\u30ED\u30FC\u30C9\u3092\u307E\u305F\u3044\u3067\u6B8B\u308B\uFF08localStorage\uFF09\u3002
|
|
26647
|
+
-->
|
|
26648
|
+
<button class="dtm-btn" data-dtm="compose-keep" title="\u4ECA\u306E\u66F2\u30921\u3064\u3060\u3051\u53D6\u3063\u3066\u304A\u304D\u307E\u3059\u3002\u4F5C\u66F2\u3092\u62BC\u3057\u76F4\u3057\u3066\u3082\u3001\u30DA\u30FC\u30B8\u3092\u958B\u304D\u76F4\u3057\u3066\u3082\u6D88\u3048\u307E\u305B\u3093">\u30AD\u30FC\u30D7</button>
|
|
26649
|
+
<button class="dtm-btn" data-dtm="compose-recall" title="\u30AD\u30FC\u30D7\u3057\u305F\u66F2\u3068\u4ECA\u306E\u66F2\u3092\u5165\u308C\u66FF\u3048\u3066\u3001\u30AD\u30FC\u30D7\u3057\u3066\u3044\u305F\u66F2\u3092\u9CF4\u3089\u3057\u307E\u3059\u3002\u3082\u3046\u4E00\u5EA6\u62BC\u3059\u3068\u623B\u308A\u307E\u3059" disabled>\u5165\u308C\u66FF\u3048</button>
|
|
26650
|
+
<span class="dtm-grow"></span>
|
|
26651
|
+
</div>
|
|
26652
|
+
<div class="dtm-row" data-dtm="compose-template-row">
|
|
26653
|
+
<span class="dtm-label">\u69CB\u6210</span>
|
|
26654
|
+
<select class="dtm-select" data-dtm="compose-template" title="J-POP\u738B\u9053\u306A\u3069\u306E\u30D7\u30EA\u30BB\u30C3\u30C8\u69CB\u6210\u3092\u9078\u3073\u307E\u3059">
|
|
26655
|
+
<option value="custom">\u81EA\u7531\u9078\u629E\uFF08\u4E0B\u8A18\u30C1\u30A7\u30C3\u30AF\uFF09</option>
|
|
26656
|
+
<option value="1chorus">1\u30B3\u30FC\u30E9\u30B9\uFF08\u77ED\u3081\u30FB\u521D\u5FC3\u8005\u5411\u3051\uFF09</option>
|
|
26657
|
+
<option value="jpop_standard">JPOP\u738B\u9053\uFF08\u30DE\u30EA\u30FC\u30B4\u30FC\u30EB\u30C9\u578B 2\u756A/C\u30E1\u30ED/\u30E9\u30B9\u30B5\u30D3\uFF09</option>
|
|
26658
|
+
<option value="jpop_drop">\u843D\u3061\u30B5\u30D3\u5165\u308A\uFF08JPOP\u738B\u9053 + \u30E9\u30B9\u30B5\u30D3\u524D\u843D\u3061\u30B5\u30D3\uFF09</option>
|
|
26659
|
+
<option value="vocaloid">\u30DC\u30AB\u30ED\u738B\u9053\uFF08\u75BE\u8D70\u30FB2\u756A/C\u30E1\u30ED/\u30E9\u30B9\u30B5\u30D3\uFF09</option>
|
|
26660
|
+
<option value="verse_chorus">Verse-Chorus\uFF08B\u30E1\u30ED\u306A\u3057\u30FB\u6D0B\u697D\u98A8\uFF09</option>
|
|
26661
|
+
</select>
|
|
26662
|
+
</div>
|
|
26663
|
+
<div class="dtm-row" data-dtm="compose-sections-row">
|
|
26664
|
+
<span class="dtm-label">\u4F5C\u308B\u90E8\u5206</span>
|
|
26665
|
+
<div class="dtm-checks" data-dtm="compose-sections">
|
|
26666
|
+
<label class="dtm-check"><input type="checkbox" value="intro" checked>\u30A4\u30F3\u30C8\u30ED</label>
|
|
26667
|
+
<label class="dtm-check"><input type="checkbox" value="verse" checked>A\u30E1\u30ED</label>
|
|
26668
|
+
<label class="dtm-check"><input type="checkbox" value="prechorus" checked>B\u30E1\u30ED</label>
|
|
26669
|
+
<label class="dtm-check"><input type="checkbox" value="chorus" checked>\u30B5\u30D3</label>
|
|
26670
|
+
<label class="dtm-check"><input type="checkbox" value="bridge">C\u30E1\u30ED</label>
|
|
26671
|
+
<label class="dtm-check"><input type="checkbox" value="drop_chorus">\u843D\u3061\u30B5\u30D3</label>
|
|
26672
|
+
<label class="dtm-check"><input type="checkbox" value="interlude">\u9593\u594F</label>
|
|
26673
|
+
<label class="dtm-check"><input type="checkbox" value="outro">\u30A2\u30A6\u30C8\u30ED</label>
|
|
25114
26674
|
</div>
|
|
26675
|
+
<span class="dtm-grow"></span>
|
|
26676
|
+
<span class="dtm-hint" data-dtm="compose-sections-len"></span>
|
|
26677
|
+
</div>
|
|
26678
|
+
<div class="dtm-row" data-dtm="compose-key-row">
|
|
26679
|
+
<span class="dtm-label">\u30D9\u30FC\u30B9\u8ABF</span>
|
|
26680
|
+
<select class="dtm-select" data-dtm="compose-key" title="\u81EA\u52D5\u4F5C\u66F2\u306E\u30D9\u30FC\u30B9\u3068\u306A\u308B\u8ABF\u3084\u96F0\u56F2\u6C17\u3092\u9078\u3073\u307E\u3059">
|
|
26681
|
+
<option value="any" title="\u516824\u8ABF\u304B\u3089\u30E9\u30F3\u30C0\u30E0\u306B\u6C7A\u5B9A\u3057\u307E\u3059">\u5E0C\u671B\u306A\u3057</option>
|
|
26682
|
+
<optgroup label="\u57FA\u672C">
|
|
26683
|
+
<option value="major" title="12\u306E\u9577\u8ABF\u306E\u4E2D\u304B\u3089\u30E9\u30F3\u30C0\u30E0\u306B\u62BD\u9078\u3057\u307E\u3059">\u9577\u8ABF</option>
|
|
26684
|
+
<option value="minor" title="12\u306E\u77ED\u8ABF\u306E\u4E2D\u304B\u3089\u30E9\u30F3\u30C0\u30E0\u306B\u62BD\u9078\u3057\u307E\u3059">\u77ED\u8ABF</option>
|
|
26685
|
+
</optgroup>
|
|
26686
|
+
<optgroup label="\u96F0\u56F2\u6C17\u304B\u3089\u9078\u3076\uFF08\u62BD\u9078\uFF09">
|
|
26687
|
+
<option value="mood_happy" title="\u30CF\u9577\u8ABF\u30FB\u30A4\u9577\u8ABF\u30FB\u5909\u30ED\u9577\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u7121\u57A2\u306B\u559C\u3070\u3057\u3044\u3001\u7267\u6B4C\u7684\u3001\u967D\u6C17\uFF09">\u559C\u3070\u3057\u3044\u30FB\u967D\u6C17\u306A\u66F2</option>
|
|
26688
|
+
<option value="mood_triumphant" title="\u30CB\u9577\u8ABF\u30FB\u5909\u30C8\u9577\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u610F\u6C17\u63DA\u3005\u3001\u52DD\u5229\u306E\u558A\u58F0\u3001\u56F0\u96E3\u6253\u7834\u3001\u5B89\u5835\uFF09">\u52DD\u5229\u30FB\u529B\u5F37\u3044\u66F2</option>
|
|
26689
|
+
<option value="mood_fierce" title="\u30DB\u9577\u8ABF\u30FB\u30D8\u9577\u8ABF\u30FB\u30ED\u9577\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u3051\u3093\u304B\u3063\u65E9\u3044\u3001\u6012\u308A\u72C2\u3063\u305F\u8352\u3005\u3057\u3055\u3001\u3069\u304E\u3064\u304F\u731B\u70C8\uFF09">\u6FC0\u3057\u3044\u30FB\u8352\u3005\u3057\u3044\u66F2</option>
|
|
26690
|
+
<option value="mood_solemn" title="\u30C8\u9577\u8ABF\u30FB\u30CB\u77ED\u8ABF\u30FB\u30A4\u77ED\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u53B3\u7C9B\u3001\u5D07\u9AD8\u3001\u5E7B\u60F3\u3001\u656C\u8654\u3001\u601D\u7D22\u7684\uFF09">\u53B3\u7C9B\u30FB\u5E7B\u60F3\u7684\u306A\u66F2</option>
|
|
26691
|
+
<option value="mood_plaintive" title="\u30CF\u77ED\u8ABF\u30FB\u30DB\u77ED\u8ABF\u30FB\u30D8\u77ED\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u7D14\u7C8B\u306B\u60B2\u3057\u3052\u3001\u604B\u308F\u305A\u3089\u3044\u3001\u843D\u3061\u7740\u304D\u306E\u306A\u3044\u3001\u7269\u60B2\u3057\u3044\u54C0\u6101\uFF09">\u7269\u60B2\u3057\u3044\u30FB\u54C0\u6101\u306E\u66F2</option>
|
|
26692
|
+
<option value="mood_melancholy" title="\u5909\u30CB\u9577\u8ABF\u30FB\u30ED\u77ED\u8ABF\u30FB\u5B30\u30CF\u77ED\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u60B2\u3057\u307F\u3001\u6182\u9B31\u3001\u5B64\u72EC\u3001\u5FCD\u8010\u3001\u843D\u80C6\u3001\u60B2\u6D99\uFF09">\u6182\u9B31\u30FB\u5B64\u72EC\u306A\u66F2</option>
|
|
26693
|
+
<option value="mood_anxious" title="\u30C8\u77ED\u8ABF\u30FB\u5909\u30DB\u77ED\u8ABF\u30FB\u5B30\u30D8\u77ED\u8ABF\u30FB\u5909\u30A4\u77ED\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u4E0D\u6E80\u3001\u4E0D\u5B89\u3001\u6DF1\u3044\u82E6\u60A9\u3001\u9670\u6C17\u306A\u61A4\u308A\uFF09">\u4E0D\u5B89\u30FB\u82E6\u60A9\u306A\u66F2</option>
|
|
26694
|
+
<option value="mood_dark" title="\u5909\u30DB\u9577\u8ABF\u30FB\u5909\u30A4\u9577\u8ABF\u30FB\u5909\u30ED\u77ED\u8ABF\u304B\u3089\u62BD\u9078\uFF08\u53B3\u3057\u3044\u611B\u3001\u6B7B\u3001\u6C38\u9060\u3001\u88C1\u304D\u3001\u6697\u95C7\u3001\u6050\u308D\u3057\u3044\u5632\u308A\uFF09">\u6697\u95C7\u30FB\u91CD\u539A\u306A\u66F2</option>
|
|
26695
|
+
</optgroup>
|
|
26696
|
+
<optgroup label="\u9577\u8ABF\uFF08\u500B\u5225\u6307\u5B9A\uFF09">
|
|
26697
|
+
<option value="key_C" title="\u7121\u57A2\u306B\u559C\u3070\u3057\u3044\u3001\u7D14\u7C8B\u3001\u7D20\u6734\u3001\u51FA\u767A">\u30CF\u9577\u8ABF (C)</option>
|
|
26698
|
+
<option value="key_Db" title="\u60B2\u3057\u307F\u3001\u6182\u9B31\u306A\u3001\u7518\u7F8E\u306A\u611F\u50B7">\u5909\u30CB\u9577\u8ABF (D\u266D)</option>
|
|
26699
|
+
<option value="key_D" title="\u610F\u6C17\u63DA\u3005\u3068\u3057\u305F\u3001\u52DD\u5229\u306E\u3001\u558A\u58F0">\u30CB\u9577\u8ABF (D)</option>
|
|
26700
|
+
<option value="key_Eb" title="\u53B3\u3057\u3044\u3001\u304D\u3064\u3044\u3001\u305D\u308C\u3067\u3044\u3066\u611B\u306B\u6E80\u3061\u305F">\u5909\u30DB\u9577\u8ABF (E\u266D)</option>
|
|
26701
|
+
<option value="key_E" title="\u3051\u3093\u304B\u3063\u65E9\u3044\u3001\u8352\u3005\u3057\u3044\u3001\u8F1D\u304B\u3057\u3044\u60C5\u71B1">\u30DB\u9577\u8ABF (E)</option>
|
|
26702
|
+
<option value="key_F" title="\u6012\u308A\u72C2\u3063\u305F\u3001\u6C17\u6027\u306E\u8352\u3044\u3001\u4E00\u6642\u7684\u306A\u60B2\u5606">\u30D8\u9577\u8ABF (F)</option>
|
|
26703
|
+
<option value="key_Gb" title="\u56F0\u96E3\u306E\u6253\u7834\u3001\u5B89\u5835\u306E\u305F\u3081\u606F\u3001\u51F1\u65CB">\u5909\u30C8\u9577\u8ABF (G\u266D)</option>
|
|
26704
|
+
<option value="key_G" title="\u53B3\u7C9B\u306A\u3001\u5D07\u9AD8\u306A\u3001\u5E7B\u60F3\u3001\u8AA0\u5B9F">\u30C8\u9577\u8ABF (G)</option>
|
|
26705
|
+
<option value="key_Ab" title="\u6B7B\u3001\u6C38\u9060\u3001\u88C1\u304D\u3001\u6DF1\u9060\u306A\u7791\u60F3">\u5909\u30A4\u9577\u8ABF (A\u266D)</option>
|
|
26706
|
+
<option value="key_A" title="\u3046\u308C\u3057\u3044\u3001\u7267\u6B4C\u7684\u306A\u3001\u611B\u306E\u544A\u767D">\u30A4\u9577\u8ABF (A)</option>
|
|
26707
|
+
<option value="key_Bb" title="\u559C\u3070\u3057\u3044\u3001\u98A8\u5909\u308F\u308A\u306A\u3001\u967D\u6C17\u306A\u3001\u8EFD\u5FEB">\u5909\u30ED\u9577\u8ABF (B\u266D)</option>
|
|
26708
|
+
<option value="key_B" title="\u3069\u304E\u3064\u3044\u3001\u5F37\u70C8\u306A\u3001\u8352\u3063\u307D\u3044\u3001\u731B\u70C8">\u30ED\u9577\u8ABF (B)</option>
|
|
26709
|
+
</optgroup>
|
|
26710
|
+
<optgroup label="\u77ED\u8ABF\uFF08\u500B\u5225\u6307\u5B9A\uFF09">
|
|
26711
|
+
<option value="key_Am" title="\u67D4\u3089\u304B\u306A\u3001\u7269\u60B2\u3057\u3044\u3001\u656C\u8654\u306A\u3001\u7D20\u6734\u306A\u54C0\u6101">\u30A4\u77ED\u8ABF (Am)</option>
|
|
26712
|
+
<option value="key_Bbm" title="\u6050\u308D\u3057\u3044\u3001\u6697\u95C7\u3001\u5632\u308B\u3088\u3046\u306A\u3001\u4E0D\u6C17\u5473">\u5909\u30ED\u77ED\u8ABF (B\u266Dm)</option>
|
|
26713
|
+
<option value="key_Bm" title="\u5B64\u72EC\u306A\u3001\u6182\u9B31\u306A\u3001\u5FCD\u8010\u3001\u9759\u304B\u306A\u8AE6\u5FF5">\u30ED\u77ED\u8ABF (Bm)</option>
|
|
26714
|
+
<option value="key_Cm" title="\u7D14\u7C8B\u306B\u60B2\u3057\u3052\u306A\u3001\u604B\u308F\u305A\u3089\u3044\u306E\u3001\u60B2\u5287\u7684">\u30CF\u77ED\u8ABF (Cm)</option>
|
|
26715
|
+
<option value="key_Csm" title="\u843D\u80C6\u3001\u6CE3\u304D\u53EB\u3093\u3060\u3001\u60B2\u6D99\u306E\u3001\u6DF1\u3044\u5606\u304D">\u5B30\u30CF\u77ED\u8ABF (C\u266Fm)</option>
|
|
26716
|
+
<option value="key_Dm" title="\u53B3\u7C9B\u306A\u3001\u656C\u8654\u306A\u3001\u601D\u7D22\u7684\u306A\u3001\u91CD\u539A\u306A\u7948\u308A">\u30CB\u77ED\u8ABF (Dm)</option>
|
|
26717
|
+
<option value="key_Ebm" title="\u6DF1\u3044\u82E6\u60A9\u3001\u5B9F\u5B58\u7684\u306A\u4E0D\u5B89\u3001\u6226\u6144">\u5909\u30DB\u77ED\u8ABF (E\u266Dm)</option>
|
|
26718
|
+
<option value="key_Em" title="\u5F31\u3005\u3057\u3044\u3001\u306A\u307E\u3081\u304B\u3057\u3044\u3001\u843D\u3061\u7740\u304D\u306E\u306A\u3044">\u30DB\u77ED\u8ABF (Em)</option>
|
|
26719
|
+
<option value="key_Fm" title="\u307C\u3093\u3084\u308A\u3057\u305F\u3001\u7269\u60B2\u3057\u3044\u3001\u3057\u3081\u3084\u304B\u306A\u3001\u846C\u9001">\u30D8\u77ED\u8ABF (Fm)</option>
|
|
26720
|
+
<option value="key_Fsm" title="\u9670\u6C17\u306A\u3001\u6FC0\u3057\u3044\u61A4\u308A\u3001\u6697\u3044\u60C5\u5FF5">\u5B30\u30D8\u77ED\u8ABF (F\u266Fm)</option>
|
|
26721
|
+
<option value="key_Gm" title="\u4E0D\u6E80\u3001\u4E0D\u5B89\u3001\u3084\u308B\u305B\u306A\u3055\u3001\u60B2\u75DB\u306A\u53EB\u3073">\u30C8\u77ED\u8ABF (Gm)</option>
|
|
26722
|
+
<option value="key_Abm" title="\u4E0D\u670D\u306A\u3001\u5606\u304D\u306E\u3001\u6CE3\u304D\u53EB\u3093\u3060">\u5909\u30A4\u77ED\u8ABF (A\u266Dm)</option>
|
|
26723
|
+
</optgroup>
|
|
26724
|
+
</select>
|
|
26725
|
+
<span class="dtm-grow"></span>
|
|
26726
|
+
<span class="dtm-hint" data-dtm="compose-key-hint"></span>
|
|
26727
|
+
</div>
|
|
26728
|
+
<div class="dtm-row" data-dtm="compose-scale-row">
|
|
26729
|
+
<span class="dtm-label">\u97F3\u968E</span>
|
|
26730
|
+
<select class="dtm-select" data-dtm="compose-scale" title="\u65CB\u5F8B\u304C\u4F7F\u3046\u97F3\u968E\u3092\u9078\u3073\u307E\u3059\u3002\u30D9\u30FC\u30B9\u8ABF\uFF08\u4E3B\u97F3\u306E\u9AD8\u3055\uFF09\u3068\u306F\u72EC\u7ACB\u3057\u305F\u8A2D\u5B9A\u3067\u3059">
|
|
26731
|
+
<option value="auto" title="\u30D9\u30FC\u30B9\u8ABF\u306E\u9577\u77ED\u306B\u5408\u308F\u305B\u3066\u3001\u967D\u97F3\u968E\uFF08\u9577\u8ABF\uFF09\u304B\u6C11\u8B21\u97F3\u968E\uFF08\u77ED\u8ABF\uFF09\u3092\u4F7F\u3044\u307E\u3059">\u304A\u307E\u304B\u305B\uFF08\u5F93\u6765\u3069\u304A\u308A\uFF09</option>
|
|
26732
|
+
<option value="any" title="9\u3064\u306E\u97F3\u968E\u304B\u3089\u30E9\u30F3\u30C0\u30E0\u306B\u62BD\u9078\u3057\u307E\u3059">\u5E0C\u671B\u306A\u3057\uFF08\u5168\u97F3\u968E\u304B\u3089\u62BD\u9078\uFF09</option>
|
|
26733
|
+
<optgroup label="\u30DA\u30F3\u30BF\u30C8\u30CB\u30C3\u30AF\uFF085\u97F3\u97F3\u968E\uFF09">
|
|
26734
|
+
<option value="yo" title="J-POP\u306E\u6A19\u6E96\u3002\u660E\u308B\u304F\u7D20\u76F4\u3067\u6B4C\u3044\u3084\u3059\u3044\u3002\u5F93\u6765\u306E\u9577\u8ABF\u3068\u540C\u3058">\u967D\u97F3\u968E\uFF08\u9577\u8ABF\u30DA\u30F3\u30BF\uFF09</option>
|
|
26735
|
+
<option value="minyo" title="\u308F\u3089\u3079\u6B4C\u30FB\u6C11\u8B21\u306E\u97F3\u968E\u3002\u7FF3\u308A\u304C\u3042\u308B\u304C\u6697\u3059\u304E\u306A\u3044\u3002\u5F93\u6765\u306E\u77ED\u8ABF\u3068\u540C\u3058">\u6C11\u8B21\u97F3\u968E\uFF08\u77ED\u8ABF\u30DA\u30F3\u30BF\uFF09</option>
|
|
26736
|
+
<option value="ryukyu" title="\u6C96\u7E04\u97F3\u968E\u3002\u30EC\u3068\u30E9\u3092\u629C\u304D\u3001\u30D5\u30A1\u3068\u30B7\u3092\u67F1\u306B\u3059\u308B\u3002\u660E\u308B\u304F\u8DF3\u306D\u308B">\u7409\u7403\u97F3\u968E\uFF08\u6C96\u7E04\uFF09</option>
|
|
26737
|
+
<option value="miyakobushi" title="\u300E\u3055\u304F\u3089\u3055\u304F\u3089\u300F\u306E\u97F3\u968E\u3002\u4E3B\u97F3\u306E\u3059\u3050\u4E0A\u304C\u534A\u97F3\u3067\u3001\u7FF3\u308A\u304C\u6FC3\u3044">\u90FD\u7BC0\u97F3\u968E\uFF08\u9670\u97F3\u968E\uFF09</option>
|
|
26738
|
+
<option value="ritsu" title="\u96C5\u697D\u30FB\u58F0\u660E\u306E\u97F3\u968E\u3002\u534A\u97F3\u3092\u542B\u307E\u305A\u3001\u5E73\u3089\u3067\u8358\u91CD\u306B\u6D41\u308C\u308B">\u5F8B\u97F3\u968E\uFF08\u96C5\u697D\uFF09</option>
|
|
26739
|
+
</optgroup>
|
|
26740
|
+
<optgroup label="\u30C1\u30E3\u30FC\u30C1\u30E2\u30FC\u30C9\uFF087\u97F3\u97F3\u968E\uFF09">
|
|
26741
|
+
<option value="dorian" title="\u77ED\u8ABF\u3060\u304C6\u5EA6\u304C\u660E\u308B\u3044\u3002\u30B1\u30EB\u30C8\u30FB\u30ED\u30C3\u30AF\u30FB\u30B7\u30C6\u30A3\u30DD\u30C3\u30D7">\u30C9\u30EA\u30A2\u30F3</option>
|
|
26742
|
+
<option value="phrygian" title="\u4E3B\u97F3\u306E\u4E0A\u304C\u534A\u97F3\u3002\u30B9\u30D1\u30CB\u30C3\u30B7\u30E5\uFF0F\u30E1\u30BF\u30EB\u306E\u7DCA\u8FEB\u3057\u305F\u97FF\u304D">\u30D5\u30EA\u30B8\u30A2\u30F3</option>
|
|
26743
|
+
<option value="lydian" title="4\u5EA6\u304C\u9AD8\u304F\u3001\u6D6E\u904A\u3057\u3066\u5E83\u304C\u308B\u3002\u6620\u753B\u97F3\u697D\u30FB\u30B2\u30FC\u30E0\u306E\u7A7A\u306E\u8272">\u30EA\u30C7\u30A3\u30A2\u30F3</option>
|
|
26744
|
+
<option value="mixolydian" title="\u9577\u8ABF\u3060\u304C7\u5EA6\u304C\u4F4E\u3044\u3002\u30D6\u30EB\u30FC\u30B9\u30ED\u30C3\u30AF\u30FB\u6C11\u65CF\u97F3\u697D\u306E\u571F\u304F\u3055\u3055">\u30DF\u30AF\u30BD\u30EA\u30C7\u30A3\u30A2\u30F3</option>
|
|
26745
|
+
</optgroup>
|
|
26746
|
+
<optgroup label="\u7279\u6B8A\u97F3\u968E\uFF08\u97F3\u7A0B\u96C6\u5408\u3054\u3068\u5165\u308C\u66FF\u308F\u308B\uFF09">
|
|
26747
|
+
<option value="harmonic_minor" title="\u5C0E\u97F3\u30BD\u266F\u3092\u6301\u3064\u77ED\u8ABF\u3002\u58972\u5EA6\u304C\u6CE3\u304D\u3092\u4F5C\u308B\u3002\u30AF\u30E9\u30B7\u30C3\u30AF\u30FBV\u7CFB\u30FB\u5287\u4F34">\u548C\u58F0\u7684\u77ED\u97F3\u968E</option>
|
|
26748
|
+
<option value="hijaz" title="\u4E3B\u97F3\u306E\u4E0A\u304C\u534A\u97F3\u3001\u4E3B\u548C\u97F3\u306F\u9577\u4E09\u548C\u97F3\u3002\u4E2D\u6771\u30FB\u30B9\u30D1\u30CB\u30C3\u30B7\u30E5\u30FB\u30E1\u30BF\u30EB">\u30D2\u30B8\u30E3\u30FC\u30BA\uFF08\u30D5\u30EA\u30B8\u30A2\u30F3\u30FB\u30C9\u30DF\u30CA\u30F3\u30C8\uFF09</option>
|
|
26749
|
+
<option value="hungarian" title="\u58972\u5EA6\u304C2\u304B\u6240\u3002\u97F3\u968E\u306E\u4E2D\u3067\u3044\u3061\u3070\u3093\u8DF3\u306D\u305F\u3001\u7570\u56FD\u3081\u3044\u305F\u97FF\u304D">\u30CF\u30F3\u30AC\u30EA\u30A2\u30F3\u30FB\u30DE\u30A4\u30CA\u30FC\uFF08\u30B8\u30D7\u30B7\u30FC\uFF09</option>
|
|
26750
|
+
<option value="blues" title="\u30D6\u30EB\u30FC\u30CE\u30FC\u30C8\u5165\u308A\u306E6\u97F3\u97F3\u968E\u3002\u77ED3\u5EA6\u3067\u6B4C\u3044\u3001\u4F34\u594F\u306F\u95773\u5EA6\u3067\u9CF4\u308B">\u30D6\u30EB\u30FC\u30B9\u97F3\u968E</option>
|
|
26751
|
+
</optgroup>
|
|
26752
|
+
</select>
|
|
26753
|
+
<span class="dtm-grow"></span>
|
|
26754
|
+
<span class="dtm-hint" data-dtm="compose-scale-hint"></span>
|
|
25115
26755
|
</div>
|
|
26756
|
+
</div>
|
|
26757
|
+
</details>
|
|
26758
|
+
|
|
26759
|
+
<details class="dtm-panel" data-dtm-acc="macro">
|
|
26760
|
+
<summary>\u4E00\u62EC\u7DE8\u96C6</summary>
|
|
26761
|
+
<div class="dtm-panel-body">
|
|
25116
26762
|
<div class="dtm-row">
|
|
25117
26763
|
<span class="dtm-label">\u5168\u4F53\u30B7\u30D5\u30C8</span>
|
|
25118
26764
|
<select class="dtm-select" data-dtm="shift-select">
|
|
@@ -25163,10 +26809,11 @@ var buildUI = (target, options) => {
|
|
|
25163
26809
|
</details>
|
|
25164
26810
|
|
|
25165
26811
|
<details class="dtm-panel" data-dtm-acc="io-out">
|
|
25166
|
-
<summary>MIDI / UST / MML \u51FA\u529B</summary>
|
|
26812
|
+
<summary>MIDI / MusicXML / UST / MML \u51FA\u529B</summary>
|
|
25167
26813
|
<div class="dtm-panel-body">
|
|
25168
26814
|
<div class="dtm-row">
|
|
25169
26815
|
<button class="dtm-btn dtm-btn--accent" data-dtm="export-midi">MIDI\u51FA\u529B</button>
|
|
26816
|
+
<button class="dtm-btn dtm-btn--accent" data-dtm="export-musicxml" title="\u5168\u30C8\u30E9\u30C3\u30AF\u3092MusicXML\uFF08\u697D\u8B5C\uFF09\u5F62\u5F0F\u3067\u66F8\u304D\u51FA\u3057\u307E\u3059">MusicXML\u51FA\u529B</button>
|
|
25170
26817
|
<button class="dtm-btn dtm-btn--accent" data-dtm="export-ust" title="\u73FE\u5728\u9078\u629E\u4E2D\u306E\u30C8\u30E9\u30C3\u30AF\u3060\u3051\u3092UST\u3067\u66F8\u304D\u51FA\u3057\u307E\u3059">UST\u51FA\u529B</button>
|
|
25171
26818
|
<button class="dtm-btn dtm-btn--success" data-dtm="generate-mml">MML\u751F\u6210</button>
|
|
25172
26819
|
<button class="dtm-btn dtm-btn--primary dtm-hidden" data-dtm="export-wav">WAV\u66F8\u304D\u51FA\u3057</button>
|
|
@@ -25216,6 +26863,7 @@ var buildUI = (target, options) => {
|
|
|
25216
26863
|
</div>
|
|
25217
26864
|
</div>
|
|
25218
26865
|
</details>
|
|
26866
|
+
</div>
|
|
25219
26867
|
|
|
25220
26868
|
<!-- \u2550\u2550\u2550\u2550 \u89E3\u8AAC\u30E2\u30FC\u30C0\u30EB \u2550\u2550\u2550\u2550 -->
|
|
25221
26869
|
<div class="dtm-modal-overlay" data-dtm="modal-overlay" hidden>
|
|
@@ -25322,6 +26970,11 @@ var buildUI = (target, options) => {
|
|
|
25322
26970
|
midiTrackSelection: sel("midi-track-selection"),
|
|
25323
26971
|
midiPanel: sel("midi-panel"),
|
|
25324
26972
|
midiSearchOpenBtn: sel("midi-search-open"),
|
|
26973
|
+
musicXmlInput: sel("musicxml-input"),
|
|
26974
|
+
musicXmlLoadBtn: sel("musicxml-load"),
|
|
26975
|
+
musicXmlInfoBtn: sel("musicxml-info"),
|
|
26976
|
+
musicXmlPartSelection: sel("musicxml-part-selection"),
|
|
26977
|
+
musicXmlLoadNote: sel("musicxml-load-note"),
|
|
25325
26978
|
ustInput: sel("ust-input"),
|
|
25326
26979
|
ustLoadBtn: sel("ust-load"),
|
|
25327
26980
|
ustInfoBtn: sel("ust-info"),
|
|
@@ -25353,6 +27006,7 @@ var buildUI = (target, options) => {
|
|
|
25353
27006
|
macroHarmonic: sel("macro-harmonic"),
|
|
25354
27007
|
macroMono: sel("macro-mono"),
|
|
25355
27008
|
exportMidiBtn: sel("export-midi"),
|
|
27009
|
+
exportMusicXmlBtn: sel("export-musicxml"),
|
|
25356
27010
|
exportUstBtn: sel("export-ust"),
|
|
25357
27011
|
exportWavBtn: sel("export-wav"),
|
|
25358
27012
|
drumJsonExportBtn: sel("drum-json-export"),
|
|
@@ -25421,6 +27075,32 @@ var readMacroSections = () => {
|
|
|
25421
27075
|
var writeMacroSections = (sections) => {
|
|
25422
27076
|
writeMacroSetting("sections", JSON.stringify(sections));
|
|
25423
27077
|
};
|
|
27078
|
+
var KEPT_STORAGE_KEY = "dtm-macro:kept";
|
|
27079
|
+
var readKeptSong = () => {
|
|
27080
|
+
try {
|
|
27081
|
+
if (typeof localStorage === "undefined" || !localStorage) return null;
|
|
27082
|
+
const raw = localStorage.getItem(KEPT_STORAGE_KEY);
|
|
27083
|
+
if (!raw) return null;
|
|
27084
|
+
const parsed = JSON.parse(raw);
|
|
27085
|
+
if (parsed && typeof parsed === "object" && typeof parsed.mml === "string" && parsed.mml.length > 0) {
|
|
27086
|
+
const startStep = typeof parsed.startStep === "number" && Number.isFinite(parsed.startStep) && parsed.startStep >= 0 ? Math.floor(parsed.startStep) : 0;
|
|
27087
|
+
return { mml: parsed.mml, startStep };
|
|
27088
|
+
}
|
|
27089
|
+
} catch (_) {
|
|
27090
|
+
}
|
|
27091
|
+
return null;
|
|
27092
|
+
};
|
|
27093
|
+
var writeKeptSong = (kept) => {
|
|
27094
|
+
try {
|
|
27095
|
+
if (typeof localStorage === "undefined" || !localStorage) return;
|
|
27096
|
+
if (kept === null) {
|
|
27097
|
+
localStorage.removeItem(KEPT_STORAGE_KEY);
|
|
27098
|
+
return;
|
|
27099
|
+
}
|
|
27100
|
+
localStorage.setItem(KEPT_STORAGE_KEY, JSON.stringify(kept));
|
|
27101
|
+
} catch (_) {
|
|
27102
|
+
}
|
|
27103
|
+
};
|
|
25424
27104
|
|
|
25425
27105
|
// src/macros.ts
|
|
25426
27106
|
var SCALES = [
|
|
@@ -25567,7 +27247,7 @@ var transposeNotes = (cores, steps) => {
|
|
|
25567
27247
|
};
|
|
25568
27248
|
|
|
25569
27249
|
// src/version.ts
|
|
25570
|
-
var DTM_VERSION = "2.1.
|
|
27250
|
+
var DTM_VERSION = "2.1.14";
|
|
25571
27251
|
|
|
25572
27252
|
// src/midi-io.ts
|
|
25573
27253
|
var STEPS_PER_BEAT3 = 48;
|
|
@@ -26734,6 +28414,275 @@ var isChordHeavyTrack = (notes, threshold = 0.6) => {
|
|
|
26734
28414
|
return chordNotes / notes.length >= threshold;
|
|
26735
28415
|
};
|
|
26736
28416
|
|
|
28417
|
+
// src/musicxml-io.ts
|
|
28418
|
+
var STEPS_PER_BEAT4 = 48;
|
|
28419
|
+
var PITCH_CLASS = {
|
|
28420
|
+
C: 0,
|
|
28421
|
+
D: 2,
|
|
28422
|
+
E: 4,
|
|
28423
|
+
F: 5,
|
|
28424
|
+
G: 7,
|
|
28425
|
+
A: 9,
|
|
28426
|
+
B: 11
|
|
28427
|
+
};
|
|
28428
|
+
var textOf = (el, tag) => el?.getElementsByTagName(tag)[0]?.textContent?.trim() ?? "";
|
|
28429
|
+
var numOf = (el, tag, fallback = 0) => {
|
|
28430
|
+
const raw = textOf(el, tag);
|
|
28431
|
+
if (!raw) return fallback;
|
|
28432
|
+
const v = Number.parseFloat(raw);
|
|
28433
|
+
return Number.isFinite(v) ? v : fallback;
|
|
28434
|
+
};
|
|
28435
|
+
var pitchOf = (note) => {
|
|
28436
|
+
const p = note.getElementsByTagName("pitch")[0];
|
|
28437
|
+
if (!p) return null;
|
|
28438
|
+
const step = textOf(p, "step").toUpperCase();
|
|
28439
|
+
const base = PITCH_CLASS[step];
|
|
28440
|
+
if (base === void 0) return null;
|
|
28441
|
+
const octave = numOf(p, "octave", 4);
|
|
28442
|
+
const alter = Math.round(numOf(p, "alter", 0));
|
|
28443
|
+
return (octave + 1) * 12 + base + alter;
|
|
28444
|
+
};
|
|
28445
|
+
var parseMusicXML = (xml) => {
|
|
28446
|
+
const doc = new DOMParser().parseFromString(xml, "application/xml");
|
|
28447
|
+
if (doc.getElementsByTagName("parserror").length > 0)
|
|
28448
|
+
throw new Error("MusicXML \u3068\u3057\u3066\u8AAD\u3081\u307E\u305B\u3093");
|
|
28449
|
+
const nameById = /* @__PURE__ */ new Map();
|
|
28450
|
+
for (const sp of Array.from(doc.getElementsByTagName("score-part"))) {
|
|
28451
|
+
const id = sp.getAttribute("id");
|
|
28452
|
+
if (id) nameById.set(id, textOf(sp, "part-name") || id);
|
|
28453
|
+
}
|
|
28454
|
+
const placements = [];
|
|
28455
|
+
const parts = [];
|
|
28456
|
+
let bpm = 0;
|
|
28457
|
+
const partEls = Array.from(doc.getElementsByTagName("part"));
|
|
28458
|
+
partEls.forEach((partEl, partIndex) => {
|
|
28459
|
+
const id = partEl.getAttribute("id") ?? "";
|
|
28460
|
+
let divisions = 1;
|
|
28461
|
+
let cursor = 0;
|
|
28462
|
+
let measureStart = 0;
|
|
28463
|
+
let lastStart = 0;
|
|
28464
|
+
let lastDurSteps = 0;
|
|
28465
|
+
const tied = /* @__PURE__ */ new Map();
|
|
28466
|
+
let count = 0;
|
|
28467
|
+
let pitchSum = 0;
|
|
28468
|
+
let lyricSeen = false;
|
|
28469
|
+
for (const measure of Array.from(partEl.getElementsByTagName("measure"))) {
|
|
28470
|
+
measureStart += cursor;
|
|
28471
|
+
cursor = 0;
|
|
28472
|
+
for (const child of Array.from(measure.children)) {
|
|
28473
|
+
const tag = child.tagName.toLowerCase();
|
|
28474
|
+
if (tag === "attributes") {
|
|
28475
|
+
const d = numOf(child, "divisions", 0);
|
|
28476
|
+
if (d > 0) divisions = d;
|
|
28477
|
+
continue;
|
|
28478
|
+
}
|
|
28479
|
+
if (tag === "direction") {
|
|
28480
|
+
const sound = child.getElementsByTagName("sound")[0];
|
|
28481
|
+
const t = Number.parseFloat(sound?.getAttribute("tempo") ?? "");
|
|
28482
|
+
if (!bpm && Number.isFinite(t) && t > 0) bpm = t;
|
|
28483
|
+
continue;
|
|
28484
|
+
}
|
|
28485
|
+
if (tag === "backup" || tag === "forward") {
|
|
28486
|
+
const steps = numOf(child, "duration", 0) / divisions * STEPS_PER_BEAT4;
|
|
28487
|
+
cursor += tag === "backup" ? -steps : steps;
|
|
28488
|
+
continue;
|
|
28489
|
+
}
|
|
28490
|
+
if (tag !== "note") continue;
|
|
28491
|
+
const durSteps = Math.round(
|
|
28492
|
+
numOf(child, "duration", 0) / divisions * STEPS_PER_BEAT4
|
|
28493
|
+
);
|
|
28494
|
+
const isChord = child.getElementsByTagName("chord").length > 0;
|
|
28495
|
+
const isRest = child.getElementsByTagName("rest").length > 0;
|
|
28496
|
+
const start = isChord ? lastStart : measureStart + cursor;
|
|
28497
|
+
if (!isRest) {
|
|
28498
|
+
const pitch = pitchOf(child);
|
|
28499
|
+
if (pitch !== null) {
|
|
28500
|
+
const lyric = textOf(
|
|
28501
|
+
child.getElementsByTagName("lyric")[0],
|
|
28502
|
+
"text"
|
|
28503
|
+
);
|
|
28504
|
+
if (lyric) lyricSeen = true;
|
|
28505
|
+
const ties = Array.from(child.getElementsByTagName("tie"));
|
|
28506
|
+
const stops = ties.some((t) => t.getAttribute("type") === "stop");
|
|
28507
|
+
const starts = ties.some((t) => t.getAttribute("type") === "start");
|
|
28508
|
+
const held = tied.get(pitch);
|
|
28509
|
+
if (stops && held) {
|
|
28510
|
+
held.durationSteps += durSteps;
|
|
28511
|
+
if (!starts) tied.delete(pitch);
|
|
28512
|
+
} else {
|
|
28513
|
+
const placed = {
|
|
28514
|
+
partIndex,
|
|
28515
|
+
startStep: Math.max(0, Math.round(start)),
|
|
28516
|
+
pitch,
|
|
28517
|
+
durationSteps: Math.max(1, durSteps),
|
|
28518
|
+
lyric
|
|
28519
|
+
};
|
|
28520
|
+
placements.push(placed);
|
|
28521
|
+
count++;
|
|
28522
|
+
pitchSum += pitch;
|
|
28523
|
+
if (starts) tied.set(pitch, placed);
|
|
28524
|
+
}
|
|
28525
|
+
}
|
|
28526
|
+
}
|
|
28527
|
+
if (!isChord) {
|
|
28528
|
+
lastStart = start;
|
|
28529
|
+
lastDurSteps = durSteps;
|
|
28530
|
+
cursor += durSteps;
|
|
28531
|
+
} else {
|
|
28532
|
+
lastDurSteps = Math.max(lastDurSteps, durSteps);
|
|
28533
|
+
}
|
|
28534
|
+
}
|
|
28535
|
+
}
|
|
28536
|
+
parts.push({
|
|
28537
|
+
index: partIndex,
|
|
28538
|
+
name: nameById.get(id) || `Part ${partIndex + 1}`,
|
|
28539
|
+
noteCount: count,
|
|
28540
|
+
hasLyrics: lyricSeen,
|
|
28541
|
+
avgPitch: count > 0 ? pitchSum / count : 0
|
|
28542
|
+
});
|
|
28543
|
+
});
|
|
28544
|
+
return { parts, placements, bpm: bpm > 0 ? Math.round(bpm) : 120 };
|
|
28545
|
+
};
|
|
28546
|
+
var SHARP_SPELLING = [
|
|
28547
|
+
["C", 0],
|
|
28548
|
+
["C", 1],
|
|
28549
|
+
["D", 0],
|
|
28550
|
+
["D", 1],
|
|
28551
|
+
["E", 0],
|
|
28552
|
+
["F", 0],
|
|
28553
|
+
["F", 1],
|
|
28554
|
+
["G", 0],
|
|
28555
|
+
["G", 1],
|
|
28556
|
+
["A", 0],
|
|
28557
|
+
["A", 1],
|
|
28558
|
+
["B", 0]
|
|
28559
|
+
];
|
|
28560
|
+
var NOTE_TYPES = [
|
|
28561
|
+
[STEPS_PER_BEAT4 * 4, "whole"],
|
|
28562
|
+
[STEPS_PER_BEAT4 * 2, "half"],
|
|
28563
|
+
[STEPS_PER_BEAT4, "quarter"],
|
|
28564
|
+
[STEPS_PER_BEAT4 / 2, "eighth"],
|
|
28565
|
+
[STEPS_PER_BEAT4 / 4, "16th"],
|
|
28566
|
+
[STEPS_PER_BEAT4 / 8, "32nd"]
|
|
28567
|
+
];
|
|
28568
|
+
var typeOf = (steps) => {
|
|
28569
|
+
for (const [len, type] of NOTE_TYPES) {
|
|
28570
|
+
if (steps >= len) {
|
|
28571
|
+
const dots = steps >= len * 1.5 && steps < len * 2 ? 1 : 0;
|
|
28572
|
+
return { type, dots };
|
|
28573
|
+
}
|
|
28574
|
+
}
|
|
28575
|
+
return { type: "32nd", dots: 0 };
|
|
28576
|
+
};
|
|
28577
|
+
var esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
28578
|
+
var exportMusicXML = (options) => {
|
|
28579
|
+
const { parts, bpm, stepsPerBar, title } = options;
|
|
28580
|
+
const beatsPerBar = Math.max(1, Math.round(stepsPerBar / STEPS_PER_BEAT4));
|
|
28581
|
+
const partList = parts.map(
|
|
28582
|
+
(p, i2) => ` <score-part id="P${i2 + 1}">
|
|
28583
|
+
<part-name>${esc(p.name)}</part-name>
|
|
28584
|
+
</score-part>`
|
|
28585
|
+
).join("\n");
|
|
28586
|
+
const body = parts.map((part, pi) => {
|
|
28587
|
+
const notes = [...part.notes].sort((a, b) => a.startStep - b.startStep);
|
|
28588
|
+
const end = notes.reduce(
|
|
28589
|
+
(m, n) => Math.max(m, n.startStep + n.durationSteps),
|
|
28590
|
+
0
|
|
28591
|
+
);
|
|
28592
|
+
const bars = Math.max(1, Math.ceil(end / stepsPerBar));
|
|
28593
|
+
const lines = [` <part id="P${pi + 1}">`];
|
|
28594
|
+
let li = 0;
|
|
28595
|
+
for (let bar = 0; bar < bars; bar++) {
|
|
28596
|
+
const from = bar * stepsPerBar;
|
|
28597
|
+
const to = from + stepsPerBar;
|
|
28598
|
+
lines.push(` <measure number="${bar + 1}">`);
|
|
28599
|
+
if (bar === 0) {
|
|
28600
|
+
lines.push(
|
|
28601
|
+
` <attributes>
|
|
28602
|
+
<divisions>${STEPS_PER_BEAT4}</divisions>`,
|
|
28603
|
+
` <key><fifths>0</fifths></key>`,
|
|
28604
|
+
` <time><beats>${beatsPerBar}</beats><beat-type>4</beat-type></time>`,
|
|
28605
|
+
` <clef><sign>G</sign><line>2</line></clef>
|
|
28606
|
+
</attributes>`
|
|
28607
|
+
);
|
|
28608
|
+
if (pi === 0)
|
|
28609
|
+
lines.push(
|
|
28610
|
+
` <direction placement="above"><direction-type><metronome><beat-unit>quarter</beat-unit><per-minute>${Math.round(bpm)}</per-minute></metronome></direction-type><sound tempo="${Math.round(bpm)}"/></direction>`
|
|
28611
|
+
);
|
|
28612
|
+
}
|
|
28613
|
+
let cursor = from;
|
|
28614
|
+
const inBar = notes.filter(
|
|
28615
|
+
(n) => n.startStep >= from && n.startStep < to
|
|
28616
|
+
);
|
|
28617
|
+
const groups = /* @__PURE__ */ new Map();
|
|
28618
|
+
for (const n of inBar) {
|
|
28619
|
+
const g = groups.get(n.startStep);
|
|
28620
|
+
if (g) g.push(n);
|
|
28621
|
+
else groups.set(n.startStep, [n]);
|
|
28622
|
+
}
|
|
28623
|
+
const starts = [...groups.keys()].sort((a, b) => a - b);
|
|
28624
|
+
for (let gi = 0; gi < starts.length; gi++) {
|
|
28625
|
+
const at = starts[gi];
|
|
28626
|
+
const group = groups.get(at);
|
|
28627
|
+
if (at > cursor) {
|
|
28628
|
+
const { type, dots } = typeOf(at - cursor);
|
|
28629
|
+
lines.push(
|
|
28630
|
+
` <note><rest/><duration>${at - cursor}</duration><type>${type}</type>${"<dot/>".repeat(dots)}</note>`
|
|
28631
|
+
);
|
|
28632
|
+
cursor = at;
|
|
28633
|
+
}
|
|
28634
|
+
const nextAt = gi + 1 < starts.length ? starts[gi + 1] : to;
|
|
28635
|
+
const room = Math.min(to, nextAt) - at;
|
|
28636
|
+
const dur = Math.max(
|
|
28637
|
+
1,
|
|
28638
|
+
Math.min(Math.max(...group.map((n) => n.durationSteps)), room)
|
|
28639
|
+
);
|
|
28640
|
+
group.forEach((n, ni) => {
|
|
28641
|
+
const semi = Math.round(n.pitchUnits / UNITS_PER_SEMITONE);
|
|
28642
|
+
const [step, alter] = SHARP_SPELLING[(semi % 12 + 12) % 12];
|
|
28643
|
+
const octave = Math.floor(semi / 12) - 1;
|
|
28644
|
+
const { type, dots } = typeOf(dur);
|
|
28645
|
+
const lyric = part.lyrics?.[li];
|
|
28646
|
+
lines.push(
|
|
28647
|
+
` <note>${ni > 0 ? "<chord/>" : ""}<pitch><step>${step}</step>${alter ? `<alter>${alter}</alter>` : ""}<octave>${octave}</octave></pitch><duration>${dur}</duration><type>${type}</type>${"<dot/>".repeat(dots)}` + (ni === 0 && lyric ? `<lyric><syllabic>single</syllabic><text>${esc(lyric)}</text></lyric>` : "") + `</note>`
|
|
28648
|
+
);
|
|
28649
|
+
if (ni === 0) li++;
|
|
28650
|
+
});
|
|
28651
|
+
cursor = at + dur;
|
|
28652
|
+
}
|
|
28653
|
+
if (cursor < to) {
|
|
28654
|
+
const { type, dots } = typeOf(to - cursor);
|
|
28655
|
+
lines.push(
|
|
28656
|
+
` <note><rest/><duration>${to - cursor}</duration><type>${type}</type>${"<dot/>".repeat(dots)}</note>`
|
|
28657
|
+
);
|
|
28658
|
+
}
|
|
28659
|
+
lines.push(" </measure>");
|
|
28660
|
+
}
|
|
28661
|
+
lines.push(" </part>");
|
|
28662
|
+
return lines.join("\n");
|
|
28663
|
+
}).join("\n");
|
|
28664
|
+
return [
|
|
28665
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
28666
|
+
'<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 4.0 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">',
|
|
28667
|
+
'<score-partwise version="4.0">',
|
|
28668
|
+
` <work><work-title>${esc(title ?? "Untitled")}</work-title></work>`,
|
|
28669
|
+
// 書き出し元を残す。MML・MIDI と揃えてある(`src/version.ts` に理由)。
|
|
28670
|
+
` <identification><encoding><software>dtm ${DTM_VERSION}</software></encoding></identification>`,
|
|
28671
|
+
" <part-list>",
|
|
28672
|
+
partList,
|
|
28673
|
+
" </part-list>",
|
|
28674
|
+
body,
|
|
28675
|
+
"</score-partwise>",
|
|
28676
|
+
""
|
|
28677
|
+
].join("\n");
|
|
28678
|
+
};
|
|
28679
|
+
var musicXmlToNotes = (placements, partIndex) => placements.filter((p) => p.partIndex === partIndex).map((p) => ({
|
|
28680
|
+
startStep: p.startStep,
|
|
28681
|
+
durationSteps: p.durationSteps,
|
|
28682
|
+
pitchUnits: p.pitch * UNITS_PER_SEMITONE,
|
|
28683
|
+
velocity: 100
|
|
28684
|
+
}));
|
|
28685
|
+
|
|
26737
28686
|
// src/renderer.ts
|
|
26738
28687
|
var KEYBOARD_WIDTH = 60;
|
|
26739
28688
|
var HEADER_HEIGHT = 20;
|
|
@@ -27062,6 +29011,76 @@ var createRenderer = (mountTarget, width = 800, height = 450, config) => {
|
|
|
27062
29011
|
}
|
|
27063
29012
|
}
|
|
27064
29013
|
};
|
|
29014
|
+
const drawSpeechSpans = (spans, color = [255, 241, 232]) => {
|
|
29015
|
+
if (spans.length === 0) return;
|
|
29016
|
+
const {
|
|
29017
|
+
keyHeight,
|
|
29018
|
+
stepWidth,
|
|
29019
|
+
keyCount,
|
|
29020
|
+
pitchRangeStart,
|
|
29021
|
+
unitsPerRow: upr = UNITS_PER_SEMITONE
|
|
29022
|
+
} = g_config;
|
|
29023
|
+
const [r, g, b] = color;
|
|
29024
|
+
g_grid_ctx.save();
|
|
29025
|
+
for (const { note, durationSteps } of spans) {
|
|
29026
|
+
if (durationSteps <= 0) continue;
|
|
29027
|
+
const x2 = note.startStep * stepWidth - g_draw_offset_x;
|
|
29028
|
+
const y = (keyCount - 1 - (note.pitchUnits - pitchRangeStart) / upr) * keyHeight - g_draw_offset_y;
|
|
29029
|
+
const w = durationSteps * stepWidth;
|
|
29030
|
+
if (x2 + w < 0 || x2 > g_grid_canvas.width) continue;
|
|
29031
|
+
if (y + keyHeight < 0 || y > g_grid_canvas.height) continue;
|
|
29032
|
+
const h = Math.max(2, Math.floor(keyHeight * 0.35));
|
|
29033
|
+
const top = y + keyHeight - h - 1;
|
|
29034
|
+
g_grid_ctx.fillStyle = `rgba(${r},${g},${b},0.28)`;
|
|
29035
|
+
g_grid_ctx.fillRect(x2 + 1, top, w - 2, h);
|
|
29036
|
+
g_grid_ctx.strokeStyle = `rgba(${r},${g},${b},0.75)`;
|
|
29037
|
+
g_grid_ctx.lineWidth = 1;
|
|
29038
|
+
g_grid_ctx.setLineDash([3, 2]);
|
|
29039
|
+
g_grid_ctx.strokeRect(x2 + 1.5, top + 0.5, w - 3, h - 1);
|
|
29040
|
+
g_grid_ctx.setLineDash([]);
|
|
29041
|
+
g_grid_ctx.beginPath();
|
|
29042
|
+
g_grid_ctx.moveTo(x2 + w - 1.5, y + 1);
|
|
29043
|
+
g_grid_ctx.lineTo(x2 + w - 1.5, y + keyHeight - 1);
|
|
29044
|
+
g_grid_ctx.stroke();
|
|
29045
|
+
}
|
|
29046
|
+
g_grid_ctx.restore();
|
|
29047
|
+
};
|
|
29048
|
+
const drawPitchGuide = (pitchUnits, label) => {
|
|
29049
|
+
const {
|
|
29050
|
+
keyHeight,
|
|
29051
|
+
keyCount,
|
|
29052
|
+
pitchRangeStart,
|
|
29053
|
+
unitsPerRow: upr = UNITS_PER_SEMITONE
|
|
29054
|
+
} = g_config;
|
|
29055
|
+
const row = Math.round((pitchUnits - pitchRangeStart) / upr);
|
|
29056
|
+
if (row < 0 || row >= keyCount) return;
|
|
29057
|
+
const y = (keyCount - 1 - row) * keyHeight - g_draw_offset_y;
|
|
29058
|
+
if (y + keyHeight < 0 || y > g_grid_canvas.height) return;
|
|
29059
|
+
g_grid_ctx.save();
|
|
29060
|
+
g_grid_ctx.fillStyle = "rgba(255,236,39,0.10)";
|
|
29061
|
+
g_grid_ctx.fillRect(0, y, g_grid_canvas.width, keyHeight);
|
|
29062
|
+
g_grid_ctx.strokeStyle = "rgba(255,236,39,0.55)";
|
|
29063
|
+
g_grid_ctx.lineWidth = 1;
|
|
29064
|
+
g_grid_ctx.setLineDash([6, 4]);
|
|
29065
|
+
g_grid_ctx.beginPath();
|
|
29066
|
+
g_grid_ctx.moveTo(0, y + keyHeight / 2 + 0.5);
|
|
29067
|
+
g_grid_ctx.lineTo(g_grid_canvas.width, y + keyHeight / 2 + 0.5);
|
|
29068
|
+
g_grid_ctx.stroke();
|
|
29069
|
+
if (keyHeight >= 7) {
|
|
29070
|
+
const fontSize = Math.min(11, Math.floor(keyHeight * 0.85));
|
|
29071
|
+
g_grid_ctx.setLineDash([]);
|
|
29072
|
+
g_grid_ctx.font = `${fontSize}px 'k8x12',sans-serif`;
|
|
29073
|
+
g_grid_ctx.textAlign = "left";
|
|
29074
|
+
g_grid_ctx.textBaseline = "middle";
|
|
29075
|
+
g_grid_ctx.lineWidth = 3;
|
|
29076
|
+
g_grid_ctx.lineJoin = "round";
|
|
29077
|
+
g_grid_ctx.strokeStyle = "rgba(0,0,0,0.85)";
|
|
29078
|
+
g_grid_ctx.strokeText(label, 4, y + keyHeight / 2);
|
|
29079
|
+
g_grid_ctx.fillStyle = "#ffec27";
|
|
29080
|
+
g_grid_ctx.fillText(label, 4, y + keyHeight / 2);
|
|
29081
|
+
}
|
|
29082
|
+
g_grid_ctx.restore();
|
|
29083
|
+
};
|
|
27065
29084
|
const drawNoteLyrics = (notes, syllables) => {
|
|
27066
29085
|
if (syllables.length === 0) return;
|
|
27067
29086
|
const {
|
|
@@ -27211,6 +29230,8 @@ var createRenderer = (mountTarget, width = 800, height = 450, config) => {
|
|
|
27211
29230
|
drawGrid,
|
|
27212
29231
|
drawNotes,
|
|
27213
29232
|
drawNoteLyrics,
|
|
29233
|
+
drawSpeechSpans,
|
|
29234
|
+
drawPitchGuide,
|
|
27214
29235
|
drawSelectionRect,
|
|
27215
29236
|
drawSelectedNotes,
|
|
27216
29237
|
getXY,
|
|
@@ -27809,7 +29830,7 @@ var VOWEL_KANA2 = {
|
|
|
27809
29830
|
o: "\u304A",
|
|
27810
29831
|
\u00FC: "\u3086"
|
|
27811
29832
|
};
|
|
27812
|
-
var
|
|
29833
|
+
var SMALL_KANA3 = {
|
|
27813
29834
|
a: "\u3083",
|
|
27814
29835
|
u: "\u3085",
|
|
27815
29836
|
e: "\u3047",
|
|
@@ -27881,7 +29902,7 @@ var pinyinToMoras = (raw) => {
|
|
|
27881
29902
|
const zero = initial === "" && medial === "i";
|
|
27882
29903
|
const head = zero ? ZERO_PALATAL[nucleus] : onset[medial];
|
|
27883
29904
|
if (!head) return null;
|
|
27884
|
-
const small =
|
|
29905
|
+
const small = SMALL_KANA3[nucleus];
|
|
27885
29906
|
if (zero) moras.push(head);
|
|
27886
29907
|
else if (head.length === 1 && small) moras.push(head + small);
|
|
27887
29908
|
else moras.push(head, VOWEL_KANA2[nucleus]);
|
|
@@ -27891,10 +29912,10 @@ var pinyinToMoras = (raw) => {
|
|
|
27891
29912
|
};
|
|
27892
29913
|
|
|
27893
29914
|
// src/ust-io.ts
|
|
27894
|
-
var
|
|
29915
|
+
var STEPS_PER_BEAT5 = 48;
|
|
27895
29916
|
var UST_TICKS_PER_BEAT = 480;
|
|
27896
|
-
var TICKS_TO_STEPS =
|
|
27897
|
-
var STEPS_TO_TICKS = UST_TICKS_PER_BEAT /
|
|
29917
|
+
var TICKS_TO_STEPS = STEPS_PER_BEAT5 / UST_TICKS_PER_BEAT;
|
|
29918
|
+
var STEPS_TO_TICKS = UST_TICKS_PER_BEAT / STEPS_PER_BEAT5;
|
|
27898
29919
|
var MORA_TAIL_MAX_STEPS = 12;
|
|
27899
29920
|
var UST_CONTINUE_LYRICS = ["+", "+~", "+-", "+*", "*", "\u30FC", "-"];
|
|
27900
29921
|
var TIE = "\u30FC";
|
|
@@ -27977,12 +29998,12 @@ var ROMAJI_KANA = (() => {
|
|
|
27977
29998
|
return map;
|
|
27978
29999
|
})();
|
|
27979
30000
|
var KANA_HEAD_RE = /^[ぁ-ゖァ-ヶー〜]+/;
|
|
27980
|
-
var
|
|
30001
|
+
var SMALL_KANA4 = "\u3041\u3043\u3045\u3047\u3049\u3083\u3085\u3087";
|
|
27981
30002
|
var splitMoras = (kana) => {
|
|
27982
30003
|
const moras = [];
|
|
27983
30004
|
for (const ch of kana) {
|
|
27984
30005
|
const last = moras.length - 1;
|
|
27985
|
-
if (last >= 0 &&
|
|
30006
|
+
if (last >= 0 && SMALL_KANA4.includes(ch)) moras[last] += ch;
|
|
27986
30007
|
else moras.push(ch);
|
|
27987
30008
|
}
|
|
27988
30009
|
return moras;
|
|
@@ -28167,7 +30188,7 @@ var parseUst = (source, name = "") => {
|
|
|
28167
30188
|
const syllables = [];
|
|
28168
30189
|
let unknownLyricCount = 0;
|
|
28169
30190
|
let hasVoiced = false;
|
|
28170
|
-
const msPerStep = 6e4 / (bpm ?? 120) /
|
|
30191
|
+
const msPerStep = 6e4 / (bpm ?? 120) / STEPS_PER_BEAT5;
|
|
28171
30192
|
for (const fields of sections) {
|
|
28172
30193
|
const length = Number.parseFloat(fields.length ?? "");
|
|
28173
30194
|
if (!Number.isFinite(length) || length <= 0) continue;
|
|
@@ -28263,6 +30284,7 @@ var ustLyricOf = (kana) => {
|
|
|
28263
30284
|
if (kana === "") return DEFAULT_UST_LYRIC;
|
|
28264
30285
|
if (kana === TIE || kana === "\u301C") return "+";
|
|
28265
30286
|
if (kana === REST) return "R";
|
|
30287
|
+
if (kana.startsWith("\u300C")) return "R";
|
|
28266
30288
|
return kana;
|
|
28267
30289
|
};
|
|
28268
30290
|
var UST_ENVELOPE_ATTACK_MS = 5;
|
|
@@ -28406,7 +30428,9 @@ var buildUst = (options) => {
|
|
|
28406
30428
|
cursorTick = startTick + length;
|
|
28407
30429
|
}
|
|
28408
30430
|
assignFadeEnvelopes(out);
|
|
28409
|
-
|
|
30431
|
+
for (const [index, note] of out.entries()) {
|
|
30432
|
+
lines.push(...ustNoteSection(index, note));
|
|
30433
|
+
}
|
|
28410
30434
|
lines.push("[#TRACKEND]");
|
|
28411
30435
|
return `${lines.join("\r\n")}\r
|
|
28412
30436
|
`;
|
|
@@ -28460,8 +30484,22 @@ c d e f g \u2190 \u97F3\u7B265\u3064</pre>
|
|
|
28460
30484
|
<li><code>\u3001</code> <strong>\u30D6\u30EC\u30B9</strong>\uFF08\u8AAD\u70B9\uFF09\u2026 <strong>\u97F3\u7B26\u306F\u6D88\u8CBB\u305B\u305A</strong>\u3001\u76F4\u524D\u306E\u97F3\u3092\u5C11\u3057\u77ED\u304F\u3057\u3066\u606F\u7D99\u304E\u3092\u5165\u308C\u307E\u3059\u3002</li>
|
|
28461
30485
|
<li><code>\u2193</code> <strong>\u3060\u3093\u3060\u3093\u5C0F\u3055\u304F</strong>\uFF08\u4E0B\u5411\u304D\u77E2\u5370\uFF09\u2026 <strong>\u97F3\u7B26\u306F\u6D88\u8CBB\u305B\u305A</strong>\u3001\u305D\u306E\u97F3\u3092\u6B4C\u3063\u305F\u307E\u307E\u58F0\u91CF\u3092\u843D\u3068\u3057\u307E\u3059\u3002\u8907\u6570\u66F8\u304F\u3068\u6E1B\u308A\u65B9\u3092\u523B\u3081\u307E\u3059\u3002</li>
|
|
28462
30486
|
<li><code>\u2191</code> <strong>\u3060\u3093\u3060\u3093\u5927\u304D\u304F</strong>\uFF08\u4E0A\u5411\u304D\u77E2\u5370\uFF09\u2026 <code>\u2193</code> \u306E\u5BFE\u3002\u5C0F\u3055\u304F\u5165\u3063\u3066\u58F0\u91CF\u3092\u4E0A\u3052\u3066\u3044\u304D\u307E\u3059\u3002</li>
|
|
30487
|
+
<li><code>\u300C\u2026\u300D</code> <strong>\u8A9E\u308A</strong>\uFF08\u304B\u304E\u62EC\u5F27\uFF09\u2026 \u56F2\u3093\u3060\u90E8\u5206\u3092\u6B4C\u308F\u305A\u306B<strong>\u8AAD\u307F\u4E0A\u3052</strong>\u307E\u3059\u3002\u62EC\u5F27\u3072\u3068\u304B\u305F\u307E\u308A\u3067\u97F3\u7B26\u30921\u3064\u6D88\u8CBB\u3057\u3001\u305D\u306E\u97F3\u7B26\u306E\u4F4D\u7F6E\u304B\u3089\u8A71\u3057\u59CB\u3081\u307E\u3059\u3002</li>
|
|
30488
|
+
</ul>
|
|
30489
|
+
<p><small>\u5168\u89D2\u30C1\u30EB\u30C0 <code>\uFF5E</code> \u306F <code>\u301C</code>\u3001\u534A\u89D2\u30AB\u30F3\u30DE <code>,</code> \u306F <code>\u3001</code>\u3001<code>\u21E9</code> <code>\u2B07</code> \u306F <code>\u2193</code>\u3001<code>\u21E7</code> <code>\u2B06</code> \u306F <code>\u2191</code> \u3068\u3057\u3066\u6271\u308F\u308C\u307E\u3059\u3002\u3053\u308C\u3089\u4EE5\u5916\u306E\u8A18\u53F7\uFF08\u82F1\u6570\u5B57\u30FB\u30B9\u30DA\u30FC\u30B9\u30FB\u53E5\u8AAD\u70B9\u306A\u3069\uFF09\u306F\u7121\u8996\u3055\u308C\u307E\u3059\uFF08<code>\u300C\u2026\u300D</code> \u306E\u4E2D\u3060\u3051\u306F\u4F8B\u5916\u3067\u3001\u305D\u306E\u307E\u307E\u8AAD\u307F\u4E0A\u3052\u306B\u6E21\u3055\u308C\u307E\u3059\uFF09\u3002</small></p>
|
|
30490
|
+
|
|
30491
|
+
<h4>\u6B4C\u306E\u4E2D\u3067\u8A9E\u308B\uFF08<code>\u300C\u2026\u300D</code>\uFF09</h4>
|
|
30492
|
+
<p>\u6B4C\u8A5E\u306E\u9014\u4E2D\u306B <code>\u300C\u2026\u300D</code> \u3092\u66F8\u304F\u3068\u3001\u305D\u306E\u90E8\u5206\u3060\u3051\u3092<strong>\u8A71\u3057\u58F0\u3067\u8AAD\u307F\u4E0A\u3052</strong>\u307E\u3059\uFF08UTAU\u97F3\u6E90\u306E\u307F\u3002klatt \u3067\u306F\u9CF4\u308A\u307E\u305B\u3093\uFF09\u3002\u4E2D\u8EAB\u306F\u6F22\u5B57\u30FB\u6570\u5B57\u30FB\u53E5\u8AAD\u70B9\u3092\u542B\u3093\u3067\u304B\u307E\u3044\u307E\u305B\u3093\u2014\u2014\u8AAD\u307F\u3068\u30A2\u30AF\u30BB\u30F3\u30C8\u306F\u8F9E\u66F8\u304C\u6C7A\u3081\u307E\u3059\u3002<code>\uFF1F</code> \u3067\u7D42\u308F\u308B\u3068\u8A9E\u5C3E\u304C\u4E0A\u304C\u308A\u307E\u3059\u3002</p>
|
|
30493
|
+
<pre>\u3075\u3064\u3046\u306B\u3046\u305F\u3046\u300C\u304B\u305F\u308B\u3070\u3057\u3087\u300D\u3075\u3064\u3046\u306B\u3046\u305F\u3046
|
|
30494
|
+
\u3069\u3093\u3050\u308A\u3053\u308D\u3053\u308D\u300C\u307F\u306A\u3055\u3093\u3001\u3053\u3093\u306B\u3061\u306F\uFF01\u300D\u3069\u3093\u3050\u308A\u3053
|
|
30495
|
+
\u300C\u30BB\u30EA\u30D5\u3060\u3051\u306E\u30C8\u30E9\u30C3\u30AF\u3082\u4F5C\u308C\u307E\u3059\u3002\u300D</pre>
|
|
30496
|
+
<ul>
|
|
30497
|
+
<li><strong>\u97F3\u7B26\u306E\u4F4D\u7F6E</strong>\u304C\u8A71\u3057\u59CB\u3081\u3067\u3059\u3002<strong>\u9577\u3055\u306F\u8AAD\u307F\u4E0A\u3052\u304C\u6C7A\u3081\u308B</strong>\u306E\u3067\u3001\u97F3\u7B26\u306E\u9577\u3055\u306F\u4F7F\u3044\u307E\u305B\u3093\u3002\u30D4\u30A2\u30CE\u30ED\u30FC\u30EB\u306B\u306F\u5B9F\u969B\u306B\u558B\u308B\u9577\u3055\u304C\u7834\u7DDA\u306E\u5E2F\u3067\u51FA\u308B\u306E\u3067\u3001\u6B21\u306E\u6B4C\u3044\u51FA\u3057\u3068\u91CD\u306A\u3089\u306A\u3044\u304B\u78BA\u304B\u3081\u3089\u308C\u307E\u3059\u3002</li>
|
|
30498
|
+
<li><strong>\u97F3\u7B26\u306E\u9AD8\u3055</strong>\u306F\u8A71\u3059\u58F0\u306E\u9AD8\u3055\u3067\u3059\u3002\u9EC4\u8272\u3044\u300C\u8A9E\u308A\u306E\u57FA\u6E96\u300D\u306E\u884C\u306B\u7F6E\u304F\u3068\u97F3\u6E90\u306E\u7D20\u306E\u58F0\u3001\u4E0A\u306B\u7F6E\u304F\u307B\u3069\u9AD8\u3044\u58F0\u306B\u306A\u308A\u307E\u3059\uFF08\u76EE\u5B89\u306F\xB17\u534A\u97F3\u307E\u3067\uFF09\u3002</li>
|
|
30499
|
+
<li>\u7A7A\u306E <code>\u300C\u300D</code> \u306F <code>_</code> \u3068\u540C\u3058\uFF08\u97F3\u7B26\u30921\u3064\u6D88\u8CBB\u3057\u3066\u7121\u97F3\uFF09\u3002\u9589\u3058\u62EC\u5F27\u304C\u7121\u3051\u308C\u3070\u884C\u672B\u307E\u3067\u304C\u8A9E\u308A\u306B\u306A\u308A\u307E\u3059\u3002</li>
|
|
30500
|
+
<li>\u4E2D\u8EAB\u306B <code>;</code> \u3068\u6539\u884C\u306F\u66F8\u3051\u307E\u305B\u3093\uFF08MML\u306E\u533A\u5207\u308A\u6587\u5B57\u306E\u305F\u3081\uFF09\u3002</li>
|
|
30501
|
+
<li>\u521D\u3081\u3066\u8A9E\u308A\u3092\u9CF4\u3089\u3059\u3068\u304D\u306F\u8F9E\u66F8\u3068\u97F3\u58F0\u30E2\u30C7\u30EB\uFF08\u7D0445MB\uFF09\u3092\u53D6\u5F97\u3059\u308B\u306E\u3067\u3001\u518D\u751F\u958B\u59CB\u307E\u3067\u5C11\u3057\u5F85\u3061\u307E\u3059\u30022\u56DE\u76EE\u4EE5\u964D\u306F\u4FDD\u5B58\u6E08\u307F\u306E\u3082\u306E\u3092\u4F7F\u3044\u307E\u3059\u3002</li>
|
|
28463
30502
|
</ul>
|
|
28464
|
-
<p><small>\u5168\u89D2\u30C1\u30EB\u30C0 <code>\uFF5E</code> \u306F <code>\u301C</code>\u3001\u534A\u89D2\u30AB\u30F3\u30DE <code>,</code> \u306F <code>\u3001</code>\u3001<code>\u21E9</code> <code>\u2B07</code> \u306F <code>\u2193</code>\u3001<code>\u21E7</code> <code>\u2B06</code> \u306F <code>\u2191</code> \u3068\u3057\u3066\u6271\u308F\u308C\u307E\u3059\u3002\u3053\u308C\u3089\u4EE5\u5916\u306E\u8A18\u53F7\uFF08\u82F1\u6570\u5B57\u30FB\u30B9\u30DA\u30FC\u30B9\u30FB\u53E5\u8AAD\u70B9\u306A\u3069\uFF09\u306F\u7121\u8996\u3055\u308C\u307E\u3059\u3002</small></p>
|
|
28465
30503
|
|
|
28466
30504
|
<h4>\u5F37\u5F31\u3092\u3064\u3051\u308B\uFF08<code>\u2193</code> <code>\u2191</code>\uFF09</h4>
|
|
28467
30505
|
<p>\u4F38\u3070\u3057\u305F\u97F3\u306B\u4ED8\u3051\u308B\u3068\u3001<strong>\u8A00\u3044\u76F4\u3055\u305A\u306B\u4F38\u3070\u3057\u305F\u307E\u307E</strong>\u58F0\u91CF\u3060\u3051\u304C\u52D5\u304D\u307E\u3059\u3002\u66F2\u306E\u7D42\u308F\u308A\u306E\u4F59\u97FB\u3084\u3001\u30B5\u30D3\u3078\u306E\u76DB\u308A\u4E0A\u3052\u306B\u4F7F\u3044\u307E\u3059\u3002</p>
|
|
@@ -28848,6 +30886,27 @@ var MIDI_INFO_HTML = `
|
|
|
28848
30886
|
<p>UTAU\u306EUST\u30D5\u30A1\u30A4\u30EB\u306F\u3001\u97F3\u7B26\u3068\u6B4C\u8A5E\u3092\u307E\u3068\u3081\u3066\u76F4\u63A5\u8AAD\u307F\u8FBC\u3081\u307E\u3059\uFF08MIDI\u3078\u66F8\u304D\u51FA\u3059\u5FC5\u8981\u306F\u3042\u308A\u307E\u305B\u3093\uFF09\u3002\u4E0B\u306E\u300CUST\u300D\u6B04\u3092\u304A\u4F7F\u3044\u304F\u3060\u3055\u3044\u3002</p>
|
|
28849
30887
|
</div>
|
|
28850
30888
|
`;
|
|
30889
|
+
var MUSICXML_INFO_HTML = `
|
|
30890
|
+
<div class="dtm-modal-body-content">
|
|
30891
|
+
<h4>1. MusicXML\u3068\u306F</h4>
|
|
30892
|
+
<p>MuseScore\u3001Finale\u3001Sibelius\u306A\u3069\u306E\u697D\u8B5C\u4F5C\u6210\u30BD\u30D5\u30C8\u3067\u5E83\u304F\u4F7F\u308F\u308C\u3066\u3044\u308B\u697D\u8B5C\u30D5\u30A9\u30FC\u30DE\u30C3\u30C8\u3067\u3059\u3002MIDI\u3068\u9055\u3063\u3066<strong>\u30D1\u30FC\u30C8\u69CB\u6210\u3084\u97F3\u7B26\u306B\u7D10\u3065\u3044\u305F\u6B4C\u8A5E</strong>\u3092\u4FDD\u6301\u3067\u304D\u308B\u305F\u3081\u3001\u4E3B\u65CB\u5F8B\u306E\u7279\u5B9A\u3084\u6B4C\u58F0\u5408\u6210\u3078\u306E\u53D7\u3051\u6E21\u3057\u304C\u78BA\u5B9F\u306B\u884C\u3048\u307E\u3059\u3002</p>
|
|
30893
|
+
|
|
30894
|
+
<h4>2. \u8AAD\u307F\u8FBC\u307F\u306E\u3057\u304B\u305F</h4>
|
|
30895
|
+
<ul>
|
|
30896
|
+
<li>\u300C\u30D5\u30A1\u30A4\u30EB\u3092\u9078\u629E\u300D\u304B\u3089 <code>.musicxml</code> \u307E\u305F\u306F <code>.xml</code> \u30D5\u30A1\u30A4\u30EB\u3092\u9078\u3073\u307E\u3059\u3002</li>
|
|
30897
|
+
<li>\u30D5\u30A1\u30A4\u30EB\u5185\u306B\u542B\u307E\u308C\u308B\u30D1\u30FC\u30C8\u4E00\u89A7\u304C\u8868\u793A\u3055\u308C\u308B\u306E\u3067\u3001\u8AAD\u307F\u8FBC\u307F\u305F\u3044\u30D1\u30FC\u30C8\u3092\u9078\u629E\u3057\u3066\u300C\u8AAD\u8FBC\u300D\u3092\u62BC\u3057\u307E\u3059\u3002</li>
|
|
30898
|
+
<li>\u6B4C\u8A5E\u304C\u542B\u307E\u308C\u3066\u3044\u308B\u30D1\u30FC\u30C8\u306F\u81EA\u52D5\u7684\u306B\u6B4C\u58F0\u5408\u6210\uFF08UTAU\uFF09\u30C8\u30E9\u30C3\u30AF\u3068\u3057\u3066\u8A2D\u5B9A\u3055\u308C\u3001\u6B4C\u8A5E\u3082\u6D41\u3057\u8FBC\u307E\u308C\u307E\u3059\u3002</li>
|
|
30899
|
+
<li>\u30C6\u30F3\u30DD\uFF08BPM\uFF09\u60C5\u5831\u304C\u3042\u308B\u5834\u5408\u306F\u81EA\u52D5\u7684\u306B\u53CD\u6620\u3055\u308C\u307E\u3059\u3002</li>
|
|
30900
|
+
<li>\u300C\u73FE\u5728\u306E\u30C8\u30E9\u30C3\u30AF\u306E\u307F\u5BFE\u8C61\u3068\u3059\u308B\u300D\u304C\u6709\u52B9\u306A\u3068\u304D\u306F\u3001\u9078\u629E\u3057\u305F\u6700\u521D\u306E\u30D1\u30FC\u30C8\u3060\u3051\u304C\u73FE\u5728\u30A2\u30AF\u30C6\u30A3\u30D6\u306A\u30C8\u30E9\u30C3\u30AF\u306B\u8AAD\u307F\u8FBC\u307E\u308C\u307E\u3059\u3002</li>
|
|
30901
|
+
</ul>
|
|
30902
|
+
|
|
30903
|
+
<h4>3. \u66F8\u304D\u51FA\u3057</h4>
|
|
30904
|
+
<ul>
|
|
30905
|
+
<li>\u300CMIDI / MusicXML / UST / MML \u51FA\u529B\u300D\u306E\u300CMusicXML\u51FA\u529B\u300D\u3067\u3001\u5168\u30C8\u30E9\u30C3\u30AF\u3092 <code>score-partwise</code> \u5F62\u5F0F\u306E MusicXML \u30D5\u30A1\u30A4\u30EB\u3068\u3057\u3066\u66F8\u304D\u51FA\u305B\u307E\u3059\u3002</li>
|
|
30906
|
+
<li>\u97F3\u7B26\u3084\u30C8\u30E9\u30C3\u30AF\u540D\u306B\u52A0\u3048\u3066\u3001\u8A2D\u5B9A\u3055\u308C\u305F\u6B4C\u8A5E\u3082\u305D\u306E\u307E\u307E\u697D\u8B5C\u306E\u6B4C\u8A5E\u3068\u3057\u3066\u51FA\u529B\u3055\u308C\u307E\u3059\u3002</li>
|
|
30907
|
+
</ul>
|
|
30908
|
+
</div>
|
|
30909
|
+
`;
|
|
28851
30910
|
var UST_INFO_HTML = `
|
|
28852
30911
|
<div class="dtm-modal-body-content">
|
|
28853
30912
|
<h4>1. UST\u30D5\u30A1\u30A4\u30EB\u3068\u306F</h4>
|
|
@@ -28989,6 +31048,7 @@ var HELP_INFO_HTML = `
|
|
|
28989
31048
|
<button class="dtm-btn dtm-btn--ghost" data-dtm-help="koe">\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0(.koe)</button>
|
|
28990
31049
|
<button class="dtm-btn dtm-btn--ghost" data-dtm-help="chord">\u30B3\u30FC\u30C9\u9032\u884C</button>
|
|
28991
31050
|
<button class="dtm-btn dtm-btn--ghost" data-dtm-help="midi">MIDI\u306E\u8AAD\u307F\u8FBC\u307F</button>
|
|
31051
|
+
<button class="dtm-btn dtm-btn--ghost" data-dtm-help="musicxml">MusicXML</button>
|
|
28992
31052
|
<button class="dtm-btn dtm-btn--ghost" data-dtm-help="ust">UST(UTAU)</button>
|
|
28993
31053
|
<button class="dtm-btn dtm-btn--ghost" data-dtm-help="mml">MML\u306E\u66F8\u304D\u65B9</button>
|
|
28994
31054
|
<button class="dtm-btn dtm-btn--ghost" data-dtm-help="loop">\u30EB\u30FC\u30D7\u518D\u751F</button>
|
|
@@ -29007,6 +31067,7 @@ var HELP_TOPICS = {
|
|
|
29007
31067
|
koe: { title: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0(.koe)\u306E\u4F7F\u3044\u65B9", html: KOE_INFO_HTML },
|
|
29008
31068
|
chord: { title: "\u30B3\u30FC\u30C9\u9032\u884C\u306E\u81EA\u52D5\u5165\u529B\u89E3\u8AAC", html: CHORD_INFO_HTML2 },
|
|
29009
31069
|
midi: { title: "MIDI\u306E\u8AAD\u307F\u8FBC\u307F\u89E3\u8AAC", html: MIDI_INFO_HTML },
|
|
31070
|
+
musicxml: { title: "MusicXML\u306E\u5165\u51FA\u529B\u89E3\u8AAC", html: MUSICXML_INFO_HTML },
|
|
29010
31071
|
ust: { title: "UST\u306E\u8AAD\u307F\u8FBC\u307F\u89E3\u8AAC", html: UST_INFO_HTML },
|
|
29011
31072
|
mml: { title: "MML\u306E\u66F8\u304D\u65B9\u89E3\u8AAC", html: MML_INFO_HTML },
|
|
29012
31073
|
loop: { title: "\u30EB\u30FC\u30D7\u518D\u751F\u306E\u89E3\u8AAC", html: LOOP_INFO_HTML },
|
|
@@ -29461,6 +31522,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
29461
31522
|
};
|
|
29462
31523
|
let currentInstrument = "";
|
|
29463
31524
|
let autoComposeInstrument = null;
|
|
31525
|
+
let composeSeed = null;
|
|
31526
|
+
let composeSetting = null;
|
|
29464
31527
|
let activeTrackId = options.initialActiveTrack ?? trackConfigs[0].id;
|
|
29465
31528
|
let trackFxAdvancedOpen = readPanelOpen("track-fx-advanced") ?? false;
|
|
29466
31529
|
let lyricAdvancedOpen = readPanelOpen("lyric-advanced") ?? false;
|
|
@@ -29703,17 +31766,66 @@ var mountDAW = (target, options = {}) => {
|
|
|
29703
31766
|
const getActive = () => trackStates.find((t) => t.config.id === activeTrackId) ?? trackStates[0];
|
|
29704
31767
|
let lyricKanaCacheText = "";
|
|
29705
31768
|
let lyricKanaCache = [];
|
|
29706
|
-
|
|
31769
|
+
let lyricSyllableCache = [];
|
|
31770
|
+
const getActiveLyricSyllables = () => {
|
|
29707
31771
|
const t = getActive();
|
|
29708
31772
|
if (!t?.lyricModel.trim()) return [];
|
|
29709
31773
|
const text = t.lyrics.trim();
|
|
29710
31774
|
if (!text) return [];
|
|
29711
31775
|
if (text !== lyricKanaCacheText) {
|
|
29712
31776
|
lyricKanaCacheText = text;
|
|
29713
|
-
|
|
31777
|
+
lyricSyllableCache = normalizeLyrics(text);
|
|
31778
|
+
lyricKanaCache = lyricSyllableCache.map(displayKana);
|
|
29714
31779
|
}
|
|
31780
|
+
return lyricSyllableCache;
|
|
31781
|
+
};
|
|
31782
|
+
const getActiveLyricKana = () => {
|
|
31783
|
+
getActiveLyricSyllables();
|
|
29715
31784
|
return lyricKanaCache;
|
|
29716
31785
|
};
|
|
31786
|
+
const speechPreviewRequested = /* @__PURE__ */ new Set();
|
|
31787
|
+
const requestSpeechPreview = (model, text) => {
|
|
31788
|
+
const voices = options.singingVoices;
|
|
31789
|
+
if (!voices?.planSpeech) return;
|
|
31790
|
+
const key = `${model}|${text}`;
|
|
31791
|
+
if (speechPreviewRequested.has(key)) return;
|
|
31792
|
+
speechPreviewRequested.add(key);
|
|
31793
|
+
void voices.planSpeech(model, text).then((d) => {
|
|
31794
|
+
if (d !== null) redrawAll();
|
|
31795
|
+
}).catch(() => {
|
|
31796
|
+
});
|
|
31797
|
+
};
|
|
31798
|
+
const drawSpeechPreview = (notes) => {
|
|
31799
|
+
const voices = options.singingVoices;
|
|
31800
|
+
if (!voices) return;
|
|
31801
|
+
const syllables = getActiveLyricSyllables();
|
|
31802
|
+
if (!syllables.some((s) => s.kind === "speak")) return;
|
|
31803
|
+
const model = getActive().lyricModel.trim().toLowerCase();
|
|
31804
|
+
const sorted = [...notes].sort((a, b) => a.startStep - b.startStep);
|
|
31805
|
+
const count = Math.min(sorted.length, syllables.length);
|
|
31806
|
+
const secondsPerStep = 60 / bpm / 48;
|
|
31807
|
+
const spans = [];
|
|
31808
|
+
for (let i2 = 0; i2 < count; i2++) {
|
|
31809
|
+
const syl = syllables[i2];
|
|
31810
|
+
if (syl.kind !== "speak" || !syl.text) continue;
|
|
31811
|
+
const d = voices.peekSpeechDurationSec?.(model, syl.text);
|
|
31812
|
+
if (d === void 0) {
|
|
31813
|
+
requestSpeechPreview(model, syl.text);
|
|
31814
|
+
continue;
|
|
31815
|
+
}
|
|
31816
|
+
spans.push({ note: sorted[i2], durationSteps: d / secondsPerStep });
|
|
31817
|
+
}
|
|
31818
|
+
renderer.drawSpeechSpans(spans, getActive().config.color);
|
|
31819
|
+
};
|
|
31820
|
+
const drawSpeechPitchGuide = () => {
|
|
31821
|
+
const voices = options.singingVoices;
|
|
31822
|
+
if (!voices?.getSpeechReferenceUnits) return;
|
|
31823
|
+
if (!getActiveLyricSyllables().some((s) => s.kind === "speak")) return;
|
|
31824
|
+
const ref = voices.getSpeechReferenceUnits(
|
|
31825
|
+
getActive().lyricModel.trim().toLowerCase()
|
|
31826
|
+
);
|
|
31827
|
+
if (ref !== void 0) renderer.drawPitchGuide(ref, "\u8A9E\u308A\u306E\u57FA\u6E96");
|
|
31828
|
+
};
|
|
29717
31829
|
let showModal;
|
|
29718
31830
|
let showConfirm;
|
|
29719
31831
|
const playableNotes = (t) => {
|
|
@@ -29798,6 +31910,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
29798
31910
|
};
|
|
29799
31911
|
const redrawAll = () => {
|
|
29800
31912
|
renderer.drawGrid(gridLineSteps);
|
|
31913
|
+
drawSpeechPitchGuide();
|
|
29801
31914
|
let lyricTargetNotes = null;
|
|
29802
31915
|
let activeTrackState = null;
|
|
29803
31916
|
for (const t of trackStates) {
|
|
@@ -29846,8 +31959,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
29846
31959
|
1
|
|
29847
31960
|
]);
|
|
29848
31961
|
}
|
|
29849
|
-
if (lyricTargetNotes)
|
|
31962
|
+
if (lyricTargetNotes) {
|
|
31963
|
+
drawSpeechPreview(lyricTargetNotes);
|
|
29850
31964
|
renderer.drawNoteLyrics(lyricTargetNotes, getActiveLyricKana());
|
|
31965
|
+
}
|
|
29851
31966
|
drawStartLine();
|
|
29852
31967
|
if (playbackState === "playing") drawPlayhead();
|
|
29853
31968
|
updateScrollbars();
|
|
@@ -31452,9 +33567,9 @@ var mountDAW = (target, options = {}) => {
|
|
|
31452
33567
|
</details>
|
|
31453
33568
|
<div class="dtm-row">
|
|
31454
33569
|
<span class="dtm-label dtm-grow">\u6B4C\u8A5E</span>
|
|
31455
|
-
<button class="dtm-infobtn" data-dtm="lyric-input-info" title="\u6B4C\u8A5E\u306E\u66F8\u304D\u65B9\uFF08\u30FC\u30FB\u301C\u30FB\u3063\u30FB_\u30FB\u3001\u306E\u610F\u5473\uFF09">${icon("info", 12)}</button>
|
|
33570
|
+
<button class="dtm-infobtn" data-dtm="lyric-input-info" title="\u6B4C\u8A5E\u306E\u66F8\u304D\u65B9\uFF08\u30FC\u30FB\u301C\u30FB\u3063\u30FB_\u30FB\u3001\u30FB\u300C\u300D\u306E\u610F\u5473\uFF09">${icon("info", 12)}</button>
|
|
31456
33571
|
</div>
|
|
31457
|
-
<textarea class="dtm-textarea" data-dtm="lyric-input" rows="2" placeholder="\u3072\u3089\u304C\u306A\u30FB\u30AB\u30BF\u30AB\u30CA\u3067\u6B4C\u8A5E\uFF08\u4F8B: \u3069\u308C\u307F\u3075\u3041\u305D\u3089\u3057\u3069\uFF09 \u30FC=\u4F38\u3070\u3059 \u301C=\u3057\u3083\u304F\u308A \u3063=\u8A70\u307E\u308B _=\u6B4C\u308F\u306A\u3044 \u3001=\u30D6\u30EC\u30B9"></textarea>
|
|
33572
|
+
<textarea class="dtm-textarea" data-dtm="lyric-input" rows="2" placeholder="\u3072\u3089\u304C\u306A\u30FB\u30AB\u30BF\u30AB\u30CA\u3067\u6B4C\u8A5E\uFF08\u4F8B: \u3069\u308C\u307F\u3075\u3041\u305D\u3089\u3057\u3069\uFF09 \u30FC=\u4F38\u3070\u3059 \u301C=\u3057\u3083\u304F\u308A \u3063=\u8A70\u307E\u308B _=\u6B4C\u308F\u306A\u3044 \u3001=\u30D6\u30EC\u30B9 \u300C\u2026\u300D=\u8A9E\u308A\uFF08\u8AAD\u307F\u4E0A\u3052\uFF09"></textarea>
|
|
31458
33573
|
</div>`;
|
|
31459
33574
|
refs.trackBody.appendChild(lyricDiv);
|
|
31460
33575
|
lyricDiv.querySelector(
|
|
@@ -31654,8 +33769,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
31654
33769
|
lyricVibrato.checked = active.vocalVibrato;
|
|
31655
33770
|
lyricOctaveUnison.value = active.vocalOctaveUnison;
|
|
31656
33771
|
const updateLyricCount = () => {
|
|
31657
|
-
const
|
|
31658
|
-
|
|
33772
|
+
const syllables = normalizeLyrics(lyricInput.value);
|
|
33773
|
+
const n = syllables.length;
|
|
33774
|
+
const speak = syllables.filter((s) => s.kind === "speak").length;
|
|
33775
|
+
lyricCount.textContent = active.lyricModel && n > 0 ? `${n}\u97F3\u7BC0${speak > 0 ? `\uFF08\u8A9E\u308A${speak}\uFF09` : ""}` : "";
|
|
31659
33776
|
};
|
|
31660
33777
|
const syncLyricTerms = () => {
|
|
31661
33778
|
if (customVocalsMap.has(active.lyricModel)) {
|
|
@@ -31967,6 +34084,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
31967
34084
|
mode,
|
|
31968
34085
|
edo: renderConfig.edo,
|
|
31969
34086
|
loop: loopEnabled ? true : void 0,
|
|
34087
|
+
seed: composeSeed ?? void 0,
|
|
34088
|
+
compose: composeSetting ?? void 0,
|
|
31970
34089
|
// アップロードされたファイルは url が空=音源関連の宣言ごと出力されない
|
|
31971
34090
|
audio: backing.url || void 0,
|
|
31972
34091
|
audioStart: backing.rangeStartSec || void 0,
|
|
@@ -32006,6 +34125,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
32006
34125
|
mode,
|
|
32007
34126
|
edo: renderConfig.edo,
|
|
32008
34127
|
loop: loopEnabled ? true : void 0,
|
|
34128
|
+
seed: composeSeed ?? void 0,
|
|
34129
|
+
compose: composeSetting ?? void 0,
|
|
32009
34130
|
// アップロードされたファイルは url が空=音源関連の宣言ごと出力されない
|
|
32010
34131
|
audio: backing.url || void 0,
|
|
32011
34132
|
audioStart: backing.rangeStartSec || void 0,
|
|
@@ -32062,7 +34183,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
32062
34183
|
const lyricLines = trackStates.map((t, i2) => ({
|
|
32063
34184
|
i: i2,
|
|
32064
34185
|
notes: clipNotes(t.core.getNotes()),
|
|
32065
|
-
|
|
34186
|
+
// `;` は MML の区切り文字なので、語り(「…」)の本文に紛れていても全角へ逃がす。
|
|
34187
|
+
text: t.lyrics.replace(/[\r\n]+/g, " ").replace(/;/g, "\uFF1B").trim(),
|
|
32066
34188
|
model: t.lyricModel.trim(),
|
|
32067
34189
|
vol: t.vocalVolume,
|
|
32068
34190
|
gate: t.vocalGate,
|
|
@@ -32205,6 +34327,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
32205
34327
|
clampTrackCount: trackStates.length
|
|
32206
34328
|
});
|
|
32207
34329
|
if (!applyActiveOnly) {
|
|
34330
|
+
composeSeed = meta.seed ?? null;
|
|
34331
|
+
composeSetting = meta.compose ?? null;
|
|
32208
34332
|
if (meta.instrument && INSTRUMENT_PRESETS[meta.instrument]) {
|
|
32209
34333
|
currentInstrument = meta.instrument;
|
|
32210
34334
|
options.onInstrumentChange?.(meta.instrument);
|
|
@@ -32594,6 +34718,92 @@ var mountDAW = (target, options = {}) => {
|
|
|
32594
34718
|
dropped: ustTracks.length - targets.length
|
|
32595
34719
|
};
|
|
32596
34720
|
};
|
|
34721
|
+
const applyMusicXmlSelection = (xml, selectedIndices, startIndex) => {
|
|
34722
|
+
stop();
|
|
34723
|
+
const applyActiveOnly = refs.applyActiveOnly?.checked ?? false;
|
|
34724
|
+
const from = Math.max(
|
|
34725
|
+
0,
|
|
34726
|
+
startIndex ?? (applyActiveOnly ? activeTrackIndexOf() : selectedIndices.length > 1 ? 0 : activeTrackIndexOf())
|
|
34727
|
+
);
|
|
34728
|
+
const maxCount = applyActiveOnly ? 1 : trackStates.length - from;
|
|
34729
|
+
const targetIndices = selectedIndices.slice(0, Math.max(0, maxCount));
|
|
34730
|
+
const voice = trackStates[from]?.lyricModel || pickComposeVocal();
|
|
34731
|
+
targetIndices.forEach((partIndex, i2) => {
|
|
34732
|
+
const t = trackStates[from + i2];
|
|
34733
|
+
if (!t) return;
|
|
34734
|
+
const notes = musicXmlToNotes(xml.placements, partIndex);
|
|
34735
|
+
t.core.setLoadMode(true);
|
|
34736
|
+
t.core.clearNotesWithoutHistory();
|
|
34737
|
+
for (const n of notes) {
|
|
34738
|
+
t.core.addNote(n.startStep, snapToEdoGrid(n.pitchUnits), {
|
|
34739
|
+
noteLengthSteps: n.durationSteps,
|
|
34740
|
+
velocity: n.velocity
|
|
34741
|
+
});
|
|
34742
|
+
}
|
|
34743
|
+
t.core.setLoadMode(false);
|
|
34744
|
+
t.core.addHistoryOnce();
|
|
34745
|
+
const partPlacements = xml.placements.filter((p) => p.partIndex === partIndex).sort((a, b) => a.startStep - b.startStep);
|
|
34746
|
+
const hasLyrics = partPlacements.some(
|
|
34747
|
+
(p) => Boolean(p.lyric && p.lyric.trim())
|
|
34748
|
+
);
|
|
34749
|
+
if (hasLyrics) {
|
|
34750
|
+
const syllables = [];
|
|
34751
|
+
for (const p of partPlacements) {
|
|
34752
|
+
const l = p.lyric?.trim();
|
|
34753
|
+
syllables.push(l || "\u30FC");
|
|
34754
|
+
}
|
|
34755
|
+
t.lyrics = syllables.join("");
|
|
34756
|
+
t.vocalVolume = DEFAULT_VOCAL_VOLUME;
|
|
34757
|
+
t.vocalGate = 100;
|
|
34758
|
+
t.vocalPan = 64;
|
|
34759
|
+
t.vocalOctave = 0;
|
|
34760
|
+
t.vocalVibrato = false;
|
|
34761
|
+
t.vocalReverb = 0;
|
|
34762
|
+
t.vocalDelay = 0;
|
|
34763
|
+
t.vocalGender = 50;
|
|
34764
|
+
t.vocalBreathiness = 50;
|
|
34765
|
+
t.vocalTension = 50;
|
|
34766
|
+
t.vocalOctaveUnison = "none";
|
|
34767
|
+
if (!t.lyricModel) t.lyricModel = voice;
|
|
34768
|
+
fireLyricsChange(t);
|
|
34769
|
+
} else {
|
|
34770
|
+
t.lyrics = "";
|
|
34771
|
+
t.lyricModel = "";
|
|
34772
|
+
}
|
|
34773
|
+
});
|
|
34774
|
+
if (xml.bpm > 0) setBpm(Math.round(xml.bpm));
|
|
34775
|
+
playStartStep = 0;
|
|
34776
|
+
currentOffsetX = 0;
|
|
34777
|
+
const firstPitch = getFirstDetectedPitch();
|
|
34778
|
+
centerPitch(firstPitch ?? pitchV1ToUnits(60));
|
|
34779
|
+
redrawAll();
|
|
34780
|
+
updateTrackPanel();
|
|
34781
|
+
updateUndoRedo();
|
|
34782
|
+
return {
|
|
34783
|
+
applied: targetIndices.length,
|
|
34784
|
+
dropped: selectedIndices.length - targetIndices.length
|
|
34785
|
+
};
|
|
34786
|
+
};
|
|
34787
|
+
const exportMusicXML2 = () => {
|
|
34788
|
+
const parts = trackStates.map((t) => {
|
|
34789
|
+
const notes = playableNotes(t);
|
|
34790
|
+
const syllables = t.lyrics ? normalizeLyrics(t.lyrics).map(displayKana) : void 0;
|
|
34791
|
+
return {
|
|
34792
|
+
name: t.config.name,
|
|
34793
|
+
notes,
|
|
34794
|
+
lyrics: syllables
|
|
34795
|
+
};
|
|
34796
|
+
});
|
|
34797
|
+
const xml = exportMusicXML({
|
|
34798
|
+
parts,
|
|
34799
|
+
bpm,
|
|
34800
|
+
stepsPerBar: renderConfig.stepsPerBar,
|
|
34801
|
+
title: "DTM Project"
|
|
34802
|
+
});
|
|
34803
|
+
return new Blob([xml], {
|
|
34804
|
+
type: "application/vnd.recordare.musicxml+xml;charset=utf-8"
|
|
34805
|
+
});
|
|
34806
|
+
};
|
|
32597
34807
|
const exportUST = () => {
|
|
32598
34808
|
const t = getActive();
|
|
32599
34809
|
const syllables = normalizeLyrics(t.lyrics).map(displayKana);
|
|
@@ -33273,17 +35483,29 @@ var mountDAW = (target, options = {}) => {
|
|
|
33273
35483
|
stop();
|
|
33274
35484
|
overlayDuring(() => {
|
|
33275
35485
|
const tmpl = selectedComposeTemplate();
|
|
35486
|
+
const sections = selectedComposeSections();
|
|
35487
|
+
const baseKey = refs.composeKey?.value ?? "any";
|
|
35488
|
+
const scale = refs.composeScale?.value ?? "auto";
|
|
35489
|
+
const seed = Math.random() * 4294967296 >>> 0;
|
|
33276
35490
|
const song = composeSong({
|
|
33277
35491
|
stepsPerBar: renderConfig.stepsPerBar,
|
|
33278
35492
|
edo: renderConfig.edo,
|
|
33279
|
-
sections
|
|
35493
|
+
sections,
|
|
33280
35494
|
template: tmpl,
|
|
33281
|
-
baseKey
|
|
33282
|
-
scale
|
|
35495
|
+
baseKey,
|
|
35496
|
+
scale,
|
|
35497
|
+
random: seededRandom(seed),
|
|
33283
35498
|
// 直近に作った曲の特徴を渡すと、それらから離れた候補に加点される。
|
|
33284
35499
|
// 「作曲」を続けて押したときに似た曲が並ぶのを防ぐ。
|
|
33285
35500
|
recent: recentComposeFingerprints
|
|
33286
35501
|
});
|
|
35502
|
+
composeSeed = seed;
|
|
35503
|
+
composeSetting = [
|
|
35504
|
+
tmpl ?? "custom",
|
|
35505
|
+
baseKey,
|
|
35506
|
+
scale,
|
|
35507
|
+
sections.join("-")
|
|
35508
|
+
].join(":");
|
|
33287
35509
|
recentComposeFingerprints.push(song.stats.fingerprint);
|
|
33288
35510
|
if (recentComposeFingerprints.length > 5)
|
|
33289
35511
|
recentComposeFingerprints.shift();
|
|
@@ -33464,6 +35686,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
33464
35686
|
updateTrackPanel();
|
|
33465
35687
|
updateUndoRedo();
|
|
33466
35688
|
composedSignature = trackSignature();
|
|
35689
|
+
void play();
|
|
33467
35690
|
});
|
|
33468
35691
|
};
|
|
33469
35692
|
const composeWithConfirm = (withVocal) => {
|
|
@@ -33483,28 +35706,47 @@ var mountDAW = (target, options = {}) => {
|
|
|
33483
35706
|
refs.macroCompose.addEventListener("click", () => {
|
|
33484
35707
|
composeWithConfirm(false);
|
|
33485
35708
|
});
|
|
33486
|
-
let
|
|
35709
|
+
let kept = readKeptSong();
|
|
33487
35710
|
const updateKeepUI = () => {
|
|
33488
|
-
refs.composeRecall.disabled =
|
|
33489
|
-
refs.composeKeep.textContent =
|
|
35711
|
+
refs.composeRecall.disabled = kept === null;
|
|
35712
|
+
refs.composeKeep.textContent = kept === null ? "\u30AD\u30FC\u30D7" : "\u30AD\u30FC\u30D7\u6E08";
|
|
33490
35713
|
};
|
|
35714
|
+
const snapshotKept = () => ({
|
|
35715
|
+
// 小節数の上限は共有リンクの文字数のためのもので、アプリ内の退避には
|
|
35716
|
+
// 効かせない(効かせると長い曲がキープした時点で切れる)。
|
|
35717
|
+
mml: generateMML({ ignoreBarLimit: true }).full,
|
|
35718
|
+
startStep: playStartStep
|
|
35719
|
+
});
|
|
33491
35720
|
refs.composeKeep.addEventListener("click", () => {
|
|
33492
35721
|
const hasNotes = trackStates.some((t) => t.core.getNotes().length > 0);
|
|
33493
35722
|
if (!hasNotes) return;
|
|
33494
|
-
|
|
35723
|
+
kept = snapshotKept();
|
|
35724
|
+
writeKeptSong(kept);
|
|
33495
35725
|
updateKeepUI();
|
|
33496
35726
|
});
|
|
33497
|
-
|
|
33498
|
-
if (keptMml === null) return;
|
|
35727
|
+
const loadKept = (song) => {
|
|
33499
35728
|
const box = refs.applyActiveOnly;
|
|
33500
35729
|
const was = box?.checked ?? false;
|
|
33501
35730
|
if (box) box.checked = false;
|
|
33502
35731
|
try {
|
|
33503
|
-
loadMML(
|
|
35732
|
+
loadMML(song.mml);
|
|
33504
35733
|
} finally {
|
|
33505
35734
|
if (box) box.checked = was;
|
|
33506
35735
|
}
|
|
35736
|
+
playStartStep = Math.min(song.startStep, getMaxNoteStep());
|
|
33507
35737
|
composedSignature = null;
|
|
35738
|
+
void play();
|
|
35739
|
+
};
|
|
35740
|
+
refs.composeRecall.addEventListener("click", () => {
|
|
35741
|
+
if (kept === null) return;
|
|
35742
|
+
const previous = kept;
|
|
35743
|
+
const hasNotes = trackStates.some((t) => t.core.getNotes().length > 0);
|
|
35744
|
+
if (hasNotes) {
|
|
35745
|
+
kept = snapshotKept();
|
|
35746
|
+
writeKeptSong(kept);
|
|
35747
|
+
}
|
|
35748
|
+
loadKept(previous);
|
|
35749
|
+
updateKeepUI();
|
|
33508
35750
|
});
|
|
33509
35751
|
updateKeepUI();
|
|
33510
35752
|
refs.macroComposeVocal.addEventListener("click", () => {
|
|
@@ -33563,6 +35805,9 @@ var mountDAW = (target, options = {}) => {
|
|
|
33563
35805
|
refs.exportMidiBtn.addEventListener("click", () => {
|
|
33564
35806
|
download(exportMIDI2(), "dtm.mid");
|
|
33565
35807
|
});
|
|
35808
|
+
refs.exportMusicXmlBtn.addEventListener("click", () => {
|
|
35809
|
+
download(exportMusicXML2(), "dtm.musicxml");
|
|
35810
|
+
});
|
|
33566
35811
|
refs.exportUstBtn.addEventListener("click", () => {
|
|
33567
35812
|
const name = getActive().config.name.replace(/[/:*?"<>|\s]+/g, "_");
|
|
33568
35813
|
download(exportUST(), `dtm_${name}.ust`);
|
|
@@ -33830,6 +36075,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
33830
36075
|
});
|
|
33831
36076
|
if (showMidi) {
|
|
33832
36077
|
wireMidi();
|
|
36078
|
+
wireMusicXml();
|
|
33833
36079
|
wireUst();
|
|
33834
36080
|
}
|
|
33835
36081
|
if (showAudio) {
|
|
@@ -33930,6 +36176,125 @@ var mountDAW = (target, options = {}) => {
|
|
|
33930
36176
|
overlayDuring(() => applyMidiSelection(pendingMidi, selected));
|
|
33931
36177
|
});
|
|
33932
36178
|
};
|
|
36179
|
+
let pendingMusicXml = null;
|
|
36180
|
+
const setMusicXmlNote = (text) => {
|
|
36181
|
+
refs.musicXmlLoadNote.textContent = text;
|
|
36182
|
+
refs.musicXmlLoadNote.classList.toggle("dtm-hidden", text === "");
|
|
36183
|
+
};
|
|
36184
|
+
const getSelectedMusicXmlPartIndices = () => {
|
|
36185
|
+
if (!pendingMusicXml) return [];
|
|
36186
|
+
const selected = [];
|
|
36187
|
+
const btns = refs.musicXmlPartSelection.querySelectorAll("button");
|
|
36188
|
+
btns.forEach((b, i2) => {
|
|
36189
|
+
if (b.dataset.selected === "true") {
|
|
36190
|
+
selected.push(pendingMusicXml.parts[i2].index);
|
|
36191
|
+
}
|
|
36192
|
+
});
|
|
36193
|
+
return selected;
|
|
36194
|
+
};
|
|
36195
|
+
const updateMusicXmlAssignmentNote = () => {
|
|
36196
|
+
if (!pendingMusicXml) return;
|
|
36197
|
+
const selected = getSelectedMusicXmlPartIndices();
|
|
36198
|
+
if (selected.length === 0) {
|
|
36199
|
+
setMusicXmlNote("\u8AAD\u307F\u8FBC\u3080\u30D1\u30FC\u30C8\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044");
|
|
36200
|
+
return;
|
|
36201
|
+
}
|
|
36202
|
+
const applyActiveOnly = refs.applyActiveOnly?.checked ?? false;
|
|
36203
|
+
const from = applyActiveOnly ? activeTrackIndexOf() : selected.length > 1 ? 0 : activeTrackIndexOf();
|
|
36204
|
+
const maxCount = applyActiveOnly ? 1 : trackStates.length - from;
|
|
36205
|
+
const appliedCount = Math.min(selected.length, maxCount);
|
|
36206
|
+
const selectedParts = selected.map((idx) => pendingMusicXml.parts.find((p) => p.index === idx)).filter(Boolean);
|
|
36207
|
+
const names = selectedParts.slice(0, appliedCount).map(
|
|
36208
|
+
(p, i2) => `${trackStates[from + i2]?.config.name ?? `\u30C8\u30E9\u30C3\u30AF${from + i2 + 1}`}\u2190${p.name}`
|
|
36209
|
+
).join(" / ");
|
|
36210
|
+
const dropped = selected.length - appliedCount;
|
|
36211
|
+
const dropReason = applyActiveOnly ? "\u300C\u73FE\u5728\u306E\u30C8\u30E9\u30C3\u30AF\u306E\u307F\u300D\u304C\u6709\u52B9\u306A\u305F\u3081" : "\u30C8\u30E9\u30C3\u30AF\u4E0D\u8DB3\u306E\u305F\u3081";
|
|
36212
|
+
setMusicXmlNote(
|
|
36213
|
+
dropped > 0 ? `${names}\uFF08${dropReason}${dropped}\u30D1\u30FC\u30C8\u306F\u8AAD\u307F\u8FBC\u307F\u307E\u305B\u3093\uFF09` : names
|
|
36214
|
+
);
|
|
36215
|
+
};
|
|
36216
|
+
const wireMusicXml = () => {
|
|
36217
|
+
refs.musicXmlInfoBtn.addEventListener("click", () => {
|
|
36218
|
+
showModal("MusicXML\u306E\u5165\u51FA\u529B\u89E3\u8AAC", MUSICXML_INFO_HTML);
|
|
36219
|
+
});
|
|
36220
|
+
refs.musicXmlInput.addEventListener("change", async () => {
|
|
36221
|
+
const file = refs.musicXmlInput.files?.[0];
|
|
36222
|
+
pendingMusicXml = null;
|
|
36223
|
+
refs.musicXmlPartSelection.innerHTML = "";
|
|
36224
|
+
if (!file) {
|
|
36225
|
+
setMusicXmlNote("");
|
|
36226
|
+
refs.musicXmlPartSelection.classList.add("dtm-hidden");
|
|
36227
|
+
return;
|
|
36228
|
+
}
|
|
36229
|
+
refs.overlay.hidden = false;
|
|
36230
|
+
setLoading(true);
|
|
36231
|
+
try {
|
|
36232
|
+
const text = await file.text();
|
|
36233
|
+
pendingMusicXml = parseMusicXML(text);
|
|
36234
|
+
if (pendingMusicXml.parts.length === 0) {
|
|
36235
|
+
setMusicXmlNote("\u30D1\u30FC\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F");
|
|
36236
|
+
refs.musicXmlPartSelection.classList.add("dtm-hidden");
|
|
36237
|
+
return;
|
|
36238
|
+
}
|
|
36239
|
+
refs.musicXmlPartSelection.innerHTML = `<span class="dtm-label">\u30D1\u30FC\u30C8</span>`;
|
|
36240
|
+
pendingMusicXml.parts.forEach((p) => {
|
|
36241
|
+
const btn = document.createElement("button");
|
|
36242
|
+
btn.className = "dtm-btn dtm-btn--primary";
|
|
36243
|
+
btn.dataset.selected = "true";
|
|
36244
|
+
const lyricBadge = p.hasLyrics ? " \u{1F3A4}" : "";
|
|
36245
|
+
btn.textContent = `${p.name} (${p.noteCount}\u97F3${lyricBadge})`;
|
|
36246
|
+
btn.title = `\u5E73\u5747\u97F3\u9AD8: MIDI ${Math.round(p.avgPitch)}${p.hasLyrics ? " / \u6B4C\u8A5E\u3042\u308A" : ""}`;
|
|
36247
|
+
btn.addEventListener("click", () => {
|
|
36248
|
+
const on = btn.dataset.selected !== "true";
|
|
36249
|
+
btn.dataset.selected = String(on);
|
|
36250
|
+
btn.classList.toggle("dtm-btn--primary", on);
|
|
36251
|
+
btn.classList.toggle("dtm-btn--ghost", !on);
|
|
36252
|
+
updateMusicXmlAssignmentNote();
|
|
36253
|
+
});
|
|
36254
|
+
refs.musicXmlPartSelection.appendChild(btn);
|
|
36255
|
+
});
|
|
36256
|
+
refs.musicXmlPartSelection.classList.remove("dtm-hidden");
|
|
36257
|
+
updateMusicXmlAssignmentNote();
|
|
36258
|
+
} catch (e) {
|
|
36259
|
+
setMusicXmlNote("MusicXML\u3068\u3057\u3066\u89E3\u6790\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F");
|
|
36260
|
+
refs.musicXmlPartSelection.classList.add("dtm-hidden");
|
|
36261
|
+
console.error(e);
|
|
36262
|
+
} finally {
|
|
36263
|
+
refs.overlay.hidden = true;
|
|
36264
|
+
setLoading(false);
|
|
36265
|
+
}
|
|
36266
|
+
});
|
|
36267
|
+
refs.musicXmlLoadBtn.addEventListener("click", async () => {
|
|
36268
|
+
if (!pendingMusicXml) return;
|
|
36269
|
+
const selected = getSelectedMusicXmlPartIndices();
|
|
36270
|
+
if (selected.length === 0) return;
|
|
36271
|
+
const applyActiveOnly = refs.applyActiveOnly?.checked ?? false;
|
|
36272
|
+
const from = applyActiveOnly ? activeTrackIndexOf() : selected.length > 1 ? 0 : activeTrackIndexOf();
|
|
36273
|
+
const maxCount = applyActiveOnly ? 1 : trackStates.length - from;
|
|
36274
|
+
if (!isAdvanced && options.onRequestAdvancedMode && !applyActiveOnly && selected.length > maxCount) {
|
|
36275
|
+
const confirmed = await showConfirmModal(
|
|
36276
|
+
"\u521D\u5FC3\u8005\u30E2\u30FC\u30C9\u3067\u306F\u30C8\u30E9\u30C3\u30AF\u304C\u8DB3\u308A\u305A\u3001\u4E00\u90E8\u306E\u30D1\u30FC\u30C8\u3092\u8AAD\u307F\u8FBC\u3081\u307E\u305B\u3093\u3002<br>\u4E0A\u7D1A\u8005\u30E2\u30FC\u30C9\u306B\u5207\u308A\u66FF\u3048\u307E\u3059\u304B\uFF1F"
|
|
36277
|
+
);
|
|
36278
|
+
if (confirmed) {
|
|
36279
|
+
const xml = pendingMusicXml;
|
|
36280
|
+
const sel = selected.slice();
|
|
36281
|
+
options.onRequestAdvancedMode(void 0, (newDaw) => {
|
|
36282
|
+
newDaw.applyMusicXmlParsed?.(xml, sel);
|
|
36283
|
+
});
|
|
36284
|
+
return;
|
|
36285
|
+
}
|
|
36286
|
+
}
|
|
36287
|
+
overlayDuring(() => {
|
|
36288
|
+
const { applied, dropped } = applyMusicXmlSelection(
|
|
36289
|
+
pendingMusicXml,
|
|
36290
|
+
selected
|
|
36291
|
+
);
|
|
36292
|
+
setMusicXmlNote(
|
|
36293
|
+
dropped > 0 ? `${applied}\u30D1\u30FC\u30C8\u3092\u8AAD\u307F\u8FBC\u307F\u307E\u3057\u305F\uFF08${dropped}\u30D1\u30FC\u30C8\u306F\u30C8\u30E9\u30C3\u30AF\u4E0D\u8DB3\u306E\u305F\u3081\u7701\u7565\uFF09` : `${applied}\u30D1\u30FC\u30C8\u3092\u8AAD\u307F\u8FBC\u307F\u307E\u3057\u305F`
|
|
36294
|
+
);
|
|
36295
|
+
});
|
|
36296
|
+
});
|
|
36297
|
+
};
|
|
33933
36298
|
let pendingUsts = [];
|
|
33934
36299
|
const setUstNote = (text) => {
|
|
33935
36300
|
refs.ustLoadNote.textContent = text;
|
|
@@ -34526,6 +36891,15 @@ var mountDAW = (target, options = {}) => {
|
|
|
34526
36891
|
resizeTimer = setTimeout(() => setupCanvas(), 150);
|
|
34527
36892
|
});
|
|
34528
36893
|
resizeObserver.observe(refs.rollContainer);
|
|
36894
|
+
const WIDE_LAYOUT_MIN_WIDTH = 1e3;
|
|
36895
|
+
const applyWideLayout = (width) => {
|
|
36896
|
+
refs.root.classList.toggle("dtm-daw--wide", width >= WIDE_LAYOUT_MIN_WIDTH);
|
|
36897
|
+
};
|
|
36898
|
+
const layoutObserver = new ResizeObserver((entries) => {
|
|
36899
|
+
for (const entry of entries) applyWideLayout(entry.contentRect.width);
|
|
36900
|
+
});
|
|
36901
|
+
layoutObserver.observe(refs.root);
|
|
36902
|
+
applyWideLayout(refs.root.getBoundingClientRect().width);
|
|
34529
36903
|
document.addEventListener("pointermove", onPointerMove);
|
|
34530
36904
|
document.addEventListener("pointerup", onPointerUp);
|
|
34531
36905
|
const setLoading = (loading) => {
|
|
@@ -34627,10 +37001,14 @@ var mountDAW = (target, options = {}) => {
|
|
|
34627
37001
|
applyMidiParsed: (midi, selectedIndices) => {
|
|
34628
37002
|
overlayDuring(() => applyMidiSelection(midi, selectedIndices));
|
|
34629
37003
|
},
|
|
37004
|
+
applyMusicXmlParsed: (xml, selectedIndices) => {
|
|
37005
|
+
overlayDuring(() => applyMusicXmlSelection(xml, selectedIndices));
|
|
37006
|
+
},
|
|
34630
37007
|
applyUstParsed: (ustTracks, startIndex) => {
|
|
34631
37008
|
overlayDuring(() => applyUstTracks(ustTracks, startIndex));
|
|
34632
37009
|
},
|
|
34633
37010
|
exportMIDI: exportMIDI2,
|
|
37011
|
+
exportMusicXML: exportMusicXML2,
|
|
34634
37012
|
exportUST,
|
|
34635
37013
|
setBpm,
|
|
34636
37014
|
getLoop: () => loopEnabled,
|
|
@@ -34765,6 +37143,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
34765
37143
|
unsubscribeClip?.();
|
|
34766
37144
|
stopPeakSampling();
|
|
34767
37145
|
resizeObserver.disconnect();
|
|
37146
|
+
layoutObserver.disconnect();
|
|
34768
37147
|
document.removeEventListener("pointermove", onPointerMove);
|
|
34769
37148
|
document.removeEventListener("pointerup", onPointerUp);
|
|
34770
37149
|
document.removeEventListener("keydown", onKeyDown);
|
|
@@ -34817,7 +37196,7 @@ var readGlobalBool = (key) => {
|
|
|
34817
37196
|
};
|
|
34818
37197
|
|
|
34819
37198
|
// src/headless-singing-player.ts
|
|
34820
|
-
var
|
|
37199
|
+
var STEPS_PER_BEAT6 = 48;
|
|
34821
37200
|
var STEPS_PER_BAR3 = 192;
|
|
34822
37201
|
var TRACK_ID_BY_INDEX2 = ["melody", "submelody", "bass", "chord"];
|
|
34823
37202
|
var playSingingMML = async (mml, options = {}) => {
|
|
@@ -34834,7 +37213,7 @@ var playSingingMML = async (mml, options = {}) => {
|
|
|
34834
37213
|
parseCustomVocals(mml).map((d) => [d.key, d])
|
|
34835
37214
|
);
|
|
34836
37215
|
const bpm = parsedBpm ?? options.defaultBpm ?? DEFAULT_BPM;
|
|
34837
|
-
const secondsPerStep = 60 / bpm /
|
|
37216
|
+
const secondsPerStep = 60 / bpm / STEPS_PER_BEAT6;
|
|
34838
37217
|
const drumPatternDict = {
|
|
34839
37218
|
...DRUM_PATTERNS,
|
|
34840
37219
|
...SONG_DRUM_PATTERNS,
|
|
@@ -35002,7 +37381,8 @@ var playSingingMML = async (mml, options = {}) => {
|
|
|
35002
37381
|
if (lyricTracks.size > 0) {
|
|
35003
37382
|
if (!voices) {
|
|
35004
37383
|
voices = createSingingVoices(ctx, destination, {
|
|
35005
|
-
voiceWorkerUrl: options.voiceWorkerUrl
|
|
37384
|
+
voiceWorkerUrl: options.voiceWorkerUrl,
|
|
37385
|
+
ttsBaseUrl: options.ttsBaseUrl
|
|
35006
37386
|
});
|
|
35007
37387
|
}
|
|
35008
37388
|
if (customVocalByKey.size > 0 && voices.registerVoicebanks) {
|
|
@@ -35066,275 +37446,6 @@ var playSingingMML = async (mml, options = {}) => {
|
|
|
35066
37446
|
return playback;
|
|
35067
37447
|
};
|
|
35068
37448
|
|
|
35069
|
-
// src/musicxml-io.ts
|
|
35070
|
-
var STEPS_PER_BEAT6 = 48;
|
|
35071
|
-
var PITCH_CLASS = {
|
|
35072
|
-
C: 0,
|
|
35073
|
-
D: 2,
|
|
35074
|
-
E: 4,
|
|
35075
|
-
F: 5,
|
|
35076
|
-
G: 7,
|
|
35077
|
-
A: 9,
|
|
35078
|
-
B: 11
|
|
35079
|
-
};
|
|
35080
|
-
var textOf = (el, tag) => el?.getElementsByTagName(tag)[0]?.textContent?.trim() ?? "";
|
|
35081
|
-
var numOf = (el, tag, fallback = 0) => {
|
|
35082
|
-
const raw = textOf(el, tag);
|
|
35083
|
-
if (!raw) return fallback;
|
|
35084
|
-
const v = Number.parseFloat(raw);
|
|
35085
|
-
return Number.isFinite(v) ? v : fallback;
|
|
35086
|
-
};
|
|
35087
|
-
var pitchOf = (note) => {
|
|
35088
|
-
const p = note.getElementsByTagName("pitch")[0];
|
|
35089
|
-
if (!p) return null;
|
|
35090
|
-
const step = textOf(p, "step").toUpperCase();
|
|
35091
|
-
const base = PITCH_CLASS[step];
|
|
35092
|
-
if (base === void 0) return null;
|
|
35093
|
-
const octave = numOf(p, "octave", 4);
|
|
35094
|
-
const alter = Math.round(numOf(p, "alter", 0));
|
|
35095
|
-
return (octave + 1) * 12 + base + alter;
|
|
35096
|
-
};
|
|
35097
|
-
var parseMusicXML = (xml) => {
|
|
35098
|
-
const doc = new DOMParser().parseFromString(xml, "application/xml");
|
|
35099
|
-
if (doc.getElementsByTagName("parserror").length > 0)
|
|
35100
|
-
throw new Error("MusicXML \u3068\u3057\u3066\u8AAD\u3081\u307E\u305B\u3093");
|
|
35101
|
-
const nameById = /* @__PURE__ */ new Map();
|
|
35102
|
-
for (const sp of Array.from(doc.getElementsByTagName("score-part"))) {
|
|
35103
|
-
const id = sp.getAttribute("id");
|
|
35104
|
-
if (id) nameById.set(id, textOf(sp, "part-name") || id);
|
|
35105
|
-
}
|
|
35106
|
-
const placements = [];
|
|
35107
|
-
const parts = [];
|
|
35108
|
-
let bpm = 0;
|
|
35109
|
-
const partEls = Array.from(doc.getElementsByTagName("part"));
|
|
35110
|
-
partEls.forEach((partEl, partIndex) => {
|
|
35111
|
-
const id = partEl.getAttribute("id") ?? "";
|
|
35112
|
-
let divisions = 1;
|
|
35113
|
-
let cursor = 0;
|
|
35114
|
-
let measureStart = 0;
|
|
35115
|
-
let lastStart = 0;
|
|
35116
|
-
let lastDurSteps = 0;
|
|
35117
|
-
const tied = /* @__PURE__ */ new Map();
|
|
35118
|
-
let count = 0;
|
|
35119
|
-
let pitchSum = 0;
|
|
35120
|
-
let lyricSeen = false;
|
|
35121
|
-
for (const measure of Array.from(partEl.getElementsByTagName("measure"))) {
|
|
35122
|
-
measureStart += cursor;
|
|
35123
|
-
cursor = 0;
|
|
35124
|
-
for (const child of Array.from(measure.children)) {
|
|
35125
|
-
const tag = child.tagName.toLowerCase();
|
|
35126
|
-
if (tag === "attributes") {
|
|
35127
|
-
const d = numOf(child, "divisions", 0);
|
|
35128
|
-
if (d > 0) divisions = d;
|
|
35129
|
-
continue;
|
|
35130
|
-
}
|
|
35131
|
-
if (tag === "direction") {
|
|
35132
|
-
const sound = child.getElementsByTagName("sound")[0];
|
|
35133
|
-
const t = Number.parseFloat(sound?.getAttribute("tempo") ?? "");
|
|
35134
|
-
if (!bpm && Number.isFinite(t) && t > 0) bpm = t;
|
|
35135
|
-
continue;
|
|
35136
|
-
}
|
|
35137
|
-
if (tag === "backup" || tag === "forward") {
|
|
35138
|
-
const steps = numOf(child, "duration", 0) / divisions * STEPS_PER_BEAT6;
|
|
35139
|
-
cursor += tag === "backup" ? -steps : steps;
|
|
35140
|
-
continue;
|
|
35141
|
-
}
|
|
35142
|
-
if (tag !== "note") continue;
|
|
35143
|
-
const durSteps = Math.round(
|
|
35144
|
-
numOf(child, "duration", 0) / divisions * STEPS_PER_BEAT6
|
|
35145
|
-
);
|
|
35146
|
-
const isChord = child.getElementsByTagName("chord").length > 0;
|
|
35147
|
-
const isRest = child.getElementsByTagName("rest").length > 0;
|
|
35148
|
-
const start = isChord ? lastStart : measureStart + cursor;
|
|
35149
|
-
if (!isRest) {
|
|
35150
|
-
const pitch = pitchOf(child);
|
|
35151
|
-
if (pitch !== null) {
|
|
35152
|
-
const lyric = textOf(
|
|
35153
|
-
child.getElementsByTagName("lyric")[0],
|
|
35154
|
-
"text"
|
|
35155
|
-
);
|
|
35156
|
-
if (lyric) lyricSeen = true;
|
|
35157
|
-
const ties = Array.from(child.getElementsByTagName("tie"));
|
|
35158
|
-
const stops = ties.some((t) => t.getAttribute("type") === "stop");
|
|
35159
|
-
const starts = ties.some((t) => t.getAttribute("type") === "start");
|
|
35160
|
-
const held = tied.get(pitch);
|
|
35161
|
-
if (stops && held) {
|
|
35162
|
-
held.durationSteps += durSteps;
|
|
35163
|
-
if (!starts) tied.delete(pitch);
|
|
35164
|
-
} else {
|
|
35165
|
-
const placed = {
|
|
35166
|
-
partIndex,
|
|
35167
|
-
startStep: Math.max(0, Math.round(start)),
|
|
35168
|
-
pitch,
|
|
35169
|
-
durationSteps: Math.max(1, durSteps),
|
|
35170
|
-
lyric
|
|
35171
|
-
};
|
|
35172
|
-
placements.push(placed);
|
|
35173
|
-
count++;
|
|
35174
|
-
pitchSum += pitch;
|
|
35175
|
-
if (starts) tied.set(pitch, placed);
|
|
35176
|
-
}
|
|
35177
|
-
}
|
|
35178
|
-
}
|
|
35179
|
-
if (!isChord) {
|
|
35180
|
-
lastStart = start;
|
|
35181
|
-
lastDurSteps = durSteps;
|
|
35182
|
-
cursor += durSteps;
|
|
35183
|
-
} else {
|
|
35184
|
-
lastDurSteps = Math.max(lastDurSteps, durSteps);
|
|
35185
|
-
}
|
|
35186
|
-
}
|
|
35187
|
-
}
|
|
35188
|
-
parts.push({
|
|
35189
|
-
index: partIndex,
|
|
35190
|
-
name: nameById.get(id) || `Part ${partIndex + 1}`,
|
|
35191
|
-
noteCount: count,
|
|
35192
|
-
hasLyrics: lyricSeen,
|
|
35193
|
-
avgPitch: count > 0 ? pitchSum / count : 0
|
|
35194
|
-
});
|
|
35195
|
-
});
|
|
35196
|
-
return { parts, placements, bpm: bpm > 0 ? Math.round(bpm) : 120 };
|
|
35197
|
-
};
|
|
35198
|
-
var SHARP_SPELLING = [
|
|
35199
|
-
["C", 0],
|
|
35200
|
-
["C", 1],
|
|
35201
|
-
["D", 0],
|
|
35202
|
-
["D", 1],
|
|
35203
|
-
["E", 0],
|
|
35204
|
-
["F", 0],
|
|
35205
|
-
["F", 1],
|
|
35206
|
-
["G", 0],
|
|
35207
|
-
["G", 1],
|
|
35208
|
-
["A", 0],
|
|
35209
|
-
["A", 1],
|
|
35210
|
-
["B", 0]
|
|
35211
|
-
];
|
|
35212
|
-
var NOTE_TYPES = [
|
|
35213
|
-
[STEPS_PER_BEAT6 * 4, "whole"],
|
|
35214
|
-
[STEPS_PER_BEAT6 * 2, "half"],
|
|
35215
|
-
[STEPS_PER_BEAT6, "quarter"],
|
|
35216
|
-
[STEPS_PER_BEAT6 / 2, "eighth"],
|
|
35217
|
-
[STEPS_PER_BEAT6 / 4, "16th"],
|
|
35218
|
-
[STEPS_PER_BEAT6 / 8, "32nd"]
|
|
35219
|
-
];
|
|
35220
|
-
var typeOf = (steps) => {
|
|
35221
|
-
for (const [len, type] of NOTE_TYPES) {
|
|
35222
|
-
if (steps >= len) {
|
|
35223
|
-
const dots = steps >= len * 1.5 && steps < len * 2 ? 1 : 0;
|
|
35224
|
-
return { type, dots };
|
|
35225
|
-
}
|
|
35226
|
-
}
|
|
35227
|
-
return { type: "32nd", dots: 0 };
|
|
35228
|
-
};
|
|
35229
|
-
var esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
35230
|
-
var exportMusicXML = (options) => {
|
|
35231
|
-
const { parts, bpm, stepsPerBar, title } = options;
|
|
35232
|
-
const beatsPerBar = Math.max(1, Math.round(stepsPerBar / STEPS_PER_BEAT6));
|
|
35233
|
-
const partList = parts.map(
|
|
35234
|
-
(p, i2) => ` <score-part id="P${i2 + 1}">
|
|
35235
|
-
<part-name>${esc(p.name)}</part-name>
|
|
35236
|
-
</score-part>`
|
|
35237
|
-
).join("\n");
|
|
35238
|
-
const body = parts.map((part, pi) => {
|
|
35239
|
-
const notes = [...part.notes].sort((a, b) => a.startStep - b.startStep);
|
|
35240
|
-
const end = notes.reduce(
|
|
35241
|
-
(m, n) => Math.max(m, n.startStep + n.durationSteps),
|
|
35242
|
-
0
|
|
35243
|
-
);
|
|
35244
|
-
const bars = Math.max(1, Math.ceil(end / stepsPerBar));
|
|
35245
|
-
const lines = [` <part id="P${pi + 1}">`];
|
|
35246
|
-
let li = 0;
|
|
35247
|
-
for (let bar = 0; bar < bars; bar++) {
|
|
35248
|
-
const from = bar * stepsPerBar;
|
|
35249
|
-
const to = from + stepsPerBar;
|
|
35250
|
-
lines.push(` <measure number="${bar + 1}">`);
|
|
35251
|
-
if (bar === 0) {
|
|
35252
|
-
lines.push(
|
|
35253
|
-
` <attributes>
|
|
35254
|
-
<divisions>${STEPS_PER_BEAT6}</divisions>`,
|
|
35255
|
-
` <key><fifths>0</fifths></key>`,
|
|
35256
|
-
` <time><beats>${beatsPerBar}</beats><beat-type>4</beat-type></time>`,
|
|
35257
|
-
` <clef><sign>G</sign><line>2</line></clef>
|
|
35258
|
-
</attributes>`
|
|
35259
|
-
);
|
|
35260
|
-
if (pi === 0)
|
|
35261
|
-
lines.push(
|
|
35262
|
-
` <direction placement="above"><direction-type><metronome><beat-unit>quarter</beat-unit><per-minute>${Math.round(bpm)}</per-minute></metronome></direction-type><sound tempo="${Math.round(bpm)}"/></direction>`
|
|
35263
|
-
);
|
|
35264
|
-
}
|
|
35265
|
-
let cursor = from;
|
|
35266
|
-
const inBar = notes.filter(
|
|
35267
|
-
(n) => n.startStep >= from && n.startStep < to
|
|
35268
|
-
);
|
|
35269
|
-
const groups = /* @__PURE__ */ new Map();
|
|
35270
|
-
for (const n of inBar) {
|
|
35271
|
-
const g = groups.get(n.startStep);
|
|
35272
|
-
if (g) g.push(n);
|
|
35273
|
-
else groups.set(n.startStep, [n]);
|
|
35274
|
-
}
|
|
35275
|
-
const starts = [...groups.keys()].sort((a, b) => a - b);
|
|
35276
|
-
for (let gi = 0; gi < starts.length; gi++) {
|
|
35277
|
-
const at = starts[gi];
|
|
35278
|
-
const group = groups.get(at);
|
|
35279
|
-
if (at > cursor) {
|
|
35280
|
-
const { type, dots } = typeOf(at - cursor);
|
|
35281
|
-
lines.push(
|
|
35282
|
-
` <note><rest/><duration>${at - cursor}</duration><type>${type}</type>${"<dot/>".repeat(dots)}</note>`
|
|
35283
|
-
);
|
|
35284
|
-
cursor = at;
|
|
35285
|
-
}
|
|
35286
|
-
const nextAt = gi + 1 < starts.length ? starts[gi + 1] : to;
|
|
35287
|
-
const room = Math.min(to, nextAt) - at;
|
|
35288
|
-
const dur = Math.max(
|
|
35289
|
-
1,
|
|
35290
|
-
Math.min(Math.max(...group.map((n) => n.durationSteps)), room)
|
|
35291
|
-
);
|
|
35292
|
-
group.forEach((n, ni) => {
|
|
35293
|
-
const semi = Math.round(n.pitchUnits / UNITS_PER_SEMITONE);
|
|
35294
|
-
const [step, alter] = SHARP_SPELLING[(semi % 12 + 12) % 12];
|
|
35295
|
-
const octave = Math.floor(semi / 12) - 1;
|
|
35296
|
-
const { type, dots } = typeOf(dur);
|
|
35297
|
-
const lyric = part.lyrics?.[li];
|
|
35298
|
-
lines.push(
|
|
35299
|
-
` <note>${ni > 0 ? "<chord/>" : ""}<pitch><step>${step}</step>${alter ? `<alter>${alter}</alter>` : ""}<octave>${octave}</octave></pitch><duration>${dur}</duration><type>${type}</type>${"<dot/>".repeat(dots)}` + (ni === 0 && lyric ? `<lyric><syllabic>single</syllabic><text>${esc(lyric)}</text></lyric>` : "") + `</note>`
|
|
35300
|
-
);
|
|
35301
|
-
if (ni === 0) li++;
|
|
35302
|
-
});
|
|
35303
|
-
cursor = at + dur;
|
|
35304
|
-
}
|
|
35305
|
-
if (cursor < to) {
|
|
35306
|
-
const { type, dots } = typeOf(to - cursor);
|
|
35307
|
-
lines.push(
|
|
35308
|
-
` <note><rest/><duration>${to - cursor}</duration><type>${type}</type>${"<dot/>".repeat(dots)}</note>`
|
|
35309
|
-
);
|
|
35310
|
-
}
|
|
35311
|
-
lines.push(" </measure>");
|
|
35312
|
-
}
|
|
35313
|
-
lines.push(" </part>");
|
|
35314
|
-
return lines.join("\n");
|
|
35315
|
-
}).join("\n");
|
|
35316
|
-
return [
|
|
35317
|
-
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
35318
|
-
'<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 4.0 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">',
|
|
35319
|
-
'<score-partwise version="4.0">',
|
|
35320
|
-
` <work><work-title>${esc(title ?? "Untitled")}</work-title></work>`,
|
|
35321
|
-
// 書き出し元を残す。MML・MIDI と揃えてある(`src/version.ts` に理由)。
|
|
35322
|
-
` <identification><encoding><software>dtm ${DTM_VERSION}</software></encoding></identification>`,
|
|
35323
|
-
" <part-list>",
|
|
35324
|
-
partList,
|
|
35325
|
-
" </part-list>",
|
|
35326
|
-
body,
|
|
35327
|
-
"</score-partwise>",
|
|
35328
|
-
""
|
|
35329
|
-
].join("\n");
|
|
35330
|
-
};
|
|
35331
|
-
var musicXmlToNotes = (placements, partIndex) => placements.filter((p) => p.partIndex === partIndex).map((p) => ({
|
|
35332
|
-
startStep: p.startStep,
|
|
35333
|
-
durationSteps: p.durationSteps,
|
|
35334
|
-
pitchUnits: p.pitch * UNITS_PER_SEMITONE,
|
|
35335
|
-
velocity: 100
|
|
35336
|
-
}));
|
|
35337
|
-
|
|
35338
37449
|
// src/piano-roll.ts
|
|
35339
37450
|
var createPianoRoll = (options, handlers) => {
|
|
35340
37451
|
const {
|
|
@@ -36269,6 +38380,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
36269
38380
|
voiceWorkerUrl,
|
|
36270
38381
|
voicebanks,
|
|
36271
38382
|
worldlineScriptUrl: options.worldlineScriptUrl,
|
|
38383
|
+
ttsBaseUrl: options.ttsBaseUrl,
|
|
36272
38384
|
// ボーカルトラック個別のリバーブセンド(`r`トークン)先。マスタリバーブの
|
|
36273
38385
|
// Convolver 入力へ直接センドする(PreDelayは経由しない。チャンネルストリップの
|
|
36274
38386
|
// reverbSend とは別経路の、音節単位でより細かく制御できるセンド)。
|
|
@@ -37318,6 +39430,7 @@ export {
|
|
|
37318
39430
|
DEFAULT_PLAYBACK_VELOCITY,
|
|
37319
39431
|
DEFAULT_SECTIONS,
|
|
37320
39432
|
DEFAULT_STEPS_PER_BAR,
|
|
39433
|
+
DEFAULT_TTS_BASE_URL,
|
|
37321
39434
|
DEFAULT_UST_LYRIC,
|
|
37322
39435
|
DEFAULT_VELOCITY,
|
|
37323
39436
|
DEFAULT_VOCAL_VOLUME,
|
|
@@ -37361,6 +39474,8 @@ export {
|
|
|
37361
39474
|
SECTION_ORDER,
|
|
37362
39475
|
SECTION_SPECS,
|
|
37363
39476
|
SEQUENCER_START_DELAY,
|
|
39477
|
+
SPEAK_CLOSE,
|
|
39478
|
+
SPEAK_OPEN,
|
|
37364
39479
|
STOP_MARK,
|
|
37365
39480
|
TIE_MARK,
|
|
37366
39481
|
TIE_MERGE_MAX_SEC,
|
|
@@ -37402,6 +39517,8 @@ export {
|
|
|
37402
39517
|
createRenderer,
|
|
37403
39518
|
createSequencer,
|
|
37404
39519
|
createSingingVoices,
|
|
39520
|
+
createSpeechPlanner,
|
|
39521
|
+
createSpeechToneView,
|
|
37405
39522
|
createSynth,
|
|
37406
39523
|
createVoiceRegistry,
|
|
37407
39524
|
decodeMml,
|
|
@@ -37430,6 +39547,7 @@ export {
|
|
|
37430
39547
|
getComposeScaleDescription,
|
|
37431
39548
|
getDrumPatternKeys,
|
|
37432
39549
|
getMidiBPM,
|
|
39550
|
+
getSpeechPlanner,
|
|
37433
39551
|
hasSeenTour,
|
|
37434
39552
|
icon,
|
|
37435
39553
|
injectStyles,
|
|
@@ -37444,6 +39562,7 @@ export {
|
|
|
37444
39562
|
looksLikePinyin,
|
|
37445
39563
|
lyricToSyllable,
|
|
37446
39564
|
markTourSeen,
|
|
39565
|
+
medianRecordedPitchHz,
|
|
37447
39566
|
midiNote,
|
|
37448
39567
|
midiToUnits,
|
|
37449
39568
|
mountChordPlayer,
|
|
@@ -37469,10 +39588,12 @@ export {
|
|
|
37469
39588
|
playNote,
|
|
37470
39589
|
playPlacements,
|
|
37471
39590
|
playSingingMML,
|
|
39591
|
+
prepareSpeechPlan,
|
|
37472
39592
|
programOfInstrumentName,
|
|
37473
39593
|
readGlobalBool,
|
|
37474
39594
|
readGlobalNumber,
|
|
37475
39595
|
readGlobalSetting,
|
|
39596
|
+
readKeptSong,
|
|
37476
39597
|
readMacroSections,
|
|
37477
39598
|
readMacroSetting,
|
|
37478
39599
|
resolveCenter,
|
|
@@ -37487,6 +39608,8 @@ export {
|
|
|
37487
39608
|
semitonesToUnits,
|
|
37488
39609
|
shiftNotes,
|
|
37489
39610
|
showLoadingOverlay,
|
|
39611
|
+
speechPlanDurationSec,
|
|
39612
|
+
speechPlanLeadingSec,
|
|
37490
39613
|
spelledToUnits,
|
|
37491
39614
|
spellingToUnits,
|
|
37492
39615
|
startTour,
|
|
@@ -37506,6 +39629,7 @@ export {
|
|
|
37506
39629
|
unitsToPitchV1,
|
|
37507
39630
|
vocalVolumeToGain,
|
|
37508
39631
|
writeGlobalSetting,
|
|
39632
|
+
writeKeptSong,
|
|
37509
39633
|
writeMacroSections,
|
|
37510
39634
|
writeMacroSetting
|
|
37511
39635
|
};
|