@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.js
CHANGED
|
@@ -41,6 +41,7 @@ __export(index_exports, {
|
|
|
41
41
|
DEFAULT_PLAYBACK_VELOCITY: () => DEFAULT_PLAYBACK_VELOCITY,
|
|
42
42
|
DEFAULT_SECTIONS: () => DEFAULT_SECTIONS,
|
|
43
43
|
DEFAULT_STEPS_PER_BAR: () => DEFAULT_STEPS_PER_BAR,
|
|
44
|
+
DEFAULT_TTS_BASE_URL: () => DEFAULT_TTS_BASE_URL,
|
|
44
45
|
DEFAULT_UST_LYRIC: () => DEFAULT_UST_LYRIC,
|
|
45
46
|
DEFAULT_VELOCITY: () => DEFAULT_VELOCITY,
|
|
46
47
|
DEFAULT_VOCAL_VOLUME: () => DEFAULT_VOCAL_VOLUME,
|
|
@@ -84,6 +85,8 @@ __export(index_exports, {
|
|
|
84
85
|
SECTION_ORDER: () => SECTION_ORDER,
|
|
85
86
|
SECTION_SPECS: () => SECTION_SPECS,
|
|
86
87
|
SEQUENCER_START_DELAY: () => SEQUENCER_START_DELAY,
|
|
88
|
+
SPEAK_CLOSE: () => SPEAK_CLOSE,
|
|
89
|
+
SPEAK_OPEN: () => SPEAK_OPEN,
|
|
87
90
|
STOP_MARK: () => STOP_MARK,
|
|
88
91
|
TIE_MARK: () => TIE_MARK,
|
|
89
92
|
TIE_MERGE_MAX_SEC: () => TIE_MERGE_MAX_SEC,
|
|
@@ -125,6 +128,8 @@ __export(index_exports, {
|
|
|
125
128
|
createRenderer: () => createRenderer,
|
|
126
129
|
createSequencer: () => createSequencer,
|
|
127
130
|
createSingingVoices: () => createSingingVoices,
|
|
131
|
+
createSpeechPlanner: () => createSpeechPlanner,
|
|
132
|
+
createSpeechToneView: () => createSpeechToneView,
|
|
128
133
|
createSynth: () => createSynth,
|
|
129
134
|
createVoiceRegistry: () => createVoiceRegistry,
|
|
130
135
|
decodeMml: () => decodeMml,
|
|
@@ -153,6 +158,7 @@ __export(index_exports, {
|
|
|
153
158
|
getComposeScaleDescription: () => getComposeScaleDescription,
|
|
154
159
|
getDrumPatternKeys: () => getDrumPatternKeys,
|
|
155
160
|
getMidiBPM: () => getMidiBPM,
|
|
161
|
+
getSpeechPlanner: () => getSpeechPlanner,
|
|
156
162
|
hasSeenTour: () => hasSeenTour,
|
|
157
163
|
icon: () => icon,
|
|
158
164
|
injectStyles: () => injectStyles,
|
|
@@ -167,6 +173,7 @@ __export(index_exports, {
|
|
|
167
173
|
looksLikePinyin: () => looksLikePinyin,
|
|
168
174
|
lyricToSyllable: () => lyricToSyllable,
|
|
169
175
|
markTourSeen: () => markTourSeen,
|
|
176
|
+
medianRecordedPitchHz: () => medianRecordedPitchHz,
|
|
170
177
|
midiNote: () => midiNote,
|
|
171
178
|
midiToUnits: () => midiToUnits,
|
|
172
179
|
mountChordPlayer: () => mountChordPlayer,
|
|
@@ -192,10 +199,12 @@ __export(index_exports, {
|
|
|
192
199
|
playNote: () => playNote,
|
|
193
200
|
playPlacements: () => playPlacements,
|
|
194
201
|
playSingingMML: () => playSingingMML,
|
|
202
|
+
prepareSpeechPlan: () => prepareSpeechPlan,
|
|
195
203
|
programOfInstrumentName: () => programOfInstrumentName,
|
|
196
204
|
readGlobalBool: () => readGlobalBool,
|
|
197
205
|
readGlobalNumber: () => readGlobalNumber,
|
|
198
206
|
readGlobalSetting: () => readGlobalSetting,
|
|
207
|
+
readKeptSong: () => readKeptSong,
|
|
199
208
|
readMacroSections: () => readMacroSections,
|
|
200
209
|
readMacroSetting: () => readMacroSetting,
|
|
201
210
|
resolveCenter: () => resolveCenter,
|
|
@@ -210,6 +219,8 @@ __export(index_exports, {
|
|
|
210
219
|
semitonesToUnits: () => semitonesToUnits,
|
|
211
220
|
shiftNotes: () => shiftNotes,
|
|
212
221
|
showLoadingOverlay: () => showLoadingOverlay,
|
|
222
|
+
speechPlanDurationSec: () => speechPlanDurationSec,
|
|
223
|
+
speechPlanLeadingSec: () => speechPlanLeadingSec,
|
|
213
224
|
spelledToUnits: () => spelledToUnits,
|
|
214
225
|
spellingToUnits: () => spellingToUnits,
|
|
215
226
|
startTour: () => startTour,
|
|
@@ -229,6 +240,7 @@ __export(index_exports, {
|
|
|
229
240
|
unitsToPitchV1: () => unitsToPitchV1,
|
|
230
241
|
vocalVolumeToGain: () => vocalVolumeToGain,
|
|
231
242
|
writeGlobalSetting: () => writeGlobalSetting,
|
|
243
|
+
writeKeptSong: () => writeKeptSong,
|
|
232
244
|
writeMacroSections: () => writeMacroSections,
|
|
233
245
|
writeMacroSetting: () => writeMacroSetting
|
|
234
246
|
});
|
|
@@ -2893,7 +2905,7 @@ var buildChordPlacements = (options) => {
|
|
|
2893
2905
|
return placements;
|
|
2894
2906
|
};
|
|
2895
2907
|
|
|
2896
|
-
// node_modules/.pnpm/@onjmin+koe@1.0.
|
|
2908
|
+
// node_modules/.pnpm/@onjmin+koe@1.0.10/node_modules/@onjmin/koe/dist/index.js
|
|
2897
2909
|
var u8 = Uint8Array;
|
|
2898
2910
|
var u16 = Uint16Array;
|
|
2899
2911
|
var i32 = Int32Array;
|
|
@@ -3281,6 +3293,130 @@ var Worldline = class _Worldline {
|
|
|
3281
3293
|
* @returns Float32 PCM, or null when `pcm` is shorter than
|
|
3282
3294
|
* {@link MIN_WORLDLINE_SAMPLES} (too short for stable F0 analysis).
|
|
3283
3295
|
*/
|
|
3296
|
+
renderPhrase(params) {
|
|
3297
|
+
const {
|
|
3298
|
+
units: units2,
|
|
3299
|
+
pitch,
|
|
3300
|
+
gender = 0.5,
|
|
3301
|
+
tension = 0.5,
|
|
3302
|
+
breathiness = 0.5,
|
|
3303
|
+
voicing = 1,
|
|
3304
|
+
tempo = 120
|
|
3305
|
+
} = params;
|
|
3306
|
+
if (units2.length === 0) return null;
|
|
3307
|
+
const WL = this.wasm;
|
|
3308
|
+
const FS2 = WORLDLINE_SAMPLE_RATE;
|
|
3309
|
+
let totalMs = 0;
|
|
3310
|
+
for (const u of units2) {
|
|
3311
|
+
const endMs = u.posMs + u.lengthMs;
|
|
3312
|
+
if (endMs > totalMs) totalMs = endMs;
|
|
3313
|
+
}
|
|
3314
|
+
const ps = WL._PhraseSynthNew();
|
|
3315
|
+
if (!ps) return null;
|
|
3316
|
+
const pointersToFree = [];
|
|
3317
|
+
for (const u of units2) {
|
|
3318
|
+
if (!u.pcm || u.pcm.length < MIN_WORLDLINE_SAMPLES) continue;
|
|
3319
|
+
const reqPtr = WL._malloc(SYNTH_REQ_SIZE);
|
|
3320
|
+
if (!reqPtr) continue;
|
|
3321
|
+
pointersToFree.push(reqPtr);
|
|
3322
|
+
const samplePtr = WL._malloc(u.pcm.length * 8);
|
|
3323
|
+
if (!samplePtr) continue;
|
|
3324
|
+
pointersToFree.push(samplePtr);
|
|
3325
|
+
WL.HEAPF64.set(u.pcm, samplePtr >> 3);
|
|
3326
|
+
const sv = (off, val, type) => WL.setValue(reqPtr + off, val, type);
|
|
3327
|
+
sv(0, FS2, "i32");
|
|
3328
|
+
sv(4, u.pcm.length, "i32");
|
|
3329
|
+
sv(8, samplePtr, "*");
|
|
3330
|
+
sv(12, 0, "i32");
|
|
3331
|
+
sv(16, 0, "*");
|
|
3332
|
+
sv(20, u.tone ?? 69, "i32");
|
|
3333
|
+
sv(24, 100, "double");
|
|
3334
|
+
sv(32, 0, "double");
|
|
3335
|
+
sv(40, u.requiredLengthMs ?? u.lengthMs, "double");
|
|
3336
|
+
sv(48, u.consonantMs, "double");
|
|
3337
|
+
const cutMs = u.cutMs ?? WL_FRAME_MS * 2;
|
|
3338
|
+
sv(56, cutMs, "double");
|
|
3339
|
+
sv(64, u.volume ?? 100, "double");
|
|
3340
|
+
sv(72, 0, "double");
|
|
3341
|
+
sv(80, tempo, "double");
|
|
3342
|
+
sv(88, 0, "i32");
|
|
3343
|
+
sv(92, 0, "*");
|
|
3344
|
+
sv(96, 0, "i32");
|
|
3345
|
+
sv(100, 0, "i32");
|
|
3346
|
+
sv(104, 100, "i32");
|
|
3347
|
+
sv(108, 0, "i32");
|
|
3348
|
+
sv(112, 0, "i32");
|
|
3349
|
+
sv(116, 100, "i32");
|
|
3350
|
+
WL._PhraseSynthAddRequest(
|
|
3351
|
+
ps,
|
|
3352
|
+
reqPtr,
|
|
3353
|
+
u.posMs,
|
|
3354
|
+
u.skipMs,
|
|
3355
|
+
u.lengthMs,
|
|
3356
|
+
u.fadeInMs,
|
|
3357
|
+
u.fadeOutMs,
|
|
3358
|
+
0
|
|
3359
|
+
);
|
|
3360
|
+
}
|
|
3361
|
+
totalMs += WL_FRAME_MS * 2;
|
|
3362
|
+
const nFrames = Math.ceil(totalMs / WL_FRAME_MS) + 4;
|
|
3363
|
+
const f0Arr = new Float64Array(nFrames);
|
|
3364
|
+
const gArr = new Float64Array(nFrames);
|
|
3365
|
+
const tArr = new Float64Array(nFrames);
|
|
3366
|
+
const bArr = new Float64Array(nFrames);
|
|
3367
|
+
const vArr = new Float64Array(nFrames);
|
|
3368
|
+
for (let i2 = 0; i2 < nFrames; i2++) {
|
|
3369
|
+
const tMs = i2 * WL_FRAME_MS;
|
|
3370
|
+
f0Arr[i2] = sampleCurve(tMs, pitch, totalMs);
|
|
3371
|
+
gArr[i2] = sampleCurve(tMs, gender, totalMs);
|
|
3372
|
+
tArr[i2] = sampleCurve(tMs, tension, totalMs);
|
|
3373
|
+
bArr[i2] = sampleCurve(tMs, breathiness, totalMs);
|
|
3374
|
+
vArr[i2] = sampleCurve(tMs, voicing, totalMs);
|
|
3375
|
+
}
|
|
3376
|
+
const f0Ptr = WL._malloc(nFrames * 8);
|
|
3377
|
+
const gPtr = WL._malloc(nFrames * 8);
|
|
3378
|
+
const tPtr = WL._malloc(nFrames * 8);
|
|
3379
|
+
const bPtr = WL._malloc(nFrames * 8);
|
|
3380
|
+
const vPtr = WL._malloc(nFrames * 8);
|
|
3381
|
+
if (f0Ptr && gPtr && tPtr && bPtr && vPtr) {
|
|
3382
|
+
WL.HEAPF64.set(f0Arr, f0Ptr >> 3);
|
|
3383
|
+
WL.HEAPF64.set(gArr, gPtr >> 3);
|
|
3384
|
+
WL.HEAPF64.set(tArr, tPtr >> 3);
|
|
3385
|
+
WL.HEAPF64.set(bArr, bPtr >> 3);
|
|
3386
|
+
WL.HEAPF64.set(vArr, vPtr >> 3);
|
|
3387
|
+
WL._PhraseSynthSetCurves(
|
|
3388
|
+
ps,
|
|
3389
|
+
f0Ptr,
|
|
3390
|
+
gPtr,
|
|
3391
|
+
tPtr,
|
|
3392
|
+
bPtr,
|
|
3393
|
+
vPtr,
|
|
3394
|
+
nFrames,
|
|
3395
|
+
WL_FRAME_MS
|
|
3396
|
+
);
|
|
3397
|
+
}
|
|
3398
|
+
if (f0Ptr) WL._free(f0Ptr);
|
|
3399
|
+
if (gPtr) WL._free(gPtr);
|
|
3400
|
+
if (tPtr) WL._free(tPtr);
|
|
3401
|
+
if (bPtr) WL._free(bPtr);
|
|
3402
|
+
if (vPtr) WL._free(vPtr);
|
|
3403
|
+
const yPtrPtr = WL._malloc(4);
|
|
3404
|
+
let audio = null;
|
|
3405
|
+
if (yPtrPtr) {
|
|
3406
|
+
const outLen = WL._PhraseSynthSynth(ps, yPtrPtr, 0);
|
|
3407
|
+
const yPtr = WL.getValue(yPtrPtr, "*");
|
|
3408
|
+
if (outLen > 0 && yPtr) {
|
|
3409
|
+
audio = new Float32Array(WL.HEAPF32.buffer, yPtr, outLen).slice();
|
|
3410
|
+
WL._free(yPtr);
|
|
3411
|
+
}
|
|
3412
|
+
WL._free(yPtrPtr);
|
|
3413
|
+
}
|
|
3414
|
+
for (const ptr of pointersToFree) {
|
|
3415
|
+
WL._free(ptr);
|
|
3416
|
+
}
|
|
3417
|
+
WL._PhraseSynthDelete(ps);
|
|
3418
|
+
return audio;
|
|
3419
|
+
}
|
|
3284
3420
|
renderNote(params) {
|
|
3285
3421
|
const {
|
|
3286
3422
|
pcm,
|
|
@@ -3296,8 +3432,12 @@ var Worldline = class _Worldline {
|
|
|
3296
3432
|
} = params;
|
|
3297
3433
|
if (!pcm || pcm.length < MIN_WORLDLINE_SAMPLES) return null;
|
|
3298
3434
|
const WL = this.wasm;
|
|
3299
|
-
const
|
|
3300
|
-
const basePitch = sampleCurve(
|
|
3435
|
+
const FS2 = WORLDLINE_SAMPLE_RATE;
|
|
3436
|
+
const basePitch = sampleCurve(
|
|
3437
|
+
preMs + durationMs / 2,
|
|
3438
|
+
pitch,
|
|
3439
|
+
preMs + durationMs
|
|
3440
|
+
);
|
|
3301
3441
|
const midiNote2 = Math.round(69 + 12 * Math.log2(basePitch / 440));
|
|
3302
3442
|
const posMs = 0;
|
|
3303
3443
|
const reqLen = preMs + durationMs;
|
|
@@ -3317,7 +3457,7 @@ var Worldline = class _Worldline {
|
|
|
3317
3457
|
}
|
|
3318
3458
|
WL.HEAPF64.set(pcm, samplePtr >> 3);
|
|
3319
3459
|
const sv = (off, val, type) => WL.setValue(reqPtr + off, val, type);
|
|
3320
|
-
sv(0,
|
|
3460
|
+
sv(0, FS2, "i32");
|
|
3321
3461
|
sv(4, pcm.length, "i32");
|
|
3322
3462
|
sv(8, samplePtr, "*");
|
|
3323
3463
|
sv(12, 0, "i32");
|
|
@@ -3405,6 +3545,836 @@ var Worldline = class _Worldline {
|
|
|
3405
3545
|
return audio;
|
|
3406
3546
|
}
|
|
3407
3547
|
};
|
|
3548
|
+
var RATE = 16e3;
|
|
3549
|
+
var HOP_MS = 2;
|
|
3550
|
+
var HOP = RATE * HOP_MS / 1e3;
|
|
3551
|
+
var FFT_SIZE = 512;
|
|
3552
|
+
var BINS = FFT_SIZE / 2;
|
|
3553
|
+
var PITCH_DECIM = 2;
|
|
3554
|
+
var PITCH_RATE = RATE / PITCH_DECIM;
|
|
3555
|
+
var WINDOW_CENTRE_MS = FFT_SIZE / 2 / RATE * 1e3;
|
|
3556
|
+
var DEFAULT_CACHE = "koe-tts-assets-v1";
|
|
3557
|
+
async function openCache(name) {
|
|
3558
|
+
if (name === null) return null;
|
|
3559
|
+
try {
|
|
3560
|
+
if (typeof caches === "undefined") return null;
|
|
3561
|
+
return await caches.open(name ?? DEFAULT_CACHE);
|
|
3562
|
+
} catch {
|
|
3563
|
+
return null;
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3566
|
+
async function cachedCopyIsCurrent(url2, cached, signal) {
|
|
3567
|
+
let head;
|
|
3568
|
+
try {
|
|
3569
|
+
head = await fetch(url2, { method: "HEAD", signal, cache: "no-cache" });
|
|
3570
|
+
} catch {
|
|
3571
|
+
return true;
|
|
3572
|
+
}
|
|
3573
|
+
if (!head.ok) return true;
|
|
3574
|
+
for (const name of ["etag", "last-modified", "content-length"]) {
|
|
3575
|
+
const remote = head.headers.get(name);
|
|
3576
|
+
const local = cached.headers.get(name);
|
|
3577
|
+
if (remote && local) return remote === local;
|
|
3578
|
+
}
|
|
3579
|
+
return true;
|
|
3580
|
+
}
|
|
3581
|
+
async function fetchAsset(url2, options = {}) {
|
|
3582
|
+
const { onProgress, signal, revalidate = true } = options;
|
|
3583
|
+
const cache2 = await openCache(options.cacheName);
|
|
3584
|
+
if (cache2) {
|
|
3585
|
+
try {
|
|
3586
|
+
const hit = await cache2.match(url2);
|
|
3587
|
+
if (hit && (!revalidate || await cachedCopyIsCurrent(url2, hit, signal))) {
|
|
3588
|
+
const total2 = Number(hit.headers.get("content-length")) || 0;
|
|
3589
|
+
onProgress?.({ url: url2, loaded: total2, total: total2, fromCache: true });
|
|
3590
|
+
return hit;
|
|
3591
|
+
}
|
|
3592
|
+
} catch {
|
|
3593
|
+
}
|
|
3594
|
+
}
|
|
3595
|
+
const response = await fetch(url2, { signal });
|
|
3596
|
+
if (!response.ok) {
|
|
3597
|
+
throw new Error(`fetch ${url2}: HTTP ${response.status}`);
|
|
3598
|
+
}
|
|
3599
|
+
const total = Number(response.headers.get("content-length")) || 0;
|
|
3600
|
+
let body;
|
|
3601
|
+
if (response.body && onProgress) {
|
|
3602
|
+
const reader = response.body.getReader();
|
|
3603
|
+
const parts = [];
|
|
3604
|
+
let loaded = 0;
|
|
3605
|
+
onProgress({ url: url2, loaded, total, fromCache: false });
|
|
3606
|
+
for (; ; ) {
|
|
3607
|
+
const { done, value } = await reader.read();
|
|
3608
|
+
if (done) break;
|
|
3609
|
+
parts.push(value);
|
|
3610
|
+
loaded += value.byteLength;
|
|
3611
|
+
onProgress({ url: url2, loaded, total, fromCache: false });
|
|
3612
|
+
}
|
|
3613
|
+
body = new Uint8Array(new ArrayBuffer(loaded));
|
|
3614
|
+
let offset = 0;
|
|
3615
|
+
for (const part of parts) {
|
|
3616
|
+
body.set(part, offset);
|
|
3617
|
+
offset += part.byteLength;
|
|
3618
|
+
}
|
|
3619
|
+
} else {
|
|
3620
|
+
body = new Uint8Array(await response.arrayBuffer());
|
|
3621
|
+
onProgress?.({
|
|
3622
|
+
url: url2,
|
|
3623
|
+
loaded: body.byteLength,
|
|
3624
|
+
total: total || body.byteLength,
|
|
3625
|
+
fromCache: false
|
|
3626
|
+
});
|
|
3627
|
+
}
|
|
3628
|
+
const headers = new Headers();
|
|
3629
|
+
for (const name of ["content-type", "etag", "last-modified"]) {
|
|
3630
|
+
const value = response.headers.get(name);
|
|
3631
|
+
if (value) headers.set(name, value);
|
|
3632
|
+
}
|
|
3633
|
+
headers.set("content-length", String(body.byteLength));
|
|
3634
|
+
const buffered = new Response(body, { status: 200, headers });
|
|
3635
|
+
if (cache2) {
|
|
3636
|
+
try {
|
|
3637
|
+
await cache2.put(url2, buffered.clone());
|
|
3638
|
+
} catch {
|
|
3639
|
+
}
|
|
3640
|
+
}
|
|
3641
|
+
return buffered;
|
|
3642
|
+
}
|
|
3643
|
+
async function fetchAssetBytes(url2, options) {
|
|
3644
|
+
return new Uint8Array(await (await fetchAsset(url2, options)).arrayBuffer());
|
|
3645
|
+
}
|
|
3646
|
+
async function fetchAssetText(url2, options) {
|
|
3647
|
+
return (await fetchAsset(url2, options)).text();
|
|
3648
|
+
}
|
|
3649
|
+
var NAIST_JDIC_FILES = [
|
|
3650
|
+
"metadata.json",
|
|
3651
|
+
"char_def.bin",
|
|
3652
|
+
"matrix.mtx",
|
|
3653
|
+
"dict.da",
|
|
3654
|
+
"dict.vals",
|
|
3655
|
+
"unk.bin",
|
|
3656
|
+
"dict.wordsidx",
|
|
3657
|
+
"dict.words"
|
|
3658
|
+
];
|
|
3659
|
+
async function inflate(bytes) {
|
|
3660
|
+
if (typeof DecompressionStream === "undefined") {
|
|
3661
|
+
throw new Error(
|
|
3662
|
+
"DecompressionStream is not available; serve the dictionary uncompressed"
|
|
3663
|
+
);
|
|
3664
|
+
}
|
|
3665
|
+
const stream = new Blob([bytes]).stream().pipeThrough(new DecompressionStream("gzip"));
|
|
3666
|
+
return new Uint8Array(await new Response(stream).arrayBuffer());
|
|
3667
|
+
}
|
|
3668
|
+
async function loadNaistJdic(baseUrl, options = {}) {
|
|
3669
|
+
const { compressed = true, ...fetchOptions } = options;
|
|
3670
|
+
const base = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
3671
|
+
const entries = await Promise.all(
|
|
3672
|
+
NAIST_JDIC_FILES.map(async (name) => {
|
|
3673
|
+
const raw = await fetchAssetBytes(
|
|
3674
|
+
`${base}${name}${compressed ? ".gz" : ""}`,
|
|
3675
|
+
fetchOptions
|
|
3676
|
+
);
|
|
3677
|
+
return [name, compressed ? await inflate(raw) : raw];
|
|
3678
|
+
})
|
|
3679
|
+
);
|
|
3680
|
+
return Object.fromEntries(entries);
|
|
3681
|
+
}
|
|
3682
|
+
function initJpreprocessDictionary(module2, data) {
|
|
3683
|
+
module2.init_dictionary(
|
|
3684
|
+
data["metadata.json"],
|
|
3685
|
+
data["char_def.bin"],
|
|
3686
|
+
data["matrix.mtx"],
|
|
3687
|
+
data["dict.da"],
|
|
3688
|
+
data["dict.vals"],
|
|
3689
|
+
data["unk.bin"],
|
|
3690
|
+
data["dict.wordsidx"],
|
|
3691
|
+
data["dict.words"]
|
|
3692
|
+
);
|
|
3693
|
+
}
|
|
3694
|
+
var NUCLEUS = /* @__PURE__ */ new Set([
|
|
3695
|
+
"a",
|
|
3696
|
+
"i",
|
|
3697
|
+
"u",
|
|
3698
|
+
"e",
|
|
3699
|
+
"o",
|
|
3700
|
+
"A",
|
|
3701
|
+
"I",
|
|
3702
|
+
"U",
|
|
3703
|
+
"E",
|
|
3704
|
+
"O",
|
|
3705
|
+
"N",
|
|
3706
|
+
"cl"
|
|
3707
|
+
]);
|
|
3708
|
+
function softLimit(value, knee, max2) {
|
|
3709
|
+
const magnitude = Math.abs(value);
|
|
3710
|
+
const limited = magnitude <= knee ? magnitude : knee + (magnitude - knee) * 0.5;
|
|
3711
|
+
return Math.sign(value) * Math.min(max2, limited);
|
|
3712
|
+
}
|
|
3713
|
+
var DEVOICED = /* @__PURE__ */ new Set(["A", "I", "U", "E", "O"]);
|
|
3714
|
+
function segmentPhonemes(phonemes) {
|
|
3715
|
+
const segments = [];
|
|
3716
|
+
let openStart = null;
|
|
3717
|
+
for (let i2 = 0; i2 < phonemes.length; i2++) {
|
|
3718
|
+
const { phone, start_ms, duration_ms } = phonemes[i2];
|
|
3719
|
+
if (phone === "sil" || phone === "pau") {
|
|
3720
|
+
if (openStart !== null) {
|
|
3721
|
+
segments.push({
|
|
3722
|
+
pause: false,
|
|
3723
|
+
startMs: openStart,
|
|
3724
|
+
durationMs: start_ms - openStart,
|
|
3725
|
+
devoiced: false
|
|
3726
|
+
});
|
|
3727
|
+
openStart = null;
|
|
3728
|
+
}
|
|
3729
|
+
if (i2 === 0) continue;
|
|
3730
|
+
segments.push({
|
|
3731
|
+
pause: true,
|
|
3732
|
+
startMs: start_ms,
|
|
3733
|
+
durationMs: duration_ms,
|
|
3734
|
+
devoiced: false
|
|
3735
|
+
});
|
|
3736
|
+
continue;
|
|
3737
|
+
}
|
|
3738
|
+
if (openStart === null) openStart = start_ms;
|
|
3739
|
+
if (NUCLEUS.has(phone)) {
|
|
3740
|
+
segments.push({
|
|
3741
|
+
pause: false,
|
|
3742
|
+
startMs: openStart,
|
|
3743
|
+
durationMs: start_ms + duration_ms - openStart,
|
|
3744
|
+
devoiced: DEVOICED.has(phone)
|
|
3745
|
+
});
|
|
3746
|
+
openStart = null;
|
|
3747
|
+
}
|
|
3748
|
+
}
|
|
3749
|
+
if (openStart !== null) {
|
|
3750
|
+
const last = phonemes[phonemes.length - 1];
|
|
3751
|
+
segments.push({
|
|
3752
|
+
pause: false,
|
|
3753
|
+
startMs: openStart,
|
|
3754
|
+
durationMs: last.start_ms + last.duration_ms - openStart,
|
|
3755
|
+
devoiced: false
|
|
3756
|
+
});
|
|
3757
|
+
}
|
|
3758
|
+
return segments;
|
|
3759
|
+
}
|
|
3760
|
+
function alignHtsProsody(frames, features, options = {}) {
|
|
3761
|
+
const {
|
|
3762
|
+
intonationStrength = 1,
|
|
3763
|
+
frameMs = 10,
|
|
3764
|
+
fallbackPauseMs = 150,
|
|
3765
|
+
kneeCents = 400,
|
|
3766
|
+
maxCents = 700
|
|
3767
|
+
} = options;
|
|
3768
|
+
const segments = segmentPhonemes(frames.phonemes);
|
|
3769
|
+
const durations = new Array(features.length).fill(0);
|
|
3770
|
+
const htsStart = new Array(features.length).fill(null);
|
|
3771
|
+
const devoiced = new Array(features.length).fill(false);
|
|
3772
|
+
let s = 0;
|
|
3773
|
+
for (let f2 = 0; f2 < features.length; f2++) {
|
|
3774
|
+
const feature = features[f2];
|
|
3775
|
+
while (s < segments.length && segments[s].pause && !feature.pause) s++;
|
|
3776
|
+
const segment = segments[s];
|
|
3777
|
+
if (feature.pause) {
|
|
3778
|
+
if (segment?.pause) {
|
|
3779
|
+
durations[f2] = segment.durationMs;
|
|
3780
|
+
htsStart[f2] = segment.startMs;
|
|
3781
|
+
s++;
|
|
3782
|
+
} else {
|
|
3783
|
+
durations[f2] = fallbackPauseMs;
|
|
3784
|
+
}
|
|
3785
|
+
continue;
|
|
3786
|
+
}
|
|
3787
|
+
if (!segment) return null;
|
|
3788
|
+
durations[f2] = segment.durationMs;
|
|
3789
|
+
htsStart[f2] = segment.startMs;
|
|
3790
|
+
devoiced[f2] = segment.devoiced;
|
|
3791
|
+
s++;
|
|
3792
|
+
}
|
|
3793
|
+
if (segments.slice(s).some((segment) => !segment.pause)) return null;
|
|
3794
|
+
const f0 = frames.f0_hz;
|
|
3795
|
+
const logF0 = new Float64Array(f0.length);
|
|
3796
|
+
const voiced = [];
|
|
3797
|
+
for (let i2 = 0; i2 < f0.length; i2++) if (f0[i2] > 0) voiced.push(i2);
|
|
3798
|
+
if (voiced.length === 0) return null;
|
|
3799
|
+
const sorted = voiced.map((i2) => f0[i2]).sort((a, b) => a - b);
|
|
3800
|
+
const medianHz = sorted[Math.floor(sorted.length / 2)];
|
|
3801
|
+
for (let i2 = 0, v = 0; i2 < f0.length; i2++) {
|
|
3802
|
+
if (f0[i2] > 0) {
|
|
3803
|
+
logF0[i2] = Math.log2(f0[i2]);
|
|
3804
|
+
continue;
|
|
3805
|
+
}
|
|
3806
|
+
while (v < voiced.length && voiced[v] < i2) v++;
|
|
3807
|
+
const right = voiced[v];
|
|
3808
|
+
const left = v > 0 ? voiced[v - 1] : void 0;
|
|
3809
|
+
if (left === void 0) logF0[i2] = Math.log2(f0[right]);
|
|
3810
|
+
else if (right === void 0) logF0[i2] = Math.log2(f0[left]);
|
|
3811
|
+
else {
|
|
3812
|
+
const t = (i2 - left) / (right - left);
|
|
3813
|
+
logF0[i2] = Math.log2(f0[left]) * (1 - t) + Math.log2(f0[right]) * t;
|
|
3814
|
+
}
|
|
3815
|
+
}
|
|
3816
|
+
const logMedian = Math.log2(medianHz);
|
|
3817
|
+
const htsCentsAt = (tMs) => {
|
|
3818
|
+
const position = Math.max(0, tMs) / frames.frame_ms;
|
|
3819
|
+
const left = Math.min(logF0.length - 1, Math.floor(position));
|
|
3820
|
+
const right = Math.min(logF0.length - 1, left + 1);
|
|
3821
|
+
const t = position - Math.floor(position);
|
|
3822
|
+
return (logF0[left] * (1 - t) + logF0[right] * t - logMedian) * 1200;
|
|
3823
|
+
};
|
|
3824
|
+
const planStart = [];
|
|
3825
|
+
let cursor = 0;
|
|
3826
|
+
for (let f2 = 0; f2 < features.length; f2++) {
|
|
3827
|
+
planStart.push(cursor);
|
|
3828
|
+
cursor += durations[f2];
|
|
3829
|
+
}
|
|
3830
|
+
const durationMs = cursor;
|
|
3831
|
+
const count = Math.max(2, Math.ceil(durationMs / frameMs) + 2);
|
|
3832
|
+
const cents = new Array(count);
|
|
3833
|
+
let f = 0;
|
|
3834
|
+
for (let frame = 0; frame < count; frame++) {
|
|
3835
|
+
const tMs = frame * frameMs;
|
|
3836
|
+
while (f + 1 < features.length && tMs >= planStart[f + 1]) f++;
|
|
3837
|
+
const start = htsStart[f];
|
|
3838
|
+
let htsMs;
|
|
3839
|
+
if (start !== null) htsMs = start + (tMs - planStart[f]);
|
|
3840
|
+
else {
|
|
3841
|
+
const next = htsStart.slice(f + 1).find((value) => value !== null);
|
|
3842
|
+
htsMs = next ?? frames.phonemes.at(-1)?.start_ms ?? 0;
|
|
3843
|
+
}
|
|
3844
|
+
cents[frame] = softLimit(
|
|
3845
|
+
htsCentsAt(htsMs) * intonationStrength,
|
|
3846
|
+
kneeCents,
|
|
3847
|
+
maxCents
|
|
3848
|
+
);
|
|
3849
|
+
}
|
|
3850
|
+
return {
|
|
3851
|
+
moraDurationsMs: durations,
|
|
3852
|
+
pitchCurve: { frame_ms: frameMs, cents },
|
|
3853
|
+
medianHz,
|
|
3854
|
+
durationMs,
|
|
3855
|
+
devoiced
|
|
3856
|
+
};
|
|
3857
|
+
}
|
|
3858
|
+
var PUNCTUATION = /* @__PURE__ */ new Set(["\u3001", "\u3002", "\uFF1F", "\uFF01", ",", ".", "?", "!"]);
|
|
3859
|
+
var SMALL_KANA = /* @__PURE__ */ new Set(["\u3041", "\u3043", "\u3045", "\u3047", "\u3049", "\u3083", "\u3085", "\u3087", "\u308E", "\u3095", "\u3096"]);
|
|
3860
|
+
function toHiragana2(character) {
|
|
3861
|
+
const code = character.charCodeAt(0);
|
|
3862
|
+
if (12449 <= code && code <= 12534) {
|
|
3863
|
+
return String.fromCharCode(code - 96);
|
|
3864
|
+
}
|
|
3865
|
+
return character;
|
|
3866
|
+
}
|
|
3867
|
+
function splitMorae(reading) {
|
|
3868
|
+
const result = [];
|
|
3869
|
+
const normalized = reading.normalize("NFC").replace(/'/g, "").replace(/’/g, "");
|
|
3870
|
+
for (const character of normalized) {
|
|
3871
|
+
if (/\s/.test(character) || PUNCTUATION.has(character)) {
|
|
3872
|
+
if (result.length > 0 && !result[result.length - 1].pause) {
|
|
3873
|
+
result.push({ mora: "", pause: true });
|
|
3874
|
+
}
|
|
3875
|
+
continue;
|
|
3876
|
+
}
|
|
3877
|
+
const mora = toHiragana2(character);
|
|
3878
|
+
if (SMALL_KANA.has(mora) && result.length > 0 && !result[result.length - 1].pause) {
|
|
3879
|
+
result[result.length - 1].mora += mora;
|
|
3880
|
+
} else {
|
|
3881
|
+
result.push({ mora, pause: false });
|
|
3882
|
+
}
|
|
3883
|
+
}
|
|
3884
|
+
return result;
|
|
3885
|
+
}
|
|
3886
|
+
function isHigh(position, accent) {
|
|
3887
|
+
if (accent === 1) return position === 1;
|
|
3888
|
+
if (accent > 1) return 2 <= position && position <= accent;
|
|
3889
|
+
return position >= 2;
|
|
3890
|
+
}
|
|
3891
|
+
function analyze2(nodes) {
|
|
3892
|
+
const reading_parts = [];
|
|
3893
|
+
const result = [];
|
|
3894
|
+
let index = 0;
|
|
3895
|
+
while (index < nodes.length) {
|
|
3896
|
+
const node = nodes[index];
|
|
3897
|
+
const pronunciation = (node.pron || "").replace(/'/g, "").replace(/’/g, "");
|
|
3898
|
+
if (node.mora_size === 0 || PUNCTUATION.has(node.string)) {
|
|
3899
|
+
reading_parts.push(node.string || "\u3001");
|
|
3900
|
+
if (result.length > 0 && !result[result.length - 1].pause) {
|
|
3901
|
+
result.push({ mora: "", pause: true });
|
|
3902
|
+
}
|
|
3903
|
+
index++;
|
|
3904
|
+
continue;
|
|
3905
|
+
}
|
|
3906
|
+
const phrase_nodes = [];
|
|
3907
|
+
while (index < nodes.length) {
|
|
3908
|
+
const current = nodes[index];
|
|
3909
|
+
if (current.mora_size === 0 || PUNCTUATION.has(current.string)) {
|
|
3910
|
+
break;
|
|
3911
|
+
}
|
|
3912
|
+
if (phrase_nodes.length > 0 && current.chain_flag !== 1) {
|
|
3913
|
+
break;
|
|
3914
|
+
}
|
|
3915
|
+
const current_pronunciation = (current.pron || "").replace(/'/g, "").replace(/’/g, "");
|
|
3916
|
+
const morae = splitMorae(current_pronunciation).filter((item) => !item.pause);
|
|
3917
|
+
phrase_nodes.push({ current, morae });
|
|
3918
|
+
reading_parts.push(current_pronunciation);
|
|
3919
|
+
index++;
|
|
3920
|
+
}
|
|
3921
|
+
const phrase_length = phrase_nodes.reduce((sum, item) => sum + item.morae.length, 0);
|
|
3922
|
+
const accent = phrase_nodes[0].current.acc || 0;
|
|
3923
|
+
let phrase_position = 0;
|
|
3924
|
+
for (const { current, morae } of phrase_nodes) {
|
|
3925
|
+
for (let word_position = 1; word_position <= morae.length; word_position++) {
|
|
3926
|
+
phrase_position++;
|
|
3927
|
+
result.push({
|
|
3928
|
+
mora: morae[word_position - 1].mora,
|
|
3929
|
+
pause: false,
|
|
3930
|
+
accent_phrase_position: phrase_position,
|
|
3931
|
+
accent_phrase_length: phrase_length,
|
|
3932
|
+
accent_nucleus: accent,
|
|
3933
|
+
accent_high: isHigh(phrase_position, accent),
|
|
3934
|
+
accent_phrase_start: phrase_position === 1,
|
|
3935
|
+
accent_phrase_end: phrase_position === phrase_length,
|
|
3936
|
+
word_start: word_position === 1,
|
|
3937
|
+
word_end: word_position === morae.length,
|
|
3938
|
+
pos: current.pos || "*",
|
|
3939
|
+
pos_group1: current.pos_group1 || "*"
|
|
3940
|
+
});
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3943
|
+
}
|
|
3944
|
+
return { reading: reading_parts.join(""), features: result };
|
|
3945
|
+
}
|
|
3946
|
+
function sparse_features(token) {
|
|
3947
|
+
if (token.pause || token.accent_phrase_position === void 0 || token.accent_phrase_length === void 0 || token.accent_nucleus === void 0) {
|
|
3948
|
+
return {};
|
|
3949
|
+
}
|
|
3950
|
+
const phrase_length = Math.max(1, token.accent_phrase_length);
|
|
3951
|
+
const phrase_position = token.accent_phrase_position;
|
|
3952
|
+
const nucleus = token.accent_nucleus;
|
|
3953
|
+
const result = {
|
|
3954
|
+
"accent_position": phrase_position / phrase_length,
|
|
3955
|
+
"accent_from_end": (phrase_length - phrase_position) / phrase_length,
|
|
3956
|
+
"accent_nucleus_position": nucleus / phrase_length,
|
|
3957
|
+
"accent_high": token.accent_high ? 1 : 0,
|
|
3958
|
+
"accent_phrase_start": token.accent_phrase_start ? 1 : 0,
|
|
3959
|
+
"accent_phrase_end": token.accent_phrase_end ? 1 : 0,
|
|
3960
|
+
"word_start": token.word_start ? 1 : 0,
|
|
3961
|
+
"word_end": token.word_end ? 1 : 0
|
|
3962
|
+
};
|
|
3963
|
+
result[`pos=${token.pos || "*"}`] = 1;
|
|
3964
|
+
result[`pos_group1=${token.pos_group1 || "*"}`] = 1;
|
|
3965
|
+
if (nucleus === 0) {
|
|
3966
|
+
result["accent_type=heiban"] = 1;
|
|
3967
|
+
} else if (phrase_position < nucleus) {
|
|
3968
|
+
result["accent_type=before"] = 1;
|
|
3969
|
+
} else if (phrase_position === nucleus) {
|
|
3970
|
+
result["accent_type=nucleus"] = 1;
|
|
3971
|
+
} else {
|
|
3972
|
+
result["accent_type=after"] = 1;
|
|
3973
|
+
}
|
|
3974
|
+
return result;
|
|
3975
|
+
}
|
|
3976
|
+
var TRAILING = /[\s」』))〕】>>"'”’]+$/u;
|
|
3977
|
+
function isQuestion(text) {
|
|
3978
|
+
return /[??]$/u.test(text.replace(TRAILING, ""));
|
|
3979
|
+
}
|
|
3980
|
+
function smoothstep(t) {
|
|
3981
|
+
const x2 = Math.min(1, Math.max(0, t));
|
|
3982
|
+
return x2 * x2 * (3 - 2 * x2);
|
|
3983
|
+
}
|
|
3984
|
+
function shapeProsody(prosody, features, options = {}) {
|
|
3985
|
+
const {
|
|
3986
|
+
question = false,
|
|
3987
|
+
questionRiseCents = 350,
|
|
3988
|
+
energyDbPerSemitone = 0.5,
|
|
3989
|
+
devoicedGain = 0.5,
|
|
3990
|
+
minGain = 0.35,
|
|
3991
|
+
maxGain = 1.8
|
|
3992
|
+
} = options;
|
|
3993
|
+
const { frame_ms: frameMs } = prosody.pitchCurve;
|
|
3994
|
+
const cents = prosody.pitchCurve.cents.slice();
|
|
3995
|
+
const count = Math.min(features.length, prosody.moraDurationsMs.length);
|
|
3996
|
+
const startFrame = [];
|
|
3997
|
+
const endFrame = [];
|
|
3998
|
+
let cursor = 0;
|
|
3999
|
+
for (let f = 0; f < count; f++) {
|
|
4000
|
+
startFrame.push(cursor / frameMs);
|
|
4001
|
+
cursor += prosody.moraDurationsMs[f];
|
|
4002
|
+
endFrame.push(cursor / frameMs);
|
|
4003
|
+
}
|
|
4004
|
+
const frameRange = (f) => [
|
|
4005
|
+
Math.max(0, Math.min(cents.length, Math.round(startFrame[f]))),
|
|
4006
|
+
Math.max(0, Math.min(cents.length, Math.round(endFrame[f])))
|
|
4007
|
+
];
|
|
4008
|
+
if (question && questionRiseCents > 0) {
|
|
4009
|
+
let last = -1;
|
|
4010
|
+
for (let f = count - 1; f >= 0; f--) {
|
|
4011
|
+
if (!features[f].pause) {
|
|
4012
|
+
last = f;
|
|
4013
|
+
break;
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
if (last >= 0) {
|
|
4017
|
+
const [from, to] = frameRange(last);
|
|
4018
|
+
if (to > from) {
|
|
4019
|
+
const existing = cents[to - 1] - cents[from];
|
|
4020
|
+
const shortfall = questionRiseCents - existing;
|
|
4021
|
+
if (shortfall > 0) {
|
|
4022
|
+
for (let frame = from; frame < cents.length; frame++) {
|
|
4023
|
+
const t = (frame - from) / (to - from);
|
|
4024
|
+
cents[frame] += shortfall * smoothstep((t - 0.25) / 0.75);
|
|
4025
|
+
}
|
|
4026
|
+
}
|
|
4027
|
+
}
|
|
4028
|
+
}
|
|
4029
|
+
}
|
|
4030
|
+
const moraGains = new Array(features.length).fill(1);
|
|
4031
|
+
for (let f = 0; f < count; f++) {
|
|
4032
|
+
if (features[f].pause) continue;
|
|
4033
|
+
let gainDb = 0;
|
|
4034
|
+
if (energyDbPerSemitone !== 0) {
|
|
4035
|
+
const [from, to] = frameRange(f);
|
|
4036
|
+
if (to > from) {
|
|
4037
|
+
let sum = 0;
|
|
4038
|
+
for (let frame = from; frame < to; frame++) sum += cents[frame];
|
|
4039
|
+
gainDb += sum / (to - from) / 100 * energyDbPerSemitone;
|
|
4040
|
+
}
|
|
4041
|
+
}
|
|
4042
|
+
let gain = 10 ** (gainDb / 20);
|
|
4043
|
+
if (prosody.devoiced[f]) gain *= devoicedGain;
|
|
4044
|
+
moraGains[f] = Math.min(maxGain, Math.max(minGain, gain));
|
|
4045
|
+
}
|
|
4046
|
+
return {
|
|
4047
|
+
...prosody,
|
|
4048
|
+
pitchCurve: { frame_ms: frameMs, cents },
|
|
4049
|
+
moraGains
|
|
4050
|
+
};
|
|
4051
|
+
}
|
|
4052
|
+
function readingFromFeatures(features) {
|
|
4053
|
+
return features.map((frame) => frame.pause ? "\u3001" : frame.mora).join("");
|
|
4054
|
+
}
|
|
4055
|
+
var FS = WORLDLINE_SAMPLE_RATE;
|
|
4056
|
+
var msToSamples2 = (ms) => Math.round(ms / 1e3 * FS);
|
|
4057
|
+
function f0At(timeline, tMs) {
|
|
4058
|
+
const curve = timeline.f0_curve;
|
|
4059
|
+
if (curve.length === 0) return timeline.reference_hz || 220;
|
|
4060
|
+
const position = Math.max(0, tMs) / timeline.frame_ms;
|
|
4061
|
+
const left = Math.floor(position);
|
|
4062
|
+
if (left >= curve.length - 1) return curve[curve.length - 1];
|
|
4063
|
+
const progress = position - left;
|
|
4064
|
+
return curve[left] * (1 - progress) + curve[left + 1] * progress;
|
|
4065
|
+
}
|
|
4066
|
+
function applyMoraGains(plan, gains) {
|
|
4067
|
+
const morae = plan.morae ?? [];
|
|
4068
|
+
const timings = plan.mora_timings ?? [];
|
|
4069
|
+
const byIndex = morae.length === gains.length;
|
|
4070
|
+
const moraAt = (unit) => {
|
|
4071
|
+
if (byIndex) return unit.position;
|
|
4072
|
+
let found = -1;
|
|
4073
|
+
for (let i2 = 0; i2 < timings.length && i2 < gains.length; i2++) {
|
|
4074
|
+
if (timings[i2].StartMS <= unit.note_start_ms + 1e-6) found = i2;
|
|
4075
|
+
else break;
|
|
4076
|
+
}
|
|
4077
|
+
return found;
|
|
4078
|
+
};
|
|
4079
|
+
for (const unit of plan.timeline.units) {
|
|
4080
|
+
const index = moraAt(unit);
|
|
4081
|
+
const gain = gains[index];
|
|
4082
|
+
if (index < 0 || gain === void 0 || !Number.isFinite(gain)) continue;
|
|
4083
|
+
unit.volume *= gain;
|
|
4084
|
+
}
|
|
4085
|
+
}
|
|
4086
|
+
function planChunks(units2, firstChunkUnits, chunkUnits) {
|
|
4087
|
+
const ranges = [];
|
|
4088
|
+
if (units2.length === 0) return ranges;
|
|
4089
|
+
let start = 0;
|
|
4090
|
+
let coveredEndMs = units2[0].position_ms + units2[0].length_ms;
|
|
4091
|
+
let headSeam = false;
|
|
4092
|
+
for (let i2 = 1; i2 <= units2.length; i2++) {
|
|
4093
|
+
const limit = ranges.length === 0 ? firstChunkUnits : chunkUnits;
|
|
4094
|
+
const atEnd = i2 === units2.length;
|
|
4095
|
+
const clean = !atEnd && units2[i2].position_ms >= coveredEndMs - 1e-6;
|
|
4096
|
+
const full = !atEnd && i2 - start >= limit;
|
|
4097
|
+
if (atEnd || clean || full) {
|
|
4098
|
+
const tailSeam = !atEnd && !clean;
|
|
4099
|
+
ranges.push({ start, end: i2, headSeam, tailSeam });
|
|
4100
|
+
start = i2;
|
|
4101
|
+
headSeam = tailSeam;
|
|
4102
|
+
if (!atEnd) coveredEndMs = units2[i2].position_ms + units2[i2].length_ms;
|
|
4103
|
+
continue;
|
|
4104
|
+
}
|
|
4105
|
+
coveredEndMs = Math.max(
|
|
4106
|
+
coveredEndMs,
|
|
4107
|
+
units2[i2].position_ms + units2[i2].length_ms
|
|
4108
|
+
);
|
|
4109
|
+
}
|
|
4110
|
+
return ranges;
|
|
4111
|
+
}
|
|
4112
|
+
function seamTimeMs(next, crossfadeMs) {
|
|
4113
|
+
const earliest = next.position_ms + next.fade_in_ms + crossfadeMs / 2;
|
|
4114
|
+
const latest = next.position_ms + next.length_ms - crossfadeMs / 2;
|
|
4115
|
+
return Math.min(
|
|
4116
|
+
earliest,
|
|
4117
|
+
Math.max(next.position_ms + crossfadeMs / 2, latest)
|
|
4118
|
+
);
|
|
4119
|
+
}
|
|
4120
|
+
function applyFade(pcm, fromSample, samples, fadeIn) {
|
|
4121
|
+
const n = Math.max(1, Math.min(samples, pcm.length - fromSample));
|
|
4122
|
+
for (let k = 0; k < n; k++) {
|
|
4123
|
+
const t = (k + 0.5) / n;
|
|
4124
|
+
const gain = fadeIn ? Math.sin(Math.PI / 2 * t) : Math.cos(Math.PI / 2 * t);
|
|
4125
|
+
pcm[fromSample + k] *= gain;
|
|
4126
|
+
}
|
|
4127
|
+
}
|
|
4128
|
+
var UtauTTSAdapter = class _UtauTTSAdapter {
|
|
4129
|
+
constructor(worldline) {
|
|
4130
|
+
this.worldline = worldline;
|
|
4131
|
+
}
|
|
4132
|
+
worldline;
|
|
4133
|
+
static modelId = null;
|
|
4134
|
+
bankAliases = /* @__PURE__ */ new WeakMap();
|
|
4135
|
+
currentBank = null;
|
|
4136
|
+
pcmCache = /* @__PURE__ */ new Map();
|
|
4137
|
+
/**
|
|
4138
|
+
* Load the UtauTTS Go wasm. `wasm_exec.js` must already be on the page.
|
|
4139
|
+
* Pass `fetch` (e.g. koe's `fetchAsset`) to stream from the Cache API.
|
|
4140
|
+
*/
|
|
4141
|
+
static async initializeWasm(wasmUrl = "utautts.wasm", options = {}) {
|
|
4142
|
+
if (typeof utautts_plan === "function") return;
|
|
4143
|
+
if (typeof Go === "undefined") {
|
|
4144
|
+
throw new Error(
|
|
4145
|
+
"wasm_exec.js must be loaded before calling initializeWasm."
|
|
4146
|
+
);
|
|
4147
|
+
}
|
|
4148
|
+
const go = new Go();
|
|
4149
|
+
const responsePromise = (options.fetch ?? fetch)(wasmUrl);
|
|
4150
|
+
let instance;
|
|
4151
|
+
try {
|
|
4152
|
+
instance = (await WebAssembly.instantiateStreaming(responsePromise, go.importObject)).instance;
|
|
4153
|
+
} catch {
|
|
4154
|
+
const bytes = await (await (options.fetch ?? fetch)(wasmUrl)).arrayBuffer();
|
|
4155
|
+
instance = (await WebAssembly.instantiate(bytes, go.importObject)).instance;
|
|
4156
|
+
}
|
|
4157
|
+
void go.run(instance);
|
|
4158
|
+
for (let attempt = 0; attempt < 100 && typeof utautts_plan !== "function"; attempt++) {
|
|
4159
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
4160
|
+
}
|
|
4161
|
+
if (typeof utautts_plan !== "function") {
|
|
4162
|
+
throw new Error("utautts_plan failed to initialize in global scope.");
|
|
4163
|
+
}
|
|
4164
|
+
}
|
|
4165
|
+
static get ready() {
|
|
4166
|
+
return typeof utautts_plan === "function";
|
|
4167
|
+
}
|
|
4168
|
+
/** Parse and cache a prosody model (e.g. `frame-intonation-v8.json`) inside the wasm. */
|
|
4169
|
+
static setModel(modelJSON) {
|
|
4170
|
+
_UtauTTSAdapter.assertReady();
|
|
4171
|
+
const result = utautts_set_model(modelJSON);
|
|
4172
|
+
if (!result.success)
|
|
4173
|
+
throw new Error(`UtauTTS model error: ${result.error}`);
|
|
4174
|
+
_UtauTTSAdapter.modelId = result.id ?? null;
|
|
4175
|
+
return _UtauTTSAdapter.modelId;
|
|
4176
|
+
}
|
|
4177
|
+
static get currentModelId() {
|
|
4178
|
+
return _UtauTTSAdapter.modelId;
|
|
4179
|
+
}
|
|
4180
|
+
static assertReady() {
|
|
4181
|
+
if (typeof utautts_plan !== "function") {
|
|
4182
|
+
throw new Error(
|
|
4183
|
+
"UtauTTS wasm is not initialized; call UtauTTSAdapter.initializeWasm() first."
|
|
4184
|
+
);
|
|
4185
|
+
}
|
|
4186
|
+
}
|
|
4187
|
+
/**
|
|
4188
|
+
* Register a koe voice bank with the planner: the manifest becomes a virtual
|
|
4189
|
+
* oto.ini (koe PCM is pre-trimmed, so offset is 0) plus each sample's
|
|
4190
|
+
* recorded pitch. Called automatically by {@link plan}; cheap when unchanged.
|
|
4191
|
+
*/
|
|
4192
|
+
setBank(bank) {
|
|
4193
|
+
_UtauTTSAdapter.assertReady();
|
|
4194
|
+
if (this.currentBank === bank && this.bankAliases.has(bank)) return;
|
|
4195
|
+
const entries = {};
|
|
4196
|
+
const pitch = {};
|
|
4197
|
+
for (const [alias, phoneme] of Object.entries(bank.manifest.phonemes)) {
|
|
4198
|
+
entries[alias] = [
|
|
4199
|
+
{
|
|
4200
|
+
Filename: `koe:${alias}`,
|
|
4201
|
+
Alias: alias,
|
|
4202
|
+
Offset: 0,
|
|
4203
|
+
Fixed: phoneme.consonant / 48,
|
|
4204
|
+
Blank: 0,
|
|
4205
|
+
Preutterance: phoneme.pre / 48,
|
|
4206
|
+
Overlap: phoneme.overlap / 48,
|
|
4207
|
+
SourceGroup: "koe"
|
|
4208
|
+
}
|
|
4209
|
+
];
|
|
4210
|
+
if (phoneme.pitch > 0) pitch[alias] = phoneme.pitch;
|
|
4211
|
+
}
|
|
4212
|
+
const result = utautts_set_bank(
|
|
4213
|
+
JSON.stringify({
|
|
4214
|
+
name: "koe",
|
|
4215
|
+
oto_entries: entries,
|
|
4216
|
+
source_pitch_hz: pitch
|
|
4217
|
+
})
|
|
4218
|
+
);
|
|
4219
|
+
if (!result.success) throw new Error(`UtauTTS bank error: ${result.error}`);
|
|
4220
|
+
this.bankAliases.set(bank, result.aliases ?? 0);
|
|
4221
|
+
this.currentBank = bank;
|
|
4222
|
+
this.pcmCache.clear();
|
|
4223
|
+
}
|
|
4224
|
+
/**
|
|
4225
|
+
* Plan an utterance: unit selection, timing, pitch contour and worldline
|
|
4226
|
+
* placement. `features` are the mora-level frames from `openjtalkAnalyze`
|
|
4227
|
+
* (pauses included); the kana reading is derived from them.
|
|
4228
|
+
*/
|
|
4229
|
+
plan(bank, text, features, options = {}) {
|
|
4230
|
+
this.setBank(bank);
|
|
4231
|
+
const request = {
|
|
4232
|
+
text,
|
|
4233
|
+
reading: readingFromFeatures(features),
|
|
4234
|
+
frames: features.map((frame) => sparse_features(frame)),
|
|
4235
|
+
tone: options.tone ?? "C4",
|
|
4236
|
+
mora_duration_ms: options.moraDurationMs ?? 0,
|
|
4237
|
+
pause_duration_ms: options.pauseDurationMs ?? 0,
|
|
4238
|
+
release_ms: options.releaseMs ?? 20,
|
|
4239
|
+
leading_preutterance_ms: options.leadingPreutteranceMs ?? 0,
|
|
4240
|
+
apply_pitch: options.applyPitch ?? true,
|
|
4241
|
+
intonation_strength: options.intonationStrength ?? 1,
|
|
4242
|
+
speech_timing: options.speechTiming ?? false,
|
|
4243
|
+
word_boundary_envelope: options.wordBoundaryEnvelope ?? false,
|
|
4244
|
+
mora_durations_ms: options.prosody?.moraDurationsMs,
|
|
4245
|
+
pitch_curve: options.prosody?.pitchCurve
|
|
4246
|
+
};
|
|
4247
|
+
const response = utautts_plan(JSON.stringify(request));
|
|
4248
|
+
if (!response.success || !response.plan) {
|
|
4249
|
+
throw new Error(`UtauTTS error: ${response.error ?? "no plan"}`);
|
|
4250
|
+
}
|
|
4251
|
+
const plan = JSON.parse(response.plan);
|
|
4252
|
+
const gains = options.prosody?.moraGains;
|
|
4253
|
+
if (gains) applyMoraGains(plan, gains);
|
|
4254
|
+
return plan;
|
|
4255
|
+
}
|
|
4256
|
+
getPcm(bank, alias) {
|
|
4257
|
+
let cached = this.pcmCache.get(alias);
|
|
4258
|
+
if (!cached) {
|
|
4259
|
+
cached = bank.getPcm(alias);
|
|
4260
|
+
this.pcmCache.set(alias, cached);
|
|
4261
|
+
}
|
|
4262
|
+
return cached;
|
|
4263
|
+
}
|
|
4264
|
+
/**
|
|
4265
|
+
* Render a plan chunk by chunk. Each chunk is independent audio positioned
|
|
4266
|
+
* at `startMs`; schedule them as they arrive (see the demo) or sum them.
|
|
4267
|
+
*
|
|
4268
|
+
* Chunk breaks fall on pauses when possible. Inside a phrase a break renders
|
|
4269
|
+
* one neighbouring unit of context on each side so the unit crossfade stays
|
|
4270
|
+
* WORLD's spectral one, then the two renders are joined with a short
|
|
4271
|
+
* equal-power crossfade in the following vowel.
|
|
4272
|
+
*/
|
|
4273
|
+
async *renderChunks(bank, plan, options = {}) {
|
|
4274
|
+
const {
|
|
4275
|
+
firstChunkUnits = 3,
|
|
4276
|
+
chunkUnits = 6,
|
|
4277
|
+
seamCrossfadeMs = 20,
|
|
4278
|
+
signal
|
|
4279
|
+
} = options;
|
|
4280
|
+
const timeline = plan.timeline;
|
|
4281
|
+
const units2 = timeline.units.filter((unit) => unit.length_ms > 0);
|
|
4282
|
+
const ranges = planChunks(
|
|
4283
|
+
units2,
|
|
4284
|
+
Math.max(1, firstChunkUnits),
|
|
4285
|
+
Math.max(1, chunkUnits)
|
|
4286
|
+
);
|
|
4287
|
+
const crossfadeSamples = Math.max(2, msToSamples2(seamCrossfadeMs));
|
|
4288
|
+
for (let index = 0; index < ranges.length; index++) {
|
|
4289
|
+
if (signal?.aborted) return;
|
|
4290
|
+
const range = ranges[index];
|
|
4291
|
+
const renderStart = range.headSeam ? range.start - 1 : range.start;
|
|
4292
|
+
const renderEnd = range.tailSeam ? range.end + 1 : range.end;
|
|
4293
|
+
const rendered = units2.slice(renderStart, renderEnd);
|
|
4294
|
+
const baseMs = Math.min(...rendered.map((unit) => unit.position_ms));
|
|
4295
|
+
const phraseUnits = [];
|
|
4296
|
+
for (const unit of rendered) {
|
|
4297
|
+
const pcm2 = await this.getPcm(bank, unit.alias);
|
|
4298
|
+
if (!pcm2 || pcm2.length < MIN_WORLDLINE_SAMPLES) {
|
|
4299
|
+
console.warn(
|
|
4300
|
+
`[utautts] no usable PCM for alias "${unit.alias}"; skipped`
|
|
4301
|
+
);
|
|
4302
|
+
continue;
|
|
4303
|
+
}
|
|
4304
|
+
phraseUnits.push({
|
|
4305
|
+
pcm: pcm2,
|
|
4306
|
+
posMs: unit.position_ms - baseMs,
|
|
4307
|
+
skipMs: unit.skip_ms,
|
|
4308
|
+
lengthMs: unit.length_ms,
|
|
4309
|
+
fadeInMs: unit.fade_in_ms,
|
|
4310
|
+
fadeOutMs: unit.fade_out_ms,
|
|
4311
|
+
consonantMs: unit.consonant_ms,
|
|
4312
|
+
requiredLengthMs: unit.required_length_ms,
|
|
4313
|
+
volume: unit.volume,
|
|
4314
|
+
tone: unit.tone
|
|
4315
|
+
});
|
|
4316
|
+
}
|
|
4317
|
+
if (signal?.aborted) return;
|
|
4318
|
+
if (phraseUnits.length === 0) continue;
|
|
4319
|
+
const audio = this.worldline.renderPhrase({
|
|
4320
|
+
units: phraseUnits,
|
|
4321
|
+
pitch: (tMs) => f0At(timeline, baseMs + tMs),
|
|
4322
|
+
gender: options.gender,
|
|
4323
|
+
tension: options.tension,
|
|
4324
|
+
breathiness: options.breathiness,
|
|
4325
|
+
voicing: options.voicing
|
|
4326
|
+
});
|
|
4327
|
+
if (!audio || audio.length === 0) continue;
|
|
4328
|
+
let fromSample = 0;
|
|
4329
|
+
let toSample = audio.length;
|
|
4330
|
+
let startMs = baseMs;
|
|
4331
|
+
if (range.headSeam) {
|
|
4332
|
+
const seamMs = seamTimeMs(units2[range.start], seamCrossfadeMs);
|
|
4333
|
+
fromSample = Math.max(
|
|
4334
|
+
0,
|
|
4335
|
+
msToSamples2(seamMs - baseMs) - crossfadeSamples / 2
|
|
4336
|
+
);
|
|
4337
|
+
startMs = baseMs + fromSample / FS * 1e3;
|
|
4338
|
+
}
|
|
4339
|
+
if (range.tailSeam) {
|
|
4340
|
+
const seamMs = seamTimeMs(units2[range.end], seamCrossfadeMs);
|
|
4341
|
+
toSample = Math.min(
|
|
4342
|
+
audio.length,
|
|
4343
|
+
msToSamples2(seamMs - baseMs) + crossfadeSamples / 2
|
|
4344
|
+
);
|
|
4345
|
+
}
|
|
4346
|
+
if (toSample <= fromSample) continue;
|
|
4347
|
+
const pcm = audio.slice(fromSample, toSample);
|
|
4348
|
+
if (range.headSeam) applyFade(pcm, 0, crossfadeSamples, true);
|
|
4349
|
+
if (range.tailSeam)
|
|
4350
|
+
applyFade(
|
|
4351
|
+
pcm,
|
|
4352
|
+
Math.max(0, pcm.length - crossfadeSamples),
|
|
4353
|
+
crossfadeSamples,
|
|
4354
|
+
false
|
|
4355
|
+
);
|
|
4356
|
+
yield { pcm, startMs, index, units: units2.slice(range.start, range.end) };
|
|
4357
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
4358
|
+
}
|
|
4359
|
+
}
|
|
4360
|
+
/**
|
|
4361
|
+
* Plan + render an utterance into one buffer (Float32, 48 kHz) covering the
|
|
4362
|
+
* whole timeline. Use {@link plan} + {@link renderChunks} for streaming.
|
|
4363
|
+
*/
|
|
4364
|
+
async synthesizeText(bank, text, features, options = {}) {
|
|
4365
|
+
const plan = this.plan(bank, text, features, options);
|
|
4366
|
+
const total = msToSamples2(plan.timeline.duration_ms) + msToSamples2(200);
|
|
4367
|
+
const out = new Float32Array(total);
|
|
4368
|
+
let any = false;
|
|
4369
|
+
for await (const chunk of this.renderChunks(bank, plan, options)) {
|
|
4370
|
+
any = true;
|
|
4371
|
+
const offset = msToSamples2(chunk.startMs);
|
|
4372
|
+
const n = Math.min(chunk.pcm.length, out.length - offset);
|
|
4373
|
+
for (let k = 0; k < n; k++) out[offset + k] += chunk.pcm[k];
|
|
4374
|
+
}
|
|
4375
|
+
return any ? out : null;
|
|
4376
|
+
}
|
|
4377
|
+
};
|
|
3408
4378
|
|
|
3409
4379
|
// src/vibrato.ts
|
|
3410
4380
|
var VIBRATO_MIN_SEC = 0.35;
|
|
@@ -3426,7 +4396,7 @@ var MAX_PORTAMENTO_MS = 400;
|
|
|
3426
4396
|
var MIN_GLIDE_MS = 4;
|
|
3427
4397
|
var MAX_GLIDE_SPAN_RATIO = 0.9;
|
|
3428
4398
|
var unitsToHz2 = (units2) => 440 * 2 ** ((units2 - 2139) / 372);
|
|
3429
|
-
var
|
|
4399
|
+
var smoothstep2 = (u) => u * u * (3 - 2 * u);
|
|
3430
4400
|
var glideMsForSegments = (segments, totalMs) => segments.map((seg, i2) => {
|
|
3431
4401
|
const atMs = seg.atSec * 1e3;
|
|
3432
4402
|
const prevMs = i2 === 0 ? 0 : segments[i2 - 1].atSec * 1e3;
|
|
@@ -3460,7 +4430,7 @@ var pitchCurveFor = (baseHz, segments, preMs, vibrato, totalMs) => {
|
|
|
3460
4430
|
continue;
|
|
3461
4431
|
}
|
|
3462
4432
|
if (sinceOnset <= k.startMs) break;
|
|
3463
|
-
const u =
|
|
4433
|
+
const u = smoothstep2((sinceOnset - k.startMs) / k.glideMs);
|
|
3464
4434
|
hz = hz * (k.hz / hz) ** u;
|
|
3465
4435
|
break;
|
|
3466
4436
|
}
|
|
@@ -3476,6 +4446,177 @@ var segmentsCacheKey = (segments) => segments?.length ? `|s${segments.map(
|
|
|
3476
4446
|
(s) => `${s.pitch}@${Math.round(s.atSec * 100)}${s.portamento ? "~" : ""}`
|
|
3477
4447
|
).join(",")}` : "";
|
|
3478
4448
|
|
|
4449
|
+
// src/speech.ts
|
|
4450
|
+
var DEFAULT_TTS_BASE_URL = "https://onjmin.github.io/koe/demo/utautts/";
|
|
4451
|
+
var joinUrl = (base, path) => `${base.endsWith("/") ? base : `${base}/`}${path}`;
|
|
4452
|
+
var loadGoRuntime = (url2) => new Promise((resolve, reject) => {
|
|
4453
|
+
if (typeof globalThis.Go !== "undefined") {
|
|
4454
|
+
resolve();
|
|
4455
|
+
return;
|
|
4456
|
+
}
|
|
4457
|
+
if (typeof document === "undefined") {
|
|
4458
|
+
reject(new Error("wasm_exec.js needs a document to load into"));
|
|
4459
|
+
return;
|
|
4460
|
+
}
|
|
4461
|
+
const script = document.createElement("script");
|
|
4462
|
+
script.src = url2;
|
|
4463
|
+
script.async = true;
|
|
4464
|
+
script.onload = () => resolve();
|
|
4465
|
+
script.onerror = () => reject(new Error(`failed to load ${url2}`));
|
|
4466
|
+
document.head.appendChild(script);
|
|
4467
|
+
});
|
|
4468
|
+
var importJpreprocess = (url2) => import(
|
|
4469
|
+
/* @vite-ignore */
|
|
4470
|
+
/* webpackIgnore: true */
|
|
4471
|
+
url2
|
|
4472
|
+
);
|
|
4473
|
+
var shared = null;
|
|
4474
|
+
var getSpeechPlanner = (options = {}) => {
|
|
4475
|
+
const baseUrl = options.baseUrl ?? DEFAULT_TTS_BASE_URL;
|
|
4476
|
+
if (shared) {
|
|
4477
|
+
if (shared.baseUrl !== baseUrl) {
|
|
4478
|
+
console.warn(
|
|
4479
|
+
`[dtm] TTS assets are already loaded from ${shared.baseUrl}; ignoring ${baseUrl}`
|
|
4480
|
+
);
|
|
4481
|
+
}
|
|
4482
|
+
return shared.planner;
|
|
4483
|
+
}
|
|
4484
|
+
const planner = createSpeechPlanner({ ...options, baseUrl });
|
|
4485
|
+
shared = { baseUrl, planner };
|
|
4486
|
+
return planner;
|
|
4487
|
+
};
|
|
4488
|
+
var createSpeechPlanner = (options = {}) => {
|
|
4489
|
+
const baseUrl = options.baseUrl ?? DEFAULT_TTS_BASE_URL;
|
|
4490
|
+
let jp = null;
|
|
4491
|
+
let adapter = null;
|
|
4492
|
+
let readyPromise = null;
|
|
4493
|
+
let isReady = false;
|
|
4494
|
+
const load2 = async () => {
|
|
4495
|
+
const progress = /* @__PURE__ */ new Map();
|
|
4496
|
+
const onProgress = (p) => {
|
|
4497
|
+
if (!options.onProgress) return;
|
|
4498
|
+
progress.set(p.url, p);
|
|
4499
|
+
let loaded = 0;
|
|
4500
|
+
let total = 0;
|
|
4501
|
+
for (const item of progress.values()) {
|
|
4502
|
+
loaded += item.loaded;
|
|
4503
|
+
total += item.total;
|
|
4504
|
+
}
|
|
4505
|
+
options.onProgress(loaded, total);
|
|
4506
|
+
};
|
|
4507
|
+
const fetchWithProgress = (url2) => fetchAsset(url2, { onProgress });
|
|
4508
|
+
const jpreprocessReady = (async () => {
|
|
4509
|
+
const glue2 = await importJpreprocess(
|
|
4510
|
+
joinUrl(baseUrl, "jpreprocess_wasm/jpreprocess_wasm.js")
|
|
4511
|
+
);
|
|
4512
|
+
await glue2.default({
|
|
4513
|
+
module_or_path: fetchWithProgress(
|
|
4514
|
+
joinUrl(baseUrl, "jpreprocess_wasm/jpreprocess_wasm_bg.wasm")
|
|
4515
|
+
)
|
|
4516
|
+
});
|
|
4517
|
+
if (!glue2.is_ready()) {
|
|
4518
|
+
const dict = await loadNaistJdic(
|
|
4519
|
+
joinUrl(baseUrl, "jpreprocess_wasm/naist-jdic"),
|
|
4520
|
+
{ onProgress }
|
|
4521
|
+
);
|
|
4522
|
+
initJpreprocessDictionary(glue2, dict);
|
|
4523
|
+
}
|
|
4524
|
+
return glue2;
|
|
4525
|
+
})();
|
|
4526
|
+
const utauttsReady = (async () => {
|
|
4527
|
+
await loadGoRuntime(joinUrl(baseUrl, "wasm_exec.js"));
|
|
4528
|
+
await UtauTTSAdapter.initializeWasm(joinUrl(baseUrl, "utautts.wasm"), {
|
|
4529
|
+
fetch: fetchWithProgress
|
|
4530
|
+
});
|
|
4531
|
+
UtauTTSAdapter.setModel(
|
|
4532
|
+
await fetchAssetText(joinUrl(baseUrl, "frame-intonation-v8.json"), {
|
|
4533
|
+
onProgress
|
|
4534
|
+
})
|
|
4535
|
+
);
|
|
4536
|
+
})();
|
|
4537
|
+
const htsVoiceReady = fetchAssetBytes(
|
|
4538
|
+
joinUrl(baseUrl, "hts/tohoku-f01-neutral.htsvoice"),
|
|
4539
|
+
{ onProgress }
|
|
4540
|
+
);
|
|
4541
|
+
const [glue, , htsVoice] = await Promise.all([
|
|
4542
|
+
jpreprocessReady,
|
|
4543
|
+
utauttsReady,
|
|
4544
|
+
htsVoiceReady
|
|
4545
|
+
]);
|
|
4546
|
+
if (!glue.is_voice_ready()) glue.init_voice(htsVoice);
|
|
4547
|
+
jp = glue;
|
|
4548
|
+
adapter = new UtauTTSAdapter(null);
|
|
4549
|
+
isReady = true;
|
|
4550
|
+
};
|
|
4551
|
+
const ready = () => {
|
|
4552
|
+
if (!readyPromise) {
|
|
4553
|
+
readyPromise = load2().catch((err2) => {
|
|
4554
|
+
readyPromise = null;
|
|
4555
|
+
throw err2;
|
|
4556
|
+
});
|
|
4557
|
+
}
|
|
4558
|
+
return readyPromise;
|
|
4559
|
+
};
|
|
4560
|
+
const plan = (bank, text, o = {}) => {
|
|
4561
|
+
if (!jp || !adapter) {
|
|
4562
|
+
throw new Error("speech planner is not ready; await ready() first");
|
|
4563
|
+
}
|
|
4564
|
+
const intonationStrength = o.intonationStrength ?? 1;
|
|
4565
|
+
const nodes = JSON.parse(jp.analyze_text(text));
|
|
4566
|
+
const { features } = analyze2(nodes);
|
|
4567
|
+
if (features.length === 0) {
|
|
4568
|
+
throw new Error(`no readable morae in "${text}"`);
|
|
4569
|
+
}
|
|
4570
|
+
let prosody = null;
|
|
4571
|
+
if (jp.is_voice_ready()) {
|
|
4572
|
+
const frames = JSON.parse(jp.analyze_prosody(text, 1));
|
|
4573
|
+
prosody = alignHtsProsody(frames, features, { intonationStrength });
|
|
4574
|
+
if (prosody) {
|
|
4575
|
+
prosody = shapeProsody(prosody, features, {
|
|
4576
|
+
question: isQuestion(text)
|
|
4577
|
+
});
|
|
4578
|
+
}
|
|
4579
|
+
}
|
|
4580
|
+
return adapter.plan(bank, text, features, {
|
|
4581
|
+
tone: o.tone ?? "C4",
|
|
4582
|
+
intonationStrength,
|
|
4583
|
+
prosody: prosody ?? void 0
|
|
4584
|
+
});
|
|
4585
|
+
};
|
|
4586
|
+
return { ready, isReady: () => isReady, plan };
|
|
4587
|
+
};
|
|
4588
|
+
var prepareSpeechPlan = (plan, ratio, toReal = (a) => a) => {
|
|
4589
|
+
const timeline = plan.timeline;
|
|
4590
|
+
return {
|
|
4591
|
+
...plan,
|
|
4592
|
+
timeline: {
|
|
4593
|
+
...timeline,
|
|
4594
|
+
f0_curve: timeline.f0_curve.map((hz) => hz * ratio),
|
|
4595
|
+
units: timeline.units.map((u) => ({
|
|
4596
|
+
...u,
|
|
4597
|
+
alias: toReal(u.alias),
|
|
4598
|
+
target_f0_hz: u.target_f0_hz * ratio
|
|
4599
|
+
}))
|
|
4600
|
+
}
|
|
4601
|
+
};
|
|
4602
|
+
};
|
|
4603
|
+
var speechPlanLeadingSec = (plan) => Math.max(0, plan.timeline.leading_ms || 0) / 1e3;
|
|
4604
|
+
var speechPlanDurationSec = (plan) => {
|
|
4605
|
+
const timeline = plan.timeline;
|
|
4606
|
+
let endMs = 0;
|
|
4607
|
+
for (const u of timeline.units) {
|
|
4608
|
+
endMs = Math.max(endMs, u.position_ms + u.length_ms);
|
|
4609
|
+
}
|
|
4610
|
+
if (endMs <= 0) endMs = timeline.duration_ms;
|
|
4611
|
+
return Math.max(0, endMs / 1e3 - speechPlanLeadingSec(plan));
|
|
4612
|
+
};
|
|
4613
|
+
var medianRecordedPitchHz = (phonemes) => {
|
|
4614
|
+
const pitches = Object.values(phonemes).map((p) => p.pitch).filter((hz) => hz > 0).sort((a, b) => a - b);
|
|
4615
|
+
if (pitches.length === 0) return void 0;
|
|
4616
|
+
const mid = pitches.length >> 1;
|
|
4617
|
+
return pitches.length % 2 ? pitches[mid] : (pitches[mid - 1] + pitches[mid]) / 2;
|
|
4618
|
+
};
|
|
4619
|
+
|
|
3479
4620
|
// src/types.ts
|
|
3480
4621
|
var DEFAULT_VOCAL_VOLUME = 200;
|
|
3481
4622
|
var DEFAULT_BPM = 120;
|
|
@@ -3574,7 +4715,7 @@ var kanaTable = {
|
|
|
3574
4715
|
\u307D: ["p", "o"],
|
|
3575
4716
|
\u3093: ["N", "N"]
|
|
3576
4717
|
};
|
|
3577
|
-
var
|
|
4718
|
+
var SMALL_KANA2 = "\u3041\u3043\u3045\u3047\u3049\u3083\u3085\u3087";
|
|
3578
4719
|
var VOWEL_KANA = {
|
|
3579
4720
|
a: "\u3042",
|
|
3580
4721
|
i: "\u3044",
|
|
@@ -3589,13 +4730,43 @@ var REST_MARK = "_";
|
|
|
3589
4730
|
var BREATH_MARK = "\u3001";
|
|
3590
4731
|
var FADE_OUT_MARK = "\u2193";
|
|
3591
4732
|
var FADE_IN_MARK = "\u2191";
|
|
4733
|
+
var SPEAK_OPEN = "\u300C";
|
|
4734
|
+
var SPEAK_CLOSE = "\u300D";
|
|
4735
|
+
var splitSpeech = (text) => {
|
|
4736
|
+
const pieces = [];
|
|
4737
|
+
let sung = "";
|
|
4738
|
+
let i2 = 0;
|
|
4739
|
+
while (i2 < text.length) {
|
|
4740
|
+
const ch = text[i2];
|
|
4741
|
+
if (ch === SPEAK_OPEN || ch === "\uFF62") {
|
|
4742
|
+
let end = -1;
|
|
4743
|
+
for (let j = i2 + 1; j < text.length; j++) {
|
|
4744
|
+
if (text[j] === SPEAK_CLOSE || text[j] === "\uFF63") {
|
|
4745
|
+
end = j;
|
|
4746
|
+
break;
|
|
4747
|
+
}
|
|
4748
|
+
}
|
|
4749
|
+
if (sung) pieces.push({ sung });
|
|
4750
|
+
sung = "";
|
|
4751
|
+
const body = end < 0 ? text.slice(i2 + 1) : text.slice(i2 + 1, end);
|
|
4752
|
+
pieces.push({ speak: body });
|
|
4753
|
+
i2 = end < 0 ? text.length : end + 1;
|
|
4754
|
+
continue;
|
|
4755
|
+
}
|
|
4756
|
+
sung += ch;
|
|
4757
|
+
i2++;
|
|
4758
|
+
}
|
|
4759
|
+
if (sung) pieces.push({ sung });
|
|
4760
|
+
return pieces;
|
|
4761
|
+
};
|
|
4762
|
+
var sanitizeSpeech = (text) => text.replace(/[\r\n;]+/g, " ").replace(/\s+/g, " ").trim();
|
|
3592
4763
|
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, "");
|
|
3593
4764
|
var splitSyllables = (text) => {
|
|
3594
4765
|
const MARKS = `${TIE_MARK}${PORTAMENTO_MARK}${REST_MARK}${BREATH_MARK}${FADE_OUT_MARK}${FADE_IN_MARK}`;
|
|
3595
4766
|
const result = [];
|
|
3596
4767
|
for (const ch of text) {
|
|
3597
4768
|
const prev = result[result.length - 1];
|
|
3598
|
-
if (prev !== void 0 &&
|
|
4769
|
+
if (prev !== void 0 && SMALL_KANA2.includes(ch) && !MARKS.includes(prev[prev.length - 1])) {
|
|
3599
4770
|
result[result.length - 1] += ch;
|
|
3600
4771
|
} else {
|
|
3601
4772
|
result.push(ch);
|
|
@@ -3644,42 +4815,52 @@ var analyzeSyllable = (syllable) => {
|
|
|
3644
4815
|
var normalizeLyrics = (text) => {
|
|
3645
4816
|
const result = [];
|
|
3646
4817
|
let prevVowel = "";
|
|
3647
|
-
for (const
|
|
3648
|
-
if (
|
|
3649
|
-
const
|
|
3650
|
-
|
|
4818
|
+
for (const piece of splitSpeech(text)) {
|
|
4819
|
+
if ("speak" in piece) {
|
|
4820
|
+
const body = sanitizeSpeech(piece.speak);
|
|
4821
|
+
result.push(
|
|
4822
|
+
body ? { kana: body, consonant: "", vowel: "", kind: "speak", text: body } : { kana: REST_MARK, consonant: "", vowel: "", kind: "rest" }
|
|
4823
|
+
);
|
|
3651
4824
|
prevVowel = "";
|
|
3652
4825
|
continue;
|
|
3653
4826
|
}
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
if (
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
if (
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
4827
|
+
for (const mark of splitSyllables(sanitizeText(piece.sung))) {
|
|
4828
|
+
if (mark === BREATH_MARK) {
|
|
4829
|
+
const last = result[result.length - 1];
|
|
4830
|
+
if (last) last.breathAfter = true;
|
|
4831
|
+
prevVowel = "";
|
|
4832
|
+
continue;
|
|
4833
|
+
}
|
|
4834
|
+
if (mark === FADE_OUT_MARK) {
|
|
4835
|
+
const last = result[result.length - 1];
|
|
4836
|
+
if (last) last.fadeOut = true;
|
|
4837
|
+
continue;
|
|
4838
|
+
}
|
|
4839
|
+
if (mark === FADE_IN_MARK) {
|
|
4840
|
+
const last = result[result.length - 1];
|
|
4841
|
+
if (last) last.fadeIn = true;
|
|
4842
|
+
continue;
|
|
4843
|
+
}
|
|
4844
|
+
const syl = analyzeSyllable(mark);
|
|
4845
|
+
if (syl.kind === "tie") {
|
|
4846
|
+
if (!prevVowel) continue;
|
|
4847
|
+
result.push({
|
|
4848
|
+
...syl,
|
|
4849
|
+
kana: prevVowel === "N" ? "\u3093" : VOWEL_KANA[prevVowel] ?? syl.kana,
|
|
4850
|
+
consonant: "",
|
|
4851
|
+
vowel: prevVowel
|
|
4852
|
+
});
|
|
4853
|
+
continue;
|
|
4854
|
+
}
|
|
4855
|
+
if (syl.kind === "rest") prevVowel = "";
|
|
4856
|
+
else if (syl.vowel) prevVowel = syl.vowel;
|
|
4857
|
+
result.push(syl);
|
|
3674
4858
|
}
|
|
3675
|
-
if (syl.kind === "rest") prevVowel = "";
|
|
3676
|
-
else if (syl.vowel) prevVowel = syl.vowel;
|
|
3677
|
-
result.push(syl);
|
|
3678
4859
|
}
|
|
3679
4860
|
return result;
|
|
3680
4861
|
};
|
|
3681
4862
|
var displayKana = (syl) => {
|
|
3682
|
-
const head = syl.kind === "tie" ? syl.portamento ? PORTAMENTO_MARK : TIE_MARK : syl.kind === "rest" ? REST_MARK : syl.kana;
|
|
4863
|
+
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;
|
|
3683
4864
|
let out = syl.breathAfter ? head + BREATH_MARK : head;
|
|
3684
4865
|
if (syl.fadeIn) out += FADE_IN_MARK;
|
|
3685
4866
|
if (syl.fadeOut) out += FADE_OUT_MARK;
|
|
@@ -4168,6 +5349,40 @@ var collectPitchTokens = (aliases) => {
|
|
|
4168
5349
|
}
|
|
4169
5350
|
return [...seen].map(([token, midi]) => ({ token, midi }));
|
|
4170
5351
|
};
|
|
5352
|
+
var nearestPitchToken = (pitchTokens, noteNum) => {
|
|
5353
|
+
let best = null;
|
|
5354
|
+
for (const t of pitchTokens) {
|
|
5355
|
+
if (!best || Math.abs(t.midi - noteNum) < Math.abs(best.midi - noteNum))
|
|
5356
|
+
best = t;
|
|
5357
|
+
}
|
|
5358
|
+
return best;
|
|
5359
|
+
};
|
|
5360
|
+
var createSpeechToneView = (phonemes, token) => {
|
|
5361
|
+
if (!token) {
|
|
5362
|
+
return {
|
|
5363
|
+
view: { manifest: { phonemes } },
|
|
5364
|
+
toReal: (a) => a,
|
|
5365
|
+
referenceHz: medianRecordedPitchHz(phonemes)
|
|
5366
|
+
};
|
|
5367
|
+
}
|
|
5368
|
+
const suffix = `_${token}`;
|
|
5369
|
+
const stripped = {};
|
|
5370
|
+
const real = /* @__PURE__ */ new Map();
|
|
5371
|
+
for (const [alias, entry] of Object.entries(phonemes)) {
|
|
5372
|
+
if (alias.endsWith(suffix)) {
|
|
5373
|
+
const bare = alias.slice(0, -suffix.length);
|
|
5374
|
+
stripped[bare] = entry;
|
|
5375
|
+
real.set(bare, alias);
|
|
5376
|
+
} else if (!PITCH_SUFFIX.test(alias) && !(alias in stripped)) {
|
|
5377
|
+
stripped[alias] = entry;
|
|
5378
|
+
}
|
|
5379
|
+
}
|
|
5380
|
+
return {
|
|
5381
|
+
view: { manifest: { phonemes: stripped } },
|
|
5382
|
+
toReal: (a) => real.get(a) ?? a,
|
|
5383
|
+
referenceHz: medianRecordedPitchHz(stripped)
|
|
5384
|
+
};
|
|
5385
|
+
};
|
|
4171
5386
|
var resolveKoeAlias = (hasAlias, pitchTokens, syl, prevVowel, noteNum) => {
|
|
4172
5387
|
const kana = syl.kana;
|
|
4173
5388
|
const cons = syl.consonant === "N" ? "n" : syl.consonant;
|
|
@@ -4323,10 +5538,29 @@ var createLocalBackend = async (options) => {
|
|
|
4323
5538
|
rate
|
|
4324
5539
|
};
|
|
4325
5540
|
};
|
|
5541
|
+
let speechAdapter = null;
|
|
5542
|
+
const renderSpeech = async (plan, expr, onChunk, signal) => {
|
|
5543
|
+
if (!worldline) return;
|
|
5544
|
+
speechAdapter ??= new UtauTTSAdapter(worldline);
|
|
5545
|
+
try {
|
|
5546
|
+
for await (const chunk of speechAdapter.renderChunks(bank, plan, {
|
|
5547
|
+
signal,
|
|
5548
|
+
gender: expr?.gender,
|
|
5549
|
+
breathiness: expr?.breathiness,
|
|
5550
|
+
tension: expr?.tension
|
|
5551
|
+
})) {
|
|
5552
|
+
onChunk({ pcm: chunk.pcm, startMs: chunk.startMs, index: chunk.index });
|
|
5553
|
+
}
|
|
5554
|
+
} catch (err2) {
|
|
5555
|
+
console.warn("[dtm] speech synthesis failed", err2);
|
|
5556
|
+
}
|
|
5557
|
+
};
|
|
4326
5558
|
return {
|
|
4327
5559
|
hasAlias: (a) => bank.has(a),
|
|
4328
5560
|
pitchTokens: collectPitchTokens(Object.keys(bank.manifest.phonemes)),
|
|
4329
5561
|
renderAlias,
|
|
5562
|
+
phonemes: bank.manifest.phonemes,
|
|
5563
|
+
renderSpeech,
|
|
4330
5564
|
dispose: () => {
|
|
4331
5565
|
}
|
|
4332
5566
|
};
|
|
@@ -4342,7 +5576,9 @@ var spawnVoiceWorker = async (url2) => {
|
|
|
4342
5576
|
var createWorkerBackend = async (workerUrl, options) => {
|
|
4343
5577
|
const worker2 = await spawnVoiceWorker(workerUrl);
|
|
4344
5578
|
const aliasSet = /* @__PURE__ */ new Set();
|
|
5579
|
+
let phonemes = {};
|
|
4345
5580
|
const pending = /* @__PURE__ */ new Map();
|
|
5581
|
+
const speechPending = /* @__PURE__ */ new Map();
|
|
4346
5582
|
let reqId = 0;
|
|
4347
5583
|
let onReady = null;
|
|
4348
5584
|
let onFail = null;
|
|
@@ -4350,6 +5586,7 @@ var createWorkerBackend = async (workerUrl, options) => {
|
|
|
4350
5586
|
const m = ev.data;
|
|
4351
5587
|
if (m.type === "ready") {
|
|
4352
5588
|
for (const a of m.aliases) aliasSet.add(a);
|
|
5589
|
+
phonemes = m.phonemes ?? {};
|
|
4353
5590
|
onReady?.();
|
|
4354
5591
|
} else if (m.type === "error") {
|
|
4355
5592
|
onFail?.(new Error(m.message));
|
|
@@ -4359,6 +5596,15 @@ var createWorkerBackend = async (workerUrl, options) => {
|
|
|
4359
5596
|
pending.delete(m.id);
|
|
4360
5597
|
cb(m);
|
|
4361
5598
|
}
|
|
5599
|
+
} else if (m.type === "speech-chunk") {
|
|
5600
|
+
speechPending.get(m.id)?.onChunk({ pcm: m.pcm, startMs: m.startMs, index: m.index });
|
|
5601
|
+
} else if (m.type === "speech-end") {
|
|
5602
|
+
const s = speechPending.get(m.id);
|
|
5603
|
+
if (s) {
|
|
5604
|
+
speechPending.delete(m.id);
|
|
5605
|
+
if (m.error) console.warn("[dtm] speech synthesis failed", m.error);
|
|
5606
|
+
s.done();
|
|
5607
|
+
}
|
|
4362
5608
|
}
|
|
4363
5609
|
};
|
|
4364
5610
|
worker2.onerror = (e) => {
|
|
@@ -4398,10 +5644,32 @@ var createWorkerBackend = async (workerUrl, options) => {
|
|
|
4398
5644
|
pitchSegments
|
|
4399
5645
|
});
|
|
4400
5646
|
});
|
|
5647
|
+
const renderSpeech = (plan, expr, onChunk, signal) => new Promise((resolve) => {
|
|
5648
|
+
const id = ++reqId;
|
|
5649
|
+
speechPending.set(id, { onChunk, done: resolve });
|
|
5650
|
+
signal?.addEventListener("abort", () => {
|
|
5651
|
+
worker2.postMessage({
|
|
5652
|
+
type: "speak-abort",
|
|
5653
|
+
id
|
|
5654
|
+
});
|
|
5655
|
+
});
|
|
5656
|
+
worker2.postMessage({
|
|
5657
|
+
type: "speak",
|
|
5658
|
+
id,
|
|
5659
|
+
plan,
|
|
5660
|
+
gender: expr?.gender,
|
|
5661
|
+
breathiness: expr?.breathiness,
|
|
5662
|
+
tension: expr?.tension
|
|
5663
|
+
});
|
|
5664
|
+
});
|
|
4401
5665
|
return {
|
|
4402
5666
|
hasAlias: (a) => aliasSet.has(a),
|
|
4403
5667
|
pitchTokens: collectPitchTokens(aliasSet),
|
|
4404
5668
|
renderAlias,
|
|
5669
|
+
get phonemes() {
|
|
5670
|
+
return phonemes;
|
|
5671
|
+
},
|
|
5672
|
+
renderSpeech,
|
|
4405
5673
|
dispose: () => worker2.terminate()
|
|
4406
5674
|
};
|
|
4407
5675
|
};
|
|
@@ -4589,6 +5857,206 @@ var createKoeVoice = async (ctx, destination, options) => {
|
|
|
4589
5857
|
durationSec
|
|
4590
5858
|
);
|
|
4591
5859
|
};
|
|
5860
|
+
const planner = getSpeechPlanner({
|
|
5861
|
+
baseUrl: options.ttsBaseUrl
|
|
5862
|
+
});
|
|
5863
|
+
const toneViews = /* @__PURE__ */ new Map();
|
|
5864
|
+
const toneViewFor = (token) => {
|
|
5865
|
+
const k = token ?? "";
|
|
5866
|
+
let v = toneViews.get(k);
|
|
5867
|
+
if (!v) {
|
|
5868
|
+
v = createSpeechToneView(backend.phonemes, token);
|
|
5869
|
+
toneViews.set(k, v);
|
|
5870
|
+
}
|
|
5871
|
+
return v;
|
|
5872
|
+
};
|
|
5873
|
+
const toneFor = (pitch) => nearestPitchToken(backend.pitchTokens, unitsToMidiFloat(pitch))?.token ?? null;
|
|
5874
|
+
const plans = /* @__PURE__ */ new Map();
|
|
5875
|
+
const planFor = (text, token) => {
|
|
5876
|
+
const k = `${token ?? ""}|${text}`;
|
|
5877
|
+
let p = plans.get(k);
|
|
5878
|
+
if (!p) {
|
|
5879
|
+
p = (async () => {
|
|
5880
|
+
try {
|
|
5881
|
+
await planner.ready();
|
|
5882
|
+
return planner.plan(toneViewFor(token).view, text, {
|
|
5883
|
+
tone: token ?? "C4"
|
|
5884
|
+
});
|
|
5885
|
+
} catch (err2) {
|
|
5886
|
+
console.warn(`[dtm] speech plan failed for "${text}"`, err2);
|
|
5887
|
+
return null;
|
|
5888
|
+
}
|
|
5889
|
+
})();
|
|
5890
|
+
plans.set(k, p);
|
|
5891
|
+
}
|
|
5892
|
+
return p;
|
|
5893
|
+
};
|
|
5894
|
+
const speechCache = /* @__PURE__ */ new Map();
|
|
5895
|
+
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)}` : ""}`;
|
|
5896
|
+
const SPEECH_PITCH_RATIO_MAX = 4;
|
|
5897
|
+
const speechEntryFor = (text, pitch, expr) => {
|
|
5898
|
+
const key = speechKeyOf(text, pitch, expr);
|
|
5899
|
+
let entry = speechCache.get(key);
|
|
5900
|
+
if (entry) return entry;
|
|
5901
|
+
const abort = new AbortController();
|
|
5902
|
+
let resolvePlanned;
|
|
5903
|
+
const planned = new Promise((r) => {
|
|
5904
|
+
resolvePlanned = r;
|
|
5905
|
+
});
|
|
5906
|
+
const e = {
|
|
5907
|
+
planned,
|
|
5908
|
+
rendered: Promise.resolve(),
|
|
5909
|
+
durationSec: 0,
|
|
5910
|
+
chunks: [],
|
|
5911
|
+
done: false,
|
|
5912
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
5913
|
+
abort
|
|
5914
|
+
};
|
|
5915
|
+
e.rendered = (async () => {
|
|
5916
|
+
const token = toneFor(pitch);
|
|
5917
|
+
const plan = await planFor(text, token);
|
|
5918
|
+
if (!plan) {
|
|
5919
|
+
e.done = true;
|
|
5920
|
+
resolvePlanned(null);
|
|
5921
|
+
return;
|
|
5922
|
+
}
|
|
5923
|
+
const view = toneViewFor(token);
|
|
5924
|
+
const reference = plan.timeline.reference_hz || view.referenceHz || 0;
|
|
5925
|
+
const ratio = reference > 0 ? Math.min(
|
|
5926
|
+
SPEECH_PITCH_RATIO_MAX,
|
|
5927
|
+
Math.max(
|
|
5928
|
+
1 / SPEECH_PITCH_RATIO_MAX,
|
|
5929
|
+
unitsToFreq(pitch) / reference
|
|
5930
|
+
)
|
|
5931
|
+
) : 1;
|
|
5932
|
+
const prepared = prepareSpeechPlan(plan, ratio, view.toReal);
|
|
5933
|
+
e.durationSec = speechPlanDurationSec(prepared);
|
|
5934
|
+
const leadingSec = speechPlanLeadingSec(prepared);
|
|
5935
|
+
resolvePlanned(e.durationSec);
|
|
5936
|
+
await backend.renderSpeech(
|
|
5937
|
+
prepared,
|
|
5938
|
+
expr,
|
|
5939
|
+
(chunk) => {
|
|
5940
|
+
const buf = ctx.createBuffer(1, chunk.pcm.length, KOE_SAMPLE_RATE);
|
|
5941
|
+
buf.copyToChannel(chunk.pcm, 0);
|
|
5942
|
+
const r = {
|
|
5943
|
+
audio: buf,
|
|
5944
|
+
startSec: chunk.startMs / 1e3 - leadingSec
|
|
5945
|
+
};
|
|
5946
|
+
e.chunks.push(r);
|
|
5947
|
+
for (const l of e.listeners) l(r);
|
|
5948
|
+
},
|
|
5949
|
+
abort.signal
|
|
5950
|
+
);
|
|
5951
|
+
e.done = true;
|
|
5952
|
+
e.listeners.clear();
|
|
5953
|
+
})();
|
|
5954
|
+
entry = e;
|
|
5955
|
+
speechCache.set(key, entry);
|
|
5956
|
+
return entry;
|
|
5957
|
+
};
|
|
5958
|
+
const placeSpeechChunk = (r, t0, peak, pan, reverbSend = 0, delaySend = 0, destOverride) => {
|
|
5959
|
+
const dest = destOverride ?? destination;
|
|
5960
|
+
let startAt = t0 + r.startSec;
|
|
5961
|
+
let offset = 0;
|
|
5962
|
+
const earliest = ctx.currentTime + 0.01;
|
|
5963
|
+
if (startAt < earliest) {
|
|
5964
|
+
offset = earliest - startAt;
|
|
5965
|
+
startAt = earliest;
|
|
5966
|
+
}
|
|
5967
|
+
if (offset >= r.audio.duration - 5e-3) return;
|
|
5968
|
+
let out = dest;
|
|
5969
|
+
let panner = null;
|
|
5970
|
+
if (typeof ctx.createStereoPanner === "function") {
|
|
5971
|
+
panner = ctx.createStereoPanner();
|
|
5972
|
+
panner.pan.value = Math.max(-1, Math.min(1, pan));
|
|
5973
|
+
panner.connect(dest);
|
|
5974
|
+
out = panner;
|
|
5975
|
+
}
|
|
5976
|
+
let reverbSendGain = null;
|
|
5977
|
+
if (options.reverbBus && reverbSend > 0 && panner) {
|
|
5978
|
+
reverbSendGain = ctx.createGain();
|
|
5979
|
+
reverbSendGain.gain.value = Math.max(0, Math.min(1, reverbSend));
|
|
5980
|
+
panner.connect(reverbSendGain).connect(options.reverbBus);
|
|
5981
|
+
}
|
|
5982
|
+
let delaySendGain = null;
|
|
5983
|
+
if (options.delayBus && delaySend > 0 && panner) {
|
|
5984
|
+
delaySendGain = ctx.createGain();
|
|
5985
|
+
delaySendGain.gain.value = Math.max(0, Math.min(1, delaySend));
|
|
5986
|
+
panner.connect(delaySendGain).connect(options.delayBus);
|
|
5987
|
+
}
|
|
5988
|
+
const env = ctx.createGain();
|
|
5989
|
+
if (offset > 0) {
|
|
5990
|
+
env.gain.setValueAtTime(1e-4, startAt);
|
|
5991
|
+
env.gain.exponentialRampToValueAtTime(peak, startAt + 5e-3);
|
|
5992
|
+
} else {
|
|
5993
|
+
env.gain.setValueAtTime(peak, startAt);
|
|
5994
|
+
}
|
|
5995
|
+
const src = ctx.createBufferSource();
|
|
5996
|
+
src.buffer = r.audio;
|
|
5997
|
+
src.connect(env).connect(out);
|
|
5998
|
+
src.start(startAt, offset);
|
|
5999
|
+
active.add(src);
|
|
6000
|
+
src.onended = () => {
|
|
6001
|
+
active.delete(src);
|
|
6002
|
+
src.disconnect();
|
|
6003
|
+
env.disconnect();
|
|
6004
|
+
panner?.disconnect();
|
|
6005
|
+
reverbSendGain?.disconnect();
|
|
6006
|
+
delaySendGain?.disconnect();
|
|
6007
|
+
};
|
|
6008
|
+
};
|
|
6009
|
+
const speechListeners = /* @__PURE__ */ new Set();
|
|
6010
|
+
model.speakToCache = async (text, pitch, expr, awaitRender) => {
|
|
6011
|
+
const entry = speechEntryFor(text, pitch, expr);
|
|
6012
|
+
const duration = await entry.planned;
|
|
6013
|
+
if (duration === null) return null;
|
|
6014
|
+
if (awaitRender) await entry.rendered;
|
|
6015
|
+
return speechKeyOf(text, pitch, expr);
|
|
6016
|
+
};
|
|
6017
|
+
model.scheduleSpeech = (key, t0, peak, pan, reverbSend, delaySend, dest) => {
|
|
6018
|
+
const entry = speechCache.get(key);
|
|
6019
|
+
if (!entry) return;
|
|
6020
|
+
const place = (r) => placeSpeechChunk(r, t0, peak, pan, reverbSend, delaySend, dest);
|
|
6021
|
+
for (const r of entry.chunks) place(r);
|
|
6022
|
+
if (!entry.done) {
|
|
6023
|
+
entry.listeners.add(place);
|
|
6024
|
+
const reg = { entry, listener: place };
|
|
6025
|
+
speechListeners.add(reg);
|
|
6026
|
+
void entry.rendered.then(() => speechListeners.delete(reg));
|
|
6027
|
+
}
|
|
6028
|
+
};
|
|
6029
|
+
model.speechDurationSec = (key) => {
|
|
6030
|
+
const entry = speechCache.get(key);
|
|
6031
|
+
return entry && entry.durationSec > 0 ? entry.durationSec : void 0;
|
|
6032
|
+
};
|
|
6033
|
+
const previewDurations = /* @__PURE__ */ new Map();
|
|
6034
|
+
model.planSpeech = async (text) => {
|
|
6035
|
+
const cached = previewDurations.get(text);
|
|
6036
|
+
if (cached !== void 0) return cached;
|
|
6037
|
+
const token = backend.pitchTokens.length > 0 ? nearestPitchToken(
|
|
6038
|
+
backend.pitchTokens,
|
|
6039
|
+
unitsToMidiFloat(model.speechReferenceUnits?.() ?? 0)
|
|
6040
|
+
)?.token ?? null : null;
|
|
6041
|
+
const plan = await planFor(text, token);
|
|
6042
|
+
if (!plan) return null;
|
|
6043
|
+
const d = speechPlanDurationSec(plan);
|
|
6044
|
+
previewDurations.set(text, d);
|
|
6045
|
+
return d;
|
|
6046
|
+
};
|
|
6047
|
+
model.peekSpeechDurationSec = (text) => previewDurations.get(text);
|
|
6048
|
+
model.speechReferenceUnits = () => {
|
|
6049
|
+
const tokens = backend.pitchTokens;
|
|
6050
|
+
let hz;
|
|
6051
|
+
if (tokens.length > 0) {
|
|
6052
|
+
const sorted = tokens.slice().sort((a, b) => a.midi - b.midi);
|
|
6053
|
+
hz = toneViewFor(sorted[sorted.length >> 1].token).referenceHz;
|
|
6054
|
+
} else {
|
|
6055
|
+
hz = toneViewFor(null).referenceHz;
|
|
6056
|
+
}
|
|
6057
|
+
if (!hz || hz <= 0) return void 0;
|
|
6058
|
+
return Math.round(2139 + 372 * Math.log2(hz / 440));
|
|
6059
|
+
};
|
|
4592
6060
|
model.stopAll = () => {
|
|
4593
6061
|
for (const src of active) {
|
|
4594
6062
|
try {
|
|
@@ -4598,6 +6066,9 @@ var createKoeVoice = async (ctx, destination, options) => {
|
|
|
4598
6066
|
src.disconnect();
|
|
4599
6067
|
}
|
|
4600
6068
|
active.clear();
|
|
6069
|
+
for (const { entry, listener } of speechListeners)
|
|
6070
|
+
entry.listeners.delete(listener);
|
|
6071
|
+
speechListeners.clear();
|
|
4601
6072
|
};
|
|
4602
6073
|
model.reset = () => {
|
|
4603
6074
|
prevVowel = "";
|
|
@@ -4776,7 +6247,8 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
4776
6247
|
lightweight: options.lightweight,
|
|
4777
6248
|
voiceWorkerUrl: options.voiceWorkerUrl,
|
|
4778
6249
|
reverbBus: options.reverbBus,
|
|
4779
|
-
delayBus: options.delayBus
|
|
6250
|
+
delayBus: options.delayBus,
|
|
6251
|
+
ttsBaseUrl: options.ttsBaseUrl
|
|
4780
6252
|
})
|
|
4781
6253
|
))().then((v) => {
|
|
4782
6254
|
loaded.set(m, v);
|
|
@@ -4811,6 +6283,11 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
4811
6283
|
prevVowel = "";
|
|
4812
6284
|
continue;
|
|
4813
6285
|
}
|
|
6286
|
+
if (syl.kind === "speak") {
|
|
6287
|
+
fn(note, "");
|
|
6288
|
+
prevVowel = "";
|
|
6289
|
+
continue;
|
|
6290
|
+
}
|
|
4814
6291
|
if (syl.consonant === "Q" || syl.vowel === "") continue;
|
|
4815
6292
|
fn(note, prevVowel);
|
|
4816
6293
|
if (syl.vowel && syl.vowel !== "N") prevVowel = syl.vowel;
|
|
@@ -4831,6 +6308,17 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
4831
6308
|
forEachSungNote(track, (note, prevVowel) => {
|
|
4832
6309
|
if (n >= count && note.startSec >= STREAM_LOOKAHEAD_SEC) return;
|
|
4833
6310
|
n++;
|
|
6311
|
+
if (note.syllable.kind === "speak") {
|
|
6312
|
+
tasks.push({
|
|
6313
|
+
model: m,
|
|
6314
|
+
note,
|
|
6315
|
+
prevVowel: "",
|
|
6316
|
+
pitch: note.pitch,
|
|
6317
|
+
expr,
|
|
6318
|
+
speak: note.syllable.text ?? ""
|
|
6319
|
+
});
|
|
6320
|
+
return;
|
|
6321
|
+
}
|
|
4834
6322
|
tasks.push({
|
|
4835
6323
|
model: m,
|
|
4836
6324
|
note,
|
|
@@ -4861,15 +6349,24 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
4861
6349
|
let done = 0;
|
|
4862
6350
|
onProgress?.(done, total);
|
|
4863
6351
|
const promises = tasks.map(async (task) => {
|
|
4864
|
-
|
|
4865
|
-
task.
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
6352
|
+
if (task.speak !== void 0) {
|
|
6353
|
+
await (task.model.speakToCache?.(
|
|
6354
|
+
task.speak,
|
|
6355
|
+
task.pitch,
|
|
6356
|
+
task.expr,
|
|
6357
|
+
true
|
|
6358
|
+
) ?? Promise.resolve(null));
|
|
6359
|
+
} else {
|
|
6360
|
+
await (task.model.renderToCache?.(
|
|
6361
|
+
task.note.syllable,
|
|
6362
|
+
task.prevVowel,
|
|
6363
|
+
task.pitch,
|
|
6364
|
+
task.note.durationSec * 1e3,
|
|
6365
|
+
task.vibrato,
|
|
6366
|
+
task.expr,
|
|
6367
|
+
task.pitchSegments
|
|
6368
|
+
) ?? Promise.resolve(null));
|
|
6369
|
+
}
|
|
4873
6370
|
done++;
|
|
4874
6371
|
onProgress?.(done, total);
|
|
4875
6372
|
});
|
|
@@ -4906,6 +6403,49 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
4906
6403
|
}
|
|
4907
6404
|
if (opts?.isAudible && !opts.isAudible(track)) continue;
|
|
4908
6405
|
const t0 = anchorTime + startSec;
|
|
6406
|
+
if (note.syllable.kind === "speak") {
|
|
6407
|
+
const text = note.syllable.text ?? "";
|
|
6408
|
+
if (text && model.speakToCache && model.scheduleSpeech) {
|
|
6409
|
+
const speakToCache = model.speakToCache;
|
|
6410
|
+
const scheduleSpeech = model.scheduleSpeech;
|
|
6411
|
+
const dest = options.getTrackDestination?.(track.id ?? "");
|
|
6412
|
+
const effPeak = dest ? peak * masterVolumeScalar : peak;
|
|
6413
|
+
void (async () => {
|
|
6414
|
+
const key = await speakToCache(text, note.pitch, {
|
|
6415
|
+
gender: track.gender,
|
|
6416
|
+
breathiness: track.breathiness,
|
|
6417
|
+
tension: track.tension
|
|
6418
|
+
});
|
|
6419
|
+
if (session !== streamSession || !key) return;
|
|
6420
|
+
const delay = ctx.currentTime - t0;
|
|
6421
|
+
if (delay < 1) {
|
|
6422
|
+
scheduleSpeech(
|
|
6423
|
+
key,
|
|
6424
|
+
t0,
|
|
6425
|
+
effPeak,
|
|
6426
|
+
track.pan,
|
|
6427
|
+
track.reverbSend,
|
|
6428
|
+
track.delaySend,
|
|
6429
|
+
dest
|
|
6430
|
+
);
|
|
6431
|
+
opts?.onScheduled?.(
|
|
6432
|
+
track,
|
|
6433
|
+
{
|
|
6434
|
+
...note,
|
|
6435
|
+
durationSec: model.speechDurationSec?.(key) ?? note.durationSec
|
|
6436
|
+
},
|
|
6437
|
+
t0
|
|
6438
|
+
);
|
|
6439
|
+
} else {
|
|
6440
|
+
console.warn(
|
|
6441
|
+
`[dtm] Speech late skip: \u300C${text}\u300D at ${startSec}s (delayed by ${delay.toFixed(3)}s)`
|
|
6442
|
+
);
|
|
6443
|
+
opts?.onLateSkip?.(note, delay);
|
|
6444
|
+
}
|
|
6445
|
+
})();
|
|
6446
|
+
}
|
|
6447
|
+
continue;
|
|
6448
|
+
}
|
|
4909
6449
|
const dispatchNote = (offsetUnits, peakScale) => {
|
|
4910
6450
|
const pitch = units(note.pitch + offsetUnits);
|
|
4911
6451
|
const pitchSegments = transposeSegments(
|
|
@@ -5032,6 +6572,12 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
5032
6572
|
masterGain.gain.value = g;
|
|
5033
6573
|
masterVolumeScalar = g;
|
|
5034
6574
|
};
|
|
6575
|
+
const planSpeech = async (model, text) => {
|
|
6576
|
+
const v = await load2(model);
|
|
6577
|
+
return v?.planSpeech ? v.planSpeech(text) : null;
|
|
6578
|
+
};
|
|
6579
|
+
const peekSpeechDurationSec = (model, text) => loaded.get(model.toLowerCase())?.peekSpeechDurationSec?.(text);
|
|
6580
|
+
const getSpeechReferenceUnits = (model) => loaded.get(model.toLowerCase())?.speechReferenceUnits?.();
|
|
5035
6581
|
return {
|
|
5036
6582
|
loadModels,
|
|
5037
6583
|
registerVoicebanks,
|
|
@@ -5039,7 +6585,10 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
5039
6585
|
startStream,
|
|
5040
6586
|
stopStream,
|
|
5041
6587
|
reset,
|
|
5042
|
-
setVolume
|
|
6588
|
+
setVolume,
|
|
6589
|
+
planSpeech,
|
|
6590
|
+
peekSpeechDurationSec,
|
|
6591
|
+
getSpeechReferenceUnits
|
|
5043
6592
|
};
|
|
5044
6593
|
};
|
|
5045
6594
|
var createVoiceRegistry = (models = {}, fallback = "klatt") => {
|
|
@@ -5060,7 +6609,7 @@ var NATURAL_STEPS2 = {
|
|
|
5060
6609
|
};
|
|
5061
6610
|
var isNatural = (ch) => Object.hasOwn(NATURAL_STEPS2[12], ch);
|
|
5062
6611
|
var clamp3 = (value, lo, hi) => Math.min(hi, Math.max(lo, value));
|
|
5063
|
-
var META_DIRECTIVE = /#(inst|drum|drumfont|volume|drumvolume|reverb|reverbdecay|reverbpredelay|delay|delaydiv|mastercomp|fadein|fadeout|mode|edo|loop)=([\w
|
|
6612
|
+
var META_DIRECTIVE = /#(ver|seed|compose|inst|drum|drumfont|volume|drumvolume|reverb|reverbdecay|reverbpredelay|delay|delaydiv|mastercomp|fadein|fadeout|mode|edo|loop)=([\w:.-]+)/gi;
|
|
5064
6613
|
var AUDIO_URL_DIRECTIVE = /#audio=([^\s#;\r\n]+)/gi;
|
|
5065
6614
|
var AUDIO_NUM_DIRECTIVE = /#audio(start|end|offset|at|vol)=(-?\d+(?:\.\d+)?)/gi;
|
|
5066
6615
|
var TRACK_INST_DIRECTIVE = /#t(\d+)inst=([^#;\r\n]+)/gi;
|
|
@@ -5077,6 +6626,10 @@ var parseMmlMeta = (mml) => {
|
|
|
5077
6626
|
for (const m of mml.matchAll(META_DIRECTIVE)) {
|
|
5078
6627
|
const key = m[1].toLowerCase();
|
|
5079
6628
|
if (key === "ver") meta.version = m[2];
|
|
6629
|
+
else if (key === "seed") {
|
|
6630
|
+
const n = Number.parseInt(m[2], 10);
|
|
6631
|
+
if (Number.isFinite(n) && n >= 0) meta.seed = n;
|
|
6632
|
+
} else if (key === "compose") meta.compose = m[2];
|
|
5080
6633
|
else if (key === "inst") meta.instrument = m[2];
|
|
5081
6634
|
else if (key === "drum") meta.drum = m[2];
|
|
5082
6635
|
else if (key === "drumfont") meta.drumFont = m[2];
|
|
@@ -5244,6 +6797,8 @@ var formatMmlMeta = (meta, space = "") => {
|
|
|
5244
6797
|
if (meta.mode) parts.push(`#mode=${meta.mode}`);
|
|
5245
6798
|
if (meta.edo !== void 0 && meta.edo !== 12) parts.push(`#edo=${meta.edo}`);
|
|
5246
6799
|
if (meta.loop) parts.push("#loop=on");
|
|
6800
|
+
if (meta.seed !== void 0) parts.push(`#seed=${meta.seed}`);
|
|
6801
|
+
if (meta.compose) parts.push(`#compose=${meta.compose}`);
|
|
5247
6802
|
if (meta.audio) {
|
|
5248
6803
|
parts.push(`#audio=${meta.audio}`);
|
|
5249
6804
|
if (meta.audioStart) parts.push(`#audiostart=${round3(meta.audioStart)}`);
|
|
@@ -8674,6 +10229,78 @@ var DAW_CSS = `
|
|
|
8674
10229
|
.dtm-daw *::before,
|
|
8675
10230
|
.dtm-daw *::after { box-sizing: border-box; }
|
|
8676
10231
|
|
|
10232
|
+
/* \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
|
|
10233
|
+
\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
|
|
10234
|
+
\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
|
|
10235
|
+
\u4F55\u679A\u958B\u3044\u3066\u3082\u30ED\u30FC\u30EB\u3068\u306E\u8DDD\u96E2\u306F0\u306E\u307E\u307E\u306B\u306A\u308B\u3002
|
|
10236
|
+
|
|
10237
|
+
\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
|
|
10238
|
+
\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
|
|
10239
|
+
\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
|
|
10240
|
+
\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
|
|
10241
|
+
\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
|
|
10242
|
+
\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 */
|
|
10243
|
+
.dtm-daw-panels {
|
|
10244
|
+
display: flex;
|
|
10245
|
+
flex-direction: column;
|
|
10246
|
+
gap: var(--dtm-gap);
|
|
10247
|
+
min-width: 0;
|
|
10248
|
+
}
|
|
10249
|
+
/* \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
|
|
10250
|
+
\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 */
|
|
10251
|
+
.dtm-daw-head { display: contents; }
|
|
10252
|
+
.dtm-topbar {
|
|
10253
|
+
position: sticky;
|
|
10254
|
+
top: 0;
|
|
10255
|
+
z-index: 20;
|
|
10256
|
+
}
|
|
10257
|
+
@media (min-height: 760px) {
|
|
10258
|
+
.dtm-daw-head {
|
|
10259
|
+
display: flex;
|
|
10260
|
+
flex-direction: column;
|
|
10261
|
+
gap: var(--dtm-gap);
|
|
10262
|
+
min-width: 0;
|
|
10263
|
+
position: sticky;
|
|
10264
|
+
top: 0;
|
|
10265
|
+
z-index: 20;
|
|
10266
|
+
/* \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
|
|
10267
|
+
.dtm-daw \u306E gap \u3076\u3093\u3092 padding \u3067\u57CB\u3081\u3001\u9699\u9593\u304B\u3089\u4E2D\u8EAB\u3092\u898B\u305B\u306A\u3044\u3002 */
|
|
10268
|
+
background: var(--dtm-bg);
|
|
10269
|
+
padding-bottom: var(--dtm-gap);
|
|
10270
|
+
}
|
|
10271
|
+
}
|
|
10272
|
+
|
|
10273
|
+
/* \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
|
|
10274
|
+
\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
|
|
10275
|
+
\u753B\u9762\u306E\u9AD8\u3055\u306B\u95A2\u4FC2\u306A\u304F\u8CBC\u308C\u308B\u3057\u3001\u30ED\u30FC\u30EB\u3082\u5E83\u304F\u4F7F\u3048\u308B
|
|
10276
|
+
\uFF08.dtm-daw--wide \u306F daw.ts \u306E ResizeObserver \u304C\u4ED8\u3051\u308B\uFF09\u3002 */
|
|
10277
|
+
.dtm-daw--wide {
|
|
10278
|
+
flex-direction: row;
|
|
10279
|
+
align-items: flex-start;
|
|
10280
|
+
}
|
|
10281
|
+
.dtm-daw--wide .dtm-daw-head {
|
|
10282
|
+
display: flex;
|
|
10283
|
+
flex-direction: column;
|
|
10284
|
+
gap: var(--dtm-gap);
|
|
10285
|
+
min-width: 0;
|
|
10286
|
+
flex: 1 1 auto;
|
|
10287
|
+
position: sticky;
|
|
10288
|
+
top: 0;
|
|
10289
|
+
z-index: 20;
|
|
10290
|
+
background: var(--dtm-bg);
|
|
10291
|
+
padding-bottom: 0;
|
|
10292
|
+
}
|
|
10293
|
+
.dtm-daw--wide .dtm-daw-panels {
|
|
10294
|
+
flex: 0 0 360px;
|
|
10295
|
+
}
|
|
10296
|
+
/* 768px \u6642\u70B9\u306E height:420px \u306F max-height:32vh \u306B\u982D\u6253\u3061\u306B\u3055\u308C\u3066\u3044\u305F\u3002
|
|
10297
|
+
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
|
|
10298
|
+
\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 */
|
|
10299
|
+
.dtm-daw--wide .dtm-roll {
|
|
10300
|
+
height: min(420px, 45vh);
|
|
10301
|
+
max-height: none;
|
|
10302
|
+
}
|
|
10303
|
+
|
|
8677
10304
|
/* \u2500\u2500\u2500 \u30B2\u30FC\u30E0\u30A6\u30A3\u30F3\u30C9\u30A6\u5171\u901A\u67A0 \u2500\u2500\u2500 */
|
|
8678
10305
|
/* \u5916\u67A0(\u9ED22px) \u2192 \u8272\u4ED8\u304D2px border \u2192 \u5185\u67A0(\u9ED2inset2px) \u306E3\u91CD\u69CB\u9020 */
|
|
8679
10306
|
.dtm-win {
|
|
@@ -8736,9 +10363,6 @@ var DAW_CSS = `
|
|
|
8736
10363
|
|
|
8737
10364
|
/* \u2500\u2500\u2500 \u30C8\u30E9\u30F3\u30B9\u30DD\u30FC\u30C8\u30D0\u30FC\uFF08HUD\u30B9\u30BF\u30A4\u30EB\uFF09 \u2500\u2500\u2500 */
|
|
8738
10365
|
.dtm-topbar {
|
|
8739
|
-
position: sticky;
|
|
8740
|
-
top: 0;
|
|
8741
|
-
z-index: 20;
|
|
8742
10366
|
display: flex;
|
|
8743
10367
|
flex-wrap: wrap;
|
|
8744
10368
|
align-items: center;
|
|
@@ -9183,19 +10807,17 @@ var DAW_CSS = `
|
|
|
9183
10807
|
.dtm-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
|
|
9184
10808
|
.dtm-track-body { display: flex; flex-direction: column; gap: 10px; }
|
|
9185
10809
|
|
|
9186
|
-
/* \u2500\u2500\u2500 \u81EA\u52D5\u4F5C\u66F2\
|
|
9187
|
-
.dtm-compose
|
|
9188
|
-
background: var(--dtm-deep);
|
|
9189
|
-
border: 2px solid var(--c-black);
|
|
10810
|
+
/* \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 */
|
|
10811
|
+
.dtm-panel--compose {
|
|
9190
10812
|
box-shadow:
|
|
9191
|
-
inset 0 0 0
|
|
9192
|
-
0 0 0
|
|
9193
|
-
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
10813
|
+
inset 0 0 0 2px var(--c-black),
|
|
10814
|
+
0 0 0 2px var(--dtm-success),
|
|
10815
|
+
4px 4px 0 var(--c-black);
|
|
10816
|
+
}
|
|
10817
|
+
.dtm-panel--compose > summary { color: var(--dtm-success); }
|
|
10818
|
+
.dtm-panel--compose > summary::before,
|
|
10819
|
+
.dtm-panel--compose[open] > summary::before {
|
|
10820
|
+
background: var(--dtm-success);
|
|
9199
10821
|
}
|
|
9200
10822
|
|
|
9201
10823
|
/* \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 */
|
|
@@ -10308,6 +11930,8 @@ var DAW_CSS = `
|
|
|
10308
11930
|
/* \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 */
|
|
10309
11931
|
.dtm-tk--lyric-stop,
|
|
10310
11932
|
.dtm-tk--lyric-rest { color: var(--dtm-border2); }
|
|
11933
|
+
/* \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 */
|
|
11934
|
+
.dtm-tk--lyric-speak { color: var(--dtm-primary); letter-spacing: 0; }
|
|
10311
11935
|
.dtm-tk--break { color: var(--dtm-muted); opacity: 0.7; margin: 0 2px; }
|
|
10312
11936
|
.dtm-tk--meta { color: var(--dtm-border2); margin-right: 4px; }
|
|
10313
11937
|
.dtm-tk.is-active {
|
|
@@ -22001,6 +23625,16 @@ var BASS_SKELETONS = [
|
|
|
22001
23625
|
"pedal"
|
|
22002
23626
|
];
|
|
22003
23627
|
var pick = (items, rnd) => items[Math.floor(rnd() * items.length)];
|
|
23628
|
+
var seededRandom = (seed) => {
|
|
23629
|
+
let a = seed >>> 0;
|
|
23630
|
+
return () => {
|
|
23631
|
+
a = a + 1831565813 >>> 0;
|
|
23632
|
+
let t = a;
|
|
23633
|
+
t = Math.imul(t ^ t >>> 15, t | 1);
|
|
23634
|
+
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
23635
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
23636
|
+
};
|
|
23637
|
+
};
|
|
22004
23638
|
var durationEntropy = (durations) => {
|
|
22005
23639
|
if (durations.length === 0) return 0;
|
|
22006
23640
|
const counts = /* @__PURE__ */ new Map();
|
|
@@ -24944,6 +26578,10 @@ var buildUI = (target, options) => {
|
|
|
24944
26578
|
).join("");
|
|
24945
26579
|
target.innerHTML = `
|
|
24946
26580
|
<div class="dtm-daw" data-dtm="root">
|
|
26581
|
+
<!-- \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
|
|
26582
|
+
\u753B\u9762\u4E0A\u90E8\u3078\u8CBC\u308A\u4ED8\u3051\u308B\uFF08.dtm-daw-head \u304C position:sticky\uFF09\u3002\u30D1\u30CD\u30EB\u3092
|
|
26583
|
+
\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 -->
|
|
26584
|
+
<div class="dtm-daw-head">
|
|
24947
26585
|
<div class="dtm-topbar" data-dtm="transport">
|
|
24948
26586
|
<div class="dtm-topbar-row1">
|
|
24949
26587
|
<button class="dtm-iconbtn" data-dtm="prev-bar" title="1\u5C0F\u7BC0\u524D">${icon("chevronLeft")}</button>
|
|
@@ -24988,6 +26626,10 @@ var buildUI = (target, options) => {
|
|
|
24988
26626
|
<div class="dtm-vscroll" data-dtm="vscroll"><div class="dtm-vscroll-thumb" data-dtm="vscroll-thumb"></div></div>
|
|
24989
26627
|
</div>
|
|
24990
26628
|
<div class="dtm-hscroll" data-dtm="hscroll"><div class="dtm-hscroll-thumb" data-dtm="hscroll-thumb"></div></div>
|
|
26629
|
+
</div>
|
|
26630
|
+
|
|
26631
|
+
<!-- \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 -->
|
|
26632
|
+
<div class="dtm-daw-panels">
|
|
24991
26633
|
|
|
24992
26634
|
<details class="dtm-panel" data-dtm-acc="track" open>
|
|
24993
26635
|
<summary>\u500B\u5225\u30C8\u30E9\u30C3\u30AF\u8A2D\u5B9A</summary>
|
|
@@ -25186,7 +26828,7 @@ var buildUI = (target, options) => {
|
|
|
25186
26828
|
</details>
|
|
25187
26829
|
|
|
25188
26830
|
<details class="dtm-panel ${showMidi ? "" : "dtm-hidden"}" data-dtm="midi-panel" data-dtm-acc="io-in">
|
|
25189
|
-
<summary>MIDI / UST / MML \u5165\u529B</summary>
|
|
26831
|
+
<summary>MIDI / MusicXML / UST / MML \u5165\u529B</summary>
|
|
25190
26832
|
<div class="dtm-panel-body">
|
|
25191
26833
|
<div class="dtm-row" style="flex-wrap:nowrap">
|
|
25192
26834
|
<div style="display: inline-flex; flex-direction: column; align-items: center; gap: 4px; justify-content: center; flex-shrink:0;">
|
|
@@ -25197,6 +26839,16 @@ var buildUI = (target, options) => {
|
|
|
25197
26839
|
<button class="dtm-btn dtm-btn--success" data-dtm="midi-load" style="flex-shrink:0">\u8AAD\u8FBC</button>
|
|
25198
26840
|
</div>
|
|
25199
26841
|
<div class="dtm-row dtm-hidden" data-dtm="midi-track-selection"></div>
|
|
26842
|
+
<div class="dtm-row" style="flex-wrap:nowrap">
|
|
26843
|
+
<div style="display: inline-flex; flex-direction: column; align-items: center; gap: 4px; justify-content: center; flex-shrink:0;">
|
|
26844
|
+
<span class="dtm-label" style="line-height: 1;">MusicXML</span>
|
|
26845
|
+
<button class="dtm-infobtn" data-dtm="musicxml-info" title="MusicXML\u306E\u8AAD\u307F\u8FBC\u307F\u89E3\u8AAC">${icon("info", 12)}</button>
|
|
26846
|
+
</div>
|
|
26847
|
+
<input type="file" class="dtm-input dtm-grow" accept=".musicxml,.xml" data-dtm="musicxml-input" style="min-width:0">
|
|
26848
|
+
<button class="dtm-btn dtm-btn--success" data-dtm="musicxml-load" style="flex-shrink:0">\u8AAD\u8FBC</button>
|
|
26849
|
+
</div>
|
|
26850
|
+
<div class="dtm-row dtm-hidden" data-dtm="musicxml-part-selection"></div>
|
|
26851
|
+
<p class="dtm-load-note dtm-hidden" data-dtm="musicxml-load-note"></p>
|
|
25200
26852
|
<div class="dtm-row" style="flex-wrap:nowrap">
|
|
25201
26853
|
<div style="display: inline-flex; flex-direction: column; align-items: center; gap: 4px; justify-content: center; flex-shrink:0;">
|
|
25202
26854
|
<span class="dtm-label" style="line-height: 1;">UST</span>
|
|
@@ -25226,129 +26878,135 @@ var buildUI = (target, options) => {
|
|
|
25226
26878
|
</div>
|
|
25227
26879
|
</details>
|
|
25228
26880
|
|
|
25229
|
-
<details class="dtm-panel" data-dtm-acc="
|
|
25230
|
-
<summary>\
|
|
26881
|
+
<details class="dtm-panel dtm-panel--compose ${showCompose ? "" : "dtm-hidden"}" data-dtm-acc="compose">
|
|
26882
|
+
<summary>\u81EA\u52D5\u4F5C\u66F2</summary>
|
|
25231
26883
|
<div class="dtm-panel-body">
|
|
25232
|
-
<div class="dtm-
|
|
25233
|
-
<
|
|
25234
|
-
|
|
25235
|
-
|
|
25236
|
-
|
|
25237
|
-
|
|
25238
|
-
|
|
25239
|
-
|
|
25240
|
-
|
|
25241
|
-
|
|
25242
|
-
|
|
25243
|
-
|
|
25244
|
-
|
|
25245
|
-
|
|
25246
|
-
|
|
25247
|
-
|
|
25248
|
-
|
|
25249
|
-
|
|
25250
|
-
|
|
25251
|
-
|
|
25252
|
-
|
|
25253
|
-
|
|
25254
|
-
|
|
25255
|
-
|
|
25256
|
-
</
|
|
25257
|
-
</
|
|
25258
|
-
|
|
25259
|
-
|
|
25260
|
-
|
|
25261
|
-
|
|
25262
|
-
|
|
25263
|
-
|
|
25264
|
-
|
|
25265
|
-
|
|
25266
|
-
|
|
25267
|
-
|
|
25268
|
-
|
|
25269
|
-
</
|
|
25270
|
-
<span class="dtm-grow"></span>
|
|
25271
|
-
<span class="dtm-hint" data-dtm="compose-sections-len"></span>
|
|
25272
|
-
</div>
|
|
25273
|
-
<div class="dtm-row" data-dtm="compose-key-row">
|
|
25274
|
-
<span class="dtm-label">\u30D9\u30FC\u30B9\u8ABF</span>
|
|
25275
|
-
<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">
|
|
25276
|
-
<option value="any" title="\u516824\u8ABF\u304B\u3089\u30E9\u30F3\u30C0\u30E0\u306B\u6C7A\u5B9A\u3057\u307E\u3059">\u5E0C\u671B\u306A\u3057</option>
|
|
25277
|
-
<optgroup label="\u57FA\u672C">
|
|
25278
|
-
<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>
|
|
25279
|
-
<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>
|
|
25280
|
-
</optgroup>
|
|
25281
|
-
<optgroup label="\u96F0\u56F2\u6C17\u304B\u3089\u9078\u3076\uFF08\u62BD\u9078\uFF09">
|
|
25282
|
-
<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>
|
|
25283
|
-
<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>
|
|
25284
|
-
<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>
|
|
25285
|
-
<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>
|
|
25286
|
-
<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>
|
|
25287
|
-
<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>
|
|
25288
|
-
<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>
|
|
25289
|
-
<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>
|
|
25290
|
-
</optgroup>
|
|
25291
|
-
<optgroup label="\u9577\u8ABF\uFF08\u500B\u5225\u6307\u5B9A\uFF09">
|
|
25292
|
-
<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>
|
|
25293
|
-
<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>
|
|
25294
|
-
<option value="key_D" title="\u610F\u6C17\u63DA\u3005\u3068\u3057\u305F\u3001\u52DD\u5229\u306E\u3001\u558A\u58F0">\u30CB\u9577\u8ABF (D)</option>
|
|
25295
|
-
<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>
|
|
25296
|
-
<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>
|
|
25297
|
-
<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>
|
|
25298
|
-
<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>
|
|
25299
|
-
<option value="key_G" title="\u53B3\u7C9B\u306A\u3001\u5D07\u9AD8\u306A\u3001\u5E7B\u60F3\u3001\u8AA0\u5B9F">\u30C8\u9577\u8ABF (G)</option>
|
|
25300
|
-
<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>
|
|
25301
|
-
<option value="key_A" title="\u3046\u308C\u3057\u3044\u3001\u7267\u6B4C\u7684\u306A\u3001\u611B\u306E\u544A\u767D">\u30A4\u9577\u8ABF (A)</option>
|
|
25302
|
-
<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>
|
|
25303
|
-
<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>
|
|
25304
|
-
</optgroup>
|
|
25305
|
-
<optgroup label="\u77ED\u8ABF\uFF08\u500B\u5225\u6307\u5B9A\uFF09">
|
|
25306
|
-
<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>
|
|
25307
|
-
<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>
|
|
25308
|
-
<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>
|
|
25309
|
-
<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>
|
|
25310
|
-
<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>
|
|
25311
|
-
<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>
|
|
25312
|
-
<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>
|
|
25313
|
-
<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>
|
|
25314
|
-
<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>
|
|
25315
|
-
<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>
|
|
25316
|
-
<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>
|
|
25317
|
-
<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>
|
|
25318
|
-
</optgroup>
|
|
25319
|
-
</select>
|
|
25320
|
-
<span class="dtm-grow"></span>
|
|
25321
|
-
<span class="dtm-hint" data-dtm="compose-key-hint"></span>
|
|
25322
|
-
</div>
|
|
25323
|
-
<div class="dtm-row" data-dtm="compose-scale-row">
|
|
25324
|
-
<span class="dtm-label">\u97F3\u968E</span>
|
|
25325
|
-
<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">
|
|
25326
|
-
<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>
|
|
25327
|
-
<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>
|
|
25328
|
-
<optgroup label="\u30DA\u30F3\u30BF\u30C8\u30CB\u30C3\u30AF\uFF085\u97F3\u97F3\u968E\uFF09">
|
|
25329
|
-
<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>
|
|
25330
|
-
<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>
|
|
25331
|
-
<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>
|
|
25332
|
-
<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>
|
|
25333
|
-
<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>
|
|
25334
|
-
</optgroup>
|
|
25335
|
-
<optgroup label="\u30C1\u30E3\u30FC\u30C1\u30E2\u30FC\u30C9\uFF087\u97F3\u97F3\u968E\uFF09">
|
|
25336
|
-
<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>
|
|
25337
|
-
<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>
|
|
25338
|
-
<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>
|
|
25339
|
-
<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>
|
|
25340
|
-
</optgroup>
|
|
25341
|
-
<optgroup label="\u7279\u6B8A\u97F3\u968E\uFF08\u97F3\u7A0B\u96C6\u5408\u3054\u3068\u5165\u308C\u66FF\u308F\u308B\uFF09">
|
|
25342
|
-
<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>
|
|
25343
|
-
<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>
|
|
25344
|
-
<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>
|
|
25345
|
-
<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>
|
|
25346
|
-
</optgroup>
|
|
25347
|
-
</select>
|
|
25348
|
-
<span class="dtm-grow"></span>
|
|
25349
|
-
<span class="dtm-hint" data-dtm="compose-scale-hint"></span>
|
|
26884
|
+
<div class="dtm-row" data-dtm="compose-row">
|
|
26885
|
+
<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>
|
|
26886
|
+
<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>
|
|
26887
|
+
<button class="dtm-infobtn" data-dtm="macro-compose-info" title="\u4F5C\u66F2\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
|
|
26888
|
+
<!--
|
|
26889
|
+
**\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
|
|
26890
|
+
\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
|
|
26891
|
+
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
|
|
26892
|
+
\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
|
|
26893
|
+
\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
|
|
26894
|
+
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
|
|
26895
|
+
-->
|
|
26896
|
+
<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>
|
|
26897
|
+
<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>
|
|
26898
|
+
<span class="dtm-grow"></span>
|
|
26899
|
+
</div>
|
|
26900
|
+
<div class="dtm-row" data-dtm="compose-template-row">
|
|
26901
|
+
<span class="dtm-label">\u69CB\u6210</span>
|
|
26902
|
+
<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">
|
|
26903
|
+
<option value="custom">\u81EA\u7531\u9078\u629E\uFF08\u4E0B\u8A18\u30C1\u30A7\u30C3\u30AF\uFF09</option>
|
|
26904
|
+
<option value="1chorus">1\u30B3\u30FC\u30E9\u30B9\uFF08\u77ED\u3081\u30FB\u521D\u5FC3\u8005\u5411\u3051\uFF09</option>
|
|
26905
|
+
<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>
|
|
26906
|
+
<option value="jpop_drop">\u843D\u3061\u30B5\u30D3\u5165\u308A\uFF08JPOP\u738B\u9053 + \u30E9\u30B9\u30B5\u30D3\u524D\u843D\u3061\u30B5\u30D3\uFF09</option>
|
|
26907
|
+
<option value="vocaloid">\u30DC\u30AB\u30ED\u738B\u9053\uFF08\u75BE\u8D70\u30FB2\u756A/C\u30E1\u30ED/\u30E9\u30B9\u30B5\u30D3\uFF09</option>
|
|
26908
|
+
<option value="verse_chorus">Verse-Chorus\uFF08B\u30E1\u30ED\u306A\u3057\u30FB\u6D0B\u697D\u98A8\uFF09</option>
|
|
26909
|
+
</select>
|
|
26910
|
+
</div>
|
|
26911
|
+
<div class="dtm-row" data-dtm="compose-sections-row">
|
|
26912
|
+
<span class="dtm-label">\u4F5C\u308B\u90E8\u5206</span>
|
|
26913
|
+
<div class="dtm-checks" data-dtm="compose-sections">
|
|
26914
|
+
<label class="dtm-check"><input type="checkbox" value="intro" checked>\u30A4\u30F3\u30C8\u30ED</label>
|
|
26915
|
+
<label class="dtm-check"><input type="checkbox" value="verse" checked>A\u30E1\u30ED</label>
|
|
26916
|
+
<label class="dtm-check"><input type="checkbox" value="prechorus" checked>B\u30E1\u30ED</label>
|
|
26917
|
+
<label class="dtm-check"><input type="checkbox" value="chorus" checked>\u30B5\u30D3</label>
|
|
26918
|
+
<label class="dtm-check"><input type="checkbox" value="bridge">C\u30E1\u30ED</label>
|
|
26919
|
+
<label class="dtm-check"><input type="checkbox" value="drop_chorus">\u843D\u3061\u30B5\u30D3</label>
|
|
26920
|
+
<label class="dtm-check"><input type="checkbox" value="interlude">\u9593\u594F</label>
|
|
26921
|
+
<label class="dtm-check"><input type="checkbox" value="outro">\u30A2\u30A6\u30C8\u30ED</label>
|
|
25350
26922
|
</div>
|
|
26923
|
+
<span class="dtm-grow"></span>
|
|
26924
|
+
<span class="dtm-hint" data-dtm="compose-sections-len"></span>
|
|
26925
|
+
</div>
|
|
26926
|
+
<div class="dtm-row" data-dtm="compose-key-row">
|
|
26927
|
+
<span class="dtm-label">\u30D9\u30FC\u30B9\u8ABF</span>
|
|
26928
|
+
<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">
|
|
26929
|
+
<option value="any" title="\u516824\u8ABF\u304B\u3089\u30E9\u30F3\u30C0\u30E0\u306B\u6C7A\u5B9A\u3057\u307E\u3059">\u5E0C\u671B\u306A\u3057</option>
|
|
26930
|
+
<optgroup label="\u57FA\u672C">
|
|
26931
|
+
<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>
|
|
26932
|
+
<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>
|
|
26933
|
+
</optgroup>
|
|
26934
|
+
<optgroup label="\u96F0\u56F2\u6C17\u304B\u3089\u9078\u3076\uFF08\u62BD\u9078\uFF09">
|
|
26935
|
+
<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>
|
|
26936
|
+
<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>
|
|
26937
|
+
<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>
|
|
26938
|
+
<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>
|
|
26939
|
+
<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>
|
|
26940
|
+
<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>
|
|
26941
|
+
<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>
|
|
26942
|
+
<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>
|
|
26943
|
+
</optgroup>
|
|
26944
|
+
<optgroup label="\u9577\u8ABF\uFF08\u500B\u5225\u6307\u5B9A\uFF09">
|
|
26945
|
+
<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>
|
|
26946
|
+
<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>
|
|
26947
|
+
<option value="key_D" title="\u610F\u6C17\u63DA\u3005\u3068\u3057\u305F\u3001\u52DD\u5229\u306E\u3001\u558A\u58F0">\u30CB\u9577\u8ABF (D)</option>
|
|
26948
|
+
<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>
|
|
26949
|
+
<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>
|
|
26950
|
+
<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>
|
|
26951
|
+
<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>
|
|
26952
|
+
<option value="key_G" title="\u53B3\u7C9B\u306A\u3001\u5D07\u9AD8\u306A\u3001\u5E7B\u60F3\u3001\u8AA0\u5B9F">\u30C8\u9577\u8ABF (G)</option>
|
|
26953
|
+
<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>
|
|
26954
|
+
<option value="key_A" title="\u3046\u308C\u3057\u3044\u3001\u7267\u6B4C\u7684\u306A\u3001\u611B\u306E\u544A\u767D">\u30A4\u9577\u8ABF (A)</option>
|
|
26955
|
+
<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>
|
|
26956
|
+
<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>
|
|
26957
|
+
</optgroup>
|
|
26958
|
+
<optgroup label="\u77ED\u8ABF\uFF08\u500B\u5225\u6307\u5B9A\uFF09">
|
|
26959
|
+
<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>
|
|
26960
|
+
<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>
|
|
26961
|
+
<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>
|
|
26962
|
+
<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>
|
|
26963
|
+
<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>
|
|
26964
|
+
<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>
|
|
26965
|
+
<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>
|
|
26966
|
+
<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>
|
|
26967
|
+
<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>
|
|
26968
|
+
<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>
|
|
26969
|
+
<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>
|
|
26970
|
+
<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>
|
|
26971
|
+
</optgroup>
|
|
26972
|
+
</select>
|
|
26973
|
+
<span class="dtm-grow"></span>
|
|
26974
|
+
<span class="dtm-hint" data-dtm="compose-key-hint"></span>
|
|
26975
|
+
</div>
|
|
26976
|
+
<div class="dtm-row" data-dtm="compose-scale-row">
|
|
26977
|
+
<span class="dtm-label">\u97F3\u968E</span>
|
|
26978
|
+
<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">
|
|
26979
|
+
<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>
|
|
26980
|
+
<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>
|
|
26981
|
+
<optgroup label="\u30DA\u30F3\u30BF\u30C8\u30CB\u30C3\u30AF\uFF085\u97F3\u97F3\u968E\uFF09">
|
|
26982
|
+
<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>
|
|
26983
|
+
<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>
|
|
26984
|
+
<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>
|
|
26985
|
+
<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>
|
|
26986
|
+
<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>
|
|
26987
|
+
</optgroup>
|
|
26988
|
+
<optgroup label="\u30C1\u30E3\u30FC\u30C1\u30E2\u30FC\u30C9\uFF087\u97F3\u97F3\u968E\uFF09">
|
|
26989
|
+
<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>
|
|
26990
|
+
<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>
|
|
26991
|
+
<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>
|
|
26992
|
+
<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>
|
|
26993
|
+
</optgroup>
|
|
26994
|
+
<optgroup label="\u7279\u6B8A\u97F3\u968E\uFF08\u97F3\u7A0B\u96C6\u5408\u3054\u3068\u5165\u308C\u66FF\u308F\u308B\uFF09">
|
|
26995
|
+
<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>
|
|
26996
|
+
<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>
|
|
26997
|
+
<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>
|
|
26998
|
+
<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>
|
|
26999
|
+
</optgroup>
|
|
27000
|
+
</select>
|
|
27001
|
+
<span class="dtm-grow"></span>
|
|
27002
|
+
<span class="dtm-hint" data-dtm="compose-scale-hint"></span>
|
|
25351
27003
|
</div>
|
|
27004
|
+
</div>
|
|
27005
|
+
</details>
|
|
27006
|
+
|
|
27007
|
+
<details class="dtm-panel" data-dtm-acc="macro">
|
|
27008
|
+
<summary>\u4E00\u62EC\u7DE8\u96C6</summary>
|
|
27009
|
+
<div class="dtm-panel-body">
|
|
25352
27010
|
<div class="dtm-row">
|
|
25353
27011
|
<span class="dtm-label">\u5168\u4F53\u30B7\u30D5\u30C8</span>
|
|
25354
27012
|
<select class="dtm-select" data-dtm="shift-select">
|
|
@@ -25399,10 +27057,11 @@ var buildUI = (target, options) => {
|
|
|
25399
27057
|
</details>
|
|
25400
27058
|
|
|
25401
27059
|
<details class="dtm-panel" data-dtm-acc="io-out">
|
|
25402
|
-
<summary>MIDI / UST / MML \u51FA\u529B</summary>
|
|
27060
|
+
<summary>MIDI / MusicXML / UST / MML \u51FA\u529B</summary>
|
|
25403
27061
|
<div class="dtm-panel-body">
|
|
25404
27062
|
<div class="dtm-row">
|
|
25405
27063
|
<button class="dtm-btn dtm-btn--accent" data-dtm="export-midi">MIDI\u51FA\u529B</button>
|
|
27064
|
+
<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>
|
|
25406
27065
|
<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>
|
|
25407
27066
|
<button class="dtm-btn dtm-btn--success" data-dtm="generate-mml">MML\u751F\u6210</button>
|
|
25408
27067
|
<button class="dtm-btn dtm-btn--primary dtm-hidden" data-dtm="export-wav">WAV\u66F8\u304D\u51FA\u3057</button>
|
|
@@ -25452,6 +27111,7 @@ var buildUI = (target, options) => {
|
|
|
25452
27111
|
</div>
|
|
25453
27112
|
</div>
|
|
25454
27113
|
</details>
|
|
27114
|
+
</div>
|
|
25455
27115
|
|
|
25456
27116
|
<!-- \u2550\u2550\u2550\u2550 \u89E3\u8AAC\u30E2\u30FC\u30C0\u30EB \u2550\u2550\u2550\u2550 -->
|
|
25457
27117
|
<div class="dtm-modal-overlay" data-dtm="modal-overlay" hidden>
|
|
@@ -25558,6 +27218,11 @@ var buildUI = (target, options) => {
|
|
|
25558
27218
|
midiTrackSelection: sel("midi-track-selection"),
|
|
25559
27219
|
midiPanel: sel("midi-panel"),
|
|
25560
27220
|
midiSearchOpenBtn: sel("midi-search-open"),
|
|
27221
|
+
musicXmlInput: sel("musicxml-input"),
|
|
27222
|
+
musicXmlLoadBtn: sel("musicxml-load"),
|
|
27223
|
+
musicXmlInfoBtn: sel("musicxml-info"),
|
|
27224
|
+
musicXmlPartSelection: sel("musicxml-part-selection"),
|
|
27225
|
+
musicXmlLoadNote: sel("musicxml-load-note"),
|
|
25561
27226
|
ustInput: sel("ust-input"),
|
|
25562
27227
|
ustLoadBtn: sel("ust-load"),
|
|
25563
27228
|
ustInfoBtn: sel("ust-info"),
|
|
@@ -25589,6 +27254,7 @@ var buildUI = (target, options) => {
|
|
|
25589
27254
|
macroHarmonic: sel("macro-harmonic"),
|
|
25590
27255
|
macroMono: sel("macro-mono"),
|
|
25591
27256
|
exportMidiBtn: sel("export-midi"),
|
|
27257
|
+
exportMusicXmlBtn: sel("export-musicxml"),
|
|
25592
27258
|
exportUstBtn: sel("export-ust"),
|
|
25593
27259
|
exportWavBtn: sel("export-wav"),
|
|
25594
27260
|
drumJsonExportBtn: sel("drum-json-export"),
|
|
@@ -25657,6 +27323,32 @@ var readMacroSections = () => {
|
|
|
25657
27323
|
var writeMacroSections = (sections) => {
|
|
25658
27324
|
writeMacroSetting("sections", JSON.stringify(sections));
|
|
25659
27325
|
};
|
|
27326
|
+
var KEPT_STORAGE_KEY = "dtm-macro:kept";
|
|
27327
|
+
var readKeptSong = () => {
|
|
27328
|
+
try {
|
|
27329
|
+
if (typeof localStorage === "undefined" || !localStorage) return null;
|
|
27330
|
+
const raw = localStorage.getItem(KEPT_STORAGE_KEY);
|
|
27331
|
+
if (!raw) return null;
|
|
27332
|
+
const parsed = JSON.parse(raw);
|
|
27333
|
+
if (parsed && typeof parsed === "object" && typeof parsed.mml === "string" && parsed.mml.length > 0) {
|
|
27334
|
+
const startStep = typeof parsed.startStep === "number" && Number.isFinite(parsed.startStep) && parsed.startStep >= 0 ? Math.floor(parsed.startStep) : 0;
|
|
27335
|
+
return { mml: parsed.mml, startStep };
|
|
27336
|
+
}
|
|
27337
|
+
} catch (_) {
|
|
27338
|
+
}
|
|
27339
|
+
return null;
|
|
27340
|
+
};
|
|
27341
|
+
var writeKeptSong = (kept) => {
|
|
27342
|
+
try {
|
|
27343
|
+
if (typeof localStorage === "undefined" || !localStorage) return;
|
|
27344
|
+
if (kept === null) {
|
|
27345
|
+
localStorage.removeItem(KEPT_STORAGE_KEY);
|
|
27346
|
+
return;
|
|
27347
|
+
}
|
|
27348
|
+
localStorage.setItem(KEPT_STORAGE_KEY, JSON.stringify(kept));
|
|
27349
|
+
} catch (_) {
|
|
27350
|
+
}
|
|
27351
|
+
};
|
|
25660
27352
|
|
|
25661
27353
|
// src/macros.ts
|
|
25662
27354
|
var SCALES = [
|
|
@@ -25803,7 +27495,7 @@ var transposeNotes = (cores, steps) => {
|
|
|
25803
27495
|
};
|
|
25804
27496
|
|
|
25805
27497
|
// src/version.ts
|
|
25806
|
-
var DTM_VERSION = "2.1.
|
|
27498
|
+
var DTM_VERSION = "2.1.14";
|
|
25807
27499
|
|
|
25808
27500
|
// src/midi-io.ts
|
|
25809
27501
|
var STEPS_PER_BEAT3 = 48;
|
|
@@ -26970,6 +28662,275 @@ var isChordHeavyTrack = (notes, threshold = 0.6) => {
|
|
|
26970
28662
|
return chordNotes / notes.length >= threshold;
|
|
26971
28663
|
};
|
|
26972
28664
|
|
|
28665
|
+
// src/musicxml-io.ts
|
|
28666
|
+
var STEPS_PER_BEAT4 = 48;
|
|
28667
|
+
var PITCH_CLASS = {
|
|
28668
|
+
C: 0,
|
|
28669
|
+
D: 2,
|
|
28670
|
+
E: 4,
|
|
28671
|
+
F: 5,
|
|
28672
|
+
G: 7,
|
|
28673
|
+
A: 9,
|
|
28674
|
+
B: 11
|
|
28675
|
+
};
|
|
28676
|
+
var textOf = (el, tag) => el?.getElementsByTagName(tag)[0]?.textContent?.trim() ?? "";
|
|
28677
|
+
var numOf = (el, tag, fallback = 0) => {
|
|
28678
|
+
const raw = textOf(el, tag);
|
|
28679
|
+
if (!raw) return fallback;
|
|
28680
|
+
const v = Number.parseFloat(raw);
|
|
28681
|
+
return Number.isFinite(v) ? v : fallback;
|
|
28682
|
+
};
|
|
28683
|
+
var pitchOf = (note) => {
|
|
28684
|
+
const p = note.getElementsByTagName("pitch")[0];
|
|
28685
|
+
if (!p) return null;
|
|
28686
|
+
const step = textOf(p, "step").toUpperCase();
|
|
28687
|
+
const base = PITCH_CLASS[step];
|
|
28688
|
+
if (base === void 0) return null;
|
|
28689
|
+
const octave = numOf(p, "octave", 4);
|
|
28690
|
+
const alter = Math.round(numOf(p, "alter", 0));
|
|
28691
|
+
return (octave + 1) * 12 + base + alter;
|
|
28692
|
+
};
|
|
28693
|
+
var parseMusicXML = (xml) => {
|
|
28694
|
+
const doc = new DOMParser().parseFromString(xml, "application/xml");
|
|
28695
|
+
if (doc.getElementsByTagName("parserror").length > 0)
|
|
28696
|
+
throw new Error("MusicXML \u3068\u3057\u3066\u8AAD\u3081\u307E\u305B\u3093");
|
|
28697
|
+
const nameById = /* @__PURE__ */ new Map();
|
|
28698
|
+
for (const sp of Array.from(doc.getElementsByTagName("score-part"))) {
|
|
28699
|
+
const id = sp.getAttribute("id");
|
|
28700
|
+
if (id) nameById.set(id, textOf(sp, "part-name") || id);
|
|
28701
|
+
}
|
|
28702
|
+
const placements = [];
|
|
28703
|
+
const parts = [];
|
|
28704
|
+
let bpm = 0;
|
|
28705
|
+
const partEls = Array.from(doc.getElementsByTagName("part"));
|
|
28706
|
+
partEls.forEach((partEl, partIndex) => {
|
|
28707
|
+
const id = partEl.getAttribute("id") ?? "";
|
|
28708
|
+
let divisions = 1;
|
|
28709
|
+
let cursor = 0;
|
|
28710
|
+
let measureStart = 0;
|
|
28711
|
+
let lastStart = 0;
|
|
28712
|
+
let lastDurSteps = 0;
|
|
28713
|
+
const tied = /* @__PURE__ */ new Map();
|
|
28714
|
+
let count = 0;
|
|
28715
|
+
let pitchSum = 0;
|
|
28716
|
+
let lyricSeen = false;
|
|
28717
|
+
for (const measure of Array.from(partEl.getElementsByTagName("measure"))) {
|
|
28718
|
+
measureStart += cursor;
|
|
28719
|
+
cursor = 0;
|
|
28720
|
+
for (const child of Array.from(measure.children)) {
|
|
28721
|
+
const tag = child.tagName.toLowerCase();
|
|
28722
|
+
if (tag === "attributes") {
|
|
28723
|
+
const d = numOf(child, "divisions", 0);
|
|
28724
|
+
if (d > 0) divisions = d;
|
|
28725
|
+
continue;
|
|
28726
|
+
}
|
|
28727
|
+
if (tag === "direction") {
|
|
28728
|
+
const sound = child.getElementsByTagName("sound")[0];
|
|
28729
|
+
const t = Number.parseFloat(sound?.getAttribute("tempo") ?? "");
|
|
28730
|
+
if (!bpm && Number.isFinite(t) && t > 0) bpm = t;
|
|
28731
|
+
continue;
|
|
28732
|
+
}
|
|
28733
|
+
if (tag === "backup" || tag === "forward") {
|
|
28734
|
+
const steps = numOf(child, "duration", 0) / divisions * STEPS_PER_BEAT4;
|
|
28735
|
+
cursor += tag === "backup" ? -steps : steps;
|
|
28736
|
+
continue;
|
|
28737
|
+
}
|
|
28738
|
+
if (tag !== "note") continue;
|
|
28739
|
+
const durSteps = Math.round(
|
|
28740
|
+
numOf(child, "duration", 0) / divisions * STEPS_PER_BEAT4
|
|
28741
|
+
);
|
|
28742
|
+
const isChord = child.getElementsByTagName("chord").length > 0;
|
|
28743
|
+
const isRest = child.getElementsByTagName("rest").length > 0;
|
|
28744
|
+
const start = isChord ? lastStart : measureStart + cursor;
|
|
28745
|
+
if (!isRest) {
|
|
28746
|
+
const pitch = pitchOf(child);
|
|
28747
|
+
if (pitch !== null) {
|
|
28748
|
+
const lyric = textOf(
|
|
28749
|
+
child.getElementsByTagName("lyric")[0],
|
|
28750
|
+
"text"
|
|
28751
|
+
);
|
|
28752
|
+
if (lyric) lyricSeen = true;
|
|
28753
|
+
const ties = Array.from(child.getElementsByTagName("tie"));
|
|
28754
|
+
const stops = ties.some((t) => t.getAttribute("type") === "stop");
|
|
28755
|
+
const starts = ties.some((t) => t.getAttribute("type") === "start");
|
|
28756
|
+
const held = tied.get(pitch);
|
|
28757
|
+
if (stops && held) {
|
|
28758
|
+
held.durationSteps += durSteps;
|
|
28759
|
+
if (!starts) tied.delete(pitch);
|
|
28760
|
+
} else {
|
|
28761
|
+
const placed = {
|
|
28762
|
+
partIndex,
|
|
28763
|
+
startStep: Math.max(0, Math.round(start)),
|
|
28764
|
+
pitch,
|
|
28765
|
+
durationSteps: Math.max(1, durSteps),
|
|
28766
|
+
lyric
|
|
28767
|
+
};
|
|
28768
|
+
placements.push(placed);
|
|
28769
|
+
count++;
|
|
28770
|
+
pitchSum += pitch;
|
|
28771
|
+
if (starts) tied.set(pitch, placed);
|
|
28772
|
+
}
|
|
28773
|
+
}
|
|
28774
|
+
}
|
|
28775
|
+
if (!isChord) {
|
|
28776
|
+
lastStart = start;
|
|
28777
|
+
lastDurSteps = durSteps;
|
|
28778
|
+
cursor += durSteps;
|
|
28779
|
+
} else {
|
|
28780
|
+
lastDurSteps = Math.max(lastDurSteps, durSteps);
|
|
28781
|
+
}
|
|
28782
|
+
}
|
|
28783
|
+
}
|
|
28784
|
+
parts.push({
|
|
28785
|
+
index: partIndex,
|
|
28786
|
+
name: nameById.get(id) || `Part ${partIndex + 1}`,
|
|
28787
|
+
noteCount: count,
|
|
28788
|
+
hasLyrics: lyricSeen,
|
|
28789
|
+
avgPitch: count > 0 ? pitchSum / count : 0
|
|
28790
|
+
});
|
|
28791
|
+
});
|
|
28792
|
+
return { parts, placements, bpm: bpm > 0 ? Math.round(bpm) : 120 };
|
|
28793
|
+
};
|
|
28794
|
+
var SHARP_SPELLING = [
|
|
28795
|
+
["C", 0],
|
|
28796
|
+
["C", 1],
|
|
28797
|
+
["D", 0],
|
|
28798
|
+
["D", 1],
|
|
28799
|
+
["E", 0],
|
|
28800
|
+
["F", 0],
|
|
28801
|
+
["F", 1],
|
|
28802
|
+
["G", 0],
|
|
28803
|
+
["G", 1],
|
|
28804
|
+
["A", 0],
|
|
28805
|
+
["A", 1],
|
|
28806
|
+
["B", 0]
|
|
28807
|
+
];
|
|
28808
|
+
var NOTE_TYPES = [
|
|
28809
|
+
[STEPS_PER_BEAT4 * 4, "whole"],
|
|
28810
|
+
[STEPS_PER_BEAT4 * 2, "half"],
|
|
28811
|
+
[STEPS_PER_BEAT4, "quarter"],
|
|
28812
|
+
[STEPS_PER_BEAT4 / 2, "eighth"],
|
|
28813
|
+
[STEPS_PER_BEAT4 / 4, "16th"],
|
|
28814
|
+
[STEPS_PER_BEAT4 / 8, "32nd"]
|
|
28815
|
+
];
|
|
28816
|
+
var typeOf = (steps) => {
|
|
28817
|
+
for (const [len, type] of NOTE_TYPES) {
|
|
28818
|
+
if (steps >= len) {
|
|
28819
|
+
const dots = steps >= len * 1.5 && steps < len * 2 ? 1 : 0;
|
|
28820
|
+
return { type, dots };
|
|
28821
|
+
}
|
|
28822
|
+
}
|
|
28823
|
+
return { type: "32nd", dots: 0 };
|
|
28824
|
+
};
|
|
28825
|
+
var esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
28826
|
+
var exportMusicXML = (options) => {
|
|
28827
|
+
const { parts, bpm, stepsPerBar, title } = options;
|
|
28828
|
+
const beatsPerBar = Math.max(1, Math.round(stepsPerBar / STEPS_PER_BEAT4));
|
|
28829
|
+
const partList = parts.map(
|
|
28830
|
+
(p, i2) => ` <score-part id="P${i2 + 1}">
|
|
28831
|
+
<part-name>${esc(p.name)}</part-name>
|
|
28832
|
+
</score-part>`
|
|
28833
|
+
).join("\n");
|
|
28834
|
+
const body = parts.map((part, pi) => {
|
|
28835
|
+
const notes = [...part.notes].sort((a, b) => a.startStep - b.startStep);
|
|
28836
|
+
const end = notes.reduce(
|
|
28837
|
+
(m, n) => Math.max(m, n.startStep + n.durationSteps),
|
|
28838
|
+
0
|
|
28839
|
+
);
|
|
28840
|
+
const bars = Math.max(1, Math.ceil(end / stepsPerBar));
|
|
28841
|
+
const lines = [` <part id="P${pi + 1}">`];
|
|
28842
|
+
let li = 0;
|
|
28843
|
+
for (let bar = 0; bar < bars; bar++) {
|
|
28844
|
+
const from = bar * stepsPerBar;
|
|
28845
|
+
const to = from + stepsPerBar;
|
|
28846
|
+
lines.push(` <measure number="${bar + 1}">`);
|
|
28847
|
+
if (bar === 0) {
|
|
28848
|
+
lines.push(
|
|
28849
|
+
` <attributes>
|
|
28850
|
+
<divisions>${STEPS_PER_BEAT4}</divisions>`,
|
|
28851
|
+
` <key><fifths>0</fifths></key>`,
|
|
28852
|
+
` <time><beats>${beatsPerBar}</beats><beat-type>4</beat-type></time>`,
|
|
28853
|
+
` <clef><sign>G</sign><line>2</line></clef>
|
|
28854
|
+
</attributes>`
|
|
28855
|
+
);
|
|
28856
|
+
if (pi === 0)
|
|
28857
|
+
lines.push(
|
|
28858
|
+
` <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>`
|
|
28859
|
+
);
|
|
28860
|
+
}
|
|
28861
|
+
let cursor = from;
|
|
28862
|
+
const inBar = notes.filter(
|
|
28863
|
+
(n) => n.startStep >= from && n.startStep < to
|
|
28864
|
+
);
|
|
28865
|
+
const groups = /* @__PURE__ */ new Map();
|
|
28866
|
+
for (const n of inBar) {
|
|
28867
|
+
const g = groups.get(n.startStep);
|
|
28868
|
+
if (g) g.push(n);
|
|
28869
|
+
else groups.set(n.startStep, [n]);
|
|
28870
|
+
}
|
|
28871
|
+
const starts = [...groups.keys()].sort((a, b) => a - b);
|
|
28872
|
+
for (let gi = 0; gi < starts.length; gi++) {
|
|
28873
|
+
const at = starts[gi];
|
|
28874
|
+
const group = groups.get(at);
|
|
28875
|
+
if (at > cursor) {
|
|
28876
|
+
const { type, dots } = typeOf(at - cursor);
|
|
28877
|
+
lines.push(
|
|
28878
|
+
` <note><rest/><duration>${at - cursor}</duration><type>${type}</type>${"<dot/>".repeat(dots)}</note>`
|
|
28879
|
+
);
|
|
28880
|
+
cursor = at;
|
|
28881
|
+
}
|
|
28882
|
+
const nextAt = gi + 1 < starts.length ? starts[gi + 1] : to;
|
|
28883
|
+
const room = Math.min(to, nextAt) - at;
|
|
28884
|
+
const dur = Math.max(
|
|
28885
|
+
1,
|
|
28886
|
+
Math.min(Math.max(...group.map((n) => n.durationSteps)), room)
|
|
28887
|
+
);
|
|
28888
|
+
group.forEach((n, ni) => {
|
|
28889
|
+
const semi = Math.round(n.pitchUnits / UNITS_PER_SEMITONE);
|
|
28890
|
+
const [step, alter] = SHARP_SPELLING[(semi % 12 + 12) % 12];
|
|
28891
|
+
const octave = Math.floor(semi / 12) - 1;
|
|
28892
|
+
const { type, dots } = typeOf(dur);
|
|
28893
|
+
const lyric = part.lyrics?.[li];
|
|
28894
|
+
lines.push(
|
|
28895
|
+
` <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>`
|
|
28896
|
+
);
|
|
28897
|
+
if (ni === 0) li++;
|
|
28898
|
+
});
|
|
28899
|
+
cursor = at + dur;
|
|
28900
|
+
}
|
|
28901
|
+
if (cursor < to) {
|
|
28902
|
+
const { type, dots } = typeOf(to - cursor);
|
|
28903
|
+
lines.push(
|
|
28904
|
+
` <note><rest/><duration>${to - cursor}</duration><type>${type}</type>${"<dot/>".repeat(dots)}</note>`
|
|
28905
|
+
);
|
|
28906
|
+
}
|
|
28907
|
+
lines.push(" </measure>");
|
|
28908
|
+
}
|
|
28909
|
+
lines.push(" </part>");
|
|
28910
|
+
return lines.join("\n");
|
|
28911
|
+
}).join("\n");
|
|
28912
|
+
return [
|
|
28913
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
28914
|
+
'<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 4.0 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">',
|
|
28915
|
+
'<score-partwise version="4.0">',
|
|
28916
|
+
` <work><work-title>${esc(title ?? "Untitled")}</work-title></work>`,
|
|
28917
|
+
// 書き出し元を残す。MML・MIDI と揃えてある(`src/version.ts` に理由)。
|
|
28918
|
+
` <identification><encoding><software>dtm ${DTM_VERSION}</software></encoding></identification>`,
|
|
28919
|
+
" <part-list>",
|
|
28920
|
+
partList,
|
|
28921
|
+
" </part-list>",
|
|
28922
|
+
body,
|
|
28923
|
+
"</score-partwise>",
|
|
28924
|
+
""
|
|
28925
|
+
].join("\n");
|
|
28926
|
+
};
|
|
28927
|
+
var musicXmlToNotes = (placements, partIndex) => placements.filter((p) => p.partIndex === partIndex).map((p) => ({
|
|
28928
|
+
startStep: p.startStep,
|
|
28929
|
+
durationSteps: p.durationSteps,
|
|
28930
|
+
pitchUnits: p.pitch * UNITS_PER_SEMITONE,
|
|
28931
|
+
velocity: 100
|
|
28932
|
+
}));
|
|
28933
|
+
|
|
26973
28934
|
// src/renderer.ts
|
|
26974
28935
|
var KEYBOARD_WIDTH = 60;
|
|
26975
28936
|
var HEADER_HEIGHT = 20;
|
|
@@ -27298,6 +29259,76 @@ var createRenderer = (mountTarget, width = 800, height = 450, config) => {
|
|
|
27298
29259
|
}
|
|
27299
29260
|
}
|
|
27300
29261
|
};
|
|
29262
|
+
const drawSpeechSpans = (spans, color = [255, 241, 232]) => {
|
|
29263
|
+
if (spans.length === 0) return;
|
|
29264
|
+
const {
|
|
29265
|
+
keyHeight,
|
|
29266
|
+
stepWidth,
|
|
29267
|
+
keyCount,
|
|
29268
|
+
pitchRangeStart,
|
|
29269
|
+
unitsPerRow: upr = UNITS_PER_SEMITONE
|
|
29270
|
+
} = g_config;
|
|
29271
|
+
const [r, g, b] = color;
|
|
29272
|
+
g_grid_ctx.save();
|
|
29273
|
+
for (const { note, durationSteps } of spans) {
|
|
29274
|
+
if (durationSteps <= 0) continue;
|
|
29275
|
+
const x2 = note.startStep * stepWidth - g_draw_offset_x;
|
|
29276
|
+
const y = (keyCount - 1 - (note.pitchUnits - pitchRangeStart) / upr) * keyHeight - g_draw_offset_y;
|
|
29277
|
+
const w = durationSteps * stepWidth;
|
|
29278
|
+
if (x2 + w < 0 || x2 > g_grid_canvas.width) continue;
|
|
29279
|
+
if (y + keyHeight < 0 || y > g_grid_canvas.height) continue;
|
|
29280
|
+
const h = Math.max(2, Math.floor(keyHeight * 0.35));
|
|
29281
|
+
const top = y + keyHeight - h - 1;
|
|
29282
|
+
g_grid_ctx.fillStyle = `rgba(${r},${g},${b},0.28)`;
|
|
29283
|
+
g_grid_ctx.fillRect(x2 + 1, top, w - 2, h);
|
|
29284
|
+
g_grid_ctx.strokeStyle = `rgba(${r},${g},${b},0.75)`;
|
|
29285
|
+
g_grid_ctx.lineWidth = 1;
|
|
29286
|
+
g_grid_ctx.setLineDash([3, 2]);
|
|
29287
|
+
g_grid_ctx.strokeRect(x2 + 1.5, top + 0.5, w - 3, h - 1);
|
|
29288
|
+
g_grid_ctx.setLineDash([]);
|
|
29289
|
+
g_grid_ctx.beginPath();
|
|
29290
|
+
g_grid_ctx.moveTo(x2 + w - 1.5, y + 1);
|
|
29291
|
+
g_grid_ctx.lineTo(x2 + w - 1.5, y + keyHeight - 1);
|
|
29292
|
+
g_grid_ctx.stroke();
|
|
29293
|
+
}
|
|
29294
|
+
g_grid_ctx.restore();
|
|
29295
|
+
};
|
|
29296
|
+
const drawPitchGuide = (pitchUnits, label) => {
|
|
29297
|
+
const {
|
|
29298
|
+
keyHeight,
|
|
29299
|
+
keyCount,
|
|
29300
|
+
pitchRangeStart,
|
|
29301
|
+
unitsPerRow: upr = UNITS_PER_SEMITONE
|
|
29302
|
+
} = g_config;
|
|
29303
|
+
const row = Math.round((pitchUnits - pitchRangeStart) / upr);
|
|
29304
|
+
if (row < 0 || row >= keyCount) return;
|
|
29305
|
+
const y = (keyCount - 1 - row) * keyHeight - g_draw_offset_y;
|
|
29306
|
+
if (y + keyHeight < 0 || y > g_grid_canvas.height) return;
|
|
29307
|
+
g_grid_ctx.save();
|
|
29308
|
+
g_grid_ctx.fillStyle = "rgba(255,236,39,0.10)";
|
|
29309
|
+
g_grid_ctx.fillRect(0, y, g_grid_canvas.width, keyHeight);
|
|
29310
|
+
g_grid_ctx.strokeStyle = "rgba(255,236,39,0.55)";
|
|
29311
|
+
g_grid_ctx.lineWidth = 1;
|
|
29312
|
+
g_grid_ctx.setLineDash([6, 4]);
|
|
29313
|
+
g_grid_ctx.beginPath();
|
|
29314
|
+
g_grid_ctx.moveTo(0, y + keyHeight / 2 + 0.5);
|
|
29315
|
+
g_grid_ctx.lineTo(g_grid_canvas.width, y + keyHeight / 2 + 0.5);
|
|
29316
|
+
g_grid_ctx.stroke();
|
|
29317
|
+
if (keyHeight >= 7) {
|
|
29318
|
+
const fontSize = Math.min(11, Math.floor(keyHeight * 0.85));
|
|
29319
|
+
g_grid_ctx.setLineDash([]);
|
|
29320
|
+
g_grid_ctx.font = `${fontSize}px 'k8x12',sans-serif`;
|
|
29321
|
+
g_grid_ctx.textAlign = "left";
|
|
29322
|
+
g_grid_ctx.textBaseline = "middle";
|
|
29323
|
+
g_grid_ctx.lineWidth = 3;
|
|
29324
|
+
g_grid_ctx.lineJoin = "round";
|
|
29325
|
+
g_grid_ctx.strokeStyle = "rgba(0,0,0,0.85)";
|
|
29326
|
+
g_grid_ctx.strokeText(label, 4, y + keyHeight / 2);
|
|
29327
|
+
g_grid_ctx.fillStyle = "#ffec27";
|
|
29328
|
+
g_grid_ctx.fillText(label, 4, y + keyHeight / 2);
|
|
29329
|
+
}
|
|
29330
|
+
g_grid_ctx.restore();
|
|
29331
|
+
};
|
|
27301
29332
|
const drawNoteLyrics = (notes, syllables) => {
|
|
27302
29333
|
if (syllables.length === 0) return;
|
|
27303
29334
|
const {
|
|
@@ -27447,6 +29478,8 @@ var createRenderer = (mountTarget, width = 800, height = 450, config) => {
|
|
|
27447
29478
|
drawGrid,
|
|
27448
29479
|
drawNotes,
|
|
27449
29480
|
drawNoteLyrics,
|
|
29481
|
+
drawSpeechSpans,
|
|
29482
|
+
drawPitchGuide,
|
|
27450
29483
|
drawSelectionRect,
|
|
27451
29484
|
drawSelectedNotes,
|
|
27452
29485
|
getXY,
|
|
@@ -28045,7 +30078,7 @@ var VOWEL_KANA2 = {
|
|
|
28045
30078
|
o: "\u304A",
|
|
28046
30079
|
\u00FC: "\u3086"
|
|
28047
30080
|
};
|
|
28048
|
-
var
|
|
30081
|
+
var SMALL_KANA3 = {
|
|
28049
30082
|
a: "\u3083",
|
|
28050
30083
|
u: "\u3085",
|
|
28051
30084
|
e: "\u3047",
|
|
@@ -28117,7 +30150,7 @@ var pinyinToMoras = (raw) => {
|
|
|
28117
30150
|
const zero = initial === "" && medial === "i";
|
|
28118
30151
|
const head = zero ? ZERO_PALATAL[nucleus] : onset[medial];
|
|
28119
30152
|
if (!head) return null;
|
|
28120
|
-
const small =
|
|
30153
|
+
const small = SMALL_KANA3[nucleus];
|
|
28121
30154
|
if (zero) moras.push(head);
|
|
28122
30155
|
else if (head.length === 1 && small) moras.push(head + small);
|
|
28123
30156
|
else moras.push(head, VOWEL_KANA2[nucleus]);
|
|
@@ -28127,10 +30160,10 @@ var pinyinToMoras = (raw) => {
|
|
|
28127
30160
|
};
|
|
28128
30161
|
|
|
28129
30162
|
// src/ust-io.ts
|
|
28130
|
-
var
|
|
30163
|
+
var STEPS_PER_BEAT5 = 48;
|
|
28131
30164
|
var UST_TICKS_PER_BEAT = 480;
|
|
28132
|
-
var TICKS_TO_STEPS =
|
|
28133
|
-
var STEPS_TO_TICKS = UST_TICKS_PER_BEAT /
|
|
30165
|
+
var TICKS_TO_STEPS = STEPS_PER_BEAT5 / UST_TICKS_PER_BEAT;
|
|
30166
|
+
var STEPS_TO_TICKS = UST_TICKS_PER_BEAT / STEPS_PER_BEAT5;
|
|
28134
30167
|
var MORA_TAIL_MAX_STEPS = 12;
|
|
28135
30168
|
var UST_CONTINUE_LYRICS = ["+", "+~", "+-", "+*", "*", "\u30FC", "-"];
|
|
28136
30169
|
var TIE = "\u30FC";
|
|
@@ -28213,12 +30246,12 @@ var ROMAJI_KANA = (() => {
|
|
|
28213
30246
|
return map;
|
|
28214
30247
|
})();
|
|
28215
30248
|
var KANA_HEAD_RE = /^[ぁ-ゖァ-ヶー〜]+/;
|
|
28216
|
-
var
|
|
30249
|
+
var SMALL_KANA4 = "\u3041\u3043\u3045\u3047\u3049\u3083\u3085\u3087";
|
|
28217
30250
|
var splitMoras = (kana) => {
|
|
28218
30251
|
const moras = [];
|
|
28219
30252
|
for (const ch of kana) {
|
|
28220
30253
|
const last = moras.length - 1;
|
|
28221
|
-
if (last >= 0 &&
|
|
30254
|
+
if (last >= 0 && SMALL_KANA4.includes(ch)) moras[last] += ch;
|
|
28222
30255
|
else moras.push(ch);
|
|
28223
30256
|
}
|
|
28224
30257
|
return moras;
|
|
@@ -28403,7 +30436,7 @@ var parseUst = (source, name = "") => {
|
|
|
28403
30436
|
const syllables = [];
|
|
28404
30437
|
let unknownLyricCount = 0;
|
|
28405
30438
|
let hasVoiced = false;
|
|
28406
|
-
const msPerStep = 6e4 / (bpm ?? 120) /
|
|
30439
|
+
const msPerStep = 6e4 / (bpm ?? 120) / STEPS_PER_BEAT5;
|
|
28407
30440
|
for (const fields of sections) {
|
|
28408
30441
|
const length = Number.parseFloat(fields.length ?? "");
|
|
28409
30442
|
if (!Number.isFinite(length) || length <= 0) continue;
|
|
@@ -28499,6 +30532,7 @@ var ustLyricOf = (kana) => {
|
|
|
28499
30532
|
if (kana === "") return DEFAULT_UST_LYRIC;
|
|
28500
30533
|
if (kana === TIE || kana === "\u301C") return "+";
|
|
28501
30534
|
if (kana === REST) return "R";
|
|
30535
|
+
if (kana.startsWith("\u300C")) return "R";
|
|
28502
30536
|
return kana;
|
|
28503
30537
|
};
|
|
28504
30538
|
var UST_ENVELOPE_ATTACK_MS = 5;
|
|
@@ -28642,7 +30676,9 @@ var buildUst = (options) => {
|
|
|
28642
30676
|
cursorTick = startTick + length;
|
|
28643
30677
|
}
|
|
28644
30678
|
assignFadeEnvelopes(out);
|
|
28645
|
-
|
|
30679
|
+
for (const [index, note] of out.entries()) {
|
|
30680
|
+
lines.push(...ustNoteSection(index, note));
|
|
30681
|
+
}
|
|
28646
30682
|
lines.push("[#TRACKEND]");
|
|
28647
30683
|
return `${lines.join("\r\n")}\r
|
|
28648
30684
|
`;
|
|
@@ -28696,8 +30732,22 @@ c d e f g \u2190 \u97F3\u7B265\u3064</pre>
|
|
|
28696
30732
|
<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>
|
|
28697
30733
|
<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>
|
|
28698
30734
|
<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>
|
|
30735
|
+
<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>
|
|
30736
|
+
</ul>
|
|
30737
|
+
<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>
|
|
30738
|
+
|
|
30739
|
+
<h4>\u6B4C\u306E\u4E2D\u3067\u8A9E\u308B\uFF08<code>\u300C\u2026\u300D</code>\uFF09</h4>
|
|
30740
|
+
<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>
|
|
30741
|
+
<pre>\u3075\u3064\u3046\u306B\u3046\u305F\u3046\u300C\u304B\u305F\u308B\u3070\u3057\u3087\u300D\u3075\u3064\u3046\u306B\u3046\u305F\u3046
|
|
30742
|
+
\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
|
|
30743
|
+
\u300C\u30BB\u30EA\u30D5\u3060\u3051\u306E\u30C8\u30E9\u30C3\u30AF\u3082\u4F5C\u308C\u307E\u3059\u3002\u300D</pre>
|
|
30744
|
+
<ul>
|
|
30745
|
+
<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>
|
|
30746
|
+
<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>
|
|
30747
|
+
<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>
|
|
30748
|
+
<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>
|
|
30749
|
+
<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>
|
|
28699
30750
|
</ul>
|
|
28700
|
-
<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>
|
|
28701
30751
|
|
|
28702
30752
|
<h4>\u5F37\u5F31\u3092\u3064\u3051\u308B\uFF08<code>\u2193</code> <code>\u2191</code>\uFF09</h4>
|
|
28703
30753
|
<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>
|
|
@@ -29084,6 +31134,27 @@ var MIDI_INFO_HTML = `
|
|
|
29084
31134
|
<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>
|
|
29085
31135
|
</div>
|
|
29086
31136
|
`;
|
|
31137
|
+
var MUSICXML_INFO_HTML = `
|
|
31138
|
+
<div class="dtm-modal-body-content">
|
|
31139
|
+
<h4>1. MusicXML\u3068\u306F</h4>
|
|
31140
|
+
<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>
|
|
31141
|
+
|
|
31142
|
+
<h4>2. \u8AAD\u307F\u8FBC\u307F\u306E\u3057\u304B\u305F</h4>
|
|
31143
|
+
<ul>
|
|
31144
|
+
<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>
|
|
31145
|
+
<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>
|
|
31146
|
+
<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>
|
|
31147
|
+
<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>
|
|
31148
|
+
<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>
|
|
31149
|
+
</ul>
|
|
31150
|
+
|
|
31151
|
+
<h4>3. \u66F8\u304D\u51FA\u3057</h4>
|
|
31152
|
+
<ul>
|
|
31153
|
+
<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>
|
|
31154
|
+
<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>
|
|
31155
|
+
</ul>
|
|
31156
|
+
</div>
|
|
31157
|
+
`;
|
|
29087
31158
|
var UST_INFO_HTML = `
|
|
29088
31159
|
<div class="dtm-modal-body-content">
|
|
29089
31160
|
<h4>1. UST\u30D5\u30A1\u30A4\u30EB\u3068\u306F</h4>
|
|
@@ -29225,6 +31296,7 @@ var HELP_INFO_HTML = `
|
|
|
29225
31296
|
<button class="dtm-btn dtm-btn--ghost" data-dtm-help="koe">\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0(.koe)</button>
|
|
29226
31297
|
<button class="dtm-btn dtm-btn--ghost" data-dtm-help="chord">\u30B3\u30FC\u30C9\u9032\u884C</button>
|
|
29227
31298
|
<button class="dtm-btn dtm-btn--ghost" data-dtm-help="midi">MIDI\u306E\u8AAD\u307F\u8FBC\u307F</button>
|
|
31299
|
+
<button class="dtm-btn dtm-btn--ghost" data-dtm-help="musicxml">MusicXML</button>
|
|
29228
31300
|
<button class="dtm-btn dtm-btn--ghost" data-dtm-help="ust">UST(UTAU)</button>
|
|
29229
31301
|
<button class="dtm-btn dtm-btn--ghost" data-dtm-help="mml">MML\u306E\u66F8\u304D\u65B9</button>
|
|
29230
31302
|
<button class="dtm-btn dtm-btn--ghost" data-dtm-help="loop">\u30EB\u30FC\u30D7\u518D\u751F</button>
|
|
@@ -29243,6 +31315,7 @@ var HELP_TOPICS = {
|
|
|
29243
31315
|
koe: { title: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0(.koe)\u306E\u4F7F\u3044\u65B9", html: KOE_INFO_HTML },
|
|
29244
31316
|
chord: { title: "\u30B3\u30FC\u30C9\u9032\u884C\u306E\u81EA\u52D5\u5165\u529B\u89E3\u8AAC", html: CHORD_INFO_HTML2 },
|
|
29245
31317
|
midi: { title: "MIDI\u306E\u8AAD\u307F\u8FBC\u307F\u89E3\u8AAC", html: MIDI_INFO_HTML },
|
|
31318
|
+
musicxml: { title: "MusicXML\u306E\u5165\u51FA\u529B\u89E3\u8AAC", html: MUSICXML_INFO_HTML },
|
|
29246
31319
|
ust: { title: "UST\u306E\u8AAD\u307F\u8FBC\u307F\u89E3\u8AAC", html: UST_INFO_HTML },
|
|
29247
31320
|
mml: { title: "MML\u306E\u66F8\u304D\u65B9\u89E3\u8AAC", html: MML_INFO_HTML },
|
|
29248
31321
|
loop: { title: "\u30EB\u30FC\u30D7\u518D\u751F\u306E\u89E3\u8AAC", html: LOOP_INFO_HTML },
|
|
@@ -29697,6 +31770,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
29697
31770
|
};
|
|
29698
31771
|
let currentInstrument = "";
|
|
29699
31772
|
let autoComposeInstrument = null;
|
|
31773
|
+
let composeSeed = null;
|
|
31774
|
+
let composeSetting = null;
|
|
29700
31775
|
let activeTrackId = options.initialActiveTrack ?? trackConfigs[0].id;
|
|
29701
31776
|
let trackFxAdvancedOpen = readPanelOpen("track-fx-advanced") ?? false;
|
|
29702
31777
|
let lyricAdvancedOpen = readPanelOpen("lyric-advanced") ?? false;
|
|
@@ -29939,17 +32014,66 @@ var mountDAW = (target, options = {}) => {
|
|
|
29939
32014
|
const getActive = () => trackStates.find((t) => t.config.id === activeTrackId) ?? trackStates[0];
|
|
29940
32015
|
let lyricKanaCacheText = "";
|
|
29941
32016
|
let lyricKanaCache = [];
|
|
29942
|
-
|
|
32017
|
+
let lyricSyllableCache = [];
|
|
32018
|
+
const getActiveLyricSyllables = () => {
|
|
29943
32019
|
const t = getActive();
|
|
29944
32020
|
if (!t?.lyricModel.trim()) return [];
|
|
29945
32021
|
const text = t.lyrics.trim();
|
|
29946
32022
|
if (!text) return [];
|
|
29947
32023
|
if (text !== lyricKanaCacheText) {
|
|
29948
32024
|
lyricKanaCacheText = text;
|
|
29949
|
-
|
|
32025
|
+
lyricSyllableCache = normalizeLyrics(text);
|
|
32026
|
+
lyricKanaCache = lyricSyllableCache.map(displayKana);
|
|
29950
32027
|
}
|
|
32028
|
+
return lyricSyllableCache;
|
|
32029
|
+
};
|
|
32030
|
+
const getActiveLyricKana = () => {
|
|
32031
|
+
getActiveLyricSyllables();
|
|
29951
32032
|
return lyricKanaCache;
|
|
29952
32033
|
};
|
|
32034
|
+
const speechPreviewRequested = /* @__PURE__ */ new Set();
|
|
32035
|
+
const requestSpeechPreview = (model, text) => {
|
|
32036
|
+
const voices = options.singingVoices;
|
|
32037
|
+
if (!voices?.planSpeech) return;
|
|
32038
|
+
const key = `${model}|${text}`;
|
|
32039
|
+
if (speechPreviewRequested.has(key)) return;
|
|
32040
|
+
speechPreviewRequested.add(key);
|
|
32041
|
+
void voices.planSpeech(model, text).then((d) => {
|
|
32042
|
+
if (d !== null) redrawAll();
|
|
32043
|
+
}).catch(() => {
|
|
32044
|
+
});
|
|
32045
|
+
};
|
|
32046
|
+
const drawSpeechPreview = (notes) => {
|
|
32047
|
+
const voices = options.singingVoices;
|
|
32048
|
+
if (!voices) return;
|
|
32049
|
+
const syllables = getActiveLyricSyllables();
|
|
32050
|
+
if (!syllables.some((s) => s.kind === "speak")) return;
|
|
32051
|
+
const model = getActive().lyricModel.trim().toLowerCase();
|
|
32052
|
+
const sorted = [...notes].sort((a, b) => a.startStep - b.startStep);
|
|
32053
|
+
const count = Math.min(sorted.length, syllables.length);
|
|
32054
|
+
const secondsPerStep = 60 / bpm / 48;
|
|
32055
|
+
const spans = [];
|
|
32056
|
+
for (let i2 = 0; i2 < count; i2++) {
|
|
32057
|
+
const syl = syllables[i2];
|
|
32058
|
+
if (syl.kind !== "speak" || !syl.text) continue;
|
|
32059
|
+
const d = voices.peekSpeechDurationSec?.(model, syl.text);
|
|
32060
|
+
if (d === void 0) {
|
|
32061
|
+
requestSpeechPreview(model, syl.text);
|
|
32062
|
+
continue;
|
|
32063
|
+
}
|
|
32064
|
+
spans.push({ note: sorted[i2], durationSteps: d / secondsPerStep });
|
|
32065
|
+
}
|
|
32066
|
+
renderer.drawSpeechSpans(spans, getActive().config.color);
|
|
32067
|
+
};
|
|
32068
|
+
const drawSpeechPitchGuide = () => {
|
|
32069
|
+
const voices = options.singingVoices;
|
|
32070
|
+
if (!voices?.getSpeechReferenceUnits) return;
|
|
32071
|
+
if (!getActiveLyricSyllables().some((s) => s.kind === "speak")) return;
|
|
32072
|
+
const ref = voices.getSpeechReferenceUnits(
|
|
32073
|
+
getActive().lyricModel.trim().toLowerCase()
|
|
32074
|
+
);
|
|
32075
|
+
if (ref !== void 0) renderer.drawPitchGuide(ref, "\u8A9E\u308A\u306E\u57FA\u6E96");
|
|
32076
|
+
};
|
|
29953
32077
|
let showModal;
|
|
29954
32078
|
let showConfirm;
|
|
29955
32079
|
const playableNotes = (t) => {
|
|
@@ -30034,6 +32158,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
30034
32158
|
};
|
|
30035
32159
|
const redrawAll = () => {
|
|
30036
32160
|
renderer.drawGrid(gridLineSteps);
|
|
32161
|
+
drawSpeechPitchGuide();
|
|
30037
32162
|
let lyricTargetNotes = null;
|
|
30038
32163
|
let activeTrackState = null;
|
|
30039
32164
|
for (const t of trackStates) {
|
|
@@ -30082,8 +32207,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
30082
32207
|
1
|
|
30083
32208
|
]);
|
|
30084
32209
|
}
|
|
30085
|
-
if (lyricTargetNotes)
|
|
32210
|
+
if (lyricTargetNotes) {
|
|
32211
|
+
drawSpeechPreview(lyricTargetNotes);
|
|
30086
32212
|
renderer.drawNoteLyrics(lyricTargetNotes, getActiveLyricKana());
|
|
32213
|
+
}
|
|
30087
32214
|
drawStartLine();
|
|
30088
32215
|
if (playbackState === "playing") drawPlayhead();
|
|
30089
32216
|
updateScrollbars();
|
|
@@ -31688,9 +33815,9 @@ var mountDAW = (target, options = {}) => {
|
|
|
31688
33815
|
</details>
|
|
31689
33816
|
<div class="dtm-row">
|
|
31690
33817
|
<span class="dtm-label dtm-grow">\u6B4C\u8A5E</span>
|
|
31691
|
-
<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>
|
|
33818
|
+
<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>
|
|
31692
33819
|
</div>
|
|
31693
|
-
<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>
|
|
33820
|
+
<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>
|
|
31694
33821
|
</div>`;
|
|
31695
33822
|
refs.trackBody.appendChild(lyricDiv);
|
|
31696
33823
|
lyricDiv.querySelector(
|
|
@@ -31890,8 +34017,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
31890
34017
|
lyricVibrato.checked = active.vocalVibrato;
|
|
31891
34018
|
lyricOctaveUnison.value = active.vocalOctaveUnison;
|
|
31892
34019
|
const updateLyricCount = () => {
|
|
31893
|
-
const
|
|
31894
|
-
|
|
34020
|
+
const syllables = normalizeLyrics(lyricInput.value);
|
|
34021
|
+
const n = syllables.length;
|
|
34022
|
+
const speak = syllables.filter((s) => s.kind === "speak").length;
|
|
34023
|
+
lyricCount.textContent = active.lyricModel && n > 0 ? `${n}\u97F3\u7BC0${speak > 0 ? `\uFF08\u8A9E\u308A${speak}\uFF09` : ""}` : "";
|
|
31895
34024
|
};
|
|
31896
34025
|
const syncLyricTerms = () => {
|
|
31897
34026
|
if (customVocalsMap.has(active.lyricModel)) {
|
|
@@ -32203,6 +34332,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
32203
34332
|
mode,
|
|
32204
34333
|
edo: renderConfig.edo,
|
|
32205
34334
|
loop: loopEnabled ? true : void 0,
|
|
34335
|
+
seed: composeSeed ?? void 0,
|
|
34336
|
+
compose: composeSetting ?? void 0,
|
|
32206
34337
|
// アップロードされたファイルは url が空=音源関連の宣言ごと出力されない
|
|
32207
34338
|
audio: backing.url || void 0,
|
|
32208
34339
|
audioStart: backing.rangeStartSec || void 0,
|
|
@@ -32242,6 +34373,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
32242
34373
|
mode,
|
|
32243
34374
|
edo: renderConfig.edo,
|
|
32244
34375
|
loop: loopEnabled ? true : void 0,
|
|
34376
|
+
seed: composeSeed ?? void 0,
|
|
34377
|
+
compose: composeSetting ?? void 0,
|
|
32245
34378
|
// アップロードされたファイルは url が空=音源関連の宣言ごと出力されない
|
|
32246
34379
|
audio: backing.url || void 0,
|
|
32247
34380
|
audioStart: backing.rangeStartSec || void 0,
|
|
@@ -32298,7 +34431,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
32298
34431
|
const lyricLines = trackStates.map((t, i2) => ({
|
|
32299
34432
|
i: i2,
|
|
32300
34433
|
notes: clipNotes(t.core.getNotes()),
|
|
32301
|
-
|
|
34434
|
+
// `;` は MML の区切り文字なので、語り(「…」)の本文に紛れていても全角へ逃がす。
|
|
34435
|
+
text: t.lyrics.replace(/[\r\n]+/g, " ").replace(/;/g, "\uFF1B").trim(),
|
|
32302
34436
|
model: t.lyricModel.trim(),
|
|
32303
34437
|
vol: t.vocalVolume,
|
|
32304
34438
|
gate: t.vocalGate,
|
|
@@ -32441,6 +34575,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
32441
34575
|
clampTrackCount: trackStates.length
|
|
32442
34576
|
});
|
|
32443
34577
|
if (!applyActiveOnly) {
|
|
34578
|
+
composeSeed = meta.seed ?? null;
|
|
34579
|
+
composeSetting = meta.compose ?? null;
|
|
32444
34580
|
if (meta.instrument && INSTRUMENT_PRESETS[meta.instrument]) {
|
|
32445
34581
|
currentInstrument = meta.instrument;
|
|
32446
34582
|
options.onInstrumentChange?.(meta.instrument);
|
|
@@ -32830,6 +34966,92 @@ var mountDAW = (target, options = {}) => {
|
|
|
32830
34966
|
dropped: ustTracks.length - targets.length
|
|
32831
34967
|
};
|
|
32832
34968
|
};
|
|
34969
|
+
const applyMusicXmlSelection = (xml, selectedIndices, startIndex) => {
|
|
34970
|
+
stop();
|
|
34971
|
+
const applyActiveOnly = refs.applyActiveOnly?.checked ?? false;
|
|
34972
|
+
const from = Math.max(
|
|
34973
|
+
0,
|
|
34974
|
+
startIndex ?? (applyActiveOnly ? activeTrackIndexOf() : selectedIndices.length > 1 ? 0 : activeTrackIndexOf())
|
|
34975
|
+
);
|
|
34976
|
+
const maxCount = applyActiveOnly ? 1 : trackStates.length - from;
|
|
34977
|
+
const targetIndices = selectedIndices.slice(0, Math.max(0, maxCount));
|
|
34978
|
+
const voice = trackStates[from]?.lyricModel || pickComposeVocal();
|
|
34979
|
+
targetIndices.forEach((partIndex, i2) => {
|
|
34980
|
+
const t = trackStates[from + i2];
|
|
34981
|
+
if (!t) return;
|
|
34982
|
+
const notes = musicXmlToNotes(xml.placements, partIndex);
|
|
34983
|
+
t.core.setLoadMode(true);
|
|
34984
|
+
t.core.clearNotesWithoutHistory();
|
|
34985
|
+
for (const n of notes) {
|
|
34986
|
+
t.core.addNote(n.startStep, snapToEdoGrid(n.pitchUnits), {
|
|
34987
|
+
noteLengthSteps: n.durationSteps,
|
|
34988
|
+
velocity: n.velocity
|
|
34989
|
+
});
|
|
34990
|
+
}
|
|
34991
|
+
t.core.setLoadMode(false);
|
|
34992
|
+
t.core.addHistoryOnce();
|
|
34993
|
+
const partPlacements = xml.placements.filter((p) => p.partIndex === partIndex).sort((a, b) => a.startStep - b.startStep);
|
|
34994
|
+
const hasLyrics = partPlacements.some(
|
|
34995
|
+
(p) => Boolean(p.lyric && p.lyric.trim())
|
|
34996
|
+
);
|
|
34997
|
+
if (hasLyrics) {
|
|
34998
|
+
const syllables = [];
|
|
34999
|
+
for (const p of partPlacements) {
|
|
35000
|
+
const l = p.lyric?.trim();
|
|
35001
|
+
syllables.push(l || "\u30FC");
|
|
35002
|
+
}
|
|
35003
|
+
t.lyrics = syllables.join("");
|
|
35004
|
+
t.vocalVolume = DEFAULT_VOCAL_VOLUME;
|
|
35005
|
+
t.vocalGate = 100;
|
|
35006
|
+
t.vocalPan = 64;
|
|
35007
|
+
t.vocalOctave = 0;
|
|
35008
|
+
t.vocalVibrato = false;
|
|
35009
|
+
t.vocalReverb = 0;
|
|
35010
|
+
t.vocalDelay = 0;
|
|
35011
|
+
t.vocalGender = 50;
|
|
35012
|
+
t.vocalBreathiness = 50;
|
|
35013
|
+
t.vocalTension = 50;
|
|
35014
|
+
t.vocalOctaveUnison = "none";
|
|
35015
|
+
if (!t.lyricModel) t.lyricModel = voice;
|
|
35016
|
+
fireLyricsChange(t);
|
|
35017
|
+
} else {
|
|
35018
|
+
t.lyrics = "";
|
|
35019
|
+
t.lyricModel = "";
|
|
35020
|
+
}
|
|
35021
|
+
});
|
|
35022
|
+
if (xml.bpm > 0) setBpm(Math.round(xml.bpm));
|
|
35023
|
+
playStartStep = 0;
|
|
35024
|
+
currentOffsetX = 0;
|
|
35025
|
+
const firstPitch = getFirstDetectedPitch();
|
|
35026
|
+
centerPitch(firstPitch ?? pitchV1ToUnits(60));
|
|
35027
|
+
redrawAll();
|
|
35028
|
+
updateTrackPanel();
|
|
35029
|
+
updateUndoRedo();
|
|
35030
|
+
return {
|
|
35031
|
+
applied: targetIndices.length,
|
|
35032
|
+
dropped: selectedIndices.length - targetIndices.length
|
|
35033
|
+
};
|
|
35034
|
+
};
|
|
35035
|
+
const exportMusicXML2 = () => {
|
|
35036
|
+
const parts = trackStates.map((t) => {
|
|
35037
|
+
const notes = playableNotes(t);
|
|
35038
|
+
const syllables = t.lyrics ? normalizeLyrics(t.lyrics).map(displayKana) : void 0;
|
|
35039
|
+
return {
|
|
35040
|
+
name: t.config.name,
|
|
35041
|
+
notes,
|
|
35042
|
+
lyrics: syllables
|
|
35043
|
+
};
|
|
35044
|
+
});
|
|
35045
|
+
const xml = exportMusicXML({
|
|
35046
|
+
parts,
|
|
35047
|
+
bpm,
|
|
35048
|
+
stepsPerBar: renderConfig.stepsPerBar,
|
|
35049
|
+
title: "DTM Project"
|
|
35050
|
+
});
|
|
35051
|
+
return new Blob([xml], {
|
|
35052
|
+
type: "application/vnd.recordare.musicxml+xml;charset=utf-8"
|
|
35053
|
+
});
|
|
35054
|
+
};
|
|
32833
35055
|
const exportUST = () => {
|
|
32834
35056
|
const t = getActive();
|
|
32835
35057
|
const syllables = normalizeLyrics(t.lyrics).map(displayKana);
|
|
@@ -33509,17 +35731,29 @@ var mountDAW = (target, options = {}) => {
|
|
|
33509
35731
|
stop();
|
|
33510
35732
|
overlayDuring(() => {
|
|
33511
35733
|
const tmpl = selectedComposeTemplate();
|
|
35734
|
+
const sections = selectedComposeSections();
|
|
35735
|
+
const baseKey = refs.composeKey?.value ?? "any";
|
|
35736
|
+
const scale = refs.composeScale?.value ?? "auto";
|
|
35737
|
+
const seed = Math.random() * 4294967296 >>> 0;
|
|
33512
35738
|
const song = composeSong({
|
|
33513
35739
|
stepsPerBar: renderConfig.stepsPerBar,
|
|
33514
35740
|
edo: renderConfig.edo,
|
|
33515
|
-
sections
|
|
35741
|
+
sections,
|
|
33516
35742
|
template: tmpl,
|
|
33517
|
-
baseKey
|
|
33518
|
-
scale
|
|
35743
|
+
baseKey,
|
|
35744
|
+
scale,
|
|
35745
|
+
random: seededRandom(seed),
|
|
33519
35746
|
// 直近に作った曲の特徴を渡すと、それらから離れた候補に加点される。
|
|
33520
35747
|
// 「作曲」を続けて押したときに似た曲が並ぶのを防ぐ。
|
|
33521
35748
|
recent: recentComposeFingerprints
|
|
33522
35749
|
});
|
|
35750
|
+
composeSeed = seed;
|
|
35751
|
+
composeSetting = [
|
|
35752
|
+
tmpl ?? "custom",
|
|
35753
|
+
baseKey,
|
|
35754
|
+
scale,
|
|
35755
|
+
sections.join("-")
|
|
35756
|
+
].join(":");
|
|
33523
35757
|
recentComposeFingerprints.push(song.stats.fingerprint);
|
|
33524
35758
|
if (recentComposeFingerprints.length > 5)
|
|
33525
35759
|
recentComposeFingerprints.shift();
|
|
@@ -33700,6 +35934,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
33700
35934
|
updateTrackPanel();
|
|
33701
35935
|
updateUndoRedo();
|
|
33702
35936
|
composedSignature = trackSignature();
|
|
35937
|
+
void play();
|
|
33703
35938
|
});
|
|
33704
35939
|
};
|
|
33705
35940
|
const composeWithConfirm = (withVocal) => {
|
|
@@ -33719,28 +35954,47 @@ var mountDAW = (target, options = {}) => {
|
|
|
33719
35954
|
refs.macroCompose.addEventListener("click", () => {
|
|
33720
35955
|
composeWithConfirm(false);
|
|
33721
35956
|
});
|
|
33722
|
-
let
|
|
35957
|
+
let kept = readKeptSong();
|
|
33723
35958
|
const updateKeepUI = () => {
|
|
33724
|
-
refs.composeRecall.disabled =
|
|
33725
|
-
refs.composeKeep.textContent =
|
|
35959
|
+
refs.composeRecall.disabled = kept === null;
|
|
35960
|
+
refs.composeKeep.textContent = kept === null ? "\u30AD\u30FC\u30D7" : "\u30AD\u30FC\u30D7\u6E08";
|
|
33726
35961
|
};
|
|
35962
|
+
const snapshotKept = () => ({
|
|
35963
|
+
// 小節数の上限は共有リンクの文字数のためのもので、アプリ内の退避には
|
|
35964
|
+
// 効かせない(効かせると長い曲がキープした時点で切れる)。
|
|
35965
|
+
mml: generateMML({ ignoreBarLimit: true }).full,
|
|
35966
|
+
startStep: playStartStep
|
|
35967
|
+
});
|
|
33727
35968
|
refs.composeKeep.addEventListener("click", () => {
|
|
33728
35969
|
const hasNotes = trackStates.some((t) => t.core.getNotes().length > 0);
|
|
33729
35970
|
if (!hasNotes) return;
|
|
33730
|
-
|
|
35971
|
+
kept = snapshotKept();
|
|
35972
|
+
writeKeptSong(kept);
|
|
33731
35973
|
updateKeepUI();
|
|
33732
35974
|
});
|
|
33733
|
-
|
|
33734
|
-
if (keptMml === null) return;
|
|
35975
|
+
const loadKept = (song) => {
|
|
33735
35976
|
const box = refs.applyActiveOnly;
|
|
33736
35977
|
const was = box?.checked ?? false;
|
|
33737
35978
|
if (box) box.checked = false;
|
|
33738
35979
|
try {
|
|
33739
|
-
loadMML(
|
|
35980
|
+
loadMML(song.mml);
|
|
33740
35981
|
} finally {
|
|
33741
35982
|
if (box) box.checked = was;
|
|
33742
35983
|
}
|
|
35984
|
+
playStartStep = Math.min(song.startStep, getMaxNoteStep());
|
|
33743
35985
|
composedSignature = null;
|
|
35986
|
+
void play();
|
|
35987
|
+
};
|
|
35988
|
+
refs.composeRecall.addEventListener("click", () => {
|
|
35989
|
+
if (kept === null) return;
|
|
35990
|
+
const previous = kept;
|
|
35991
|
+
const hasNotes = trackStates.some((t) => t.core.getNotes().length > 0);
|
|
35992
|
+
if (hasNotes) {
|
|
35993
|
+
kept = snapshotKept();
|
|
35994
|
+
writeKeptSong(kept);
|
|
35995
|
+
}
|
|
35996
|
+
loadKept(previous);
|
|
35997
|
+
updateKeepUI();
|
|
33744
35998
|
});
|
|
33745
35999
|
updateKeepUI();
|
|
33746
36000
|
refs.macroComposeVocal.addEventListener("click", () => {
|
|
@@ -33799,6 +36053,9 @@ var mountDAW = (target, options = {}) => {
|
|
|
33799
36053
|
refs.exportMidiBtn.addEventListener("click", () => {
|
|
33800
36054
|
download(exportMIDI2(), "dtm.mid");
|
|
33801
36055
|
});
|
|
36056
|
+
refs.exportMusicXmlBtn.addEventListener("click", () => {
|
|
36057
|
+
download(exportMusicXML2(), "dtm.musicxml");
|
|
36058
|
+
});
|
|
33802
36059
|
refs.exportUstBtn.addEventListener("click", () => {
|
|
33803
36060
|
const name = getActive().config.name.replace(/[/:*?"<>|\s]+/g, "_");
|
|
33804
36061
|
download(exportUST(), `dtm_${name}.ust`);
|
|
@@ -34066,6 +36323,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
34066
36323
|
});
|
|
34067
36324
|
if (showMidi) {
|
|
34068
36325
|
wireMidi();
|
|
36326
|
+
wireMusicXml();
|
|
34069
36327
|
wireUst();
|
|
34070
36328
|
}
|
|
34071
36329
|
if (showAudio) {
|
|
@@ -34166,6 +36424,125 @@ var mountDAW = (target, options = {}) => {
|
|
|
34166
36424
|
overlayDuring(() => applyMidiSelection(pendingMidi, selected));
|
|
34167
36425
|
});
|
|
34168
36426
|
};
|
|
36427
|
+
let pendingMusicXml = null;
|
|
36428
|
+
const setMusicXmlNote = (text) => {
|
|
36429
|
+
refs.musicXmlLoadNote.textContent = text;
|
|
36430
|
+
refs.musicXmlLoadNote.classList.toggle("dtm-hidden", text === "");
|
|
36431
|
+
};
|
|
36432
|
+
const getSelectedMusicXmlPartIndices = () => {
|
|
36433
|
+
if (!pendingMusicXml) return [];
|
|
36434
|
+
const selected = [];
|
|
36435
|
+
const btns = refs.musicXmlPartSelection.querySelectorAll("button");
|
|
36436
|
+
btns.forEach((b, i2) => {
|
|
36437
|
+
if (b.dataset.selected === "true") {
|
|
36438
|
+
selected.push(pendingMusicXml.parts[i2].index);
|
|
36439
|
+
}
|
|
36440
|
+
});
|
|
36441
|
+
return selected;
|
|
36442
|
+
};
|
|
36443
|
+
const updateMusicXmlAssignmentNote = () => {
|
|
36444
|
+
if (!pendingMusicXml) return;
|
|
36445
|
+
const selected = getSelectedMusicXmlPartIndices();
|
|
36446
|
+
if (selected.length === 0) {
|
|
36447
|
+
setMusicXmlNote("\u8AAD\u307F\u8FBC\u3080\u30D1\u30FC\u30C8\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044");
|
|
36448
|
+
return;
|
|
36449
|
+
}
|
|
36450
|
+
const applyActiveOnly = refs.applyActiveOnly?.checked ?? false;
|
|
36451
|
+
const from = applyActiveOnly ? activeTrackIndexOf() : selected.length > 1 ? 0 : activeTrackIndexOf();
|
|
36452
|
+
const maxCount = applyActiveOnly ? 1 : trackStates.length - from;
|
|
36453
|
+
const appliedCount = Math.min(selected.length, maxCount);
|
|
36454
|
+
const selectedParts = selected.map((idx) => pendingMusicXml.parts.find((p) => p.index === idx)).filter(Boolean);
|
|
36455
|
+
const names = selectedParts.slice(0, appliedCount).map(
|
|
36456
|
+
(p, i2) => `${trackStates[from + i2]?.config.name ?? `\u30C8\u30E9\u30C3\u30AF${from + i2 + 1}`}\u2190${p.name}`
|
|
36457
|
+
).join(" / ");
|
|
36458
|
+
const dropped = selected.length - appliedCount;
|
|
36459
|
+
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";
|
|
36460
|
+
setMusicXmlNote(
|
|
36461
|
+
dropped > 0 ? `${names}\uFF08${dropReason}${dropped}\u30D1\u30FC\u30C8\u306F\u8AAD\u307F\u8FBC\u307F\u307E\u305B\u3093\uFF09` : names
|
|
36462
|
+
);
|
|
36463
|
+
};
|
|
36464
|
+
const wireMusicXml = () => {
|
|
36465
|
+
refs.musicXmlInfoBtn.addEventListener("click", () => {
|
|
36466
|
+
showModal("MusicXML\u306E\u5165\u51FA\u529B\u89E3\u8AAC", MUSICXML_INFO_HTML);
|
|
36467
|
+
});
|
|
36468
|
+
refs.musicXmlInput.addEventListener("change", async () => {
|
|
36469
|
+
const file = refs.musicXmlInput.files?.[0];
|
|
36470
|
+
pendingMusicXml = null;
|
|
36471
|
+
refs.musicXmlPartSelection.innerHTML = "";
|
|
36472
|
+
if (!file) {
|
|
36473
|
+
setMusicXmlNote("");
|
|
36474
|
+
refs.musicXmlPartSelection.classList.add("dtm-hidden");
|
|
36475
|
+
return;
|
|
36476
|
+
}
|
|
36477
|
+
refs.overlay.hidden = false;
|
|
36478
|
+
setLoading(true);
|
|
36479
|
+
try {
|
|
36480
|
+
const text = await file.text();
|
|
36481
|
+
pendingMusicXml = parseMusicXML(text);
|
|
36482
|
+
if (pendingMusicXml.parts.length === 0) {
|
|
36483
|
+
setMusicXmlNote("\u30D1\u30FC\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F");
|
|
36484
|
+
refs.musicXmlPartSelection.classList.add("dtm-hidden");
|
|
36485
|
+
return;
|
|
36486
|
+
}
|
|
36487
|
+
refs.musicXmlPartSelection.innerHTML = `<span class="dtm-label">\u30D1\u30FC\u30C8</span>`;
|
|
36488
|
+
pendingMusicXml.parts.forEach((p) => {
|
|
36489
|
+
const btn = document.createElement("button");
|
|
36490
|
+
btn.className = "dtm-btn dtm-btn--primary";
|
|
36491
|
+
btn.dataset.selected = "true";
|
|
36492
|
+
const lyricBadge = p.hasLyrics ? " \u{1F3A4}" : "";
|
|
36493
|
+
btn.textContent = `${p.name} (${p.noteCount}\u97F3${lyricBadge})`;
|
|
36494
|
+
btn.title = `\u5E73\u5747\u97F3\u9AD8: MIDI ${Math.round(p.avgPitch)}${p.hasLyrics ? " / \u6B4C\u8A5E\u3042\u308A" : ""}`;
|
|
36495
|
+
btn.addEventListener("click", () => {
|
|
36496
|
+
const on = btn.dataset.selected !== "true";
|
|
36497
|
+
btn.dataset.selected = String(on);
|
|
36498
|
+
btn.classList.toggle("dtm-btn--primary", on);
|
|
36499
|
+
btn.classList.toggle("dtm-btn--ghost", !on);
|
|
36500
|
+
updateMusicXmlAssignmentNote();
|
|
36501
|
+
});
|
|
36502
|
+
refs.musicXmlPartSelection.appendChild(btn);
|
|
36503
|
+
});
|
|
36504
|
+
refs.musicXmlPartSelection.classList.remove("dtm-hidden");
|
|
36505
|
+
updateMusicXmlAssignmentNote();
|
|
36506
|
+
} catch (e) {
|
|
36507
|
+
setMusicXmlNote("MusicXML\u3068\u3057\u3066\u89E3\u6790\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F");
|
|
36508
|
+
refs.musicXmlPartSelection.classList.add("dtm-hidden");
|
|
36509
|
+
console.error(e);
|
|
36510
|
+
} finally {
|
|
36511
|
+
refs.overlay.hidden = true;
|
|
36512
|
+
setLoading(false);
|
|
36513
|
+
}
|
|
36514
|
+
});
|
|
36515
|
+
refs.musicXmlLoadBtn.addEventListener("click", async () => {
|
|
36516
|
+
if (!pendingMusicXml) return;
|
|
36517
|
+
const selected = getSelectedMusicXmlPartIndices();
|
|
36518
|
+
if (selected.length === 0) return;
|
|
36519
|
+
const applyActiveOnly = refs.applyActiveOnly?.checked ?? false;
|
|
36520
|
+
const from = applyActiveOnly ? activeTrackIndexOf() : selected.length > 1 ? 0 : activeTrackIndexOf();
|
|
36521
|
+
const maxCount = applyActiveOnly ? 1 : trackStates.length - from;
|
|
36522
|
+
if (!isAdvanced && options.onRequestAdvancedMode && !applyActiveOnly && selected.length > maxCount) {
|
|
36523
|
+
const confirmed = await showConfirmModal(
|
|
36524
|
+
"\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"
|
|
36525
|
+
);
|
|
36526
|
+
if (confirmed) {
|
|
36527
|
+
const xml = pendingMusicXml;
|
|
36528
|
+
const sel = selected.slice();
|
|
36529
|
+
options.onRequestAdvancedMode(void 0, (newDaw) => {
|
|
36530
|
+
newDaw.applyMusicXmlParsed?.(xml, sel);
|
|
36531
|
+
});
|
|
36532
|
+
return;
|
|
36533
|
+
}
|
|
36534
|
+
}
|
|
36535
|
+
overlayDuring(() => {
|
|
36536
|
+
const { applied, dropped } = applyMusicXmlSelection(
|
|
36537
|
+
pendingMusicXml,
|
|
36538
|
+
selected
|
|
36539
|
+
);
|
|
36540
|
+
setMusicXmlNote(
|
|
36541
|
+
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`
|
|
36542
|
+
);
|
|
36543
|
+
});
|
|
36544
|
+
});
|
|
36545
|
+
};
|
|
34169
36546
|
let pendingUsts = [];
|
|
34170
36547
|
const setUstNote = (text) => {
|
|
34171
36548
|
refs.ustLoadNote.textContent = text;
|
|
@@ -34762,6 +37139,15 @@ var mountDAW = (target, options = {}) => {
|
|
|
34762
37139
|
resizeTimer = setTimeout(() => setupCanvas(), 150);
|
|
34763
37140
|
});
|
|
34764
37141
|
resizeObserver.observe(refs.rollContainer);
|
|
37142
|
+
const WIDE_LAYOUT_MIN_WIDTH = 1e3;
|
|
37143
|
+
const applyWideLayout = (width) => {
|
|
37144
|
+
refs.root.classList.toggle("dtm-daw--wide", width >= WIDE_LAYOUT_MIN_WIDTH);
|
|
37145
|
+
};
|
|
37146
|
+
const layoutObserver = new ResizeObserver((entries) => {
|
|
37147
|
+
for (const entry of entries) applyWideLayout(entry.contentRect.width);
|
|
37148
|
+
});
|
|
37149
|
+
layoutObserver.observe(refs.root);
|
|
37150
|
+
applyWideLayout(refs.root.getBoundingClientRect().width);
|
|
34765
37151
|
document.addEventListener("pointermove", onPointerMove);
|
|
34766
37152
|
document.addEventListener("pointerup", onPointerUp);
|
|
34767
37153
|
const setLoading = (loading) => {
|
|
@@ -34863,10 +37249,14 @@ var mountDAW = (target, options = {}) => {
|
|
|
34863
37249
|
applyMidiParsed: (midi, selectedIndices) => {
|
|
34864
37250
|
overlayDuring(() => applyMidiSelection(midi, selectedIndices));
|
|
34865
37251
|
},
|
|
37252
|
+
applyMusicXmlParsed: (xml, selectedIndices) => {
|
|
37253
|
+
overlayDuring(() => applyMusicXmlSelection(xml, selectedIndices));
|
|
37254
|
+
},
|
|
34866
37255
|
applyUstParsed: (ustTracks, startIndex) => {
|
|
34867
37256
|
overlayDuring(() => applyUstTracks(ustTracks, startIndex));
|
|
34868
37257
|
},
|
|
34869
37258
|
exportMIDI: exportMIDI2,
|
|
37259
|
+
exportMusicXML: exportMusicXML2,
|
|
34870
37260
|
exportUST,
|
|
34871
37261
|
setBpm,
|
|
34872
37262
|
getLoop: () => loopEnabled,
|
|
@@ -35001,6 +37391,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
35001
37391
|
unsubscribeClip?.();
|
|
35002
37392
|
stopPeakSampling();
|
|
35003
37393
|
resizeObserver.disconnect();
|
|
37394
|
+
layoutObserver.disconnect();
|
|
35004
37395
|
document.removeEventListener("pointermove", onPointerMove);
|
|
35005
37396
|
document.removeEventListener("pointerup", onPointerUp);
|
|
35006
37397
|
document.removeEventListener("keydown", onKeyDown);
|
|
@@ -35053,7 +37444,7 @@ var readGlobalBool = (key) => {
|
|
|
35053
37444
|
};
|
|
35054
37445
|
|
|
35055
37446
|
// src/headless-singing-player.ts
|
|
35056
|
-
var
|
|
37447
|
+
var STEPS_PER_BEAT6 = 48;
|
|
35057
37448
|
var STEPS_PER_BAR3 = 192;
|
|
35058
37449
|
var TRACK_ID_BY_INDEX2 = ["melody", "submelody", "bass", "chord"];
|
|
35059
37450
|
var playSingingMML = async (mml, options = {}) => {
|
|
@@ -35070,7 +37461,7 @@ var playSingingMML = async (mml, options = {}) => {
|
|
|
35070
37461
|
parseCustomVocals(mml).map((d) => [d.key, d])
|
|
35071
37462
|
);
|
|
35072
37463
|
const bpm = parsedBpm ?? options.defaultBpm ?? DEFAULT_BPM;
|
|
35073
|
-
const secondsPerStep = 60 / bpm /
|
|
37464
|
+
const secondsPerStep = 60 / bpm / STEPS_PER_BEAT6;
|
|
35074
37465
|
const drumPatternDict = {
|
|
35075
37466
|
...DRUM_PATTERNS,
|
|
35076
37467
|
...SONG_DRUM_PATTERNS,
|
|
@@ -35238,7 +37629,8 @@ var playSingingMML = async (mml, options = {}) => {
|
|
|
35238
37629
|
if (lyricTracks.size > 0) {
|
|
35239
37630
|
if (!voices) {
|
|
35240
37631
|
voices = createSingingVoices(ctx, destination, {
|
|
35241
|
-
voiceWorkerUrl: options.voiceWorkerUrl
|
|
37632
|
+
voiceWorkerUrl: options.voiceWorkerUrl,
|
|
37633
|
+
ttsBaseUrl: options.ttsBaseUrl
|
|
35242
37634
|
});
|
|
35243
37635
|
}
|
|
35244
37636
|
if (customVocalByKey.size > 0 && voices.registerVoicebanks) {
|
|
@@ -35302,275 +37694,6 @@ var playSingingMML = async (mml, options = {}) => {
|
|
|
35302
37694
|
return playback;
|
|
35303
37695
|
};
|
|
35304
37696
|
|
|
35305
|
-
// src/musicxml-io.ts
|
|
35306
|
-
var STEPS_PER_BEAT6 = 48;
|
|
35307
|
-
var PITCH_CLASS = {
|
|
35308
|
-
C: 0,
|
|
35309
|
-
D: 2,
|
|
35310
|
-
E: 4,
|
|
35311
|
-
F: 5,
|
|
35312
|
-
G: 7,
|
|
35313
|
-
A: 9,
|
|
35314
|
-
B: 11
|
|
35315
|
-
};
|
|
35316
|
-
var textOf = (el, tag) => el?.getElementsByTagName(tag)[0]?.textContent?.trim() ?? "";
|
|
35317
|
-
var numOf = (el, tag, fallback = 0) => {
|
|
35318
|
-
const raw = textOf(el, tag);
|
|
35319
|
-
if (!raw) return fallback;
|
|
35320
|
-
const v = Number.parseFloat(raw);
|
|
35321
|
-
return Number.isFinite(v) ? v : fallback;
|
|
35322
|
-
};
|
|
35323
|
-
var pitchOf = (note) => {
|
|
35324
|
-
const p = note.getElementsByTagName("pitch")[0];
|
|
35325
|
-
if (!p) return null;
|
|
35326
|
-
const step = textOf(p, "step").toUpperCase();
|
|
35327
|
-
const base = PITCH_CLASS[step];
|
|
35328
|
-
if (base === void 0) return null;
|
|
35329
|
-
const octave = numOf(p, "octave", 4);
|
|
35330
|
-
const alter = Math.round(numOf(p, "alter", 0));
|
|
35331
|
-
return (octave + 1) * 12 + base + alter;
|
|
35332
|
-
};
|
|
35333
|
-
var parseMusicXML = (xml) => {
|
|
35334
|
-
const doc = new DOMParser().parseFromString(xml, "application/xml");
|
|
35335
|
-
if (doc.getElementsByTagName("parserror").length > 0)
|
|
35336
|
-
throw new Error("MusicXML \u3068\u3057\u3066\u8AAD\u3081\u307E\u305B\u3093");
|
|
35337
|
-
const nameById = /* @__PURE__ */ new Map();
|
|
35338
|
-
for (const sp of Array.from(doc.getElementsByTagName("score-part"))) {
|
|
35339
|
-
const id = sp.getAttribute("id");
|
|
35340
|
-
if (id) nameById.set(id, textOf(sp, "part-name") || id);
|
|
35341
|
-
}
|
|
35342
|
-
const placements = [];
|
|
35343
|
-
const parts = [];
|
|
35344
|
-
let bpm = 0;
|
|
35345
|
-
const partEls = Array.from(doc.getElementsByTagName("part"));
|
|
35346
|
-
partEls.forEach((partEl, partIndex) => {
|
|
35347
|
-
const id = partEl.getAttribute("id") ?? "";
|
|
35348
|
-
let divisions = 1;
|
|
35349
|
-
let cursor = 0;
|
|
35350
|
-
let measureStart = 0;
|
|
35351
|
-
let lastStart = 0;
|
|
35352
|
-
let lastDurSteps = 0;
|
|
35353
|
-
const tied = /* @__PURE__ */ new Map();
|
|
35354
|
-
let count = 0;
|
|
35355
|
-
let pitchSum = 0;
|
|
35356
|
-
let lyricSeen = false;
|
|
35357
|
-
for (const measure of Array.from(partEl.getElementsByTagName("measure"))) {
|
|
35358
|
-
measureStart += cursor;
|
|
35359
|
-
cursor = 0;
|
|
35360
|
-
for (const child of Array.from(measure.children)) {
|
|
35361
|
-
const tag = child.tagName.toLowerCase();
|
|
35362
|
-
if (tag === "attributes") {
|
|
35363
|
-
const d = numOf(child, "divisions", 0);
|
|
35364
|
-
if (d > 0) divisions = d;
|
|
35365
|
-
continue;
|
|
35366
|
-
}
|
|
35367
|
-
if (tag === "direction") {
|
|
35368
|
-
const sound = child.getElementsByTagName("sound")[0];
|
|
35369
|
-
const t = Number.parseFloat(sound?.getAttribute("tempo") ?? "");
|
|
35370
|
-
if (!bpm && Number.isFinite(t) && t > 0) bpm = t;
|
|
35371
|
-
continue;
|
|
35372
|
-
}
|
|
35373
|
-
if (tag === "backup" || tag === "forward") {
|
|
35374
|
-
const steps = numOf(child, "duration", 0) / divisions * STEPS_PER_BEAT6;
|
|
35375
|
-
cursor += tag === "backup" ? -steps : steps;
|
|
35376
|
-
continue;
|
|
35377
|
-
}
|
|
35378
|
-
if (tag !== "note") continue;
|
|
35379
|
-
const durSteps = Math.round(
|
|
35380
|
-
numOf(child, "duration", 0) / divisions * STEPS_PER_BEAT6
|
|
35381
|
-
);
|
|
35382
|
-
const isChord = child.getElementsByTagName("chord").length > 0;
|
|
35383
|
-
const isRest = child.getElementsByTagName("rest").length > 0;
|
|
35384
|
-
const start = isChord ? lastStart : measureStart + cursor;
|
|
35385
|
-
if (!isRest) {
|
|
35386
|
-
const pitch = pitchOf(child);
|
|
35387
|
-
if (pitch !== null) {
|
|
35388
|
-
const lyric = textOf(
|
|
35389
|
-
child.getElementsByTagName("lyric")[0],
|
|
35390
|
-
"text"
|
|
35391
|
-
);
|
|
35392
|
-
if (lyric) lyricSeen = true;
|
|
35393
|
-
const ties = Array.from(child.getElementsByTagName("tie"));
|
|
35394
|
-
const stops = ties.some((t) => t.getAttribute("type") === "stop");
|
|
35395
|
-
const starts = ties.some((t) => t.getAttribute("type") === "start");
|
|
35396
|
-
const held = tied.get(pitch);
|
|
35397
|
-
if (stops && held) {
|
|
35398
|
-
held.durationSteps += durSteps;
|
|
35399
|
-
if (!starts) tied.delete(pitch);
|
|
35400
|
-
} else {
|
|
35401
|
-
const placed = {
|
|
35402
|
-
partIndex,
|
|
35403
|
-
startStep: Math.max(0, Math.round(start)),
|
|
35404
|
-
pitch,
|
|
35405
|
-
durationSteps: Math.max(1, durSteps),
|
|
35406
|
-
lyric
|
|
35407
|
-
};
|
|
35408
|
-
placements.push(placed);
|
|
35409
|
-
count++;
|
|
35410
|
-
pitchSum += pitch;
|
|
35411
|
-
if (starts) tied.set(pitch, placed);
|
|
35412
|
-
}
|
|
35413
|
-
}
|
|
35414
|
-
}
|
|
35415
|
-
if (!isChord) {
|
|
35416
|
-
lastStart = start;
|
|
35417
|
-
lastDurSteps = durSteps;
|
|
35418
|
-
cursor += durSteps;
|
|
35419
|
-
} else {
|
|
35420
|
-
lastDurSteps = Math.max(lastDurSteps, durSteps);
|
|
35421
|
-
}
|
|
35422
|
-
}
|
|
35423
|
-
}
|
|
35424
|
-
parts.push({
|
|
35425
|
-
index: partIndex,
|
|
35426
|
-
name: nameById.get(id) || `Part ${partIndex + 1}`,
|
|
35427
|
-
noteCount: count,
|
|
35428
|
-
hasLyrics: lyricSeen,
|
|
35429
|
-
avgPitch: count > 0 ? pitchSum / count : 0
|
|
35430
|
-
});
|
|
35431
|
-
});
|
|
35432
|
-
return { parts, placements, bpm: bpm > 0 ? Math.round(bpm) : 120 };
|
|
35433
|
-
};
|
|
35434
|
-
var SHARP_SPELLING = [
|
|
35435
|
-
["C", 0],
|
|
35436
|
-
["C", 1],
|
|
35437
|
-
["D", 0],
|
|
35438
|
-
["D", 1],
|
|
35439
|
-
["E", 0],
|
|
35440
|
-
["F", 0],
|
|
35441
|
-
["F", 1],
|
|
35442
|
-
["G", 0],
|
|
35443
|
-
["G", 1],
|
|
35444
|
-
["A", 0],
|
|
35445
|
-
["A", 1],
|
|
35446
|
-
["B", 0]
|
|
35447
|
-
];
|
|
35448
|
-
var NOTE_TYPES = [
|
|
35449
|
-
[STEPS_PER_BEAT6 * 4, "whole"],
|
|
35450
|
-
[STEPS_PER_BEAT6 * 2, "half"],
|
|
35451
|
-
[STEPS_PER_BEAT6, "quarter"],
|
|
35452
|
-
[STEPS_PER_BEAT6 / 2, "eighth"],
|
|
35453
|
-
[STEPS_PER_BEAT6 / 4, "16th"],
|
|
35454
|
-
[STEPS_PER_BEAT6 / 8, "32nd"]
|
|
35455
|
-
];
|
|
35456
|
-
var typeOf = (steps) => {
|
|
35457
|
-
for (const [len, type] of NOTE_TYPES) {
|
|
35458
|
-
if (steps >= len) {
|
|
35459
|
-
const dots = steps >= len * 1.5 && steps < len * 2 ? 1 : 0;
|
|
35460
|
-
return { type, dots };
|
|
35461
|
-
}
|
|
35462
|
-
}
|
|
35463
|
-
return { type: "32nd", dots: 0 };
|
|
35464
|
-
};
|
|
35465
|
-
var esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
35466
|
-
var exportMusicXML = (options) => {
|
|
35467
|
-
const { parts, bpm, stepsPerBar, title } = options;
|
|
35468
|
-
const beatsPerBar = Math.max(1, Math.round(stepsPerBar / STEPS_PER_BEAT6));
|
|
35469
|
-
const partList = parts.map(
|
|
35470
|
-
(p, i2) => ` <score-part id="P${i2 + 1}">
|
|
35471
|
-
<part-name>${esc(p.name)}</part-name>
|
|
35472
|
-
</score-part>`
|
|
35473
|
-
).join("\n");
|
|
35474
|
-
const body = parts.map((part, pi) => {
|
|
35475
|
-
const notes = [...part.notes].sort((a, b) => a.startStep - b.startStep);
|
|
35476
|
-
const end = notes.reduce(
|
|
35477
|
-
(m, n) => Math.max(m, n.startStep + n.durationSteps),
|
|
35478
|
-
0
|
|
35479
|
-
);
|
|
35480
|
-
const bars = Math.max(1, Math.ceil(end / stepsPerBar));
|
|
35481
|
-
const lines = [` <part id="P${pi + 1}">`];
|
|
35482
|
-
let li = 0;
|
|
35483
|
-
for (let bar = 0; bar < bars; bar++) {
|
|
35484
|
-
const from = bar * stepsPerBar;
|
|
35485
|
-
const to = from + stepsPerBar;
|
|
35486
|
-
lines.push(` <measure number="${bar + 1}">`);
|
|
35487
|
-
if (bar === 0) {
|
|
35488
|
-
lines.push(
|
|
35489
|
-
` <attributes>
|
|
35490
|
-
<divisions>${STEPS_PER_BEAT6}</divisions>`,
|
|
35491
|
-
` <key><fifths>0</fifths></key>`,
|
|
35492
|
-
` <time><beats>${beatsPerBar}</beats><beat-type>4</beat-type></time>`,
|
|
35493
|
-
` <clef><sign>G</sign><line>2</line></clef>
|
|
35494
|
-
</attributes>`
|
|
35495
|
-
);
|
|
35496
|
-
if (pi === 0)
|
|
35497
|
-
lines.push(
|
|
35498
|
-
` <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>`
|
|
35499
|
-
);
|
|
35500
|
-
}
|
|
35501
|
-
let cursor = from;
|
|
35502
|
-
const inBar = notes.filter(
|
|
35503
|
-
(n) => n.startStep >= from && n.startStep < to
|
|
35504
|
-
);
|
|
35505
|
-
const groups = /* @__PURE__ */ new Map();
|
|
35506
|
-
for (const n of inBar) {
|
|
35507
|
-
const g = groups.get(n.startStep);
|
|
35508
|
-
if (g) g.push(n);
|
|
35509
|
-
else groups.set(n.startStep, [n]);
|
|
35510
|
-
}
|
|
35511
|
-
const starts = [...groups.keys()].sort((a, b) => a - b);
|
|
35512
|
-
for (let gi = 0; gi < starts.length; gi++) {
|
|
35513
|
-
const at = starts[gi];
|
|
35514
|
-
const group = groups.get(at);
|
|
35515
|
-
if (at > cursor) {
|
|
35516
|
-
const { type, dots } = typeOf(at - cursor);
|
|
35517
|
-
lines.push(
|
|
35518
|
-
` <note><rest/><duration>${at - cursor}</duration><type>${type}</type>${"<dot/>".repeat(dots)}</note>`
|
|
35519
|
-
);
|
|
35520
|
-
cursor = at;
|
|
35521
|
-
}
|
|
35522
|
-
const nextAt = gi + 1 < starts.length ? starts[gi + 1] : to;
|
|
35523
|
-
const room = Math.min(to, nextAt) - at;
|
|
35524
|
-
const dur = Math.max(
|
|
35525
|
-
1,
|
|
35526
|
-
Math.min(Math.max(...group.map((n) => n.durationSteps)), room)
|
|
35527
|
-
);
|
|
35528
|
-
group.forEach((n, ni) => {
|
|
35529
|
-
const semi = Math.round(n.pitchUnits / UNITS_PER_SEMITONE);
|
|
35530
|
-
const [step, alter] = SHARP_SPELLING[(semi % 12 + 12) % 12];
|
|
35531
|
-
const octave = Math.floor(semi / 12) - 1;
|
|
35532
|
-
const { type, dots } = typeOf(dur);
|
|
35533
|
-
const lyric = part.lyrics?.[li];
|
|
35534
|
-
lines.push(
|
|
35535
|
-
` <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>`
|
|
35536
|
-
);
|
|
35537
|
-
if (ni === 0) li++;
|
|
35538
|
-
});
|
|
35539
|
-
cursor = at + dur;
|
|
35540
|
-
}
|
|
35541
|
-
if (cursor < to) {
|
|
35542
|
-
const { type, dots } = typeOf(to - cursor);
|
|
35543
|
-
lines.push(
|
|
35544
|
-
` <note><rest/><duration>${to - cursor}</duration><type>${type}</type>${"<dot/>".repeat(dots)}</note>`
|
|
35545
|
-
);
|
|
35546
|
-
}
|
|
35547
|
-
lines.push(" </measure>");
|
|
35548
|
-
}
|
|
35549
|
-
lines.push(" </part>");
|
|
35550
|
-
return lines.join("\n");
|
|
35551
|
-
}).join("\n");
|
|
35552
|
-
return [
|
|
35553
|
-
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
35554
|
-
'<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 4.0 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">',
|
|
35555
|
-
'<score-partwise version="4.0">',
|
|
35556
|
-
` <work><work-title>${esc(title ?? "Untitled")}</work-title></work>`,
|
|
35557
|
-
// 書き出し元を残す。MML・MIDI と揃えてある(`src/version.ts` に理由)。
|
|
35558
|
-
` <identification><encoding><software>dtm ${DTM_VERSION}</software></encoding></identification>`,
|
|
35559
|
-
" <part-list>",
|
|
35560
|
-
partList,
|
|
35561
|
-
" </part-list>",
|
|
35562
|
-
body,
|
|
35563
|
-
"</score-partwise>",
|
|
35564
|
-
""
|
|
35565
|
-
].join("\n");
|
|
35566
|
-
};
|
|
35567
|
-
var musicXmlToNotes = (placements, partIndex) => placements.filter((p) => p.partIndex === partIndex).map((p) => ({
|
|
35568
|
-
startStep: p.startStep,
|
|
35569
|
-
durationSteps: p.durationSteps,
|
|
35570
|
-
pitchUnits: p.pitch * UNITS_PER_SEMITONE,
|
|
35571
|
-
velocity: 100
|
|
35572
|
-
}));
|
|
35573
|
-
|
|
35574
37697
|
// src/piano-roll.ts
|
|
35575
37698
|
var createPianoRoll = (options, handlers) => {
|
|
35576
37699
|
const {
|
|
@@ -36506,6 +38629,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
36506
38629
|
voiceWorkerUrl,
|
|
36507
38630
|
voicebanks,
|
|
36508
38631
|
worldlineScriptUrl: options.worldlineScriptUrl,
|
|
38632
|
+
ttsBaseUrl: options.ttsBaseUrl,
|
|
36509
38633
|
// ボーカルトラック個別のリバーブセンド(`r`トークン)先。マスタリバーブの
|
|
36510
38634
|
// Convolver 入力へ直接センドする(PreDelayは経由しない。チャンネルストリップの
|
|
36511
38635
|
// reverbSend とは別経路の、音節単位でより細かく制御できるセンド)。
|
|
@@ -37556,6 +39680,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
37556
39680
|
DEFAULT_PLAYBACK_VELOCITY,
|
|
37557
39681
|
DEFAULT_SECTIONS,
|
|
37558
39682
|
DEFAULT_STEPS_PER_BAR,
|
|
39683
|
+
DEFAULT_TTS_BASE_URL,
|
|
37559
39684
|
DEFAULT_UST_LYRIC,
|
|
37560
39685
|
DEFAULT_VELOCITY,
|
|
37561
39686
|
DEFAULT_VOCAL_VOLUME,
|
|
@@ -37599,6 +39724,8 @@ var createDtmStudio = async (options = {}) => {
|
|
|
37599
39724
|
SECTION_ORDER,
|
|
37600
39725
|
SECTION_SPECS,
|
|
37601
39726
|
SEQUENCER_START_DELAY,
|
|
39727
|
+
SPEAK_CLOSE,
|
|
39728
|
+
SPEAK_OPEN,
|
|
37602
39729
|
STOP_MARK,
|
|
37603
39730
|
TIE_MARK,
|
|
37604
39731
|
TIE_MERGE_MAX_SEC,
|
|
@@ -37640,6 +39767,8 @@ var createDtmStudio = async (options = {}) => {
|
|
|
37640
39767
|
createRenderer,
|
|
37641
39768
|
createSequencer,
|
|
37642
39769
|
createSingingVoices,
|
|
39770
|
+
createSpeechPlanner,
|
|
39771
|
+
createSpeechToneView,
|
|
37643
39772
|
createSynth,
|
|
37644
39773
|
createVoiceRegistry,
|
|
37645
39774
|
decodeMml,
|
|
@@ -37668,6 +39797,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
37668
39797
|
getComposeScaleDescription,
|
|
37669
39798
|
getDrumPatternKeys,
|
|
37670
39799
|
getMidiBPM,
|
|
39800
|
+
getSpeechPlanner,
|
|
37671
39801
|
hasSeenTour,
|
|
37672
39802
|
icon,
|
|
37673
39803
|
injectStyles,
|
|
@@ -37682,6 +39812,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
37682
39812
|
looksLikePinyin,
|
|
37683
39813
|
lyricToSyllable,
|
|
37684
39814
|
markTourSeen,
|
|
39815
|
+
medianRecordedPitchHz,
|
|
37685
39816
|
midiNote,
|
|
37686
39817
|
midiToUnits,
|
|
37687
39818
|
mountChordPlayer,
|
|
@@ -37707,10 +39838,12 @@ var createDtmStudio = async (options = {}) => {
|
|
|
37707
39838
|
playNote,
|
|
37708
39839
|
playPlacements,
|
|
37709
39840
|
playSingingMML,
|
|
39841
|
+
prepareSpeechPlan,
|
|
37710
39842
|
programOfInstrumentName,
|
|
37711
39843
|
readGlobalBool,
|
|
37712
39844
|
readGlobalNumber,
|
|
37713
39845
|
readGlobalSetting,
|
|
39846
|
+
readKeptSong,
|
|
37714
39847
|
readMacroSections,
|
|
37715
39848
|
readMacroSetting,
|
|
37716
39849
|
resolveCenter,
|
|
@@ -37725,6 +39858,8 @@ var createDtmStudio = async (options = {}) => {
|
|
|
37725
39858
|
semitonesToUnits,
|
|
37726
39859
|
shiftNotes,
|
|
37727
39860
|
showLoadingOverlay,
|
|
39861
|
+
speechPlanDurationSec,
|
|
39862
|
+
speechPlanLeadingSec,
|
|
37728
39863
|
spelledToUnits,
|
|
37729
39864
|
spellingToUnits,
|
|
37730
39865
|
startTour,
|
|
@@ -37744,6 +39879,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
37744
39879
|
unitsToPitchV1,
|
|
37745
39880
|
vocalVolumeToGain,
|
|
37746
39881
|
writeGlobalSetting,
|
|
39882
|
+
writeKeptSong,
|
|
37747
39883
|
writeMacroSections,
|
|
37748
39884
|
writeMacroSetting
|
|
37749
39885
|
});
|