@onjmin/dtm 1.0.7 → 2.0.2
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 +115 -0
- package/dist/index.d.mts +268 -101
- package/dist/index.d.ts +268 -101
- package/dist/index.js +1384 -719
- package/dist/index.mjs +1355 -701
- package/dist/voice-worker.js +3 -3
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -20,8 +20,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
A4_HZ: () => A4_HZ,
|
|
24
|
+
A4_UNITS: () => A4_UNITS,
|
|
25
|
+
CENTS_PER_UNIT: () => CENTS_PER_UNIT,
|
|
23
26
|
DAW_CSS: () => DAW_CSS,
|
|
24
27
|
DEFAULT_BPM: () => DEFAULT_BPM,
|
|
28
|
+
DEFAULT_EDO: () => DEFAULT_EDO,
|
|
25
29
|
DEFAULT_GATE: () => DEFAULT_GATE,
|
|
26
30
|
DEFAULT_PAN: () => DEFAULT_PAN,
|
|
27
31
|
DEFAULT_PLAYBACK_VELOCITY: () => DEFAULT_PLAYBACK_VELOCITY,
|
|
@@ -31,21 +35,30 @@ __export(index_exports, {
|
|
|
31
35
|
DRUM_FONT: () => DRUM_FONT,
|
|
32
36
|
DRUM_KEYS: () => DRUM_KEYS,
|
|
33
37
|
DRUM_PATTERNS: () => DRUM_PATTERNS,
|
|
38
|
+
EDO31_NAMES: () => EDO31_NAMES,
|
|
34
39
|
GM_INSTRUMENT_NAMES: () => GM_INSTRUMENT_NAMES,
|
|
35
40
|
INSTRUMENT_PRESETS: () => INSTRUMENT_PRESETS,
|
|
41
|
+
KEY_COUNT: () => KEY_COUNT,
|
|
36
42
|
KOE_BASE_URL: () => KOE_BASE_URL,
|
|
37
43
|
KOE_VOICEBANKS: () => KOE_VOICEBANKS,
|
|
38
44
|
KOE_VOICEBANK_LABELS: () => KOE_VOICEBANK_LABELS,
|
|
39
45
|
KOE_VOICEBANK_TERMS: () => KOE_VOICEBANK_TERMS,
|
|
40
46
|
LinkedList: () => LinkedList,
|
|
41
47
|
MAX_VOCAL_VOLUME: () => MAX_VOCAL_VOLUME,
|
|
48
|
+
MICRO_STEP: () => MICRO_STEP,
|
|
42
49
|
MMLCore: () => MMLCore,
|
|
43
50
|
MML_END_MARKER: () => MML_END_MARKER,
|
|
44
51
|
MidiSearchClient: () => MidiSearchClient,
|
|
45
|
-
|
|
52
|
+
PITCH_ENCODING_VERSION: () => PITCH_ENCODING_VERSION,
|
|
53
|
+
PITCH_MAP: () => PITCH_MAP,
|
|
54
|
+
PITCH_RANGE_END: () => PITCH_RANGE_END,
|
|
55
|
+
PITCH_RANGE_START: () => PITCH_RANGE_START,
|
|
46
56
|
PREWARM_NOTES: () => PREWARM_NOTES,
|
|
47
57
|
TRACKS_ADVANCED: () => TRACKS_ADVANCED,
|
|
48
58
|
TRACKS_SIMPLE: () => TRACKS_SIMPLE,
|
|
59
|
+
UNITS_PER_EDO31_DEGREE: () => UNITS_PER_EDO31_DEGREE,
|
|
60
|
+
UNITS_PER_OCTAVE: () => UNITS_PER_OCTAVE,
|
|
61
|
+
UNITS_PER_SEMITONE: () => UNITS_PER_SEMITONE,
|
|
49
62
|
VIBRATO_MIN_SEC: () => VIBRATO_MIN_SEC,
|
|
50
63
|
VOICE_IMAGES: () => VOICE_IMAGES,
|
|
51
64
|
VOICE_IMAGE_KEY: () => VOICE_IMAGE_KEY,
|
|
@@ -55,6 +68,7 @@ __export(index_exports, {
|
|
|
55
68
|
buildChordPlacements: () => buildChordPlacements,
|
|
56
69
|
buildDrumPatternJson: () => buildDrumPatternJson,
|
|
57
70
|
buildNameToKeyMapping: () => buildNameToKeyMapping,
|
|
71
|
+
chromaticStep: () => chromaticStep,
|
|
58
72
|
collectPitchTokens: () => collectPitchTokens,
|
|
59
73
|
concatFloat32: () => concatFloat32,
|
|
60
74
|
createAudioContext: () => createAudioContext,
|
|
@@ -63,19 +77,13 @@ __export(index_exports, {
|
|
|
63
77
|
createKoeVoice: () => createKoeVoice,
|
|
64
78
|
createLyricsConductor: () => createLyricsConductor,
|
|
65
79
|
createPianoRoll: () => createPianoRoll,
|
|
80
|
+
createRenderer: () => createRenderer,
|
|
66
81
|
createSequencer: () => createSequencer,
|
|
67
82
|
createSingingVoices: () => createSingingVoices,
|
|
68
83
|
createSynth: () => createSynth,
|
|
69
84
|
createVoiceRegistry: () => createVoiceRegistry,
|
|
70
85
|
decodeMml: () => decodeMml,
|
|
71
86
|
decomposeToMonophonic: () => decomposeToMonophonic,
|
|
72
|
-
drawGrid: () => drawGrid,
|
|
73
|
-
drawHeader: () => drawHeader,
|
|
74
|
-
drawKeyboard: () => drawKeyboard,
|
|
75
|
-
drawNoteLyrics: () => drawNoteLyrics,
|
|
76
|
-
drawNotes: () => drawNotes,
|
|
77
|
-
drawSelectedNotes: () => drawSelectedNotes,
|
|
78
|
-
drawSelectionRect: () => drawSelectionRect,
|
|
79
87
|
encodeMml: () => encodeMml,
|
|
80
88
|
encodeWavPCM16: () => encodeWavPCM16,
|
|
81
89
|
exportMIDI: () => exportMIDI,
|
|
@@ -83,36 +91,34 @@ __export(index_exports, {
|
|
|
83
91
|
extractMidiDrumPattern: () => extractMidiDrumPattern,
|
|
84
92
|
extractMidiPlacements: () => extractMidiPlacements,
|
|
85
93
|
extractMidiPlacementsByTrack: () => extractMidiPlacementsByTrack,
|
|
94
|
+
fifthToStep: () => fifthToStep,
|
|
95
|
+
fifthToUnits: () => fifthToUnits,
|
|
86
96
|
formatMmlMeta: () => formatMmlMeta,
|
|
87
97
|
freqFromPitch: () => freqFromPitch,
|
|
88
98
|
generateRandomPattern: () => generateRandomPattern,
|
|
89
|
-
getDrawOffset: () => getDrawOffset,
|
|
90
99
|
getDrumPatternKeys: () => getDrumPatternKeys,
|
|
91
|
-
getGridCanvas: () => getGridCanvas,
|
|
92
|
-
getGridContext: () => getGridContext,
|
|
93
|
-
getGridPosition: () => getGridPosition,
|
|
94
|
-
getHeaderCanvas: () => getHeaderCanvas,
|
|
95
100
|
getMidiBPM: () => getMidiBPM,
|
|
96
|
-
getRenderConfig: () => getRenderConfig,
|
|
97
|
-
getXY: () => getXY,
|
|
98
101
|
icon: () => icon,
|
|
99
|
-
init: () => init,
|
|
100
102
|
injectStyles: () => injectStyles,
|
|
101
103
|
isChordHeavyTrack: () => isChordHeavyTrack,
|
|
104
|
+
isNaturalLetter: () => isNaturalLetter,
|
|
102
105
|
isPlausibleMidiTranscription: () => isPlausibleMidiTranscription,
|
|
103
106
|
isValidHttpUrl: () => isValidHttpUrl,
|
|
107
|
+
keyCountFor: () => keyCountFor,
|
|
104
108
|
koeUrl: () => koeUrl,
|
|
109
|
+
midiToUnits: () => midiToUnits,
|
|
105
110
|
mountChordPlayer: () => mountChordPlayer,
|
|
106
111
|
mountDAW: () => mountDAW,
|
|
107
112
|
mountMmlPlayer: () => mountMmlPlayer,
|
|
113
|
+
naturalStep: () => naturalStep,
|
|
108
114
|
normalizeDrumPatterns: () => normalizeDrumPatterns,
|
|
109
115
|
normalizeLyrics: () => normalizeLyrics,
|
|
110
|
-
onClick: () => onClick,
|
|
111
116
|
panToStereo: () => panToStereo,
|
|
112
117
|
parseCustomVocals: () => parseCustomVocals,
|
|
113
118
|
parseLyrics: () => parseLyrics,
|
|
114
119
|
parseMML: () => parseMML,
|
|
115
120
|
parseMmlMeta: () => parseMmlMeta,
|
|
121
|
+
pitchV1ToUnits: () => pitchV1ToUnits,
|
|
116
122
|
playChords: () => playChords,
|
|
117
123
|
playMML: () => playMML,
|
|
118
124
|
playNote: () => playNote,
|
|
@@ -120,14 +126,19 @@ __export(index_exports, {
|
|
|
120
126
|
playSingingMML: () => playSingingMML,
|
|
121
127
|
resolveDrumPattern: () => resolveDrumPattern,
|
|
122
128
|
resolveLoopPoint: () => resolveLoopPoint,
|
|
123
|
-
setBackgroundActive: () => setBackgroundActive,
|
|
124
|
-
setDrawOffset: () => setDrawOffset,
|
|
125
129
|
shiftNotes: () => shiftNotes,
|
|
126
130
|
showLoadingOverlay: () => showLoadingOverlay,
|
|
131
|
+
spellingToUnits: () => spellingToUnits,
|
|
127
132
|
stripCustomVocals: () => stripCustomVocals,
|
|
128
133
|
stripLyrics: () => stripLyrics,
|
|
129
134
|
stripMmlMeta: () => stripMmlMeta,
|
|
130
135
|
transposeNotes: () => transposeNotes,
|
|
136
|
+
unitsPerRow: () => unitsPerRow,
|
|
137
|
+
unitsPerStep: () => unitsPerStep,
|
|
138
|
+
unitsToHz: () => unitsToHz,
|
|
139
|
+
unitsToMidi: () => unitsToMidi,
|
|
140
|
+
unitsToMidiDetune: () => unitsToMidiDetune,
|
|
141
|
+
unitsToPitchV1: () => unitsToPitchV1,
|
|
131
142
|
vocalVolumeToGain: () => vocalVolumeToGain
|
|
132
143
|
});
|
|
133
144
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -282,7 +293,7 @@ async function buildNameToKeyMapping() {
|
|
|
282
293
|
}
|
|
283
294
|
var GM_INSTRUMENT_NAMES = FONT_NAME_SURIKOV.trim().split("\n").map((line) => line.slice(line.indexOf(" ") + 1));
|
|
284
295
|
|
|
285
|
-
// node_modules/.pnpm/@onjmin+chord-parser@1.
|
|
296
|
+
// node_modules/.pnpm/@onjmin+chord-parser@1.1.1/node_modules/@onjmin/chord-parser/dist/index.mjs
|
|
286
297
|
var SHARP_NAMES = [
|
|
287
298
|
"C",
|
|
288
299
|
"C#",
|
|
@@ -313,6 +324,10 @@ var FLAT_NAMES = [
|
|
|
313
324
|
];
|
|
314
325
|
var toPitchClass = (n) => (n % 12 + 12) % 12;
|
|
315
326
|
var noteName = (pc, flat = false) => (flat ? FLAT_NAMES : SHARP_NAMES)[toPitchClass(pc)];
|
|
327
|
+
var LETTER_FIFTH = [0, 2, 4, -1, 1, 3, 5];
|
|
328
|
+
var DEGREE_FIFTH = [0, 2, 4, -1, 1, 3, 5];
|
|
329
|
+
var deg2fifth = (deg) => DEGREE_FIFTH[(deg - 1) % 7];
|
|
330
|
+
var HALF_FIFTH = 7;
|
|
316
331
|
var SyntaxErrorWithPos = class extends Error {
|
|
317
332
|
constructor(input, msg) {
|
|
318
333
|
super(
|
|
@@ -359,6 +374,16 @@ var Input = class _Input {
|
|
|
359
374
|
};
|
|
360
375
|
var Output = class {
|
|
361
376
|
pitch = null;
|
|
377
|
+
/** ルート音の五度圏インデックス。`pitch` と対で持つ。 */
|
|
378
|
+
rootFifth = 0;
|
|
379
|
+
/**
|
|
380
|
+
* 構成音の五度圏オフセット。キーは「ルートからの相対半音 mod 12」。
|
|
381
|
+
*
|
|
382
|
+
* 五度圏インデックスはオクターブ不変なので、分数コードのオクターブシフト
|
|
383
|
+
* (`value` の配列を ±12 する処理)の影響を受けない。そのため構成音の集合とは
|
|
384
|
+
* 別にこの対応表を持っておけば、配列操作側に手を入れずに綴りを保てる。
|
|
385
|
+
*/
|
|
386
|
+
relFifths = /* @__PURE__ */ new Map();
|
|
362
387
|
chord = null;
|
|
363
388
|
isChord = false;
|
|
364
389
|
pending = null;
|
|
@@ -442,9 +467,17 @@ var parseFormula = (input, output = new Output(), nest = 0) => {
|
|
|
442
467
|
case DIVIDE: {
|
|
443
468
|
const o = parseFormula(input, new Output(), nest);
|
|
444
469
|
const v = [...output.value];
|
|
470
|
+
const rebase = (semi, fifth) => noteFifth(
|
|
471
|
+
output,
|
|
472
|
+
semi - output.pitch,
|
|
473
|
+
fifth - output.rootFifth
|
|
474
|
+
);
|
|
445
475
|
if (o.isChord) {
|
|
476
|
+
for (const [rel, f] of o.relFifths)
|
|
477
|
+
rebase(o.pitch + rel, o.rootFifth + f);
|
|
446
478
|
output.value = [...o.value].concat(v);
|
|
447
479
|
} else {
|
|
480
|
+
rebase(o.pitch, o.rootFifth);
|
|
448
481
|
const a = v.sort((x2, y) => x2 - y);
|
|
449
482
|
const pitch = (o.pitch + 3) % 12 - 3;
|
|
450
483
|
if (a[0] < pitch) {
|
|
@@ -491,63 +524,91 @@ var parsePitch = (input, output) => {
|
|
|
491
524
|
const pitch = pitchMatcher.parse(input);
|
|
492
525
|
if (pitch === null) err(input, "Not found pitch");
|
|
493
526
|
output.pitch = pitch;
|
|
527
|
+
output.rootFifth = LETTER_FIFTH[idx2pitch.slice(0, 7).indexOf(pitch)];
|
|
494
528
|
for (let i2 = 0; i2 < 2; i2++) {
|
|
495
529
|
const half = parseHalf(input, true);
|
|
496
530
|
if (half === null) break;
|
|
497
531
|
output.pitch += half;
|
|
532
|
+
output.rootFifth += half * HALF_FIFTH;
|
|
498
533
|
}
|
|
499
534
|
return parseBase(input, output);
|
|
500
535
|
};
|
|
536
|
+
var noteFifth = (out, semi, fifth) => {
|
|
537
|
+
out.relFifths.set((semi % 12 + 12) % 12, fifth);
|
|
538
|
+
};
|
|
501
539
|
var MAJOR = [0, 4, 7];
|
|
502
540
|
var DIM = [0, 3, 6];
|
|
541
|
+
var BASE_FIFTHS = /* @__PURE__ */ new Map();
|
|
542
|
+
var MINOR = [0, 3, 7];
|
|
543
|
+
var AUG = [0, 4, 8];
|
|
544
|
+
var HALF_DIM = [0, 3, 6, 10];
|
|
545
|
+
BASE_FIFTHS.set(MAJOR, [0, 4, 1]);
|
|
546
|
+
BASE_FIFTHS.set(MINOR, [0, -3, 1]);
|
|
547
|
+
BASE_FIFTHS.set(DIM, [0, -3, -6]);
|
|
548
|
+
BASE_FIFTHS.set(AUG, [0, 4, 8]);
|
|
549
|
+
BASE_FIFTHS.set(HALF_DIM, [0, -3, -6, -2]);
|
|
503
550
|
var baseMatcher = new Matcher();
|
|
504
|
-
baseMatcher.set(["m", "min", "Min", "minor", "Minor", "-"],
|
|
551
|
+
baseMatcher.set(["m", "min", "Min", "minor", "Minor", "-"], MINOR);
|
|
505
552
|
baseMatcher.set(["dim", "\u3007"], DIM);
|
|
506
|
-
baseMatcher.set("+",
|
|
507
|
-
baseMatcher.set(["\u03A6", "\u03C6", "\xF8"],
|
|
553
|
+
baseMatcher.set("+", AUG);
|
|
554
|
+
baseMatcher.set(["\u03A6", "\u03C6", "\xF8"], HALF_DIM);
|
|
508
555
|
var parseBase = (input, output) => {
|
|
509
556
|
const isMajMarker = /^maj/i.test(input.str.slice(input.idx));
|
|
510
557
|
const res = isMajMarker ? null : baseMatcher.parse(input);
|
|
511
558
|
if (res !== null) output.isChord = true;
|
|
512
|
-
|
|
559
|
+
const base = res || MAJOR;
|
|
560
|
+
output.chord = new Set(base);
|
|
561
|
+
const baseFifths = BASE_FIFTHS.get(base);
|
|
562
|
+
if (baseFifths)
|
|
563
|
+
for (const [i2, semi] of base.entries())
|
|
564
|
+
noteFifth(output, semi, baseFifths[i2]);
|
|
513
565
|
if (res === DIM) {
|
|
514
566
|
const { num } = input;
|
|
515
567
|
const chord = output.chord;
|
|
516
|
-
if (num !== null)
|
|
568
|
+
if (num !== null) {
|
|
569
|
+
chord.add(deg2pitch(num) - 2);
|
|
570
|
+
noteFifth(output, deg2pitch(num) - 2, deg2fifth(num) - 2 * HALF_FIFTH);
|
|
571
|
+
}
|
|
517
572
|
}
|
|
518
573
|
output.nest = input.nest;
|
|
519
574
|
return parseTerm(input, output);
|
|
520
575
|
};
|
|
521
|
-
var
|
|
522
|
-
chord.add(deg2pitch(
|
|
576
|
+
var put = (chord, out, deg, half = 0) => {
|
|
577
|
+
chord.add(deg2pitch(deg) + half);
|
|
578
|
+
noteFifth(out, deg2pitch(deg) + half, deg2fifth(deg) + half * HALF_FIFTH);
|
|
523
579
|
};
|
|
524
|
-
var
|
|
580
|
+
var add = (chord, n, half, out) => {
|
|
581
|
+
put(chord, out, n, half);
|
|
582
|
+
};
|
|
583
|
+
var aug = (chord, out) => {
|
|
525
584
|
chord.delete(deg2pitch(5));
|
|
526
|
-
chord
|
|
585
|
+
put(chord, out, 5, 1);
|
|
527
586
|
};
|
|
528
|
-
var _7th = (chord, n, _half2, isFlat = false) => {
|
|
587
|
+
var _7th = (chord, n, _half2, out, isFlat = false) => {
|
|
529
588
|
if (n === 5) chord.delete(deg2pitch(3));
|
|
530
|
-
else if (n === 6) chord
|
|
531
|
-
else if (n === 69)
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
if (n >=
|
|
536
|
-
if (n >=
|
|
589
|
+
else if (n === 6) put(chord, out, 6);
|
|
590
|
+
else if (n === 69) {
|
|
591
|
+
put(chord, out, 6);
|
|
592
|
+
put(chord, out, 9);
|
|
593
|
+
} else {
|
|
594
|
+
if (n >= 7) put(chord, out, 7, isFlat ? -1 : 0);
|
|
595
|
+
if (n >= 9) put(chord, out, 9);
|
|
596
|
+
if (n >= 11) put(chord, out, 11);
|
|
597
|
+
if (n >= 13) put(chord, out, 13);
|
|
537
598
|
}
|
|
538
599
|
};
|
|
539
|
-
var _half = (chord, n, half) => {
|
|
600
|
+
var _half = (chord, n, half, out) => {
|
|
540
601
|
chord.delete(deg2pitch(n));
|
|
541
|
-
chord
|
|
602
|
+
put(chord, out, n, half);
|
|
542
603
|
};
|
|
543
604
|
var funcMatcher = new Matcher();
|
|
544
605
|
funcMatcher.set("add", add);
|
|
545
606
|
funcMatcher.set(["omit", "no"], (chord, n, half) => {
|
|
546
607
|
chord.delete(deg2pitch(n) + half);
|
|
547
608
|
});
|
|
548
|
-
funcMatcher.set("sus", (chord, n, half) => {
|
|
609
|
+
funcMatcher.set("sus", (chord, n, half, out) => {
|
|
549
610
|
chord.delete(deg2pitch(3));
|
|
550
|
-
chord
|
|
611
|
+
put(chord, out, n, half);
|
|
551
612
|
});
|
|
552
613
|
funcMatcher.set(
|
|
553
614
|
["M", "maj", "Maj", "major", "Major", "\u25B3", "\u0394"],
|
|
@@ -563,17 +624,18 @@ var parseFunc = (input, output) => {
|
|
|
563
624
|
const half = parseHalf(input);
|
|
564
625
|
const { num } = input;
|
|
565
626
|
if (num === null) {
|
|
566
|
-
if (isAug) aug(chord);
|
|
627
|
+
if (isAug) aug(chord, output);
|
|
567
628
|
else err(input, "Not found number");
|
|
568
629
|
}
|
|
569
630
|
if (half === null) {
|
|
570
|
-
if (input.nest === output.nest)
|
|
571
|
-
|
|
631
|
+
if (input.nest === output.nest)
|
|
632
|
+
_7th(chord, num, 0, output, true);
|
|
633
|
+
else add(chord, num, 0, output);
|
|
572
634
|
} else {
|
|
573
|
-
_half(chord, num, half);
|
|
635
|
+
_half(chord, num, half, output);
|
|
574
636
|
}
|
|
575
637
|
} else if (func === aug) {
|
|
576
|
-
aug(chord);
|
|
638
|
+
aug(chord, output);
|
|
577
639
|
} else {
|
|
578
640
|
output.pending = func;
|
|
579
641
|
}
|
|
@@ -587,7 +649,8 @@ var parsePending = (input, output) => {
|
|
|
587
649
|
pending(
|
|
588
650
|
chord,
|
|
589
651
|
num,
|
|
590
|
-
half === null ? 0 : half
|
|
652
|
+
half === null ? 0 : half,
|
|
653
|
+
output
|
|
591
654
|
);
|
|
592
655
|
output.pending = null;
|
|
593
656
|
return parseTerm(input, output);
|
|
@@ -599,12 +662,29 @@ var parseChord = (symbol) => {
|
|
|
599
662
|
const pitchClasses = [...new Set(notes.map(toPitchClass))].sort(
|
|
600
663
|
(a, b) => a - b
|
|
601
664
|
);
|
|
665
|
+
const rootPitch = output.pitch;
|
|
666
|
+
const rootFifth = output.rootFifth;
|
|
667
|
+
const noteFifths = notes.map((n) => {
|
|
668
|
+
const rel = ((n - rootPitch) % 12 + 12) % 12;
|
|
669
|
+
const f = output.relFifths.get(rel);
|
|
670
|
+
if (f !== void 0) return rootFifth + f;
|
|
671
|
+
let best = 0;
|
|
672
|
+
for (let k = -6; k <= 6; k++) {
|
|
673
|
+
if ((k * 7 % 12 + 12) % 12 === rel) {
|
|
674
|
+
best = k;
|
|
675
|
+
break;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
return rootFifth + best;
|
|
679
|
+
});
|
|
602
680
|
return {
|
|
603
681
|
symbol,
|
|
604
|
-
root: toPitchClass(
|
|
682
|
+
root: toPitchClass(rootPitch),
|
|
605
683
|
notes,
|
|
606
684
|
pitchClasses,
|
|
607
|
-
intervals
|
|
685
|
+
intervals,
|
|
686
|
+
rootFifth,
|
|
687
|
+
noteFifths
|
|
608
688
|
};
|
|
609
689
|
};
|
|
610
690
|
var QUALITY_SOURCE = [
|
|
@@ -1633,10 +1713,72 @@ var createChannelStrip = (ctx, destination, options = {}) => {
|
|
|
1633
1713
|
};
|
|
1634
1714
|
};
|
|
1635
1715
|
|
|
1716
|
+
// src/tuning.ts
|
|
1717
|
+
var UNITS_PER_OCTAVE = 372;
|
|
1718
|
+
var UNITS_PER_SEMITONE = 372 / 12;
|
|
1719
|
+
var UNITS_PER_EDO31_DEGREE = 372 / 31;
|
|
1720
|
+
var CENTS_PER_UNIT = 1200 / UNITS_PER_OCTAVE;
|
|
1721
|
+
var A4_UNITS = 69 * UNITS_PER_SEMITONE;
|
|
1722
|
+
var A4_HZ = 440;
|
|
1723
|
+
var DEFAULT_EDO = 12;
|
|
1724
|
+
var unitsPerStep = (edo) => UNITS_PER_OCTAVE / edo;
|
|
1725
|
+
var midiToUnits = (midi) => midi * UNITS_PER_SEMITONE;
|
|
1726
|
+
var unitsToMidi = (units) => units / UNITS_PER_SEMITONE;
|
|
1727
|
+
var unitsToHz = (units) => A4_HZ * 2 ** ((units - A4_UNITS) / UNITS_PER_OCTAVE);
|
|
1728
|
+
var unitsToMidiDetune = (units) => {
|
|
1729
|
+
const midi = Math.round(units / UNITS_PER_SEMITONE);
|
|
1730
|
+
return {
|
|
1731
|
+
midi,
|
|
1732
|
+
detuneCents: (units - midi * UNITS_PER_SEMITONE) * CENTS_PER_UNIT
|
|
1733
|
+
};
|
|
1734
|
+
};
|
|
1735
|
+
var NATURAL_STEPS = {
|
|
1736
|
+
12: { c: 0, d: 2, e: 4, f: 5, g: 7, a: 9, b: 11 },
|
|
1737
|
+
31: { c: 0, d: 5, e: 10, f: 13, g: 18, a: 23, b: 28 }
|
|
1738
|
+
};
|
|
1739
|
+
var chromaticStep = (edo) => edo === 31 ? 2 : 1;
|
|
1740
|
+
var MICRO_STEP = 1;
|
|
1741
|
+
var isNaturalLetter = (ch) => Object.hasOwn(NATURAL_STEPS[12], ch);
|
|
1742
|
+
var naturalStep = (ch, edo) => NATURAL_STEPS[edo][ch] ?? null;
|
|
1743
|
+
var spellingToUnits = (letter, octave, chromatic, micro, edo) => {
|
|
1744
|
+
const step = naturalStep(letter, edo);
|
|
1745
|
+
if (step === null) return null;
|
|
1746
|
+
const perStep = unitsPerStep(edo);
|
|
1747
|
+
const stepsFromC = step + chromatic * chromaticStep(edo) + micro * MICRO_STEP;
|
|
1748
|
+
return (octave + 1) * UNITS_PER_OCTAVE + stepsFromC * perStep;
|
|
1749
|
+
};
|
|
1750
|
+
var FIFTH_STEPS = { 12: 7, 31: 18 };
|
|
1751
|
+
var fifthToStep = (fifthIndex, edo) => {
|
|
1752
|
+
const n = fifthIndex * FIFTH_STEPS[edo];
|
|
1753
|
+
return (n % edo + edo) % edo;
|
|
1754
|
+
};
|
|
1755
|
+
var fifthToUnits = (fifthIndex, edo) => fifthToStep(fifthIndex, edo) * unitsPerStep(edo);
|
|
1756
|
+
var PITCH_ENCODING_VERSION = 2;
|
|
1757
|
+
var pitchV1ToUnits = (pitch) => pitch * UNITS_PER_SEMITONE;
|
|
1758
|
+
var unitsToPitchV1 = (units) => Math.round(units / UNITS_PER_SEMITONE);
|
|
1759
|
+
|
|
1636
1760
|
// src/chords.ts
|
|
1637
1761
|
var C3 = 48;
|
|
1762
|
+
var MEANTONE_STEP_BY_SEMITONE = [0, 3, 5, 8, 10, 13, 16, 18, 21, 23, 26, 28];
|
|
1763
|
+
var chordToneToUnits = (relSemitone, relFifth, rootSemitone, rootFifth, edo) => {
|
|
1764
|
+
const perStep = UNITS_PER_OCTAVE / edo;
|
|
1765
|
+
const rootOct = Math.floor(rootSemitone / 12);
|
|
1766
|
+
const rootWithin = (rootSemitone % 12 + 12) % 12;
|
|
1767
|
+
const rootStep = edo === 31 ? (fifthToStep(rootFifth, 31) + MEANTONE_STEP_BY_SEMITONE[rootWithin] - fifthToStep(rootFifth, 31) + 31) % 31 : rootWithin;
|
|
1768
|
+
const relWithin12 = (relSemitone % 12 + 12) % 12;
|
|
1769
|
+
const relOct = Math.round((relSemitone - relWithin12) / 12);
|
|
1770
|
+
const relStep = (fifthToStep(relFifth, edo) - fifthToStep(rootFifth, edo) + edo) % edo;
|
|
1771
|
+
return (rootOct + relOct) * UNITS_PER_OCTAVE + (rootStep + relStep) * perStep;
|
|
1772
|
+
};
|
|
1638
1773
|
var buildChordPlacements = (options) => {
|
|
1639
|
-
const {
|
|
1774
|
+
const {
|
|
1775
|
+
chordStr,
|
|
1776
|
+
patternType,
|
|
1777
|
+
rootShift,
|
|
1778
|
+
bpm,
|
|
1779
|
+
stepsPerBar,
|
|
1780
|
+
edo = 12
|
|
1781
|
+
} = options;
|
|
1640
1782
|
const placements = [];
|
|
1641
1783
|
if (!chordStr.trim()) return placements;
|
|
1642
1784
|
const offset = rootShift;
|
|
@@ -1665,8 +1807,20 @@ var buildChordPlacements = (options) => {
|
|
|
1665
1807
|
for (const group of Object.values(chordGroups)) {
|
|
1666
1808
|
for (const chord of group) {
|
|
1667
1809
|
let notes;
|
|
1810
|
+
let toUnits;
|
|
1668
1811
|
try {
|
|
1669
|
-
|
|
1812
|
+
const parsed = parseChord(`${chord.key}${chord.chord}`);
|
|
1813
|
+
notes = [...parsed.notes];
|
|
1814
|
+
const fifthOf = new Map(
|
|
1815
|
+
parsed.notes.map((n, i2) => [n, parsed.noteFifths[i2]])
|
|
1816
|
+
);
|
|
1817
|
+
toUnits = (rel) => chordToneToUnits(
|
|
1818
|
+
rel,
|
|
1819
|
+
fifthOf.get(rel) ?? parsed.rootFifth,
|
|
1820
|
+
C3 + offset,
|
|
1821
|
+
parsed.rootFifth,
|
|
1822
|
+
edo === 31 ? 31 : 12
|
|
1823
|
+
);
|
|
1670
1824
|
} catch {
|
|
1671
1825
|
continue;
|
|
1672
1826
|
}
|
|
@@ -1675,7 +1829,7 @@ var buildChordPlacements = (options) => {
|
|
|
1675
1829
|
for (const noteOffset of notes) {
|
|
1676
1830
|
placements.push({
|
|
1677
1831
|
startStep: chord.whenStep,
|
|
1678
|
-
|
|
1832
|
+
pitchUnits: toUnits(noteOffset),
|
|
1679
1833
|
durationSteps: noteLength,
|
|
1680
1834
|
velocity: 100
|
|
1681
1835
|
});
|
|
@@ -1685,7 +1839,7 @@ var buildChordPlacements = (options) => {
|
|
|
1685
1839
|
notes.forEach((noteOffset, i2) => {
|
|
1686
1840
|
placements.push({
|
|
1687
1841
|
startStep: chord.whenStep + i2 * arpInterval,
|
|
1688
|
-
|
|
1842
|
+
pitchUnits: toUnits(noteOffset),
|
|
1689
1843
|
durationSteps: noteLength - i2 * arpInterval,
|
|
1690
1844
|
velocity: 100
|
|
1691
1845
|
});
|
|
@@ -1695,7 +1849,7 @@ var buildChordPlacements = (options) => {
|
|
|
1695
1849
|
notes.forEach((noteOffset, i2) => {
|
|
1696
1850
|
placements.push({
|
|
1697
1851
|
startStep: chord.whenStep + i2 * arpInterval,
|
|
1698
|
-
|
|
1852
|
+
pitchUnits: toUnits(noteOffset),
|
|
1699
1853
|
durationSteps: Math.max(12, noteLength - i2 * arpInterval),
|
|
1700
1854
|
velocity: 100
|
|
1701
1855
|
});
|
|
@@ -1709,7 +1863,7 @@ var buildChordPlacements = (options) => {
|
|
|
1709
1863
|
for (const noteOffset of notes) {
|
|
1710
1864
|
placements.push({
|
|
1711
1865
|
startStep: syncopatedStep,
|
|
1712
|
-
|
|
1866
|
+
pitchUnits: toUnits(noteOffset),
|
|
1713
1867
|
durationSteps: Math.min(halfBeat, 12),
|
|
1714
1868
|
velocity: 100
|
|
1715
1869
|
});
|
|
@@ -1728,7 +1882,7 @@ var buildChordPlacements = (options) => {
|
|
|
1728
1882
|
for (const noteOffset of notes) {
|
|
1729
1883
|
placements.push({
|
|
1730
1884
|
startStep: noteStart,
|
|
1731
|
-
|
|
1885
|
+
pitchUnits: toUnits(noteOffset),
|
|
1732
1886
|
durationSteps: yatsumeLengthSteps,
|
|
1733
1887
|
velocity: 100
|
|
1734
1888
|
});
|
|
@@ -1740,7 +1894,7 @@ var buildChordPlacements = (options) => {
|
|
|
1740
1894
|
const stepOffset = i2 * Math.floor(stepsPerBar / 4);
|
|
1741
1895
|
placements.push({
|
|
1742
1896
|
startStep: chord.whenStep + stepOffset,
|
|
1743
|
-
|
|
1897
|
+
pitchUnits: toUnits(noteOffset),
|
|
1744
1898
|
durationSteps: Math.max(12, Math.floor(stepsPerBar / 4)),
|
|
1745
1899
|
velocity: 100
|
|
1746
1900
|
});
|
|
@@ -1752,8 +1906,20 @@ var buildChordPlacements = (options) => {
|
|
|
1752
1906
|
const chordNames = chordStr.split(/[\s,]+/).filter((c) => c);
|
|
1753
1907
|
chordNames.forEach((chordName, barIndex) => {
|
|
1754
1908
|
let notes;
|
|
1909
|
+
let toUnits;
|
|
1755
1910
|
try {
|
|
1756
|
-
|
|
1911
|
+
const parsed = parseChord(chordName);
|
|
1912
|
+
notes = [...parsed.notes];
|
|
1913
|
+
const fifthOf = new Map(
|
|
1914
|
+
parsed.notes.map((n, i2) => [n, parsed.noteFifths[i2]])
|
|
1915
|
+
);
|
|
1916
|
+
toUnits = (rel) => chordToneToUnits(
|
|
1917
|
+
rel,
|
|
1918
|
+
fifthOf.get(rel) ?? parsed.rootFifth,
|
|
1919
|
+
C3 + offset,
|
|
1920
|
+
parsed.rootFifth,
|
|
1921
|
+
edo === 31 ? 31 : 12
|
|
1922
|
+
);
|
|
1757
1923
|
} catch {
|
|
1758
1924
|
return;
|
|
1759
1925
|
}
|
|
@@ -1763,7 +1929,7 @@ var buildChordPlacements = (options) => {
|
|
|
1763
1929
|
const stepOffset = i2 * 3;
|
|
1764
1930
|
placements.push({
|
|
1765
1931
|
startStep: startStep + stepOffset,
|
|
1766
|
-
|
|
1932
|
+
pitchUnits: toUnits(noteOffset),
|
|
1767
1933
|
durationSteps: chordLength - stepOffset,
|
|
1768
1934
|
velocity: 100
|
|
1769
1935
|
});
|
|
@@ -2295,6 +2461,11 @@ var DEFAULT_VELOCITY = 100;
|
|
|
2295
2461
|
var DEFAULT_PLAYBACK_VELOCITY = 127;
|
|
2296
2462
|
var DEFAULT_STEPS_PER_BAR = 192;
|
|
2297
2463
|
var MML_END_MARKER = "#end;";
|
|
2464
|
+
var PITCH_RANGE_START = 0;
|
|
2465
|
+
var PITCH_RANGE_END = 3937;
|
|
2466
|
+
var unitsPerRow = (edo) => 372 / edo;
|
|
2467
|
+
var keyCountFor = (edo) => Math.floor((PITCH_RANGE_END - PITCH_RANGE_START) / unitsPerRow(edo)) + 1;
|
|
2468
|
+
var KEY_COUNT = keyCountFor(12);
|
|
2298
2469
|
|
|
2299
2470
|
// src/vibrato.ts
|
|
2300
2471
|
var VIBRATO_MIN_SEC = 0.35;
|
|
@@ -2694,7 +2865,8 @@ var FORMANTS = {
|
|
|
2694
2865
|
// 撥音(ん)は鼻音寄りの低フォルマント
|
|
2695
2866
|
N: [250, 1e3]
|
|
2696
2867
|
};
|
|
2697
|
-
var
|
|
2868
|
+
var unitsToFreq = (units) => 440 * 2 ** ((units - 2139) / 372);
|
|
2869
|
+
var unitsToMidiFloat = (units) => units / 31;
|
|
2698
2870
|
var createKlattVoice = (ctx, destination, reverbBus, delayBus) => {
|
|
2699
2871
|
const active = /* @__PURE__ */ new Set();
|
|
2700
2872
|
const voice = (syllable, e) => {
|
|
@@ -2728,7 +2900,7 @@ var createKlattVoice = (ctx, destination, reverbBus, delayBus) => {
|
|
|
2728
2900
|
}
|
|
2729
2901
|
const osc = ctx.createOscillator();
|
|
2730
2902
|
osc.type = "sawtooth";
|
|
2731
|
-
osc.frequency.value =
|
|
2903
|
+
osc.frequency.value = unitsToFreq(e.pitchUnits);
|
|
2732
2904
|
const makeFormant = (freq, q2, gainScale) => {
|
|
2733
2905
|
const filter = ctx.createBiquadFilter();
|
|
2734
2906
|
filter.type = "bandpass";
|
|
@@ -2980,7 +3152,7 @@ var createLocalBackend = async (options) => {
|
|
|
2980
3152
|
if (!consonantPcm || !vowelPcm) return null;
|
|
2981
3153
|
const spliced = spliceCompositePcm(consonantPcm, vowelPcm, KOE_SAMPLE_RATE);
|
|
2982
3154
|
if (!spliced) return null;
|
|
2983
|
-
const targetHz =
|
|
3155
|
+
const targetHz = unitsToFreq(pitch);
|
|
2984
3156
|
const audio = worldline.renderNote({
|
|
2985
3157
|
pcm: spliced.pcm,
|
|
2986
3158
|
pitch: vibrato ? vibratoPitchCurve(targetHz, spliced.preMs) : targetHz,
|
|
@@ -3009,7 +3181,7 @@ var createLocalBackend = async (options) => {
|
|
|
3009
3181
|
if (!pcm || pcm.length === 0) return null;
|
|
3010
3182
|
const entry = bank.manifest.phonemes[alias];
|
|
3011
3183
|
const lead = leadInFromEntry(entry);
|
|
3012
|
-
const targetHz =
|
|
3184
|
+
const targetHz = unitsToFreq(pitch);
|
|
3013
3185
|
if (worldline) {
|
|
3014
3186
|
const audio = worldline.renderNote({
|
|
3015
3187
|
pcm,
|
|
@@ -3216,7 +3388,7 @@ var createKoeVoice = async (ctx, destination, options) => {
|
|
|
3216
3388
|
backend.pitchTokens,
|
|
3217
3389
|
syllable,
|
|
3218
3390
|
prevVowel,
|
|
3219
|
-
e.
|
|
3391
|
+
unitsToMidiFloat(e.pitchUnits)
|
|
3220
3392
|
);
|
|
3221
3393
|
if (syllable.vowel && syllable.vowel !== "N") prevVowel = syllable.vowel;
|
|
3222
3394
|
if (!alias) return;
|
|
@@ -3224,7 +3396,7 @@ var createKoeVoice = async (ctx, destination, options) => {
|
|
|
3224
3396
|
const peak = Math.max(1e-4, e.volume);
|
|
3225
3397
|
const pan = e.pan ?? 0;
|
|
3226
3398
|
const durationMs = Math.max(60, e.duration * 1e3);
|
|
3227
|
-
void renderInto(alias, e.
|
|
3399
|
+
void renderInto(alias, e.pitchUnits, durationMs).then((r) => {
|
|
3228
3400
|
if (r)
|
|
3229
3401
|
schedule(r, t0, peak, pan, e.reverbSend, e.delaySend, e.destination);
|
|
3230
3402
|
});
|
|
@@ -3236,7 +3408,10 @@ var createKoeVoice = async (ctx, destination, options) => {
|
|
|
3236
3408
|
backend.pitchTokens,
|
|
3237
3409
|
syllable,
|
|
3238
3410
|
prevVowelArg,
|
|
3239
|
-
|
|
3411
|
+
// 多音階バンクのピッチトークン("_G4" 等)は録音の音名なので、
|
|
3412
|
+
// 最寄り選択はMIDIノート番号の尺度で行う。units のまま渡すと常に
|
|
3413
|
+
// 最高音のトークンが選ばれてしまう。
|
|
3414
|
+
unitsToMidiFloat(pitch)
|
|
3240
3415
|
);
|
|
3241
3416
|
if (!alias) return null;
|
|
3242
3417
|
const dMs = Math.max(60, durationMs);
|
|
@@ -3268,13 +3443,14 @@ var STREAM_LOOKAHEAD_SEC = 1.5;
|
|
|
3268
3443
|
var STREAM_POLL_MS = 100;
|
|
3269
3444
|
var OCTAVE_UNISON_PEAK_SCALE = 0.6;
|
|
3270
3445
|
var octaveUnisonOffsets = (mode) => {
|
|
3446
|
+
const OCT = 372;
|
|
3271
3447
|
switch (mode) {
|
|
3272
3448
|
case "down":
|
|
3273
|
-
return [-
|
|
3449
|
+
return [-OCT];
|
|
3274
3450
|
case "up":
|
|
3275
|
-
return [
|
|
3451
|
+
return [OCT];
|
|
3276
3452
|
case "both":
|
|
3277
|
-
return [-
|
|
3453
|
+
return [-OCT, OCT];
|
|
3278
3454
|
default:
|
|
3279
3455
|
return [];
|
|
3280
3456
|
}
|
|
@@ -3484,7 +3660,7 @@ var createSingingVoices = (ctx, destination, options = {}) => {
|
|
|
3484
3660
|
const effPeak = (dest ? peak * masterVolumeScalar : peak) * peakScale;
|
|
3485
3661
|
model(note.syllable, {
|
|
3486
3662
|
trackId: track.id ?? "",
|
|
3487
|
-
pitch,
|
|
3663
|
+
pitchUnits: pitch,
|
|
3488
3664
|
velocity: 100,
|
|
3489
3665
|
volume: effPeak,
|
|
3490
3666
|
when,
|
|
@@ -3550,17 +3726,13 @@ var createVoiceRegistry = (models = {}, fallback = "klatt") => {
|
|
|
3550
3726
|
};
|
|
3551
3727
|
|
|
3552
3728
|
// src/mml-parser.ts
|
|
3553
|
-
var
|
|
3554
|
-
c: 0,
|
|
3555
|
-
d:
|
|
3556
|
-
e: 4,
|
|
3557
|
-
f: 5,
|
|
3558
|
-
g: 7,
|
|
3559
|
-
a: 9,
|
|
3560
|
-
b: 11
|
|
3729
|
+
var NATURAL_STEPS2 = {
|
|
3730
|
+
12: { c: 0, d: 2, e: 4, f: 5, g: 7, a: 9, b: 11 },
|
|
3731
|
+
31: { c: 0, d: 5, e: 10, f: 13, g: 18, a: 23, b: 28 }
|
|
3561
3732
|
};
|
|
3733
|
+
var isNatural = (ch) => Object.hasOwn(NATURAL_STEPS2[12], ch);
|
|
3562
3734
|
var clamp3 = (value, lo, hi) => Math.min(hi, Math.max(lo, value));
|
|
3563
|
-
var META_DIRECTIVE = /#(inst|drum|drumfont|volume|drumvolume|reverb|reverbdecay|reverbpredelay|delay|delaydiv|mastercomp|fadein|fadeout|mode)=([\w-]+)/gi;
|
|
3735
|
+
var META_DIRECTIVE = /#(inst|drum|drumfont|volume|drumvolume|reverb|reverbdecay|reverbpredelay|delay|delaydiv|mastercomp|fadein|fadeout|mode|edo)=([\w-]+)/gi;
|
|
3564
3736
|
var TRACK_INST_DIRECTIVE = /#t(\d+)inst=([^#;\r\n]+)/gi;
|
|
3565
3737
|
var TRACK_COMP_DIRECTIVE = /#t(\d+)comp=(\d+)/gi;
|
|
3566
3738
|
var TRACK_WIDTH_DIRECTIVE = /#t(\d+)width=(\d+)/gi;
|
|
@@ -3610,6 +3782,9 @@ var parseMmlMeta = (mml) => {
|
|
|
3610
3782
|
if (m[2] === "simple" || m[2] === "advanced") {
|
|
3611
3783
|
meta.mode = m[2];
|
|
3612
3784
|
}
|
|
3785
|
+
} else if (key === "edo") {
|
|
3786
|
+
const e = Number.parseInt(m[2], 10);
|
|
3787
|
+
if (e === 12 || e === 31) meta.edo = e;
|
|
3613
3788
|
}
|
|
3614
3789
|
}
|
|
3615
3790
|
for (const m of mml.matchAll(TRACK_INST_DIRECTIVE)) {
|
|
@@ -3712,6 +3887,7 @@ var formatMmlMeta = (meta, space = "") => {
|
|
|
3712
3887
|
if (meta.fadeOut !== void 0 && meta.fadeOut !== 0)
|
|
3713
3888
|
parts.push(`#fadeout=${meta.fadeOut}`);
|
|
3714
3889
|
if (meta.mode) parts.push(`#mode=${meta.mode}`);
|
|
3890
|
+
if (meta.edo !== void 0 && meta.edo !== 12) parts.push(`#edo=${meta.edo}`);
|
|
3715
3891
|
if (meta.trackInstruments) {
|
|
3716
3892
|
for (const [idx, name] of Object.entries(meta.trackInstruments)) {
|
|
3717
3893
|
if (name) parts.push(`#t${idx}inst=${name}`);
|
|
@@ -3786,6 +3962,10 @@ var parseMML = (mml, options = {}) => {
|
|
|
3786
3962
|
const endMarkerBase = MML_END_MARKER.replace(/;+$/, "");
|
|
3787
3963
|
const endRegex = new RegExp(`(?<![cdafgCDAFG])${endMarkerBase}\\b;?`, "gi");
|
|
3788
3964
|
const fullMML = stripLyrics(noMeta).replace(endRegex, "").replace(/[\n\r]+/g, " ").trim();
|
|
3965
|
+
const edo = meta.edo === 31 ? 31 : 12;
|
|
3966
|
+
const naturals = NATURAL_STEPS2[edo];
|
|
3967
|
+
const unitsPerStep2 = 372 / edo;
|
|
3968
|
+
const chromaticStep2 = edo === 31 ? 2 : 1;
|
|
3789
3969
|
const parts = fullMML.split(/(@\d+)/).filter((p) => p.trim().length > 0);
|
|
3790
3970
|
let trackIndex = 0;
|
|
3791
3971
|
let sourceTrackIndex = 0;
|
|
@@ -3833,6 +4013,19 @@ var parseMML = (mml, options = {}) => {
|
|
|
3833
4013
|
type
|
|
3834
4014
|
});
|
|
3835
4015
|
};
|
|
4016
|
+
const readAccidentals = () => {
|
|
4017
|
+
let delta = 0;
|
|
4018
|
+
while (j < body.length) {
|
|
4019
|
+
const a = body[j];
|
|
4020
|
+
if (a === "#") delta += chromaticStep2;
|
|
4021
|
+
else if (a === "-") delta -= chromaticStep2;
|
|
4022
|
+
else if (a === "+") delta += edo === 31 ? 1 : chromaticStep2;
|
|
4023
|
+
else if (a === "_") delta -= edo === 31 ? 1 : chromaticStep2;
|
|
4024
|
+
else break;
|
|
4025
|
+
j++;
|
|
4026
|
+
}
|
|
4027
|
+
return delta;
|
|
4028
|
+
};
|
|
3836
4029
|
const parseLength = () => {
|
|
3837
4030
|
let numStr = "";
|
|
3838
4031
|
while (j < body.length && /\d/.test(body[j])) {
|
|
@@ -3904,17 +4097,11 @@ var parseMML = (mml, options = {}) => {
|
|
|
3904
4097
|
const savedOctave = octave;
|
|
3905
4098
|
while (j < body.length && body[j] !== "]") {
|
|
3906
4099
|
const c = body[j];
|
|
3907
|
-
if (
|
|
3908
|
-
let
|
|
4100
|
+
if (isNatural(c)) {
|
|
4101
|
+
let pitchSteps = naturals[c];
|
|
3909
4102
|
j++;
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
j++;
|
|
3913
|
-
} else if (j < body.length && body[j] === "-") {
|
|
3914
|
-
pitch--;
|
|
3915
|
-
j++;
|
|
3916
|
-
}
|
|
3917
|
-
chordNotes.push((octave + 1) * 12 + pitch);
|
|
4103
|
+
pitchSteps += readAccidentals();
|
|
4104
|
+
chordNotes.push((octave + 1) * 372 + pitchSteps * unitsPerStep2);
|
|
3918
4105
|
} else if (c === ">") {
|
|
3919
4106
|
octave = Math.min(8, octave + 1);
|
|
3920
4107
|
j++;
|
|
@@ -3940,7 +4127,7 @@ var parseMML = (mml, options = {}) => {
|
|
|
3940
4127
|
placements.push({
|
|
3941
4128
|
trackIndex,
|
|
3942
4129
|
startStep: currentStep,
|
|
3943
|
-
|
|
4130
|
+
pitchUnits: p,
|
|
3944
4131
|
durationSteps: Math.max(1, steps),
|
|
3945
4132
|
velocity
|
|
3946
4133
|
});
|
|
@@ -3948,23 +4135,17 @@ var parseMML = (mml, options = {}) => {
|
|
|
3948
4135
|
pushTok("chord", currentStep, Math.max(1, steps), tokStart);
|
|
3949
4136
|
currentStep += steps;
|
|
3950
4137
|
octave = savedOctave;
|
|
3951
|
-
} else if (
|
|
3952
|
-
let
|
|
4138
|
+
} else if (isNatural(ch)) {
|
|
4139
|
+
let pitchSteps = naturals[ch];
|
|
3953
4140
|
j++;
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
j++;
|
|
3957
|
-
} else if (j < body.length && body[j] === "-") {
|
|
3958
|
-
pitch--;
|
|
3959
|
-
j++;
|
|
3960
|
-
}
|
|
3961
|
-
const midiPitch = (octave + 1) * 12 + pitch;
|
|
4141
|
+
pitchSteps += readAccidentals();
|
|
4142
|
+
const midiPitch = (octave + 1) * 372 + pitchSteps * unitsPerStep2;
|
|
3962
4143
|
const steps = parseLength();
|
|
3963
4144
|
recordContributor();
|
|
3964
4145
|
placements.push({
|
|
3965
4146
|
trackIndex,
|
|
3966
4147
|
startStep: currentStep,
|
|
3967
|
-
|
|
4148
|
+
pitchUnits: midiPitch,
|
|
3968
4149
|
durationSteps: Math.max(1, steps),
|
|
3969
4150
|
velocity
|
|
3970
4151
|
});
|
|
@@ -4096,7 +4277,7 @@ var createSequencer = (options) => {
|
|
|
4096
4277
|
maxEndStep = Math.max(maxEndStep, note.startStep + note.durationSteps);
|
|
4097
4278
|
timeline.push({
|
|
4098
4279
|
trackId: track.id,
|
|
4099
|
-
pitch: note.
|
|
4280
|
+
pitch: note.pitchUnits,
|
|
4100
4281
|
volume: track.volume / 100,
|
|
4101
4282
|
velocity: note.velocity ?? DEFAULT_PLAYBACK_VELOCITY,
|
|
4102
4283
|
when,
|
|
@@ -4159,7 +4340,7 @@ var createSequencer = (options) => {
|
|
|
4159
4340
|
const currentVolume = (trackVolumeMap.get(ev.trackId) ?? ev.volume * 100) / 100;
|
|
4160
4341
|
options.onPlayNote({
|
|
4161
4342
|
trackId: ev.trackId,
|
|
4162
|
-
|
|
4343
|
+
pitchUnits: ev.pitch,
|
|
4163
4344
|
velocity: ev.velocity,
|
|
4164
4345
|
volume: currentVolume * velocityVolume,
|
|
4165
4346
|
when: Math.max(0, _when),
|
|
@@ -6479,7 +6660,7 @@ var SONG_DRUM_PATTERNS = {
|
|
|
6479
6660
|
};
|
|
6480
6661
|
|
|
6481
6662
|
// src/synth.ts
|
|
6482
|
-
var freqFromPitch = (
|
|
6663
|
+
var freqFromPitch = (pitchUnits) => unitsToHz(pitchUnits);
|
|
6483
6664
|
var createSynth = (ctx, destination = ctx.destination, tone = {}) => {
|
|
6484
6665
|
const wave = tone.wave ?? "square";
|
|
6485
6666
|
const attack = tone.attack ?? 0;
|
|
@@ -6495,7 +6676,7 @@ var createSynth = (ctx, destination = ctx.destination, tone = {}) => {
|
|
|
6495
6676
|
const osc = ctx.createOscillator();
|
|
6496
6677
|
const gain = ctx.createGain();
|
|
6497
6678
|
osc.type = wave;
|
|
6498
|
-
osc.frequency.value = freqFromPitch(e.
|
|
6679
|
+
osc.frequency.value = freqFromPitch(e.pitchUnits);
|
|
6499
6680
|
const t0 = ctx.currentTime + e.when;
|
|
6500
6681
|
const peak = Math.max(1e-4, 0.06 * e.volume * 1.5 * gainScale);
|
|
6501
6682
|
if (tone.decay) {
|
|
@@ -6600,7 +6781,7 @@ var playPlacements = (placements, options) => {
|
|
|
6600
6781
|
id: id++,
|
|
6601
6782
|
startStep: p.startStep,
|
|
6602
6783
|
durationSteps: p.durationSteps,
|
|
6603
|
-
|
|
6784
|
+
pitchUnits: p.pitchUnits,
|
|
6604
6785
|
velocity: p.velocity
|
|
6605
6786
|
}));
|
|
6606
6787
|
return {
|
|
@@ -6771,7 +6952,7 @@ var playNote = (options) => {
|
|
|
6771
6952
|
const dur = options.duration ?? 1;
|
|
6772
6953
|
synth.playNote({
|
|
6773
6954
|
trackId: "melody",
|
|
6774
|
-
|
|
6955
|
+
pitchUnits: options.pitchUnits,
|
|
6775
6956
|
velocity: 100,
|
|
6776
6957
|
volume: vol / 100,
|
|
6777
6958
|
when: 0,
|
|
@@ -6792,7 +6973,7 @@ var playChords = (chordStr, options = {}) => {
|
|
|
6792
6973
|
// 伴奏トラック
|
|
6793
6974
|
startStep: p.startStep,
|
|
6794
6975
|
durationSteps: p.durationSteps,
|
|
6795
|
-
|
|
6976
|
+
pitchUnits: p.pitchUnits,
|
|
6796
6977
|
velocity: p.velocity
|
|
6797
6978
|
}));
|
|
6798
6979
|
return playPlacements(placements, {
|
|
@@ -7325,38 +7506,66 @@ var DAW_CSS = `
|
|
|
7325
7506
|
flex-basis: 100%;
|
|
7326
7507
|
display: flex;
|
|
7327
7508
|
flex-wrap: nowrap;
|
|
7328
|
-
gap:
|
|
7509
|
+
gap: 4px;
|
|
7510
|
+
align-items: flex-end;
|
|
7329
7511
|
}
|
|
7330
7512
|
.dtm-pill {
|
|
7331
7513
|
--dtm-pill-color: var(--dtm-primary);
|
|
7514
|
+
position: relative;
|
|
7332
7515
|
display: inline-flex;
|
|
7333
7516
|
align-items: center;
|
|
7334
7517
|
justify-content: center;
|
|
7335
7518
|
flex: 1 1 0;
|
|
7336
7519
|
min-width: 0;
|
|
7337
|
-
height:
|
|
7520
|
+
height: 24px;
|
|
7338
7521
|
padding: 0;
|
|
7339
7522
|
border: 2px solid var(--c-black);
|
|
7340
|
-
background: color-mix(in srgb, var(--dtm-pill-color)
|
|
7341
|
-
color: var(--c-white);
|
|
7523
|
+
background: color-mix(in srgb, var(--dtm-pill-color) 22%, #0e121e);
|
|
7524
|
+
color: color-mix(in srgb, var(--c-white) 60%, transparent);
|
|
7342
7525
|
font-family: var(--dtm-font);
|
|
7343
7526
|
font-size: 11px;
|
|
7344
7527
|
font-weight: bold;
|
|
7345
7528
|
cursor: pointer;
|
|
7346
|
-
box-shadow:
|
|
7347
|
-
opacity: 0.
|
|
7529
|
+
box-shadow: 1px 1px 0 var(--c-black);
|
|
7530
|
+
opacity: 0.55;
|
|
7531
|
+
transition: opacity 120ms ease, background 120ms ease, transform 120ms ease;
|
|
7348
7532
|
}
|
|
7349
|
-
|
|
7533
|
+
.dtm-pill:hover:not(.dtm-pill--active) {
|
|
7534
|
+
opacity: 0.85;
|
|
7535
|
+
background: color-mix(in srgb, var(--dtm-pill-color) 45%, #0e121e);
|
|
7536
|
+
color: var(--c-white);
|
|
7537
|
+
}
|
|
7538
|
+
/* \u30A2\u30AF\u30C6\u30A3\u30D6\u9078\u629E = \u9BAE\u660E\u306A\u30C8\u30E9\u30C3\u30AF\u8272 + \u30B4\u30FC\u30EB\u30C9\u67A0 + \u30B0\u30ED\u30FC + \u4E0A\u6607\u7ACB\u4F53\u30BF\u30D6\u8868\u73FE */
|
|
7350
7539
|
.dtm-pill--active {
|
|
7351
7540
|
opacity: 1;
|
|
7541
|
+
height: 28px;
|
|
7542
|
+
background: var(--dtm-pill-color);
|
|
7543
|
+
color: #ffffff;
|
|
7544
|
+
text-shadow: 1px 1px 0 #000, -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000;
|
|
7352
7545
|
border-color: var(--dtm-gold);
|
|
7353
|
-
box-shadow:
|
|
7546
|
+
box-shadow:
|
|
7547
|
+
0 0 0 1.5px var(--dtm-gold),
|
|
7548
|
+
0 0 10px color-mix(in srgb, var(--dtm-pill-color) 70%, transparent),
|
|
7549
|
+
2px 2px 0 var(--c-black);
|
|
7550
|
+
z-index: 1;
|
|
7551
|
+
transform: translateY(-2px);
|
|
7354
7552
|
}
|
|
7355
|
-
.dtm-pill
|
|
7553
|
+
.dtm-pill--active::before {
|
|
7554
|
+
content: '';
|
|
7555
|
+
position: absolute;
|
|
7556
|
+
top: -2px;
|
|
7557
|
+
left: 2px;
|
|
7558
|
+
right: 2px;
|
|
7559
|
+
height: 2px;
|
|
7560
|
+
background: #ffffff;
|
|
7561
|
+
border-radius: 1px;
|
|
7562
|
+
opacity: 0.85;
|
|
7563
|
+
}
|
|
7564
|
+
.dtm-pill:not(.dtm-pill--active):active { transform: translate(1px,1px); box-shadow: none; }
|
|
7356
7565
|
/* \u518D\u751F\u4E2D\u3001\u5B9F\u969B\u306B\u767A\u97F3\u3057\u305F\u77AC\u9593\u3060\u3051\u70B9\u706F\uFF08\u3069\u306E\u30BF\u30D6\u304C\u4ECA\u9CF4\u3063\u3066\u3044\u308B\u304B\u8996\u899A\u7684\u306B\u5206\u304B\u308B\u3088\u3046\u306B\uFF09 */
|
|
7357
7566
|
.dtm-pill--sounding {
|
|
7358
|
-
|
|
7359
|
-
transition:
|
|
7567
|
+
filter: brightness(1.6);
|
|
7568
|
+
transition: filter 30ms linear;
|
|
7360
7569
|
}
|
|
7361
7570
|
|
|
7362
7571
|
/* \u2500\u2500\u2500 \u30D4\u30A2\u30CE\u30ED\u30FC\u30EB\uFF08\u30C8\u30E9\u30C3\u30AB\u30FC\u98A8\uFF09 \u2500\u2500\u2500 */
|
|
@@ -7462,6 +7671,49 @@ var DAW_CSS = `
|
|
|
7462
7671
|
.dtm-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
|
|
7463
7672
|
.dtm-track-body { display: flex; flex-direction: column; gap: 10px; }
|
|
7464
7673
|
|
|
7674
|
+
/* \u2500\u2500\u2500 \u30A2\u30AF\u30C6\u30A3\u30D6\u30C8\u30E9\u30C3\u30AF\u60C5\u5831\u30D0\u30CA\u30FC \u2500\u2500\u2500 */
|
|
7675
|
+
.dtm-active-track-banner {
|
|
7676
|
+
display: flex;
|
|
7677
|
+
align-items: center;
|
|
7678
|
+
gap: 8px;
|
|
7679
|
+
padding: 6px 10px;
|
|
7680
|
+
background: color-mix(in srgb, var(--dtm-track-color, var(--dtm-primary)) 16%, var(--dtm-deep));
|
|
7681
|
+
border: 2px solid var(--c-black);
|
|
7682
|
+
border-left: 5px solid var(--dtm-track-color, var(--dtm-primary));
|
|
7683
|
+
box-shadow: 2px 2px 0 var(--c-black);
|
|
7684
|
+
margin-bottom: 2px;
|
|
7685
|
+
}
|
|
7686
|
+
.dtm-active-track-badge {
|
|
7687
|
+
font-family: var(--dtm-font);
|
|
7688
|
+
font-size: 11px;
|
|
7689
|
+
font-weight: bold;
|
|
7690
|
+
background: var(--dtm-track-color, var(--dtm-primary));
|
|
7691
|
+
color: #ffffff;
|
|
7692
|
+
text-shadow: 1px 1px 0 #000, -1px -1px 0 #000;
|
|
7693
|
+
padding: 2px 6px;
|
|
7694
|
+
border: 1px solid var(--c-black);
|
|
7695
|
+
border-radius: 2px;
|
|
7696
|
+
letter-spacing: .06em;
|
|
7697
|
+
}
|
|
7698
|
+
.dtm-active-track-name {
|
|
7699
|
+
font-family: var(--dtm-font);
|
|
7700
|
+
font-size: 12px;
|
|
7701
|
+
font-weight: bold;
|
|
7702
|
+
color: var(--c-white);
|
|
7703
|
+
flex: 1 1 auto;
|
|
7704
|
+
}
|
|
7705
|
+
.dtm-active-track-pill {
|
|
7706
|
+
font-family: var(--dtm-font);
|
|
7707
|
+
font-size: 9px;
|
|
7708
|
+
font-weight: bold;
|
|
7709
|
+
letter-spacing: .08em;
|
|
7710
|
+
padding: 2px 5px;
|
|
7711
|
+
color: var(--dtm-gold);
|
|
7712
|
+
border: 1px solid var(--dtm-gold);
|
|
7713
|
+
background: rgba(0, 0, 0, 0.45);
|
|
7714
|
+
border-radius: 2px;
|
|
7715
|
+
}
|
|
7716
|
+
|
|
7465
7717
|
/* \u2500\u2500\u2500 MML\u51FA\u529B\uFF08CRT\u30BF\u30FC\u30DF\u30CA\u30EB\uFF09 \u2500\u2500\u2500 */
|
|
7466
7718
|
.dtm-output {
|
|
7467
7719
|
background: var(--c-black);
|
|
@@ -8878,7 +9130,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
8878
9130
|
0
|
|
8879
9131
|
);
|
|
8880
9132
|
const timedNotes = placements.map((p) => ({
|
|
8881
|
-
pitch: p.
|
|
9133
|
+
pitch: unitsToPitchV1(p.pitchUnits),
|
|
8882
9134
|
when: p.startStep * secondsPerStep,
|
|
8883
9135
|
duration: p.durationSteps * secondsPerStep
|
|
8884
9136
|
}));
|
|
@@ -8911,7 +9163,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
8911
9163
|
id: id++,
|
|
8912
9164
|
startStep: p.startStep,
|
|
8913
9165
|
durationSteps: p.durationSteps,
|
|
8914
|
-
|
|
9166
|
+
pitchUnits: p.pitchUnits,
|
|
8915
9167
|
velocity: DEFAULT_VELOCITY
|
|
8916
9168
|
}));
|
|
8917
9169
|
return {
|
|
@@ -9736,7 +9988,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
9736
9988
|
(a, b) => a.startStep - b.startStep
|
|
9737
9989
|
);
|
|
9738
9990
|
const gate = (lt.gate ?? DEFAULT_GATE) / 100;
|
|
9739
|
-
const semis = (lt.octave ?? 0) *
|
|
9991
|
+
const semis = (lt.octave ?? 0) * UNITS_PER_OCTAVE;
|
|
9740
9992
|
const count = Math.min(sorted.length, lt.syllables.length);
|
|
9741
9993
|
const notes = [];
|
|
9742
9994
|
for (let i2 = 0; i2 < count; i2++) {
|
|
@@ -9744,7 +9996,7 @@ var mountMmlPlayer = (target, mml, options = {}) => {
|
|
|
9744
9996
|
if (n.startStep < fromStep) continue;
|
|
9745
9997
|
notes.push({
|
|
9746
9998
|
syllable: lt.syllables[i2],
|
|
9747
|
-
pitch: n.
|
|
9999
|
+
pitch: n.pitchUnits + semis,
|
|
9748
10000
|
startSec: (n.startStep - fromStep) * secondsPerStep,
|
|
9749
10001
|
durationSec: n.durationSteps * secondsPerStep * gate
|
|
9750
10002
|
});
|
|
@@ -10019,6 +10271,7 @@ var SoundFont = class _SoundFont {
|
|
|
10019
10271
|
pitch = 60,
|
|
10020
10272
|
volume = 1,
|
|
10021
10273
|
velocity,
|
|
10274
|
+
detuneCents = 0,
|
|
10022
10275
|
when = 0,
|
|
10023
10276
|
duration = 1
|
|
10024
10277
|
} = {}) {
|
|
@@ -10038,7 +10291,7 @@ var SoundFont = class _SoundFont {
|
|
|
10038
10291
|
Object.assign(src, _param.src);
|
|
10039
10292
|
const humanizeCents = (Math.random() * 2 - 1) * _SoundFont.humanizeDetuneCents;
|
|
10040
10293
|
const humanizeGainMul = 1 + (Math.random() * 2 - 1) * _SoundFont.humanizeGainRatio;
|
|
10041
|
-
src.detune.setValueAtTime(humanizeCents, 0);
|
|
10294
|
+
src.detune.setValueAtTime(humanizeCents + detuneCents, 0);
|
|
10042
10295
|
const effectiveVolume = volume * humanizeGainMul;
|
|
10043
10296
|
const brightness = velocity === void 0 ? 1 : Math.max(0, Math.min(1, velocity / 127));
|
|
10044
10297
|
const filter = brightness >= _SoundFont.brightnessBypassAbove ? void 0 : ctx.createBiquadFilter();
|
|
@@ -11177,7 +11430,7 @@ var mountChordPlayer = (target, chords, options = {}) => {
|
|
|
11177
11430
|
scheduleMetronomeBeats(ctx, cutGain);
|
|
11178
11431
|
};
|
|
11179
11432
|
const wafPlayDynamic = ({
|
|
11180
|
-
|
|
11433
|
+
pitchUnits,
|
|
11181
11434
|
velocity,
|
|
11182
11435
|
volume,
|
|
11183
11436
|
when,
|
|
@@ -11187,10 +11440,12 @@ var mountChordPlayer = (target, chords, options = {}) => {
|
|
|
11187
11440
|
}) => {
|
|
11188
11441
|
const waf = activeGmName ? _wafCache.get(activeGmName) : null;
|
|
11189
11442
|
if (waf) {
|
|
11443
|
+
const { midi, detuneCents } = unitsToMidiDetune(pitchUnits);
|
|
11190
11444
|
waf.play({
|
|
11191
11445
|
ctx,
|
|
11192
11446
|
destination,
|
|
11193
|
-
pitch,
|
|
11447
|
+
pitch: midi,
|
|
11448
|
+
detuneCents,
|
|
11194
11449
|
volume: volume * 0.85,
|
|
11195
11450
|
velocity,
|
|
11196
11451
|
when,
|
|
@@ -11200,7 +11455,7 @@ var mountChordPlayer = (target, chords, options = {}) => {
|
|
|
11200
11455
|
const synth = createSynth(ctx, destination);
|
|
11201
11456
|
synth.playNote({
|
|
11202
11457
|
trackId: "chord",
|
|
11203
|
-
|
|
11458
|
+
pitchUnits,
|
|
11204
11459
|
velocity: 100,
|
|
11205
11460
|
volume,
|
|
11206
11461
|
when,
|
|
@@ -11229,7 +11484,8 @@ var mountChordPlayer = (target, chords, options = {}) => {
|
|
|
11229
11484
|
trackIndex: 3,
|
|
11230
11485
|
startStep: whenStep,
|
|
11231
11486
|
durationSteps,
|
|
11232
|
-
|
|
11487
|
+
// chord-parser の構成音は12平均律の半音。内部表現の units へ写す。
|
|
11488
|
+
pitchUnits: pitchV1ToUnits(C32 + noteOffset),
|
|
11233
11489
|
velocity: 100
|
|
11234
11490
|
});
|
|
11235
11491
|
}
|
|
@@ -11242,7 +11498,7 @@ var mountChordPlayer = (target, chords, options = {}) => {
|
|
|
11242
11498
|
synth: false,
|
|
11243
11499
|
onPlayNote: (e) => {
|
|
11244
11500
|
wafPlayDynamic({
|
|
11245
|
-
|
|
11501
|
+
pitchUnits: e.pitchUnits,
|
|
11246
11502
|
velocity: e.velocity,
|
|
11247
11503
|
volume: e.volume,
|
|
11248
11504
|
when: e.when,
|
|
@@ -11684,6 +11940,14 @@ var buildUI = (target, options) => {
|
|
|
11684
11940
|
<summary>\u5168\u4F53\u30C8\u30E9\u30C3\u30AF\u8A2D\u5B9A</summary>
|
|
11685
11941
|
<div class="dtm-panel-body">
|
|
11686
11942
|
<div data-dtm="preset-select-slot"></div>
|
|
11943
|
+
<div class="dtm-row">
|
|
11944
|
+
<span class="dtm-label">\u97F3\u5F8B</span>
|
|
11945
|
+
<select class="dtm-select dtm-grow" data-dtm="edo-select">
|
|
11946
|
+
<option value="12">12\u5E73\u5747\u5F8B\uFF08\u901A\u5E38\uFF09</option>
|
|
11947
|
+
<option value="31">31\u5E73\u5747\u5F8B\uFF08\u5FAE\u5206\u97F3\uFF09</option>
|
|
11948
|
+
</select>
|
|
11949
|
+
<button class="dtm-infobtn" data-dtm="edo-info" title="\u97F3\u5F8B\u306E\u89E3\u8AAC">${icon("info", 12)}</button>
|
|
11950
|
+
</div>
|
|
11687
11951
|
<div class="dtm-row">
|
|
11688
11952
|
<span class="dtm-label">\u5168\u4F53\u97F3\u91CF</span>
|
|
11689
11953
|
<input type="range" class="dtm-range dtm-grow" data-dtm="master-volume" value="50" min="0" max="100">
|
|
@@ -12016,6 +12280,8 @@ var buildUI = (target, options) => {
|
|
|
12016
12280
|
copyMiniBtn: sel("copy-mini"),
|
|
12017
12281
|
overlay: sel("overlay"),
|
|
12018
12282
|
mmlInfoBtn: sel("mml-info"),
|
|
12283
|
+
edoSelect: sel("edo-select"),
|
|
12284
|
+
edoInfoBtn: sel("edo-info"),
|
|
12019
12285
|
modalOverlay: sel("modal-overlay"),
|
|
12020
12286
|
modalTitle: sel("modal-title"),
|
|
12021
12287
|
modalBody: sel("modal-body"),
|
|
@@ -12136,17 +12402,21 @@ var SCALES = [
|
|
|
12136
12402
|
[0, 2, 4, 7, 9]
|
|
12137
12403
|
// Pentatonic Major
|
|
12138
12404
|
];
|
|
12405
|
+
var MEANTONE_STEP_BY_SEMITONE2 = [0, 3, 5, 8, 10, 13, 16, 18, 21, 23, 26, 28];
|
|
12139
12406
|
var generateRandomPattern = (core, options) => {
|
|
12140
|
-
const { stepsPerBar, startStep, pitchRangeStart } = options;
|
|
12407
|
+
const { stepsPerBar, startStep, pitchRangeStart, edo = 12 } = options;
|
|
12141
12408
|
const numBars = 8;
|
|
12142
12409
|
const noteLength = 24;
|
|
12143
|
-
const
|
|
12410
|
+
const semitoneToStep = (semi) => edo === 31 ? MEANTONE_STEP_BY_SEMITONE2[semi] : semi;
|
|
12411
|
+
const upr = UNITS_PER_OCTAVE / edo;
|
|
12412
|
+
const basePitch = pitchRangeStart + 60 * UNITS_PER_SEMITONE;
|
|
12144
12413
|
const scale = SCALES[Math.floor(Math.random() * SCALES.length)];
|
|
12145
12414
|
const rootOffset = Math.floor(Math.random() * 12);
|
|
12146
12415
|
const availablePitches = [];
|
|
12147
12416
|
for (let i2 = 0; i2 < 12; i2++) {
|
|
12148
12417
|
const noteInOctave = (i2 - rootOffset + 12) % 12;
|
|
12149
|
-
if (scale.includes(noteInOctave))
|
|
12418
|
+
if (scale.includes(noteInOctave))
|
|
12419
|
+
availablePitches.push(basePitch + semitoneToStep(i2) * upr);
|
|
12150
12420
|
}
|
|
12151
12421
|
core.beginBatch();
|
|
12152
12422
|
for (let bar = 0; bar < numBars; bar++) {
|
|
@@ -12181,14 +12451,17 @@ var applyHarmonicFilter = (targetCore, chordCore, options) => {
|
|
|
12181
12451
|
const isNewBar = halfBar % 2 === 0;
|
|
12182
12452
|
const chordHere = chordCore.getNotes().filter((n) => n.startStep >= start && n.startStep < end);
|
|
12183
12453
|
if (chordHere.length > 0) {
|
|
12184
|
-
currentClasses = new Set(
|
|
12454
|
+
currentClasses = new Set(
|
|
12455
|
+
chordHere.map((n) => n.pitchUnits % UNITS_PER_OCTAVE)
|
|
12456
|
+
);
|
|
12185
12457
|
} else if (isNewBar) {
|
|
12186
12458
|
currentClasses = /* @__PURE__ */ new Set();
|
|
12187
12459
|
}
|
|
12188
12460
|
if (currentClasses.size === 0) continue;
|
|
12189
12461
|
const activeHere = targetCore.getNotes().filter((n) => n.startStep >= start && n.startStep < end);
|
|
12190
12462
|
for (const n of activeHere) {
|
|
12191
|
-
if (!currentClasses.has(n.
|
|
12463
|
+
if (!currentClasses.has(n.pitchUnits % UNITS_PER_OCTAVE))
|
|
12464
|
+
targetCore.deleteNoteById(n.id);
|
|
12192
12465
|
}
|
|
12193
12466
|
}
|
|
12194
12467
|
targetCore.endBatch();
|
|
@@ -12210,13 +12483,17 @@ var applyMonophonic = (targetCore, chordCore, options) => {
|
|
|
12210
12483
|
const isNewBar = halfBar % 2 === 0;
|
|
12211
12484
|
const chordHere = chordCore.getNotes().filter((n) => n.startStep >= start && n.startStep < end);
|
|
12212
12485
|
if (chordHere.length > 0) {
|
|
12213
|
-
currentClasses = new Set(
|
|
12486
|
+
currentClasses = new Set(
|
|
12487
|
+
chordHere.map((n) => n.pitchUnits % UNITS_PER_OCTAVE)
|
|
12488
|
+
);
|
|
12214
12489
|
} else if (isNewBar) {
|
|
12215
12490
|
currentClasses = /* @__PURE__ */ new Set();
|
|
12216
12491
|
}
|
|
12217
12492
|
if (currentClasses.size === 0) continue;
|
|
12218
12493
|
const activeHere = targetCore.getNotes().filter((n) => n.startStep >= start && n.startStep < end);
|
|
12219
|
-
const filtered = activeHere.filter(
|
|
12494
|
+
const filtered = activeHere.filter(
|
|
12495
|
+
(n) => currentClasses.has(n.pitchUnits % UNITS_PER_OCTAVE)
|
|
12496
|
+
);
|
|
12220
12497
|
const filteredIds = new Set(filtered.map((n) => n.id));
|
|
12221
12498
|
for (const n of activeHere) {
|
|
12222
12499
|
if (!filteredIds.has(n.id)) targetCore.deleteNoteById(n.id);
|
|
@@ -12228,7 +12505,7 @@ var applyMonophonic = (targetCore, chordCore, options) => {
|
|
|
12228
12505
|
}
|
|
12229
12506
|
for (const notesAtTime of timeMap.values()) {
|
|
12230
12507
|
if (notesAtTime.length > 1) {
|
|
12231
|
-
notesAtTime.sort((a, b) => b.
|
|
12508
|
+
notesAtTime.sort((a, b) => b.pitchUnits - a.pitchUnits);
|
|
12232
12509
|
const [, ...others] = notesAtTime;
|
|
12233
12510
|
for (const on of others) targetCore.deleteNoteById(on.id);
|
|
12234
12511
|
}
|
|
@@ -12244,18 +12521,21 @@ var shiftNotes = (cores, shiftSteps) => {
|
|
|
12244
12521
|
for (const note of notes) {
|
|
12245
12522
|
const newStart = note.startStep + shiftSteps;
|
|
12246
12523
|
if (newStart < 0) core.deleteNoteById(note.id);
|
|
12247
|
-
else core.moveNote(note.id, newStart, note.
|
|
12524
|
+
else core.moveNote(note.id, newStart, note.pitchUnits);
|
|
12248
12525
|
}
|
|
12249
12526
|
core.saveHistory();
|
|
12250
12527
|
}
|
|
12251
12528
|
};
|
|
12252
|
-
var transposeNotes = (cores,
|
|
12253
|
-
if (
|
|
12529
|
+
var transposeNotes = (cores, steps) => {
|
|
12530
|
+
if (steps === 0) return;
|
|
12254
12531
|
for (const core of cores) {
|
|
12255
12532
|
const notes = [...core.getNotes()];
|
|
12256
12533
|
for (const note of notes) {
|
|
12257
|
-
const newPitch = Math.max(
|
|
12258
|
-
|
|
12534
|
+
const newPitch = Math.max(
|
|
12535
|
+
PITCH_RANGE_START,
|
|
12536
|
+
Math.min(PITCH_RANGE_END, note.pitchUnits + steps)
|
|
12537
|
+
);
|
|
12538
|
+
if (newPitch !== note.pitchUnits) {
|
|
12259
12539
|
core.moveNote(note.id, note.startStep, newPitch);
|
|
12260
12540
|
}
|
|
12261
12541
|
}
|
|
@@ -12580,11 +12860,31 @@ var exportMIDI = (options) => {
|
|
|
12580
12860
|
const div = 480;
|
|
12581
12861
|
const tickPerStep = div / STEPS_PER_BEAT3;
|
|
12582
12862
|
const midiTracks = [];
|
|
12583
|
-
|
|
12863
|
+
const NOTE_CHANNELS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15];
|
|
12864
|
+
const BEND_RANGE_SEMITONES = 2;
|
|
12865
|
+
const bendKey = (cents) => Math.round(cents * 100);
|
|
12866
|
+
const bendUse = /* @__PURE__ */ new Map();
|
|
12867
|
+
for (const track of tracks) {
|
|
12868
|
+
for (const n of track.notes) {
|
|
12869
|
+
const { detuneCents } = unitsToMidiDetune(n.pitchUnits);
|
|
12870
|
+
const k = bendKey(detuneCents);
|
|
12871
|
+
bendUse.set(k, (bendUse.get(k) ?? 0) + 1);
|
|
12872
|
+
}
|
|
12873
|
+
}
|
|
12874
|
+
const bendChannel = /* @__PURE__ */ new Map();
|
|
12875
|
+
const ordered = [...bendUse.entries()].sort((a, b) => b[1] - a[1]);
|
|
12876
|
+
for (const [k] of ordered) {
|
|
12877
|
+
if (bendChannel.size >= NOTE_CHANNELS.length) break;
|
|
12878
|
+
bendChannel.set(k, NOTE_CHANNELS[bendChannel.size]);
|
|
12879
|
+
}
|
|
12880
|
+
tracks.forEach((track) => {
|
|
12584
12881
|
if (track.notes.length === 0) return;
|
|
12585
|
-
const channel = ch < 9 ? ch : ch + 1 & 15;
|
|
12586
12882
|
const events = [];
|
|
12587
12883
|
for (const n of track.notes) {
|
|
12884
|
+
const { midi, detuneCents } = unitsToMidiDetune(n.pitchUnits);
|
|
12885
|
+
const k = bendKey(detuneCents);
|
|
12886
|
+
const channel = bendChannel.get(k) ?? bendChannel.get(0) ?? 0;
|
|
12887
|
+
const note = Math.max(0, Math.min(127, midi));
|
|
12588
12888
|
const startTick = Math.round(n.startStep * tickPerStep);
|
|
12589
12889
|
const endTick = Math.round(
|
|
12590
12890
|
(n.startStep + (n.durationSteps || 1)) * tickPerStep
|
|
@@ -12592,12 +12892,25 @@ var exportMIDI = (options) => {
|
|
|
12592
12892
|
const vel = Math.round(
|
|
12593
12893
|
(n.velocity ?? DEFAULT_VELOCITY) * (track.volume ?? 100) / 100
|
|
12594
12894
|
);
|
|
12595
|
-
events.push({ t: startTick, m: [144 | channel,
|
|
12596
|
-
events.push({ t: endTick, m: [144 | channel,
|
|
12895
|
+
events.push({ t: startTick, m: [144 | channel, note, vel] });
|
|
12896
|
+
events.push({ t: endTick, m: [144 | channel, note, 0] });
|
|
12597
12897
|
}
|
|
12598
12898
|
events.sort((a, b) => a.t - b.t);
|
|
12599
12899
|
midiTracks.push(events);
|
|
12600
12900
|
});
|
|
12901
|
+
const bendSetup = [];
|
|
12902
|
+
for (const [k, channel] of bendChannel) {
|
|
12903
|
+
const cents = k / 100;
|
|
12904
|
+
bendSetup.push({ t: 0, m: [176 | channel, 101, 0] });
|
|
12905
|
+
bendSetup.push({ t: 0, m: [176 | channel, 100, 0] });
|
|
12906
|
+
bendSetup.push({ t: 0, m: [176 | channel, 6, BEND_RANGE_SEMITONES] });
|
|
12907
|
+
bendSetup.push({ t: 0, m: [176 | channel, 38, 0] });
|
|
12908
|
+
bendSetup.push({ t: 0, m: [176 | channel, 101, 127] });
|
|
12909
|
+
bendSetup.push({ t: 0, m: [176 | channel, 100, 127] });
|
|
12910
|
+
const raw = 8192 + Math.round(cents / (BEND_RANGE_SEMITONES * 100) * 8192);
|
|
12911
|
+
const v = Math.max(0, Math.min(16383, raw));
|
|
12912
|
+
bendSetup.push({ t: 0, m: [224 | channel, v & 127, v >> 7 & 127] });
|
|
12913
|
+
}
|
|
12601
12914
|
const maxStep = Math.max(
|
|
12602
12915
|
...tracks.filter((t) => t.notes.length > 0).map(
|
|
12603
12916
|
(t) => Math.max(...t.notes.map((n) => n.startStep + n.durationSteps))
|
|
@@ -12634,6 +12947,7 @@ var exportMIDI = (options) => {
|
|
|
12634
12947
|
headerChunks(arr, midiTracks.length + 1, div);
|
|
12635
12948
|
trackChunks(arr, (a) => {
|
|
12636
12949
|
a.push(0, 255, 81, 3, ...to3byte(Math.round(6e7 / bpm)));
|
|
12950
|
+
for (const ev of bendSetup) a.push(0, ...ev.m);
|
|
12637
12951
|
});
|
|
12638
12952
|
for (const events of midiTracks) {
|
|
12639
12953
|
trackChunks(arr, (a) => {
|
|
@@ -12880,380 +13194,8 @@ var LinkedList = class {
|
|
|
12880
13194
|
}
|
|
12881
13195
|
};
|
|
12882
13196
|
|
|
12883
|
-
// src/renderer.ts
|
|
12884
|
-
var g_header_canvas;
|
|
12885
|
-
var g_key_canvas;
|
|
12886
|
-
var g_grid_canvas;
|
|
12887
|
-
var g_header_ctx;
|
|
12888
|
-
var g_key_ctx;
|
|
12889
|
-
var g_grid_ctx;
|
|
12890
|
-
var g_config;
|
|
12891
|
-
var KEYBOARD_WIDTH = 60;
|
|
12892
|
-
var HEADER_HEIGHT = 20;
|
|
12893
|
-
var getRenderConfig = () => g_config;
|
|
12894
|
-
var g_draw_offset_x = 0;
|
|
12895
|
-
var g_draw_offset_y = 0;
|
|
12896
|
-
var g_bg_active = false;
|
|
12897
|
-
var setBackgroundActive = (active) => {
|
|
12898
|
-
g_bg_active = active;
|
|
12899
|
-
};
|
|
12900
|
-
var getDrawOffset = () => ({
|
|
12901
|
-
x: g_draw_offset_x,
|
|
12902
|
-
y: g_draw_offset_y
|
|
12903
|
-
});
|
|
12904
|
-
var getGridCanvas = () => g_grid_canvas;
|
|
12905
|
-
var getGridContext = () => g_grid_ctx;
|
|
12906
|
-
var getHeaderCanvas = () => g_header_canvas;
|
|
12907
|
-
var init = (mountTarget, width = 800, height = 450, config) => {
|
|
12908
|
-
g_config = config;
|
|
12909
|
-
const headerCanvas = document.createElement("canvas");
|
|
12910
|
-
g_header_canvas = headerCanvas;
|
|
12911
|
-
headerCanvas.width = width - KEYBOARD_WIDTH;
|
|
12912
|
-
headerCanvas.height = HEADER_HEIGHT;
|
|
12913
|
-
headerCanvas.style.position = "absolute";
|
|
12914
|
-
headerCanvas.style.left = `${KEYBOARD_WIDTH}px`;
|
|
12915
|
-
headerCanvas.style.top = "0px";
|
|
12916
|
-
const headerCtx = headerCanvas.getContext("2d");
|
|
12917
|
-
if (!headerCtx)
|
|
12918
|
-
throw new Error("Failed to get 2D rendering context for header.");
|
|
12919
|
-
g_header_ctx = headerCtx;
|
|
12920
|
-
const keyCanvas = document.createElement("canvas");
|
|
12921
|
-
g_key_canvas = keyCanvas;
|
|
12922
|
-
keyCanvas.width = KEYBOARD_WIDTH;
|
|
12923
|
-
keyCanvas.height = height - HEADER_HEIGHT;
|
|
12924
|
-
keyCanvas.style.position = "absolute";
|
|
12925
|
-
keyCanvas.style.left = "0px";
|
|
12926
|
-
keyCanvas.style.top = `${HEADER_HEIGHT}px`;
|
|
12927
|
-
const keyCtx = keyCanvas.getContext("2d");
|
|
12928
|
-
if (!keyCtx)
|
|
12929
|
-
throw new Error("Failed to get 2D rendering context for keyboard.");
|
|
12930
|
-
g_key_ctx = keyCtx;
|
|
12931
|
-
const gridCanvas = document.createElement("canvas");
|
|
12932
|
-
g_grid_canvas = gridCanvas;
|
|
12933
|
-
gridCanvas.width = width - KEYBOARD_WIDTH;
|
|
12934
|
-
gridCanvas.height = height - HEADER_HEIGHT;
|
|
12935
|
-
gridCanvas.style.position = "absolute";
|
|
12936
|
-
gridCanvas.style.left = `${KEYBOARD_WIDTH}px`;
|
|
12937
|
-
gridCanvas.style.top = `${HEADER_HEIGHT}px`;
|
|
12938
|
-
gridCanvas.style.touchAction = "none";
|
|
12939
|
-
gridCanvas.style.userSelect = "none";
|
|
12940
|
-
const gridCtx = gridCanvas.getContext("2d", { willReadFrequently: true });
|
|
12941
|
-
if (!gridCtx) throw new Error("Failed to get 2D rendering context for grid.");
|
|
12942
|
-
g_grid_ctx = gridCtx;
|
|
12943
|
-
mountTarget.innerHTML = "";
|
|
12944
|
-
mountTarget.style.position = "relative";
|
|
12945
|
-
mountTarget.style.width = `${width + KEYBOARD_WIDTH}px`;
|
|
12946
|
-
mountTarget.style.height = `${height}px`;
|
|
12947
|
-
mountTarget.append(headerCanvas, keyCanvas, gridCanvas);
|
|
12948
|
-
drawHeaderCorner();
|
|
12949
|
-
};
|
|
12950
|
-
var blackKeyPitches = /* @__PURE__ */ new Set([1, 3, 6, 8, 10]);
|
|
12951
|
-
var KEY_NAMES = [
|
|
12952
|
-
"C",
|
|
12953
|
-
"C#",
|
|
12954
|
-
"D",
|
|
12955
|
-
"D#",
|
|
12956
|
-
"E",
|
|
12957
|
-
"F",
|
|
12958
|
-
"F#",
|
|
12959
|
-
"G",
|
|
12960
|
-
"G#",
|
|
12961
|
-
"A",
|
|
12962
|
-
"A#",
|
|
12963
|
-
"B"
|
|
12964
|
-
];
|
|
12965
|
-
var drawHeaderCorner = () => {
|
|
12966
|
-
const mountTarget = g_key_canvas.parentElement;
|
|
12967
|
-
if (!mountTarget) return;
|
|
12968
|
-
let cornerDiv = mountTarget.querySelector("#header-corner");
|
|
12969
|
-
if (!cornerDiv) {
|
|
12970
|
-
cornerDiv = document.createElement("div");
|
|
12971
|
-
cornerDiv.id = "header-corner";
|
|
12972
|
-
cornerDiv.style.position = "absolute";
|
|
12973
|
-
cornerDiv.style.left = "0px";
|
|
12974
|
-
cornerDiv.style.top = "0px";
|
|
12975
|
-
cornerDiv.style.width = `${KEYBOARD_WIDTH}px`;
|
|
12976
|
-
cornerDiv.style.height = `${HEADER_HEIGHT}px`;
|
|
12977
|
-
cornerDiv.style.backgroundColor = "#0a0f1f";
|
|
12978
|
-
cornerDiv.style.borderRight = "2px solid #29adff";
|
|
12979
|
-
cornerDiv.style.borderBottom = "2px solid #29adff";
|
|
12980
|
-
mountTarget.insertBefore(cornerDiv, g_header_canvas);
|
|
12981
|
-
}
|
|
12982
|
-
};
|
|
12983
|
-
var drawKeyboard = () => {
|
|
12984
|
-
g_key_ctx.clearRect(0, 0, g_key_canvas.width, g_key_canvas.height);
|
|
12985
|
-
const { keyHeight, keyCount, pitchRangeStart } = g_config;
|
|
12986
|
-
const startY = Math.floor(g_draw_offset_y / keyHeight) * keyHeight;
|
|
12987
|
-
const endY = g_draw_offset_y + g_key_canvas.height;
|
|
12988
|
-
const WHITE_KEY = "#ccc8b4";
|
|
12989
|
-
const BLACK_KEY = "#111111";
|
|
12990
|
-
const BK_EDGE = "#383838";
|
|
12991
|
-
const WW_SEP = "#807a6a";
|
|
12992
|
-
const BK_RATIO = 0.62;
|
|
12993
|
-
for (let y = startY; y < endY; y += keyHeight) {
|
|
12994
|
-
const pitchIndex = keyCount - 1 - y / keyHeight;
|
|
12995
|
-
const totalPitch = pitchIndex + pitchRangeStart;
|
|
12996
|
-
const pitchMod12 = totalPitch % 12;
|
|
12997
|
-
const isBlackKey = blackKeyPitches.has(pitchMod12);
|
|
12998
|
-
const octave = Math.floor(totalPitch / 12) - 1;
|
|
12999
|
-
const isC4Range = octave === 4;
|
|
13000
|
-
const screenY = y - g_draw_offset_y;
|
|
13001
|
-
const bkW = Math.floor(KEYBOARD_WIDTH * BK_RATIO);
|
|
13002
|
-
if (isBlackKey) {
|
|
13003
|
-
g_key_ctx.fillStyle = isC4Range ? "#d8d4be" : WHITE_KEY;
|
|
13004
|
-
g_key_ctx.fillRect(0, screenY, KEYBOARD_WIDTH, keyHeight);
|
|
13005
|
-
g_key_ctx.fillStyle = isC4Range ? "#1a1408" : BLACK_KEY;
|
|
13006
|
-
g_key_ctx.fillRect(0, screenY, bkW, keyHeight);
|
|
13007
|
-
g_key_ctx.strokeStyle = BK_EDGE;
|
|
13008
|
-
g_key_ctx.lineWidth = 1;
|
|
13009
|
-
g_key_ctx.beginPath();
|
|
13010
|
-
g_key_ctx.moveTo(bkW, screenY);
|
|
13011
|
-
g_key_ctx.lineTo(bkW, screenY + keyHeight);
|
|
13012
|
-
g_key_ctx.stroke();
|
|
13013
|
-
} else {
|
|
13014
|
-
g_key_ctx.fillStyle = isC4Range ? "#dedad0" : WHITE_KEY;
|
|
13015
|
-
g_key_ctx.fillRect(0, screenY, KEYBOARD_WIDTH, keyHeight);
|
|
13016
|
-
if (pitchMod12 === 5 || pitchMod12 === 0) {
|
|
13017
|
-
g_key_ctx.strokeStyle = WW_SEP;
|
|
13018
|
-
g_key_ctx.lineWidth = 1;
|
|
13019
|
-
g_key_ctx.beginPath();
|
|
13020
|
-
g_key_ctx.moveTo(0, screenY + keyHeight - 0.5);
|
|
13021
|
-
g_key_ctx.lineTo(KEYBOARD_WIDTH, screenY + keyHeight - 0.5);
|
|
13022
|
-
g_key_ctx.stroke();
|
|
13023
|
-
}
|
|
13024
|
-
}
|
|
13025
|
-
if (pitchMod12 === 0) {
|
|
13026
|
-
const octave2 = Math.floor(totalPitch / 12) - 1;
|
|
13027
|
-
g_key_ctx.fillStyle = "#555040";
|
|
13028
|
-
g_key_ctx.font = "10px 'k8x12',monospace";
|
|
13029
|
-
g_key_ctx.textAlign = "right";
|
|
13030
|
-
g_key_ctx.textBaseline = "bottom";
|
|
13031
|
-
g_key_ctx.fillText(
|
|
13032
|
-
`${KEY_NAMES[pitchMod12]}${octave2}`,
|
|
13033
|
-
KEYBOARD_WIDTH - 4,
|
|
13034
|
-
screenY + keyHeight - 2
|
|
13035
|
-
);
|
|
13036
|
-
}
|
|
13037
|
-
}
|
|
13038
|
-
g_key_ctx.beginPath();
|
|
13039
|
-
g_key_ctx.strokeStyle = "#29adff";
|
|
13040
|
-
g_key_ctx.lineWidth = 2;
|
|
13041
|
-
g_key_ctx.moveTo(KEYBOARD_WIDTH, 0);
|
|
13042
|
-
g_key_ctx.lineTo(KEYBOARD_WIDTH, g_key_canvas.height);
|
|
13043
|
-
g_key_ctx.stroke();
|
|
13044
|
-
};
|
|
13045
|
-
var drawHeader = () => {
|
|
13046
|
-
g_header_ctx.clearRect(0, 0, g_header_canvas.width, g_header_canvas.height);
|
|
13047
|
-
const { stepWidth, stepsPerBar } = g_config;
|
|
13048
|
-
g_header_ctx.save();
|
|
13049
|
-
g_header_ctx.translate(-g_draw_offset_x, 0);
|
|
13050
|
-
g_header_ctx.fillStyle = g_bg_active ? "rgba(10,15,31,0.55)" : "#0a0f1f";
|
|
13051
|
-
g_header_ctx.fillRect(
|
|
13052
|
-
g_draw_offset_x,
|
|
13053
|
-
0,
|
|
13054
|
-
g_header_canvas.width,
|
|
13055
|
-
HEADER_HEIGHT
|
|
13056
|
-
);
|
|
13057
|
-
g_header_ctx.strokeStyle = "#3d405b";
|
|
13058
|
-
g_header_ctx.lineWidth = 1;
|
|
13059
|
-
g_header_ctx.font = "11px 'k8x12',monospace";
|
|
13060
|
-
g_header_ctx.fillStyle = "#83769c";
|
|
13061
|
-
const startBar = Math.floor(g_draw_offset_x / (stepsPerBar * stepWidth));
|
|
13062
|
-
const endBar = Math.ceil(
|
|
13063
|
-
(g_draw_offset_x + g_header_canvas.width) / (stepsPerBar * stepWidth)
|
|
13064
|
-
);
|
|
13065
|
-
for (let bar = startBar; bar <= endBar + 1; bar++) {
|
|
13066
|
-
const x2 = bar * stepsPerBar * stepWidth;
|
|
13067
|
-
const screenX = x2;
|
|
13068
|
-
g_header_ctx.beginPath();
|
|
13069
|
-
g_header_ctx.moveTo(screenX, 0);
|
|
13070
|
-
g_header_ctx.lineTo(screenX, HEADER_HEIGHT);
|
|
13071
|
-
g_header_ctx.stroke();
|
|
13072
|
-
if (bar >= 0) {
|
|
13073
|
-
g_header_ctx.textAlign = "left";
|
|
13074
|
-
g_header_ctx.textBaseline = "middle";
|
|
13075
|
-
g_header_ctx.fillText(`${bar + 1}`, screenX + 5, HEADER_HEIGHT / 2);
|
|
13076
|
-
}
|
|
13077
|
-
}
|
|
13078
|
-
g_header_ctx.restore();
|
|
13079
|
-
};
|
|
13080
|
-
var drawGrid = (noteLengthSteps = 1) => {
|
|
13081
|
-
drawKeyboard();
|
|
13082
|
-
drawHeader();
|
|
13083
|
-
g_grid_ctx.clearRect(0, 0, g_grid_canvas.width, g_grid_canvas.height);
|
|
13084
|
-
const { keyHeight, keyCount, stepWidth, stepsPerBar, pitchRangeStart } = g_config;
|
|
13085
|
-
const startY = Math.floor(g_draw_offset_y / keyHeight) * keyHeight;
|
|
13086
|
-
const endY = g_draw_offset_y + g_grid_canvas.height;
|
|
13087
|
-
for (let y = startY; y < endY; y += keyHeight) {
|
|
13088
|
-
const pitchIndex = keyCount - 1 - y / keyHeight;
|
|
13089
|
-
const totalPitch = pitchIndex + pitchRangeStart;
|
|
13090
|
-
const pitchMod12 = totalPitch % 12;
|
|
13091
|
-
const isBlackKey = blackKeyPitches.has(pitchMod12);
|
|
13092
|
-
const isC = pitchMod12 === 0;
|
|
13093
|
-
const octave = Math.floor(totalPitch / 12) - 1;
|
|
13094
|
-
const isC4Range = octave === 4;
|
|
13095
|
-
const screenY = y - g_draw_offset_y;
|
|
13096
|
-
g_grid_ctx.fillStyle = g_bg_active ? isBlackKey ? "rgba(8,11,22,0.55)" : "rgba(17,22,40,0.45)" : isBlackKey ? "#080b16" : "#111628";
|
|
13097
|
-
g_grid_ctx.fillRect(0, screenY, g_grid_canvas.width, keyHeight);
|
|
13098
|
-
if (isC4Range) {
|
|
13099
|
-
g_grid_ctx.fillStyle = "rgba(41,173,255,0.05)";
|
|
13100
|
-
g_grid_ctx.fillRect(0, screenY, g_grid_canvas.width, keyHeight);
|
|
13101
|
-
}
|
|
13102
|
-
g_grid_ctx.beginPath();
|
|
13103
|
-
g_grid_ctx.strokeStyle = isC ? "#3d405b" : "#1a1d30";
|
|
13104
|
-
g_grid_ctx.lineWidth = 1;
|
|
13105
|
-
const lineY = screenY + keyHeight;
|
|
13106
|
-
g_grid_ctx.moveTo(0, lineY);
|
|
13107
|
-
g_grid_ctx.lineTo(g_grid_canvas.width, lineY);
|
|
13108
|
-
g_grid_ctx.stroke();
|
|
13109
|
-
}
|
|
13110
|
-
const gridStep = noteLengthSteps || 48;
|
|
13111
|
-
const startX = Math.floor(g_draw_offset_x / (stepWidth * gridStep)) * stepWidth * gridStep;
|
|
13112
|
-
const endX = g_draw_offset_x + g_grid_canvas.width;
|
|
13113
|
-
const lineStep = stepWidth * gridStep;
|
|
13114
|
-
for (let x2 = startX; x2 <= endX; x2 += lineStep) {
|
|
13115
|
-
const step = x2 / stepWidth;
|
|
13116
|
-
const isBarLine = step % stepsPerBar === 0;
|
|
13117
|
-
const isNoteLine = step % gridStep === 0;
|
|
13118
|
-
const screenX = x2 - g_draw_offset_x;
|
|
13119
|
-
g_grid_ctx.beginPath();
|
|
13120
|
-
g_grid_ctx.strokeStyle = isBarLine ? "#3d405b" : isNoteLine ? "#242840" : "#1a1d30";
|
|
13121
|
-
g_grid_ctx.lineWidth = isBarLine ? 2 : 1;
|
|
13122
|
-
g_grid_ctx.moveTo(screenX, 0);
|
|
13123
|
-
g_grid_ctx.lineTo(screenX, g_grid_canvas.height);
|
|
13124
|
-
g_grid_ctx.stroke();
|
|
13125
|
-
}
|
|
13126
|
-
};
|
|
13127
|
-
var drawNotes = (notes, color = [59, 130, 246, 1]) => {
|
|
13128
|
-
const { keyHeight, stepWidth, keyCount, pitchRangeStart } = g_config;
|
|
13129
|
-
for (const note of notes) {
|
|
13130
|
-
const logicalX = note.startStep * stepWidth;
|
|
13131
|
-
const yIndex = keyCount - 1 - (note.pitch - pitchRangeStart);
|
|
13132
|
-
const logicalY = yIndex * keyHeight;
|
|
13133
|
-
const w = note.durationSteps * stepWidth;
|
|
13134
|
-
const h = keyHeight;
|
|
13135
|
-
const renderX = logicalX - g_draw_offset_x;
|
|
13136
|
-
const renderY = logicalY - g_draw_offset_y;
|
|
13137
|
-
const velocityOpacity = note.velocity !== void 0 ? 0.5 + note.velocity / 127 * 0.5 : 1;
|
|
13138
|
-
const [r, g, b, a] = color;
|
|
13139
|
-
const finalOpacity = a * velocityOpacity;
|
|
13140
|
-
g_grid_ctx.fillStyle = `rgba(${r},${g},${b},${finalOpacity})`;
|
|
13141
|
-
g_grid_ctx.fillRect(renderX + 1, renderY + 1, w - 2, h - 2);
|
|
13142
|
-
}
|
|
13143
|
-
};
|
|
13144
|
-
var drawNoteLyrics = (notes, syllables) => {
|
|
13145
|
-
if (syllables.length === 0) return;
|
|
13146
|
-
const { keyHeight, stepWidth, keyCount, pitchRangeStart } = g_config;
|
|
13147
|
-
if (keyHeight < 10) return;
|
|
13148
|
-
const fontSize = Math.min(12, Math.floor(keyHeight * 0.85));
|
|
13149
|
-
const sorted = [...notes].sort((a, b) => a.startStep - b.startStep);
|
|
13150
|
-
const count = Math.min(sorted.length, syllables.length);
|
|
13151
|
-
g_grid_ctx.save();
|
|
13152
|
-
g_grid_ctx.font = `${fontSize}px 'k8x12',sans-serif`;
|
|
13153
|
-
g_grid_ctx.textAlign = "left";
|
|
13154
|
-
g_grid_ctx.textBaseline = "middle";
|
|
13155
|
-
g_grid_ctx.lineWidth = 3;
|
|
13156
|
-
g_grid_ctx.lineJoin = "round";
|
|
13157
|
-
for (let i2 = 0; i2 < count; i2++) {
|
|
13158
|
-
const kana = syllables[i2];
|
|
13159
|
-
if (!kana) continue;
|
|
13160
|
-
const note = sorted[i2];
|
|
13161
|
-
const renderX = note.startStep * stepWidth - g_draw_offset_x;
|
|
13162
|
-
const renderY = (keyCount - 1 - (note.pitch - pitchRangeStart)) * keyHeight - g_draw_offset_y;
|
|
13163
|
-
const w = note.durationSteps * stepWidth;
|
|
13164
|
-
if (w < 6) continue;
|
|
13165
|
-
if (renderX + w < 0 || renderX > g_grid_canvas.width) continue;
|
|
13166
|
-
if (renderY + keyHeight < 0 || renderY > g_grid_canvas.height) continue;
|
|
13167
|
-
const textX = renderX + 2;
|
|
13168
|
-
const textY = renderY + keyHeight / 2;
|
|
13169
|
-
g_grid_ctx.save();
|
|
13170
|
-
g_grid_ctx.beginPath();
|
|
13171
|
-
g_grid_ctx.rect(renderX + 1, renderY + 1, w - 2, keyHeight - 2);
|
|
13172
|
-
g_grid_ctx.clip();
|
|
13173
|
-
g_grid_ctx.strokeStyle = "rgba(0,0,0,0.85)";
|
|
13174
|
-
g_grid_ctx.strokeText(kana, textX, textY);
|
|
13175
|
-
g_grid_ctx.fillStyle = "#fff1e8";
|
|
13176
|
-
g_grid_ctx.fillText(kana, textX, textY);
|
|
13177
|
-
g_grid_ctx.restore();
|
|
13178
|
-
}
|
|
13179
|
-
g_grid_ctx.restore();
|
|
13180
|
-
};
|
|
13181
|
-
var drawSelectionRect = (rect) => {
|
|
13182
|
-
if (!rect) return;
|
|
13183
|
-
g_grid_ctx.save();
|
|
13184
|
-
g_grid_ctx.strokeStyle = "#ffec27";
|
|
13185
|
-
g_grid_ctx.lineWidth = 2;
|
|
13186
|
-
g_grid_ctx.setLineDash([4, 4]);
|
|
13187
|
-
g_grid_ctx.strokeRect(rect.x, rect.y, rect.width, rect.height);
|
|
13188
|
-
g_grid_ctx.fillStyle = "rgba(255,236,39,0.08)";
|
|
13189
|
-
g_grid_ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
|
|
13190
|
-
g_grid_ctx.restore();
|
|
13191
|
-
};
|
|
13192
|
-
var drawSelectedNotes = (notes, selectedIds, baseColor = [59, 130, 246, 1]) => {
|
|
13193
|
-
const { keyHeight, stepWidth, keyCount, pitchRangeStart } = g_config;
|
|
13194
|
-
for (const note of notes) {
|
|
13195
|
-
if (!selectedIds.has(note.id)) continue;
|
|
13196
|
-
const logicalX = note.startStep * stepWidth;
|
|
13197
|
-
const yIndex = keyCount - 1 - (note.pitch - pitchRangeStart);
|
|
13198
|
-
const logicalY = yIndex * keyHeight;
|
|
13199
|
-
const w = note.durationSteps * stepWidth;
|
|
13200
|
-
const h = keyHeight;
|
|
13201
|
-
const renderX = logicalX - g_draw_offset_x;
|
|
13202
|
-
const renderY = logicalY - g_draw_offset_y;
|
|
13203
|
-
const velocityOpacity = note.velocity !== void 0 ? 0.5 + note.velocity / 127 * 0.5 : 1;
|
|
13204
|
-
const [r, g, b, a] = baseColor;
|
|
13205
|
-
const darkenFactor = 1.3;
|
|
13206
|
-
const darkerR = Math.min(255, r * darkenFactor);
|
|
13207
|
-
const darkerG = Math.min(255, g * darkenFactor);
|
|
13208
|
-
const darkerB = Math.min(255, b * darkenFactor);
|
|
13209
|
-
const finalOpacity = a * velocityOpacity;
|
|
13210
|
-
g_grid_ctx.fillStyle = `rgba(${darkerR},${darkerG},${darkerB},${finalOpacity})`;
|
|
13211
|
-
g_grid_ctx.fillRect(renderX + 1, renderY + 1, w - 2, h - 2);
|
|
13212
|
-
}
|
|
13213
|
-
};
|
|
13214
|
-
var getXY = (e) => {
|
|
13215
|
-
const { clientX, clientY } = e;
|
|
13216
|
-
const rect = g_grid_canvas.getBoundingClientRect();
|
|
13217
|
-
const x2 = Math.floor(clientX - rect.left);
|
|
13218
|
-
const y = Math.floor(clientY - rect.top);
|
|
13219
|
-
return [x2, y, e.buttons];
|
|
13220
|
-
};
|
|
13221
|
-
var getGridPosition = (e) => {
|
|
13222
|
-
const [x2, y] = getXY(e);
|
|
13223
|
-
const { keyCount, pitchRangeStart, keyHeight, stepWidth } = g_config;
|
|
13224
|
-
const step = Math.floor((x2 + g_draw_offset_x) / stepWidth);
|
|
13225
|
-
const absoluteY = y + g_draw_offset_y;
|
|
13226
|
-
const yIndex = Math.floor(absoluteY / keyHeight);
|
|
13227
|
-
const pitch = keyCount - 1 - yIndex + pitchRangeStart;
|
|
13228
|
-
return { step, pitch, x: x2, y };
|
|
13229
|
-
};
|
|
13230
|
-
var onClick = (callback) => {
|
|
13231
|
-
g_grid_canvas.addEventListener(
|
|
13232
|
-
"click",
|
|
13233
|
-
(e) => {
|
|
13234
|
-
const [x2, y] = getXY(e);
|
|
13235
|
-
const { keyCount, pitchRangeStart, keyHeight, stepWidth } = g_config;
|
|
13236
|
-
const step = Math.floor((x2 + g_draw_offset_x) / stepWidth);
|
|
13237
|
-
const absoluteY = y + g_draw_offset_y;
|
|
13238
|
-
const yIndex = Math.floor(absoluteY / keyHeight);
|
|
13239
|
-
const pitch = keyCount - 1 - yIndex + pitchRangeStart;
|
|
13240
|
-
if (pitch >= pitchRangeStart && pitch < pitchRangeStart + keyCount) {
|
|
13241
|
-
requestAnimationFrame(() => callback(step, pitch));
|
|
13242
|
-
}
|
|
13243
|
-
},
|
|
13244
|
-
{ passive: true }
|
|
13245
|
-
);
|
|
13246
|
-
g_grid_canvas.addEventListener("contextmenu", (e) => e.preventDefault());
|
|
13247
|
-
};
|
|
13248
|
-
var setDrawOffset = (x2, y) => {
|
|
13249
|
-
g_draw_offset_x = x2;
|
|
13250
|
-
g_draw_offset_y = y;
|
|
13251
|
-
drawKeyboard();
|
|
13252
|
-
drawHeader();
|
|
13253
|
-
};
|
|
13254
|
-
|
|
13255
13197
|
// src/mml-core.ts
|
|
13256
|
-
var
|
|
13198
|
+
var PITCH_MAP = [
|
|
13257
13199
|
"c",
|
|
13258
13200
|
"c+",
|
|
13259
13201
|
"d",
|
|
@@ -13267,6 +13209,39 @@ var PITCH_MAP2 = [
|
|
|
13267
13209
|
"a+",
|
|
13268
13210
|
"b"
|
|
13269
13211
|
];
|
|
13212
|
+
var EDO31_NAMES = [
|
|
13213
|
+
"c",
|
|
13214
|
+
"c+",
|
|
13215
|
+
"c#",
|
|
13216
|
+
"d-",
|
|
13217
|
+
"d_",
|
|
13218
|
+
"d",
|
|
13219
|
+
"d+",
|
|
13220
|
+
"d#",
|
|
13221
|
+
"e-",
|
|
13222
|
+
"e_",
|
|
13223
|
+
"e",
|
|
13224
|
+
"f-",
|
|
13225
|
+
"e#",
|
|
13226
|
+
"f",
|
|
13227
|
+
"f+",
|
|
13228
|
+
"f#",
|
|
13229
|
+
"g-",
|
|
13230
|
+
"g_",
|
|
13231
|
+
"g",
|
|
13232
|
+
"g+",
|
|
13233
|
+
"g#",
|
|
13234
|
+
"a-",
|
|
13235
|
+
"a_",
|
|
13236
|
+
"a",
|
|
13237
|
+
"a+",
|
|
13238
|
+
"a#",
|
|
13239
|
+
"b-",
|
|
13240
|
+
"b_",
|
|
13241
|
+
"b",
|
|
13242
|
+
"b+",
|
|
13243
|
+
"b#"
|
|
13244
|
+
];
|
|
13270
13245
|
var MMLCore = class _MMLCore {
|
|
13271
13246
|
notes = [];
|
|
13272
13247
|
nextNoteId = 0;
|
|
@@ -13280,9 +13255,12 @@ var MMLCore = class _MMLCore {
|
|
|
13280
13255
|
lastUndoTime = 0;
|
|
13281
13256
|
static UNDO_DEBOUNCE_MS = 100;
|
|
13282
13257
|
toolMode = "pen";
|
|
13283
|
-
|
|
13258
|
+
/** グリッド寸法の供給元(描画器ではなく呼び出し側が持つ設定を読む) */
|
|
13259
|
+
getConfig;
|
|
13260
|
+
constructor(handlers, volume = 80, getConfig) {
|
|
13284
13261
|
this.handlers = handlers;
|
|
13285
13262
|
this.volume = volume;
|
|
13263
|
+
this.getConfig = getConfig;
|
|
13286
13264
|
this.lastHistorySnapshot = JSON.stringify(this.notes);
|
|
13287
13265
|
this.history.add([]);
|
|
13288
13266
|
this.generateAndNotify();
|
|
@@ -13369,14 +13347,14 @@ var MMLCore = class _MMLCore {
|
|
|
13369
13347
|
*/
|
|
13370
13348
|
addNote(step, pitch, options) {
|
|
13371
13349
|
const existingIndex = this.notes.findIndex(
|
|
13372
|
-
(n) => n.startStep === step && n.
|
|
13350
|
+
(n) => n.startStep === step && n.pitchUnits === pitch
|
|
13373
13351
|
);
|
|
13374
13352
|
if (existingIndex === -1) {
|
|
13375
13353
|
const newNote = {
|
|
13376
13354
|
id: this.nextNoteId++,
|
|
13377
13355
|
startStep: step,
|
|
13378
13356
|
durationSteps: options.noteLengthSteps,
|
|
13379
|
-
pitch,
|
|
13357
|
+
pitchUnits: pitch,
|
|
13380
13358
|
velocity: options.velocity ?? DEFAULT_VELOCITY
|
|
13381
13359
|
};
|
|
13382
13360
|
this.notes.push(newNote);
|
|
@@ -13404,9 +13382,11 @@ var MMLCore = class _MMLCore {
|
|
|
13404
13382
|
moveNote(noteId, startStep, pitch) {
|
|
13405
13383
|
const note = this.notes.find((target) => target.id === noteId);
|
|
13406
13384
|
if (!note) return;
|
|
13407
|
-
const totalSteps = this.getMaxStep() +
|
|
13408
|
-
const
|
|
13409
|
-
const
|
|
13385
|
+
const totalSteps = this.getMaxStep() + this.getConfig().stepsPerBar;
|
|
13386
|
+
const cfg = this.getConfig();
|
|
13387
|
+
const upr = cfg.unitsPerRow ?? UNITS_PER_SEMITONE;
|
|
13388
|
+
const pitchRangeStart = cfg.pitchRangeStart;
|
|
13389
|
+
const pitchRangeEnd = pitchRangeStart + (cfg.keyCount - 1) * upr;
|
|
13410
13390
|
const clampedPitch = Math.min(
|
|
13411
13391
|
Math.max(pitch, pitchRangeStart),
|
|
13412
13392
|
pitchRangeEnd
|
|
@@ -13416,7 +13396,7 @@ var MMLCore = class _MMLCore {
|
|
|
13416
13396
|
totalSteps - note.durationSteps
|
|
13417
13397
|
);
|
|
13418
13398
|
note.startStep = clampedStart;
|
|
13419
|
-
note.
|
|
13399
|
+
note.pitchUnits = clampedPitch;
|
|
13420
13400
|
this.notes.sort((a, b) => a.startStep - b.startStep);
|
|
13421
13401
|
this.generateAndNotify();
|
|
13422
13402
|
}
|
|
@@ -13461,7 +13441,7 @@ var MMLCore = class _MMLCore {
|
|
|
13461
13441
|
* ただし、残りステップ(limit)は絶対に超えない。
|
|
13462
13442
|
*/
|
|
13463
13443
|
stepsToMMLDuration(steps, limit) {
|
|
13464
|
-
const config =
|
|
13444
|
+
const config = this.getConfig();
|
|
13465
13445
|
const total = config.stepsPerBar;
|
|
13466
13446
|
const candidates = [
|
|
13467
13447
|
{ dur: "1.", s: total * 1.5 },
|
|
@@ -13497,7 +13477,7 @@ var MMLCore = class _MMLCore {
|
|
|
13497
13477
|
* ギャップに収まる最大の音符を探す(減算アルゴリズム用)
|
|
13498
13478
|
*/
|
|
13499
13479
|
findBestFitDuration(gap) {
|
|
13500
|
-
const config =
|
|
13480
|
+
const config = this.getConfig();
|
|
13501
13481
|
const durations = [1, 2, 4, 8, 12, 16, 24, 32, 48, 64];
|
|
13502
13482
|
for (const d of durations) {
|
|
13503
13483
|
const stepLen = config.stepsPerBar / d;
|
|
@@ -13507,12 +13487,20 @@ var MMLCore = class _MMLCore {
|
|
|
13507
13487
|
}
|
|
13508
13488
|
return { dur: 64, steps: config.stepsPerBar / 64 };
|
|
13509
13489
|
}
|
|
13490
|
+
/** ピッチ(units) → その音律での「オクターブ番号」と「音名」。 */
|
|
13491
|
+
spell(pitchUnits) {
|
|
13492
|
+
const edo = this.getConfig().edo ?? 12;
|
|
13493
|
+
const names = edo === 31 ? EDO31_NAMES : PITCH_MAP;
|
|
13494
|
+
const octave = Math.floor(pitchUnits / 372) - 1;
|
|
13495
|
+
const within = (pitchUnits % 372 + 372) % 372;
|
|
13496
|
+
const step = Math.round(within / (372 / edo));
|
|
13497
|
+
return { octave, name: names[step % edo] };
|
|
13498
|
+
}
|
|
13510
13499
|
/**
|
|
13511
13500
|
* ピッチからオクターブ最適化のある音名を取得
|
|
13512
13501
|
*/
|
|
13513
13502
|
getNoteWithOctave(pitch, lastOctave) {
|
|
13514
|
-
const octave =
|
|
13515
|
-
const name = PITCH_MAP2[pitch % 12];
|
|
13503
|
+
const { octave, name } = this.spell(pitch);
|
|
13516
13504
|
if (lastOctave === -1 || Math.abs(octave - lastOctave) >= 2) {
|
|
13517
13505
|
return { text: `o${octave}${name}`, currentOctave: octave };
|
|
13518
13506
|
}
|
|
@@ -13535,7 +13523,7 @@ var MMLCore = class _MMLCore {
|
|
|
13535
13523
|
* 各音符の長さを忠実に出力する(次の音符がなければ曲末まで伸ばせる)。
|
|
13536
13524
|
*/
|
|
13537
13525
|
generateMML = (volumeOverride) => {
|
|
13538
|
-
const config =
|
|
13526
|
+
const config = this.getConfig();
|
|
13539
13527
|
const vol = volumeOverride ?? this.volume;
|
|
13540
13528
|
const header = `t${this.tempo} v${vol}`;
|
|
13541
13529
|
const segments = [];
|
|
@@ -13576,14 +13564,13 @@ var MMLCore = class _MMLCore {
|
|
|
13576
13564
|
const actualStepGenerated = this.getStepFromDottedMML(durStr);
|
|
13577
13565
|
if (notes.length > 1) {
|
|
13578
13566
|
const noteStrs = notes.map((n) => {
|
|
13579
|
-
const oct =
|
|
13580
|
-
const name = PITCH_MAP2[n.pitch % 12];
|
|
13567
|
+
const { octave: oct, name } = this.spell(n.pitchUnits);
|
|
13581
13568
|
return `o${oct}${name}`;
|
|
13582
13569
|
});
|
|
13583
13570
|
segments.push(`[${noteStrs.join("")}]${durStr}`);
|
|
13584
13571
|
} else {
|
|
13585
13572
|
const { text, currentOctave } = this.getNoteWithOctave(
|
|
13586
|
-
notes[0].
|
|
13573
|
+
notes[0].pitchUnits,
|
|
13587
13574
|
lastOctave
|
|
13588
13575
|
);
|
|
13589
13576
|
segments.push(`${text}${durStr}`);
|
|
@@ -13614,7 +13601,7 @@ var MMLCore = class _MMLCore {
|
|
|
13614
13601
|
* MMLの音長文字列("4", "4.", "12"など)をステップ数に変換する
|
|
13615
13602
|
*/
|
|
13616
13603
|
getStepFromDottedMML(durStr) {
|
|
13617
|
-
const config =
|
|
13604
|
+
const config = this.getConfig();
|
|
13618
13605
|
const total = config.stepsPerBar;
|
|
13619
13606
|
const isDotted = durStr.endsWith(".");
|
|
13620
13607
|
const baseDur = parseInt(isDotted ? durStr.slice(0, -1) : durStr, 10);
|
|
@@ -13624,7 +13611,7 @@ var MMLCore = class _MMLCore {
|
|
|
13624
13611
|
};
|
|
13625
13612
|
var decomposeToMonophonic = (notes) => {
|
|
13626
13613
|
const sorted = [...notes].sort(
|
|
13627
|
-
(a, b) => a.startStep - b.startStep || a.
|
|
13614
|
+
(a, b) => a.startStep - b.startStep || a.pitchUnits - b.pitchUnits
|
|
13628
13615
|
);
|
|
13629
13616
|
const tracks = [];
|
|
13630
13617
|
const trackEnds = [];
|
|
@@ -13659,6 +13646,493 @@ var isChordHeavyTrack = (notes, threshold = 0.6) => {
|
|
|
13659
13646
|
return chordNotes / notes.length >= threshold;
|
|
13660
13647
|
};
|
|
13661
13648
|
|
|
13649
|
+
// src/renderer.ts
|
|
13650
|
+
var KEYBOARD_WIDTH = 60;
|
|
13651
|
+
var HEADER_HEIGHT = 20;
|
|
13652
|
+
var NATURAL_STEPS3 = {
|
|
13653
|
+
12: [0, 2, 4, 5, 7, 9, 11],
|
|
13654
|
+
31: [0, 5, 10, 13, 18, 23, 28]
|
|
13655
|
+
};
|
|
13656
|
+
var keyTier = (step, edo) => {
|
|
13657
|
+
const naturals = NATURAL_STEPS3[edo] ?? NATURAL_STEPS3[12];
|
|
13658
|
+
let best = edo;
|
|
13659
|
+
for (const n of naturals) {
|
|
13660
|
+
const d = Math.abs(step - n);
|
|
13661
|
+
best = Math.min(best, d, edo - d);
|
|
13662
|
+
}
|
|
13663
|
+
if (best === 0) return 0;
|
|
13664
|
+
return best >= (edo === 31 ? 2 : 1) ? 2 : 1;
|
|
13665
|
+
};
|
|
13666
|
+
var KEY_NAMES = [
|
|
13667
|
+
"C",
|
|
13668
|
+
"C#",
|
|
13669
|
+
"D",
|
|
13670
|
+
"D#",
|
|
13671
|
+
"E",
|
|
13672
|
+
"F",
|
|
13673
|
+
"F#",
|
|
13674
|
+
"G",
|
|
13675
|
+
"G#",
|
|
13676
|
+
"A",
|
|
13677
|
+
"A#",
|
|
13678
|
+
"B"
|
|
13679
|
+
];
|
|
13680
|
+
var createRenderer = (mountTarget, width = 800, height = 450, config) => {
|
|
13681
|
+
let g_header_canvas;
|
|
13682
|
+
let g_key_canvas;
|
|
13683
|
+
let g_grid_canvas;
|
|
13684
|
+
let g_header_ctx;
|
|
13685
|
+
let g_key_ctx;
|
|
13686
|
+
let g_grid_ctx;
|
|
13687
|
+
let g_config = config;
|
|
13688
|
+
const getRenderConfig = () => g_config;
|
|
13689
|
+
let g_draw_offset_x = 0;
|
|
13690
|
+
let g_draw_offset_y = 0;
|
|
13691
|
+
let g_bg_active = false;
|
|
13692
|
+
const setBackgroundActive = (active) => {
|
|
13693
|
+
g_bg_active = active;
|
|
13694
|
+
};
|
|
13695
|
+
const getDrawOffset = () => ({
|
|
13696
|
+
x: g_draw_offset_x,
|
|
13697
|
+
y: g_draw_offset_y
|
|
13698
|
+
});
|
|
13699
|
+
const getGridCanvas = () => g_grid_canvas;
|
|
13700
|
+
const getGridContext = () => g_grid_ctx;
|
|
13701
|
+
const getHeaderCanvas = () => g_header_canvas;
|
|
13702
|
+
const setup = () => {
|
|
13703
|
+
g_config = config;
|
|
13704
|
+
const headerCanvas = document.createElement("canvas");
|
|
13705
|
+
g_header_canvas = headerCanvas;
|
|
13706
|
+
headerCanvas.width = width - KEYBOARD_WIDTH;
|
|
13707
|
+
headerCanvas.height = HEADER_HEIGHT;
|
|
13708
|
+
headerCanvas.style.position = "absolute";
|
|
13709
|
+
headerCanvas.style.left = `${KEYBOARD_WIDTH}px`;
|
|
13710
|
+
headerCanvas.style.top = "0px";
|
|
13711
|
+
const headerCtx = headerCanvas.getContext("2d");
|
|
13712
|
+
if (!headerCtx)
|
|
13713
|
+
throw new Error("Failed to get 2D rendering context for header.");
|
|
13714
|
+
g_header_ctx = headerCtx;
|
|
13715
|
+
const keyCanvas = document.createElement("canvas");
|
|
13716
|
+
g_key_canvas = keyCanvas;
|
|
13717
|
+
keyCanvas.width = KEYBOARD_WIDTH;
|
|
13718
|
+
keyCanvas.height = height - HEADER_HEIGHT;
|
|
13719
|
+
keyCanvas.style.position = "absolute";
|
|
13720
|
+
keyCanvas.style.left = "0px";
|
|
13721
|
+
keyCanvas.style.top = `${HEADER_HEIGHT}px`;
|
|
13722
|
+
const keyCtx = keyCanvas.getContext("2d");
|
|
13723
|
+
if (!keyCtx)
|
|
13724
|
+
throw new Error("Failed to get 2D rendering context for keyboard.");
|
|
13725
|
+
g_key_ctx = keyCtx;
|
|
13726
|
+
const gridCanvas = document.createElement("canvas");
|
|
13727
|
+
g_grid_canvas = gridCanvas;
|
|
13728
|
+
gridCanvas.width = width - KEYBOARD_WIDTH;
|
|
13729
|
+
gridCanvas.height = height - HEADER_HEIGHT;
|
|
13730
|
+
gridCanvas.style.position = "absolute";
|
|
13731
|
+
gridCanvas.style.left = `${KEYBOARD_WIDTH}px`;
|
|
13732
|
+
gridCanvas.style.top = `${HEADER_HEIGHT}px`;
|
|
13733
|
+
gridCanvas.style.touchAction = "none";
|
|
13734
|
+
gridCanvas.style.userSelect = "none";
|
|
13735
|
+
const gridCtx = gridCanvas.getContext("2d", { willReadFrequently: true });
|
|
13736
|
+
if (!gridCtx)
|
|
13737
|
+
throw new Error("Failed to get 2D rendering context for grid.");
|
|
13738
|
+
g_grid_ctx = gridCtx;
|
|
13739
|
+
mountTarget.innerHTML = "";
|
|
13740
|
+
mountTarget.style.position = "relative";
|
|
13741
|
+
mountTarget.style.width = `${width + KEYBOARD_WIDTH}px`;
|
|
13742
|
+
mountTarget.style.height = `${height}px`;
|
|
13743
|
+
mountTarget.append(headerCanvas, keyCanvas, gridCanvas);
|
|
13744
|
+
drawHeaderCorner();
|
|
13745
|
+
};
|
|
13746
|
+
const drawHeaderCorner = () => {
|
|
13747
|
+
const mountTarget2 = g_key_canvas.parentElement;
|
|
13748
|
+
if (!mountTarget2) return;
|
|
13749
|
+
let cornerDiv = mountTarget2.querySelector(
|
|
13750
|
+
"#header-corner"
|
|
13751
|
+
);
|
|
13752
|
+
if (!cornerDiv) {
|
|
13753
|
+
cornerDiv = document.createElement("div");
|
|
13754
|
+
cornerDiv.id = "header-corner";
|
|
13755
|
+
cornerDiv.style.position = "absolute";
|
|
13756
|
+
cornerDiv.style.left = "0px";
|
|
13757
|
+
cornerDiv.style.top = "0px";
|
|
13758
|
+
cornerDiv.style.width = `${KEYBOARD_WIDTH}px`;
|
|
13759
|
+
cornerDiv.style.height = `${HEADER_HEIGHT}px`;
|
|
13760
|
+
cornerDiv.style.backgroundColor = "#0a0f1f";
|
|
13761
|
+
cornerDiv.style.borderRight = "2px solid #29adff";
|
|
13762
|
+
cornerDiv.style.borderBottom = "2px solid #29adff";
|
|
13763
|
+
mountTarget2.insertBefore(cornerDiv, g_header_canvas);
|
|
13764
|
+
}
|
|
13765
|
+
};
|
|
13766
|
+
const drawKeyboard = () => {
|
|
13767
|
+
g_key_ctx.clearRect(0, 0, g_key_canvas.width, g_key_canvas.height);
|
|
13768
|
+
const {
|
|
13769
|
+
keyHeight,
|
|
13770
|
+
keyCount,
|
|
13771
|
+
pitchRangeStart,
|
|
13772
|
+
unitsPerRow: upr = UNITS_PER_SEMITONE,
|
|
13773
|
+
edo = 12
|
|
13774
|
+
} = g_config;
|
|
13775
|
+
const startY = Math.floor(g_draw_offset_y / keyHeight) * keyHeight;
|
|
13776
|
+
const endY = g_draw_offset_y + g_key_canvas.height;
|
|
13777
|
+
const WHITE_KEY = "#ccc8b4";
|
|
13778
|
+
const BLACK_KEY = "#111111";
|
|
13779
|
+
const MICRO_KEY = "#4a4a4a";
|
|
13780
|
+
const BK_EDGE = "#383838";
|
|
13781
|
+
const WW_SEP = "#807a6a";
|
|
13782
|
+
const BK_RATIO = 0.62;
|
|
13783
|
+
const MICRO_RATIO = 0.45;
|
|
13784
|
+
for (let y = startY; y < endY; y += keyHeight) {
|
|
13785
|
+
const rowIndex = keyCount - 1 - y / keyHeight;
|
|
13786
|
+
const units = pitchRangeStart + rowIndex * upr;
|
|
13787
|
+
const step = Math.round(
|
|
13788
|
+
(units % UNITS_PER_OCTAVE + UNITS_PER_OCTAVE) % UNITS_PER_OCTAVE / upr % edo
|
|
13789
|
+
);
|
|
13790
|
+
const tier = keyTier(step, edo);
|
|
13791
|
+
const octave = Math.floor(units / UNITS_PER_OCTAVE) - 1;
|
|
13792
|
+
const isC4Range = octave === 4;
|
|
13793
|
+
const screenY = y - g_draw_offset_y;
|
|
13794
|
+
const bkW = Math.floor(
|
|
13795
|
+
KEYBOARD_WIDTH * (tier === 1 ? MICRO_RATIO : BK_RATIO)
|
|
13796
|
+
);
|
|
13797
|
+
if (tier !== 0) {
|
|
13798
|
+
g_key_ctx.fillStyle = isC4Range ? "#d8d4be" : WHITE_KEY;
|
|
13799
|
+
g_key_ctx.fillRect(0, screenY, KEYBOARD_WIDTH, keyHeight);
|
|
13800
|
+
g_key_ctx.fillStyle = tier === 1 ? MICRO_KEY : isC4Range ? "#1a1408" : BLACK_KEY;
|
|
13801
|
+
g_key_ctx.fillRect(0, screenY, bkW, keyHeight);
|
|
13802
|
+
g_key_ctx.strokeStyle = BK_EDGE;
|
|
13803
|
+
g_key_ctx.lineWidth = 1;
|
|
13804
|
+
g_key_ctx.beginPath();
|
|
13805
|
+
g_key_ctx.moveTo(bkW, screenY);
|
|
13806
|
+
g_key_ctx.lineTo(bkW, screenY + keyHeight);
|
|
13807
|
+
g_key_ctx.stroke();
|
|
13808
|
+
} else {
|
|
13809
|
+
g_key_ctx.fillStyle = isC4Range ? "#dedad0" : WHITE_KEY;
|
|
13810
|
+
g_key_ctx.fillRect(0, screenY, KEYBOARD_WIDTH, keyHeight);
|
|
13811
|
+
if (edo === 12 && (step === 5 || step === 0)) {
|
|
13812
|
+
g_key_ctx.strokeStyle = WW_SEP;
|
|
13813
|
+
g_key_ctx.lineWidth = 1;
|
|
13814
|
+
g_key_ctx.beginPath();
|
|
13815
|
+
g_key_ctx.moveTo(0, screenY + keyHeight - 0.5);
|
|
13816
|
+
g_key_ctx.lineTo(KEYBOARD_WIDTH, screenY + keyHeight - 0.5);
|
|
13817
|
+
g_key_ctx.stroke();
|
|
13818
|
+
}
|
|
13819
|
+
}
|
|
13820
|
+
if (step === 0) {
|
|
13821
|
+
g_key_ctx.fillStyle = "#555040";
|
|
13822
|
+
g_key_ctx.font = "10px 'k8x12',monospace";
|
|
13823
|
+
g_key_ctx.textAlign = "right";
|
|
13824
|
+
g_key_ctx.textBaseline = "bottom";
|
|
13825
|
+
g_key_ctx.fillText(
|
|
13826
|
+
`${KEY_NAMES[0]}${octave}`,
|
|
13827
|
+
KEYBOARD_WIDTH - 4,
|
|
13828
|
+
screenY + keyHeight - 2
|
|
13829
|
+
);
|
|
13830
|
+
}
|
|
13831
|
+
}
|
|
13832
|
+
g_key_ctx.beginPath();
|
|
13833
|
+
g_key_ctx.strokeStyle = "#29adff";
|
|
13834
|
+
g_key_ctx.lineWidth = 2;
|
|
13835
|
+
g_key_ctx.moveTo(KEYBOARD_WIDTH, 0);
|
|
13836
|
+
g_key_ctx.lineTo(KEYBOARD_WIDTH, g_key_canvas.height);
|
|
13837
|
+
g_key_ctx.stroke();
|
|
13838
|
+
};
|
|
13839
|
+
const drawHeader = () => {
|
|
13840
|
+
g_header_ctx.clearRect(0, 0, g_header_canvas.width, g_header_canvas.height);
|
|
13841
|
+
const { stepWidth, stepsPerBar } = g_config;
|
|
13842
|
+
g_header_ctx.save();
|
|
13843
|
+
g_header_ctx.translate(-g_draw_offset_x, 0);
|
|
13844
|
+
g_header_ctx.fillStyle = g_bg_active ? "rgba(10,15,31,0.55)" : "#0a0f1f";
|
|
13845
|
+
g_header_ctx.fillRect(
|
|
13846
|
+
g_draw_offset_x,
|
|
13847
|
+
0,
|
|
13848
|
+
g_header_canvas.width,
|
|
13849
|
+
HEADER_HEIGHT
|
|
13850
|
+
);
|
|
13851
|
+
g_header_ctx.strokeStyle = "#3d405b";
|
|
13852
|
+
g_header_ctx.lineWidth = 1;
|
|
13853
|
+
g_header_ctx.font = "11px 'k8x12',monospace";
|
|
13854
|
+
g_header_ctx.fillStyle = "#83769c";
|
|
13855
|
+
const startBar = Math.floor(g_draw_offset_x / (stepsPerBar * stepWidth));
|
|
13856
|
+
const endBar = Math.ceil(
|
|
13857
|
+
(g_draw_offset_x + g_header_canvas.width) / (stepsPerBar * stepWidth)
|
|
13858
|
+
);
|
|
13859
|
+
for (let bar = startBar; bar <= endBar + 1; bar++) {
|
|
13860
|
+
const x2 = bar * stepsPerBar * stepWidth;
|
|
13861
|
+
const screenX = x2;
|
|
13862
|
+
g_header_ctx.beginPath();
|
|
13863
|
+
g_header_ctx.moveTo(screenX, 0);
|
|
13864
|
+
g_header_ctx.lineTo(screenX, HEADER_HEIGHT);
|
|
13865
|
+
g_header_ctx.stroke();
|
|
13866
|
+
if (bar >= 0) {
|
|
13867
|
+
g_header_ctx.textAlign = "left";
|
|
13868
|
+
g_header_ctx.textBaseline = "middle";
|
|
13869
|
+
g_header_ctx.fillText(`${bar + 1}`, screenX + 5, HEADER_HEIGHT / 2);
|
|
13870
|
+
}
|
|
13871
|
+
}
|
|
13872
|
+
g_header_ctx.restore();
|
|
13873
|
+
};
|
|
13874
|
+
const drawGrid = (noteLengthSteps = 1) => {
|
|
13875
|
+
drawKeyboard();
|
|
13876
|
+
drawHeader();
|
|
13877
|
+
g_grid_ctx.clearRect(0, 0, g_grid_canvas.width, g_grid_canvas.height);
|
|
13878
|
+
const {
|
|
13879
|
+
keyHeight,
|
|
13880
|
+
keyCount,
|
|
13881
|
+
stepWidth,
|
|
13882
|
+
stepsPerBar,
|
|
13883
|
+
pitchRangeStart,
|
|
13884
|
+
unitsPerRow: upr = UNITS_PER_SEMITONE,
|
|
13885
|
+
edo = 12
|
|
13886
|
+
} = g_config;
|
|
13887
|
+
const startY = Math.floor(g_draw_offset_y / keyHeight) * keyHeight;
|
|
13888
|
+
const endY = g_draw_offset_y + g_grid_canvas.height;
|
|
13889
|
+
for (let y = startY; y < endY; y += keyHeight) {
|
|
13890
|
+
const rowIndex = keyCount - 1 - y / keyHeight;
|
|
13891
|
+
const units = pitchRangeStart + rowIndex * upr;
|
|
13892
|
+
const step = Math.round(
|
|
13893
|
+
(units % UNITS_PER_OCTAVE + UNITS_PER_OCTAVE) % UNITS_PER_OCTAVE / upr % edo
|
|
13894
|
+
);
|
|
13895
|
+
const tier = keyTier(step, edo);
|
|
13896
|
+
const isC = step === 0;
|
|
13897
|
+
const octave = Math.floor(units / UNITS_PER_OCTAVE) - 1;
|
|
13898
|
+
const isC4Range = octave === 4;
|
|
13899
|
+
const screenY = y - g_draw_offset_y;
|
|
13900
|
+
g_grid_ctx.fillStyle = g_bg_active ? tier === 2 ? "rgba(8,11,22,0.55)" : tier === 1 ? "rgba(12,16,30,0.5)" : "rgba(17,22,40,0.45)" : tier === 2 ? "#080b16" : tier === 1 ? "#0c101d" : "#111628";
|
|
13901
|
+
g_grid_ctx.fillRect(0, screenY, g_grid_canvas.width, keyHeight);
|
|
13902
|
+
if (isC4Range) {
|
|
13903
|
+
g_grid_ctx.fillStyle = "rgba(41,173,255,0.05)";
|
|
13904
|
+
g_grid_ctx.fillRect(0, screenY, g_grid_canvas.width, keyHeight);
|
|
13905
|
+
}
|
|
13906
|
+
g_grid_ctx.beginPath();
|
|
13907
|
+
g_grid_ctx.strokeStyle = isC ? "#3d405b" : "#1a1d30";
|
|
13908
|
+
g_grid_ctx.lineWidth = 1;
|
|
13909
|
+
const lineY = screenY + keyHeight;
|
|
13910
|
+
g_grid_ctx.moveTo(0, lineY);
|
|
13911
|
+
g_grid_ctx.lineTo(g_grid_canvas.width, lineY);
|
|
13912
|
+
g_grid_ctx.stroke();
|
|
13913
|
+
}
|
|
13914
|
+
const gridStep = noteLengthSteps || 48;
|
|
13915
|
+
const startX = Math.floor(g_draw_offset_x / (stepWidth * gridStep)) * stepWidth * gridStep;
|
|
13916
|
+
const endX = g_draw_offset_x + g_grid_canvas.width;
|
|
13917
|
+
const lineStep = stepWidth * gridStep;
|
|
13918
|
+
for (let x2 = startX; x2 <= endX; x2 += lineStep) {
|
|
13919
|
+
const step = x2 / stepWidth;
|
|
13920
|
+
const isBarLine = step % stepsPerBar === 0;
|
|
13921
|
+
const isNoteLine = step % gridStep === 0;
|
|
13922
|
+
const screenX = x2 - g_draw_offset_x;
|
|
13923
|
+
g_grid_ctx.beginPath();
|
|
13924
|
+
g_grid_ctx.strokeStyle = isBarLine ? "#3d405b" : isNoteLine ? "#242840" : "#1a1d30";
|
|
13925
|
+
g_grid_ctx.lineWidth = isBarLine ? 2 : 1;
|
|
13926
|
+
g_grid_ctx.moveTo(screenX, 0);
|
|
13927
|
+
g_grid_ctx.lineTo(screenX, g_grid_canvas.height);
|
|
13928
|
+
g_grid_ctx.stroke();
|
|
13929
|
+
}
|
|
13930
|
+
};
|
|
13931
|
+
const drawNotes = (notes, color = [59, 130, 246, 1], isActive = true) => {
|
|
13932
|
+
const {
|
|
13933
|
+
keyHeight,
|
|
13934
|
+
stepWidth,
|
|
13935
|
+
keyCount,
|
|
13936
|
+
pitchRangeStart,
|
|
13937
|
+
unitsPerRow: upr = UNITS_PER_SEMITONE
|
|
13938
|
+
} = g_config;
|
|
13939
|
+
const canvasWidth = g_grid_canvas.width;
|
|
13940
|
+
const canvasHeight = g_grid_canvas.height;
|
|
13941
|
+
const [r, g, b, a] = color;
|
|
13942
|
+
for (const note of notes) {
|
|
13943
|
+
const logicalX = note.startStep * stepWidth;
|
|
13944
|
+
const yIndex = keyCount - 1 - (note.pitchUnits - pitchRangeStart) / upr;
|
|
13945
|
+
const logicalY = yIndex * keyHeight;
|
|
13946
|
+
const w = note.durationSteps * stepWidth;
|
|
13947
|
+
const h = keyHeight;
|
|
13948
|
+
const renderX = logicalX - g_draw_offset_x;
|
|
13949
|
+
const renderY = logicalY - g_draw_offset_y;
|
|
13950
|
+
if (renderX + w < 0 || renderX > canvasWidth) continue;
|
|
13951
|
+
if (renderY + h < 0 || renderY > canvasHeight) continue;
|
|
13952
|
+
if (isActive) {
|
|
13953
|
+
const velocityOpacity = note.velocity !== void 0 ? 0.6 + note.velocity / 127 * 0.4 : 1;
|
|
13954
|
+
const finalOpacity = a * velocityOpacity;
|
|
13955
|
+
g_grid_ctx.fillStyle = `rgba(${r},${g},${b},${finalOpacity})`;
|
|
13956
|
+
g_grid_ctx.fillRect(renderX + 1, renderY + 1, w - 2, h - 2);
|
|
13957
|
+
if (w >= 4 && h >= 4) {
|
|
13958
|
+
g_grid_ctx.fillStyle = "rgba(255,255,255,0.4)";
|
|
13959
|
+
g_grid_ctx.fillRect(renderX + 1, renderY + 1, w - 2, 1);
|
|
13960
|
+
g_grid_ctx.fillRect(renderX + 1, renderY + 1, 1, h - 2);
|
|
13961
|
+
g_grid_ctx.fillStyle = "rgba(0,0,0,0.45)";
|
|
13962
|
+
g_grid_ctx.fillRect(renderX + 1, renderY + h - 2, w - 2, 1);
|
|
13963
|
+
g_grid_ctx.fillRect(renderX + w - 2, renderY + 1, 1, h - 2);
|
|
13964
|
+
}
|
|
13965
|
+
} else {
|
|
13966
|
+
const velocityOpacity = note.velocity !== void 0 ? 0.7 + note.velocity / 127 * 0.3 : 1;
|
|
13967
|
+
const finalOpacity = Math.min(0.25, a * 0.22) * velocityOpacity;
|
|
13968
|
+
const gray = 0.299 * r + 0.587 * g + 0.114 * b;
|
|
13969
|
+
const ghostR = Math.round(r * 0.6 + gray * 0.4);
|
|
13970
|
+
const ghostG = Math.round(g * 0.6 + gray * 0.4);
|
|
13971
|
+
const ghostB = Math.round(b * 0.6 + gray * 0.4);
|
|
13972
|
+
g_grid_ctx.fillStyle = `rgba(${ghostR},${ghostG},${ghostB},${finalOpacity})`;
|
|
13973
|
+
g_grid_ctx.fillRect(renderX + 1, renderY + 1, w - 2, h - 2);
|
|
13974
|
+
}
|
|
13975
|
+
}
|
|
13976
|
+
};
|
|
13977
|
+
const drawNoteLyrics = (notes, syllables) => {
|
|
13978
|
+
if (syllables.length === 0) return;
|
|
13979
|
+
const {
|
|
13980
|
+
keyHeight,
|
|
13981
|
+
stepWidth,
|
|
13982
|
+
keyCount,
|
|
13983
|
+
pitchRangeStart,
|
|
13984
|
+
unitsPerRow: upr = UNITS_PER_SEMITONE
|
|
13985
|
+
} = g_config;
|
|
13986
|
+
if (keyHeight < 7) return;
|
|
13987
|
+
const fontSize = Math.min(12, Math.floor(keyHeight * 0.85));
|
|
13988
|
+
const sorted = [...notes].sort((a, b) => a.startStep - b.startStep);
|
|
13989
|
+
const count = Math.min(sorted.length, syllables.length);
|
|
13990
|
+
g_grid_ctx.save();
|
|
13991
|
+
g_grid_ctx.font = `${fontSize}px 'k8x12',sans-serif`;
|
|
13992
|
+
g_grid_ctx.textAlign = "left";
|
|
13993
|
+
g_grid_ctx.textBaseline = "middle";
|
|
13994
|
+
g_grid_ctx.lineWidth = 3;
|
|
13995
|
+
g_grid_ctx.lineJoin = "round";
|
|
13996
|
+
for (let i2 = 0; i2 < count; i2++) {
|
|
13997
|
+
const kana = syllables[i2];
|
|
13998
|
+
if (!kana) continue;
|
|
13999
|
+
const note = sorted[i2];
|
|
14000
|
+
const renderX = note.startStep * stepWidth - g_draw_offset_x;
|
|
14001
|
+
const renderY = (keyCount - 1 - (note.pitchUnits - pitchRangeStart) / upr) * keyHeight - g_draw_offset_y;
|
|
14002
|
+
const w = note.durationSteps * stepWidth;
|
|
14003
|
+
if (w < 6) continue;
|
|
14004
|
+
if (renderX + w < 0 || renderX > g_grid_canvas.width) continue;
|
|
14005
|
+
if (renderY + keyHeight < 0 || renderY > g_grid_canvas.height) continue;
|
|
14006
|
+
const textX = renderX + 2;
|
|
14007
|
+
const textY = renderY + keyHeight / 2;
|
|
14008
|
+
g_grid_ctx.save();
|
|
14009
|
+
g_grid_ctx.beginPath();
|
|
14010
|
+
g_grid_ctx.rect(renderX + 1, renderY + 1, w - 2, keyHeight - 2);
|
|
14011
|
+
g_grid_ctx.clip();
|
|
14012
|
+
g_grid_ctx.strokeStyle = "rgba(0,0,0,0.85)";
|
|
14013
|
+
g_grid_ctx.strokeText(kana, textX, textY);
|
|
14014
|
+
g_grid_ctx.fillStyle = "#fff1e8";
|
|
14015
|
+
g_grid_ctx.fillText(kana, textX, textY);
|
|
14016
|
+
g_grid_ctx.restore();
|
|
14017
|
+
}
|
|
14018
|
+
g_grid_ctx.restore();
|
|
14019
|
+
};
|
|
14020
|
+
const drawSelectionRect = (rect) => {
|
|
14021
|
+
if (!rect) return;
|
|
14022
|
+
g_grid_ctx.save();
|
|
14023
|
+
g_grid_ctx.strokeStyle = "#ffec27";
|
|
14024
|
+
g_grid_ctx.lineWidth = 2;
|
|
14025
|
+
g_grid_ctx.setLineDash([4, 4]);
|
|
14026
|
+
g_grid_ctx.strokeRect(rect.x, rect.y, rect.width, rect.height);
|
|
14027
|
+
g_grid_ctx.fillStyle = "rgba(255,236,39,0.08)";
|
|
14028
|
+
g_grid_ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
|
|
14029
|
+
g_grid_ctx.restore();
|
|
14030
|
+
};
|
|
14031
|
+
const drawSelectedNotes = (notes, selectedIds, baseColor = [59, 130, 246, 1]) => {
|
|
14032
|
+
const {
|
|
14033
|
+
keyHeight,
|
|
14034
|
+
stepWidth,
|
|
14035
|
+
keyCount,
|
|
14036
|
+
pitchRangeStart,
|
|
14037
|
+
unitsPerRow: upr = UNITS_PER_SEMITONE
|
|
14038
|
+
} = g_config;
|
|
14039
|
+
for (const note of notes) {
|
|
14040
|
+
if (!selectedIds.has(note.id)) continue;
|
|
14041
|
+
const logicalX = note.startStep * stepWidth;
|
|
14042
|
+
const yIndex = keyCount - 1 - (note.pitchUnits - pitchRangeStart) / upr;
|
|
14043
|
+
const logicalY = yIndex * keyHeight;
|
|
14044
|
+
const w = note.durationSteps * stepWidth;
|
|
14045
|
+
const h = keyHeight;
|
|
14046
|
+
const renderX = logicalX - g_draw_offset_x;
|
|
14047
|
+
const renderY = logicalY - g_draw_offset_y;
|
|
14048
|
+
const velocityOpacity = note.velocity !== void 0 ? 0.5 + note.velocity / 127 * 0.5 : 1;
|
|
14049
|
+
const [r, g, b, a] = baseColor;
|
|
14050
|
+
const darkenFactor = 1.3;
|
|
14051
|
+
const darkerR = Math.min(255, r * darkenFactor);
|
|
14052
|
+
const darkerG = Math.min(255, g * darkenFactor);
|
|
14053
|
+
const darkerB = Math.min(255, b * darkenFactor);
|
|
14054
|
+
const finalOpacity = a * velocityOpacity;
|
|
14055
|
+
g_grid_ctx.fillStyle = `rgba(${darkerR},${darkerG},${darkerB},${finalOpacity})`;
|
|
14056
|
+
g_grid_ctx.fillRect(renderX + 1, renderY + 1, w - 2, h - 2);
|
|
14057
|
+
}
|
|
14058
|
+
};
|
|
14059
|
+
const getXY = (e) => {
|
|
14060
|
+
const { clientX, clientY } = e;
|
|
14061
|
+
const rect = g_grid_canvas.getBoundingClientRect();
|
|
14062
|
+
const x2 = Math.floor(clientX - rect.left);
|
|
14063
|
+
const y = Math.floor(clientY - rect.top);
|
|
14064
|
+
return [x2, y, e.buttons];
|
|
14065
|
+
};
|
|
14066
|
+
const getGridPosition = (e) => {
|
|
14067
|
+
const [x2, y] = getXY(e);
|
|
14068
|
+
const {
|
|
14069
|
+
keyCount,
|
|
14070
|
+
pitchRangeStart,
|
|
14071
|
+
keyHeight,
|
|
14072
|
+
stepWidth,
|
|
14073
|
+
unitsPerRow: upr = UNITS_PER_SEMITONE
|
|
14074
|
+
} = g_config;
|
|
14075
|
+
const step = Math.floor((x2 + g_draw_offset_x) / stepWidth);
|
|
14076
|
+
const absoluteY = y + g_draw_offset_y;
|
|
14077
|
+
const yIndex = Math.floor(absoluteY / keyHeight);
|
|
14078
|
+
const pitch = pitchRangeStart + (keyCount - 1 - yIndex) * upr;
|
|
14079
|
+
return { step, pitch, x: x2, y };
|
|
14080
|
+
};
|
|
14081
|
+
const onClick = (callback) => {
|
|
14082
|
+
g_grid_canvas.addEventListener(
|
|
14083
|
+
"click",
|
|
14084
|
+
(e) => {
|
|
14085
|
+
const [x2, y] = getXY(e);
|
|
14086
|
+
const {
|
|
14087
|
+
keyCount,
|
|
14088
|
+
pitchRangeStart,
|
|
14089
|
+
keyHeight,
|
|
14090
|
+
stepWidth,
|
|
14091
|
+
unitsPerRow: upr = UNITS_PER_SEMITONE
|
|
14092
|
+
} = g_config;
|
|
14093
|
+
const step = Math.floor((x2 + g_draw_offset_x) / stepWidth);
|
|
14094
|
+
const absoluteY = y + g_draw_offset_y;
|
|
14095
|
+
const yIndex = Math.floor(absoluteY / keyHeight);
|
|
14096
|
+
const pitch = pitchRangeStart + (keyCount - 1 - yIndex) * upr;
|
|
14097
|
+
if (pitch >= pitchRangeStart && pitch < pitchRangeStart + keyCount) {
|
|
14098
|
+
requestAnimationFrame(() => callback(step, pitch));
|
|
14099
|
+
}
|
|
14100
|
+
},
|
|
14101
|
+
{ passive: true }
|
|
14102
|
+
);
|
|
14103
|
+
g_grid_canvas.addEventListener("contextmenu", (e) => e.preventDefault());
|
|
14104
|
+
};
|
|
14105
|
+
const setDrawOffset = (x2, y) => {
|
|
14106
|
+
g_draw_offset_x = x2;
|
|
14107
|
+
g_draw_offset_y = y;
|
|
14108
|
+
drawKeyboard();
|
|
14109
|
+
drawHeader();
|
|
14110
|
+
};
|
|
14111
|
+
setup();
|
|
14112
|
+
return {
|
|
14113
|
+
getRenderConfig,
|
|
14114
|
+
setBackgroundActive,
|
|
14115
|
+
getDrawOffset,
|
|
14116
|
+
getGridCanvas,
|
|
14117
|
+
getGridContext,
|
|
14118
|
+
getHeaderCanvas,
|
|
14119
|
+
drawKeyboard,
|
|
14120
|
+
drawHeader,
|
|
14121
|
+
drawGrid,
|
|
14122
|
+
drawNotes,
|
|
14123
|
+
drawNoteLyrics,
|
|
14124
|
+
drawSelectionRect,
|
|
14125
|
+
drawSelectedNotes,
|
|
14126
|
+
getXY,
|
|
14127
|
+
getGridPosition,
|
|
14128
|
+
onClick,
|
|
14129
|
+
setDrawOffset,
|
|
14130
|
+
destroy: () => {
|
|
14131
|
+
mountTarget.innerHTML = "";
|
|
14132
|
+
}
|
|
14133
|
+
};
|
|
14134
|
+
};
|
|
14135
|
+
|
|
13662
14136
|
// src/daw.ts
|
|
13663
14137
|
var CHORD_INFO_HTML2 = `
|
|
13664
14138
|
<div class="dtm-modal-body-content">
|
|
@@ -13945,6 +14419,27 @@ var MIDI_INFO_HTML = `
|
|
|
13945
14419
|
<p style="margin-top:4px;"><small>\u6B4C\u8A5E\u306E\u62BD\u51FA: <a href="https://rpgen3.github.io/ust2txt/" target="_blank" rel="noopener">ust2txt</a></small></p>
|
|
13946
14420
|
</div>
|
|
13947
14421
|
`;
|
|
14422
|
+
var EDO_INFO_HTML = `
|
|
14423
|
+
<div class="dtm-modal-section">
|
|
14424
|
+
<p><strong>\u97F3\u5F8B</strong>\u306F1\u30AA\u30AF\u30BF\u30FC\u30D6\u3092\u4F55\u7B49\u5206\u3059\u308B\u304B\u306E\u8A2D\u5B9A\u3067\u3059\u3002\u66F2\u5168\u4F53\u306B\u52B9\u304D\u307E\u3059\uFF08\u30C8\u30E9\u30C3\u30AF\u3054\u3068\u30FB\u5C0F\u7BC0\u3054\u3068\u306B\u306F\u5909\u3048\u3089\u308C\u307E\u305B\u3093\uFF09\u3002</p>
|
|
14425
|
+
<p><strong>12\u5E73\u5747\u5F8B</strong>\u304C\u901A\u5E38\u306E\u30D4\u30A2\u30CE\u3068\u540C\u3058\u8ABF\u5F8B\u3067\u3059\u3002<strong>31\u5E73\u5747\u5F8B</strong>\u306F1\u30AA\u30AF\u30BF\u30FC\u30D6\u309231\u7B49\u5206\u3057\u3001\u30D4\u30A2\u30CE\u306B\u306F\u7121\u3044\u97F3\uFF08\u5FAE\u5206\u97F3\uFF09\u304C\u4F7F\u3048\u307E\u3059\u3002</p>
|
|
14426
|
+
<p style="margin-top:8px;"><strong>31\u5E73\u5747\u5F8B\u306E\u7279\u5FB4</strong></p>
|
|
14427
|
+
<ul>
|
|
14428
|
+
<li>\u95773\u5EA6\u304C\u7D14\u6B63\u306B\u3054\u304F\u8FD1\u304F\uFF08\u8AA4\u5DEE0.8\u30BB\u30F3\u30C8\uFF09\u3001<strong>\u548C\u97F3\u304C12\u5E73\u5747\u5F8B\u3088\u308A\u7DBA\u9E97\u306B\u97FF\u304D\u307E\u3059</strong>\u3002</li>
|
|
14429
|
+
<li>\u30D4\u30A2\u30CE\u306B\u7121\u3044\u97F3\u304C\u4F7F\u3048\u307E\u3059\u3002\u4E2D\u7ACB3\u5EA6\uFF08\u9577\u77ED\u3069\u3061\u3089\u3067\u3082\u306A\u30443\u5EA6\uFF09\u3001\u81EA\u71367\u5EA6\uFF08\u30D0\u30FC\u30D0\u30FC\u30B7\u30E7\u30C3\u30D7\u306E\u97FF\u304D\uFF09\u306A\u3069\u3002</li>
|
|
14430
|
+
<li>C\u266F \u3068 D\u266D \u304C<strong>\u5225\u306E\u97F3</strong>\u306B\u306A\u308A\u307E\u3059\uFF0812\u5E73\u5747\u5F8B\u3067\u306F\u540C\u3058\u97F3\uFF09\u3002</li>
|
|
14431
|
+
</ul>
|
|
14432
|
+
<p style="margin-top:8px;"><strong>MML\u3067\u306E\u66F8\u304D\u65B9</strong></p>
|
|
14433
|
+
<p><code>#edo=31</code> \u3092\u5BA3\u8A00\u3057\u307E\u3059\u3002\u81E8\u6642\u8A18\u53F7\u306F4\u3064\u4F7F\u3044\u5206\u3051\u307E\u3059\u3002</p>
|
|
14434
|
+
<ul>
|
|
14435
|
+
<li><code>#</code> \u2026 \u534A\u97F3\u4E0A\u3052\uFF082\u5EA6\uFF09\u3000<code>-</code> \u2026 \u534A\u97F3\u4E0B\u3052\uFF082\u5EA6\uFF09</li>
|
|
14436
|
+
<li><code>+</code> \u2026 \u5FAE\u5206\u97F31\u3064\u4E0A\u3052\uFF081\u5EA6\uFF09\u3000<code>_</code> \u2026 \u5FAE\u5206\u97F31\u3064\u4E0B\u3052\uFF081\u5EA6\uFF09</li>
|
|
14437
|
+
</ul>
|
|
14438
|
+
<p>\u4F8B: <code>#edo=31 @0 o4 c c+ c# d- d_ d;</code></p>
|
|
14439
|
+
<p style="margin-top:8px;"><small>12\u5E73\u5747\u5F8B\u3067\u306F4\u8A18\u53F7\u3068\u3082\u5F93\u6765\u901A\u308A\u306E\u610F\u5473\uFF08\u30B7\u30E3\u30FC\u30D72\u3064\u30FB\u30D5\u30E9\u30C3\u30C82\u3064\uFF09\u306B\u306A\u308B\u306E\u3067\u3001\u65E2\u5B58\u306E\u66F2\u306E\u89E3\u91C8\u306F\u5909\u308F\u308A\u307E\u305B\u3093\u3002</small></p>
|
|
14440
|
+
<p style="margin-top:8px;"><small>\u5207\u308A\u66FF\u3048\u308B\u3068\u3001\u65E2\u5B58\u306E\u97F3\u7B26\u306F\u65B0\u3057\u3044\u97F3\u5F8B\u306E\u683C\u5B50\u3078\u4E38\u3081\u3089\u308C\u307E\u3059\u300212\u219231 \u306F\u6700\u592719\u30BB\u30F3\u30C8\u306E\u79FB\u52D5\u3067\u5143\u306B\u623B\u305B\u307E\u3059\u304C\u3001<strong>31\u219212 \u306F\u6700\u592748\u30BB\u30F3\u30C8\u52D5\u304D\u300112\u5E73\u5747\u5F8B\u306B\u7121\u3044\u97F3\u306F\u5931\u308F\u308C\u307E\u3059</strong>\u3002</small></p>
|
|
14441
|
+
</div>
|
|
14442
|
+
`;
|
|
13948
14443
|
var KOE_INFO_HTML = `
|
|
13949
14444
|
<div class="dtm-modal-body-content">
|
|
13950
14445
|
<h4>1. UTAU\u97F3\u6E90\u3092 .koe \u306B\u5909\u63DB\u3059\u308B</h4>
|
|
@@ -14162,7 +14657,7 @@ var AUTO_ROLE_BASE_VOLUME = {
|
|
|
14162
14657
|
chord: 72
|
|
14163
14658
|
};
|
|
14164
14659
|
var computeAutoRoleStats = (notes) => {
|
|
14165
|
-
const avgPitch = notes.reduce((s, n) => s + n.
|
|
14660
|
+
const avgPitch = notes.reduce((s, n) => s + n.pitchUnits, 0) / notes.length;
|
|
14166
14661
|
const avgDur = notes.reduce((s, n) => s + n.durationSteps, 0) / notes.length;
|
|
14167
14662
|
const byStart = /* @__PURE__ */ new Map();
|
|
14168
14663
|
for (const n of notes)
|
|
@@ -14176,8 +14671,9 @@ var computeAutoRoleStats = (notes) => {
|
|
|
14176
14671
|
const notesPerBeat = notes.length / spanBeats;
|
|
14177
14672
|
return { avgPitch, avgDur, avgPoly, maxPoly, notesPerBeat };
|
|
14178
14673
|
};
|
|
14674
|
+
var AUTO_ROLE_BASS_UNITS = 48 * UNITS_PER_SEMITONE;
|
|
14179
14675
|
var classifyTrackRole = (stats) => {
|
|
14180
|
-
if (stats.avgPitch <
|
|
14676
|
+
if (stats.avgPitch < AUTO_ROLE_BASS_UNITS) return "bass";
|
|
14181
14677
|
if (stats.maxPoly >= 2 && stats.avgDur >= AUTO_ROLE_STEPS_PER_BEAT)
|
|
14182
14678
|
return "chord";
|
|
14183
14679
|
if (stats.notesPerBeat < 1.2 && stats.avgDur >= AUTO_ROLE_STEPS_PER_BEAT * 1.5)
|
|
@@ -14189,7 +14685,10 @@ var computeAutoRoleVolume = (role, stats) => {
|
|
|
14189
14685
|
if (role === "chord" && stats.avgPoly > 1) {
|
|
14190
14686
|
vol /= Math.sqrt(stats.avgPoly);
|
|
14191
14687
|
} else if (role === "bass") {
|
|
14192
|
-
const depthSemitones = Math.max(
|
|
14688
|
+
const depthSemitones = Math.max(
|
|
14689
|
+
0,
|
|
14690
|
+
(AUTO_ROLE_BASS_UNITS - stats.avgPitch) / UNITS_PER_SEMITONE
|
|
14691
|
+
);
|
|
14193
14692
|
vol += Math.min(12, depthSemitones * 0.5);
|
|
14194
14693
|
}
|
|
14195
14694
|
return clamp5(Math.round(vol), 1, 127);
|
|
@@ -14281,17 +14780,48 @@ var mountDAW = (target, options = {}) => {
|
|
|
14281
14780
|
refs.drumVolumeLabel.textContent = `${options.drumVolume ?? 80}%`;
|
|
14282
14781
|
const renderConfig = {
|
|
14283
14782
|
stepsPerBar: 192,
|
|
14284
|
-
keyCount:
|
|
14285
|
-
pitchRangeStart:
|
|
14783
|
+
keyCount: KEY_COUNT,
|
|
14784
|
+
pitchRangeStart: PITCH_RANGE_START,
|
|
14785
|
+
unitsPerRow: unitsPerRow(12),
|
|
14786
|
+
edo: 12,
|
|
14286
14787
|
keyHeight: BASE_KEY_HEIGHT,
|
|
14287
14788
|
stepWidth: BASE_STEP_WIDTH * 2
|
|
14288
14789
|
// zoom100% 相当
|
|
14289
14790
|
};
|
|
14791
|
+
let renderer;
|
|
14290
14792
|
let zoomX = 100;
|
|
14291
14793
|
let zoomY = 100;
|
|
14292
14794
|
let bpm = options.defaultBpm ?? DEFAULT_BPM;
|
|
14293
14795
|
let masterVolume = options.masterVolume ?? 50;
|
|
14294
|
-
|
|
14796
|
+
const applyMasterVolume = (volume) => {
|
|
14797
|
+
masterVolume = clamp5(Math.round(volume), 0, 100);
|
|
14798
|
+
refs.masterVolume.value = String(masterVolume);
|
|
14799
|
+
refs.masterVolumeLabel.textContent = `${masterVolume}%`;
|
|
14800
|
+
options.singingVoices?.setVolume(masterVolume / 100);
|
|
14801
|
+
};
|
|
14802
|
+
applyMasterVolume(masterVolume);
|
|
14803
|
+
const snapToEdoGrid = (units) => {
|
|
14804
|
+
const upr = renderConfig.unitsPerRow ?? UNITS_PER_SEMITONE;
|
|
14805
|
+
return Math.round(units / upr) * upr;
|
|
14806
|
+
};
|
|
14807
|
+
const applyEdo = (edo, opts) => {
|
|
14808
|
+
const next = edo === 31 ? 31 : 12;
|
|
14809
|
+
if (renderConfig.edo === next) return;
|
|
14810
|
+
const upr = unitsPerRow(next);
|
|
14811
|
+
renderConfig.edo = next;
|
|
14812
|
+
renderConfig.unitsPerRow = upr;
|
|
14813
|
+
renderConfig.keyCount = keyCountFor(next);
|
|
14814
|
+
if (!opts?.snap) return;
|
|
14815
|
+
for (const t of trackStates) {
|
|
14816
|
+
t.core.beginBatch();
|
|
14817
|
+
for (const note of [...t.core.getNotes()]) {
|
|
14818
|
+
const snapped = Math.round(note.pitchUnits / upr) * upr;
|
|
14819
|
+
if (snapped !== note.pitchUnits)
|
|
14820
|
+
t.core.moveNote(note.id, note.startStep, snapped);
|
|
14821
|
+
}
|
|
14822
|
+
t.core.endBatch();
|
|
14823
|
+
}
|
|
14824
|
+
};
|
|
14295
14825
|
let reverbAmount = options.reverbAmount ?? 0;
|
|
14296
14826
|
let reverbDecay = options.reverbDecay ?? DEFAULT_REVERB_DECAY_SEC;
|
|
14297
14827
|
let reverbPreDelay = options.reverbPreDelay ?? DEFAULT_REVERB_PREDELAY_MS;
|
|
@@ -14349,8 +14879,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
14349
14879
|
let snapGridSteps = 12;
|
|
14350
14880
|
const gridLineSteps = 48;
|
|
14351
14881
|
let currentOffsetX = 0;
|
|
14352
|
-
const _initPitch = options.initialScrollPitch ?? 48;
|
|
14353
|
-
let currentOffsetY = (renderConfig.keyCount - 1 - _initPitch) * renderConfig.keyHeight - 215;
|
|
14882
|
+
const _initPitch = pitchV1ToUnits(options.initialScrollPitch ?? 48);
|
|
14883
|
+
let currentOffsetY = (renderConfig.keyCount - 1 - (_initPitch - renderConfig.pitchRangeStart) / (renderConfig.unitsPerRow ?? UNITS_PER_SEMITONE)) * renderConfig.keyHeight - 215;
|
|
14354
14884
|
let playStartStep = 0;
|
|
14355
14885
|
let isSolo = false;
|
|
14356
14886
|
let lyricTrackIndices = /* @__PURE__ */ new Set();
|
|
@@ -14445,21 +14975,28 @@ var mountDAW = (target, options = {}) => {
|
|
|
14445
14975
|
if (!ready) return;
|
|
14446
14976
|
if (!suppressPatch && options.onNotesPatch) {
|
|
14447
14977
|
const prevByKey = new Map(
|
|
14448
|
-
prevNotes.map((n) => [`${n.startStep}_${n.
|
|
14978
|
+
prevNotes.map((n) => [`${n.startStep}_${n.pitchUnits}`, n])
|
|
14449
14979
|
);
|
|
14450
14980
|
const currByKey = new Map(
|
|
14451
|
-
notes.map((n) => [`${n.startStep}_${n.
|
|
14981
|
+
notes.map((n) => [`${n.startStep}_${n.pitchUnits}`, n])
|
|
14452
14982
|
);
|
|
14453
14983
|
const added = notes.filter((n) => {
|
|
14454
|
-
const prev = prevByKey.get(
|
|
14984
|
+
const prev = prevByKey.get(
|
|
14985
|
+
`${n.startStep}_${n.pitchUnits}`
|
|
14986
|
+
);
|
|
14455
14987
|
return !prev || prev.durationSteps !== n.durationSteps || prev.velocity !== n.velocity;
|
|
14456
14988
|
}).map((n) => ({
|
|
14457
14989
|
startStep: n.startStep,
|
|
14458
|
-
|
|
14990
|
+
pitchUnits: n.pitchUnits,
|
|
14459
14991
|
durationSteps: n.durationSteps,
|
|
14460
14992
|
velocity: n.velocity
|
|
14461
14993
|
}));
|
|
14462
|
-
const removed = prevNotes.filter(
|
|
14994
|
+
const removed = prevNotes.filter(
|
|
14995
|
+
(n) => !currByKey.has(`${n.startStep}_${n.pitchUnits}`)
|
|
14996
|
+
).map((n) => ({
|
|
14997
|
+
startStep: n.startStep,
|
|
14998
|
+
pitchUnits: n.pitchUnits
|
|
14999
|
+
}));
|
|
14463
15000
|
if (added.length > 0 || removed.length > 0) {
|
|
14464
15001
|
options.onNotesPatch(config.id, added, removed);
|
|
14465
15002
|
}
|
|
@@ -14469,7 +15006,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
14469
15006
|
updateUndoRedo();
|
|
14470
15007
|
}
|
|
14471
15008
|
},
|
|
14472
|
-
config.volume
|
|
15009
|
+
config.volume,
|
|
15010
|
+
() => renderConfig
|
|
14473
15011
|
),
|
|
14474
15012
|
volume: config.volume,
|
|
14475
15013
|
savedChordInput: "",
|
|
@@ -14564,18 +15102,18 @@ var mountDAW = (target, options = {}) => {
|
|
|
14564
15102
|
return (lastNoteMeasure + 2) * renderConfig.stepsPerBar;
|
|
14565
15103
|
};
|
|
14566
15104
|
const getMaxOffsetX = () => {
|
|
14567
|
-
const canvas = getGridCanvas();
|
|
15105
|
+
const canvas = renderer.getGridCanvas();
|
|
14568
15106
|
const maxNoteStep = getMaxNoteStep();
|
|
14569
15107
|
const totalContentWidth = maxNoteStep * renderConfig.stepWidth;
|
|
14570
15108
|
return Math.max(0, totalContentWidth - canvas.width);
|
|
14571
15109
|
};
|
|
14572
15110
|
const getMaxOffsetY = () => {
|
|
14573
15111
|
const totalHeight = renderConfig.keyCount * renderConfig.keyHeight;
|
|
14574
|
-
return Math.max(0, totalHeight - getGridCanvas().height);
|
|
15112
|
+
return Math.max(0, totalHeight - renderer.getGridCanvas().height);
|
|
14575
15113
|
};
|
|
14576
15114
|
const drawStartLine = () => {
|
|
14577
|
-
const ctx = getGridContext();
|
|
14578
|
-
const canvas = getGridCanvas();
|
|
15115
|
+
const ctx = renderer.getGridContext();
|
|
15116
|
+
const canvas = renderer.getGridCanvas();
|
|
14579
15117
|
if (!ctx) return;
|
|
14580
15118
|
const x2 = playStartStep * renderConfig.stepWidth - currentOffsetX;
|
|
14581
15119
|
if (x2 < -10 || x2 > canvas.width + 10) return;
|
|
@@ -14590,8 +15128,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
14590
15128
|
ctx.restore();
|
|
14591
15129
|
};
|
|
14592
15130
|
const drawPlayhead = () => {
|
|
14593
|
-
const ctx = getGridContext();
|
|
14594
|
-
const canvas = getGridCanvas();
|
|
15131
|
+
const ctx = renderer.getGridContext();
|
|
15132
|
+
const canvas = renderer.getGridCanvas();
|
|
14595
15133
|
if (!ctx) return;
|
|
14596
15134
|
const x2 = currentPlayStep * renderConfig.stepWidth - currentOffsetX;
|
|
14597
15135
|
if (x2 < 0 || x2 > canvas.width) return;
|
|
@@ -14605,20 +15143,28 @@ var mountDAW = (target, options = {}) => {
|
|
|
14605
15143
|
ctx.restore();
|
|
14606
15144
|
};
|
|
14607
15145
|
const redrawAll = () => {
|
|
14608
|
-
drawGrid(gridLineSteps);
|
|
15146
|
+
renderer.drawGrid(gridLineSteps);
|
|
14609
15147
|
let lyricTargetNotes = null;
|
|
15148
|
+
let activeTrackState = null;
|
|
14610
15149
|
for (const t of trackStates) {
|
|
14611
15150
|
if (hiddenTracks.has(t.config.id)) continue;
|
|
14612
15151
|
if (isSolo && t.config.id !== activeTrackId) continue;
|
|
15152
|
+
if (t.config.id === activeTrackId) {
|
|
15153
|
+
activeTrackState = t;
|
|
15154
|
+
continue;
|
|
15155
|
+
}
|
|
14613
15156
|
const [r, g, b] = t.config.color;
|
|
14614
|
-
const isActive = t.config.id === activeTrackId;
|
|
14615
|
-
const a = isActive ? 1 : 0.3;
|
|
14616
15157
|
const notes = t.core.getNotes();
|
|
14617
|
-
drawNotes(notes, [r, g, b,
|
|
14618
|
-
|
|
15158
|
+
renderer.drawNotes(notes, [r, g, b, 1], false);
|
|
15159
|
+
}
|
|
15160
|
+
if (activeTrackState) {
|
|
15161
|
+
const [r, g, b] = activeTrackState.config.color;
|
|
15162
|
+
const notes = activeTrackState.core.getNotes();
|
|
15163
|
+
renderer.drawNotes(notes, [r, g, b, 1], true);
|
|
15164
|
+
lyricTargetNotes = notes;
|
|
14619
15165
|
}
|
|
14620
15166
|
if (activeToolMode === "select" && selectionRect) {
|
|
14621
|
-
const ctx = getGridContext();
|
|
15167
|
+
const ctx = renderer.getGridContext();
|
|
14622
15168
|
ctx.save();
|
|
14623
15169
|
ctx.strokeStyle = "#ffec27";
|
|
14624
15170
|
ctx.lineWidth = 2;
|
|
@@ -14641,19 +15187,19 @@ var mountDAW = (target, options = {}) => {
|
|
|
14641
15187
|
if (activeToolMode === "select" && selectedNotes.length > 0) {
|
|
14642
15188
|
const ids = new Set(selectedNotes.map((n) => n.id));
|
|
14643
15189
|
const active = getActive();
|
|
14644
|
-
drawSelectedNotes(active.core.getNotes(), ids, [
|
|
15190
|
+
renderer.drawSelectedNotes(active.core.getNotes(), ids, [
|
|
14645
15191
|
...active.config.color,
|
|
14646
15192
|
1
|
|
14647
15193
|
]);
|
|
14648
15194
|
}
|
|
14649
15195
|
if (lyricTargetNotes)
|
|
14650
|
-
drawNoteLyrics(lyricTargetNotes, getActiveLyricKana());
|
|
15196
|
+
renderer.drawNoteLyrics(lyricTargetNotes, getActiveLyricKana());
|
|
14651
15197
|
drawStartLine();
|
|
14652
15198
|
if (playbackState === "playing") drawPlayhead();
|
|
14653
15199
|
updateScrollbars();
|
|
14654
15200
|
};
|
|
14655
15201
|
const updateScrollbars = () => {
|
|
14656
|
-
const canvas = getGridCanvas();
|
|
15202
|
+
const canvas = renderer.getGridCanvas();
|
|
14657
15203
|
const maxOffsetX = getMaxOffsetX();
|
|
14658
15204
|
const sbW = refs.hScroll.clientWidth;
|
|
14659
15205
|
if (maxOffsetX <= 0) {
|
|
@@ -14737,7 +15283,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14737
15283
|
const x2 = clamp5(clientX - rect.left - thumbW / 2, 0, rect.width - thumbW);
|
|
14738
15284
|
const ratio = x2 / (rect.width - thumbW);
|
|
14739
15285
|
currentOffsetX = clamp5(ratio * maxOffsetX, 0, maxOffsetX);
|
|
14740
|
-
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
15286
|
+
renderer.setDrawOffset(currentOffsetX, currentOffsetY);
|
|
14741
15287
|
redrawAll();
|
|
14742
15288
|
};
|
|
14743
15289
|
const moveV = (clientY) => {
|
|
@@ -14748,7 +15294,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14748
15294
|
const y = clamp5(clientY - rect.top - thumbH / 2, 0, rect.height - thumbH);
|
|
14749
15295
|
const ratio = y / (rect.height - thumbH);
|
|
14750
15296
|
currentOffsetY = clamp5(ratio * maxOffset, 0, maxOffset);
|
|
14751
|
-
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
15297
|
+
renderer.setDrawOffset(currentOffsetX, currentOffsetY);
|
|
14752
15298
|
redrawAll();
|
|
14753
15299
|
};
|
|
14754
15300
|
};
|
|
@@ -14787,8 +15333,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
14787
15333
|
const autoScrollTick = () => {
|
|
14788
15334
|
autoScrollRAF = null;
|
|
14789
15335
|
if (!isSelecting || !lastMoveEvent) return;
|
|
14790
|
-
const canvas = getGridCanvas();
|
|
14791
|
-
const { x: x2, y } = getGridPosition(lastMoveEvent);
|
|
15336
|
+
const canvas = renderer.getGridCanvas();
|
|
15337
|
+
const { x: x2, y } = renderer.getGridPosition(lastMoveEvent);
|
|
14792
15338
|
const dx = computeEdgeSpeed(x2, canvas.width);
|
|
14793
15339
|
const dy = computeEdgeSpeed(y, canvas.height);
|
|
14794
15340
|
if (dx !== 0 || dy !== 0) {
|
|
@@ -14796,7 +15342,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14796
15342
|
const maxOffsetY = getMaxOffsetY();
|
|
14797
15343
|
currentOffsetX = clamp5(currentOffsetX + dx, 0, maxOffsetX);
|
|
14798
15344
|
currentOffsetY = clamp5(currentOffsetY + dy, 0, maxOffsetY);
|
|
14799
|
-
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
15345
|
+
renderer.setDrawOffset(currentOffsetX, currentOffsetY);
|
|
14800
15346
|
onPointerMove(lastMoveEvent);
|
|
14801
15347
|
}
|
|
14802
15348
|
if (isSelecting) {
|
|
@@ -14817,11 +15363,17 @@ var mountDAW = (target, options = {}) => {
|
|
|
14817
15363
|
};
|
|
14818
15364
|
const findActiveNoteAt = (x2, y, margin = 0) => {
|
|
14819
15365
|
const active = getActive();
|
|
14820
|
-
const {
|
|
14821
|
-
|
|
15366
|
+
const {
|
|
15367
|
+
stepWidth,
|
|
15368
|
+
keyHeight,
|
|
15369
|
+
keyCount,
|
|
15370
|
+
pitchRangeStart,
|
|
15371
|
+
unitsPerRow: upr = UNITS_PER_SEMITONE
|
|
15372
|
+
} = renderConfig;
|
|
15373
|
+
const offset = renderer.getDrawOffset();
|
|
14822
15374
|
for (const note of active.core.getNotes()) {
|
|
14823
15375
|
const logicalX = note.startStep * stepWidth;
|
|
14824
|
-
const yIndex = keyCount - 1 - (note.
|
|
15376
|
+
const yIndex = keyCount - 1 - (note.pitchUnits - pitchRangeStart) / upr;
|
|
14825
15377
|
const logicalY = yIndex * keyHeight;
|
|
14826
15378
|
const w = note.durationSteps * stepWidth;
|
|
14827
15379
|
const renderX = logicalX - offset.x;
|
|
@@ -14834,7 +15386,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14834
15386
|
const hasNoteAt = (step, pitch, excludeId) => {
|
|
14835
15387
|
const active = getActive();
|
|
14836
15388
|
return active.core.getNotes().some(
|
|
14837
|
-
(n) => n.id !== excludeId && n.
|
|
15389
|
+
(n) => n.id !== excludeId && n.pitchUnits === pitch && step >= n.startStep && step < n.startStep + n.durationSteps
|
|
14838
15390
|
);
|
|
14839
15391
|
};
|
|
14840
15392
|
const snapToGrid = (duration) => Math.max(
|
|
@@ -14845,7 +15397,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14845
15397
|
const onGridPointerDown = (event) => {
|
|
14846
15398
|
event.preventDefault();
|
|
14847
15399
|
options.onResumeAudio?.();
|
|
14848
|
-
const { x: x2, y, step, pitch } = getGridPosition(event);
|
|
15400
|
+
const { x: x2, y, step, pitch } = renderer.getGridPosition(event);
|
|
14849
15401
|
const active = getActive();
|
|
14850
15402
|
if (activeToolMode === "eraser") {
|
|
14851
15403
|
if (isActiveLocked()) return;
|
|
@@ -14860,7 +15412,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14860
15412
|
selectedOriginal = selectedNotes.map((n) => ({
|
|
14861
15413
|
id: n.id,
|
|
14862
15414
|
startStep: n.startStep,
|
|
14863
|
-
pitch: n.
|
|
15415
|
+
pitch: n.pitchUnits
|
|
14864
15416
|
}));
|
|
14865
15417
|
isSelecting = true;
|
|
14866
15418
|
dragMode = "move";
|
|
@@ -14879,7 +15431,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14879
15431
|
{
|
|
14880
15432
|
id: clicked.id,
|
|
14881
15433
|
startStep: clicked.startStep,
|
|
14882
|
-
pitch: clicked.
|
|
15434
|
+
pitch: clicked.pitchUnits
|
|
14883
15435
|
}
|
|
14884
15436
|
];
|
|
14885
15437
|
isSelecting = true;
|
|
@@ -14897,9 +15449,9 @@ var mountDAW = (target, options = {}) => {
|
|
|
14897
15449
|
hasDragged = false;
|
|
14898
15450
|
const existing = findActiveNoteAt(x2, y, TOUCH_HIT_MARGIN);
|
|
14899
15451
|
if (existing) {
|
|
14900
|
-
playPreview(existing.
|
|
15452
|
+
playPreview(existing.pitchUnits);
|
|
14901
15453
|
const { stepWidth } = renderConfig;
|
|
14902
|
-
const offset = getDrawOffset();
|
|
15454
|
+
const offset = renderer.getDrawOffset();
|
|
14903
15455
|
const renderX = existing.startStep * stepWidth - offset.x;
|
|
14904
15456
|
const w = existing.durationSteps * stepWidth;
|
|
14905
15457
|
if (x2 >= renderX + w - resizeHandleWidth && x2 <= renderX + w) {
|
|
@@ -14910,17 +15462,17 @@ var mountDAW = (target, options = {}) => {
|
|
|
14910
15462
|
dragOffsetPitch: 0,
|
|
14911
15463
|
startStep: existing.startStep,
|
|
14912
15464
|
durationSteps: existing.durationSteps,
|
|
14913
|
-
lastPreviewPitch: existing.
|
|
15465
|
+
lastPreviewPitch: existing.pitchUnits
|
|
14914
15466
|
};
|
|
14915
15467
|
} else {
|
|
14916
15468
|
dragState = {
|
|
14917
15469
|
noteId: existing.id,
|
|
14918
15470
|
mode: "move",
|
|
14919
15471
|
dragOffsetStep: step - existing.startStep,
|
|
14920
|
-
dragOffsetPitch: pitch - existing.
|
|
15472
|
+
dragOffsetPitch: pitch - existing.pitchUnits,
|
|
14921
15473
|
startStep: existing.startStep,
|
|
14922
15474
|
durationSteps: existing.durationSteps,
|
|
14923
|
-
lastPreviewPitch: existing.
|
|
15475
|
+
lastPreviewPitch: existing.pitchUnits
|
|
14924
15476
|
};
|
|
14925
15477
|
}
|
|
14926
15478
|
suppressClick = true;
|
|
@@ -14931,14 +15483,14 @@ var mountDAW = (target, options = {}) => {
|
|
|
14931
15483
|
const newStart = snappedStep;
|
|
14932
15484
|
const newEnd = newStart + currentInsertLength;
|
|
14933
15485
|
const overlapping = active.core.getNotes().some(
|
|
14934
|
-
(n) => n.
|
|
15486
|
+
(n) => n.pitchUnits === pitch && newStart < n.startStep + n.durationSteps && newEnd > n.startStep
|
|
14935
15487
|
);
|
|
14936
15488
|
if (!overlapping) {
|
|
14937
15489
|
active.core.addNote(snappedStep, pitch, {
|
|
14938
15490
|
noteLengthSteps: currentInsertLength
|
|
14939
15491
|
});
|
|
14940
15492
|
playPreview(pitch);
|
|
14941
|
-
const newNote = active.core.getNotes().find((n) => n.startStep === snappedStep && n.
|
|
15493
|
+
const newNote = active.core.getNotes().find((n) => n.startStep === snappedStep && n.pitchUnits === pitch);
|
|
14942
15494
|
if (newNote) {
|
|
14943
15495
|
dragState = {
|
|
14944
15496
|
noteId: newNote.id,
|
|
@@ -14947,7 +15499,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14947
15499
|
dragOffsetPitch: 0,
|
|
14948
15500
|
startStep: newNote.startStep,
|
|
14949
15501
|
durationSteps: newNote.durationSteps,
|
|
14950
|
-
lastPreviewPitch: newNote.
|
|
15502
|
+
lastPreviewPitch: newNote.pitchUnits
|
|
14951
15503
|
};
|
|
14952
15504
|
hasDragged = true;
|
|
14953
15505
|
}
|
|
@@ -14958,7 +15510,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14958
15510
|
const active = getActive();
|
|
14959
15511
|
if (activeToolMode === "pen") {
|
|
14960
15512
|
if (!dragState) return;
|
|
14961
|
-
const { step, pitch } = getGridPosition(event);
|
|
15513
|
+
const { step, pitch } = renderer.getGridPosition(event);
|
|
14962
15514
|
hasDragged = true;
|
|
14963
15515
|
if (dragState.mode === "move") {
|
|
14964
15516
|
const nextStart = step - dragState.dragOffsetStep;
|
|
@@ -14982,7 +15534,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
14982
15534
|
}
|
|
14983
15535
|
if (activeToolMode === "select" && isSelecting && selectionStart) {
|
|
14984
15536
|
ensureAutoScroll(event);
|
|
14985
|
-
const { x: x2, y, step, pitch } = getGridPosition(event);
|
|
15537
|
+
const { x: x2, y, step, pitch } = renderer.getGridPosition(event);
|
|
14986
15538
|
if (dragMode === "rect") {
|
|
14987
15539
|
const rect = {
|
|
14988
15540
|
x: Math.min(x2, selectionStart.x),
|
|
@@ -14991,11 +15543,17 @@ var mountDAW = (target, options = {}) => {
|
|
|
14991
15543
|
height: Math.abs(y - selectionStart.y)
|
|
14992
15544
|
};
|
|
14993
15545
|
selectionRect = rect;
|
|
14994
|
-
const {
|
|
14995
|
-
|
|
15546
|
+
const {
|
|
15547
|
+
stepWidth,
|
|
15548
|
+
keyHeight,
|
|
15549
|
+
keyCount,
|
|
15550
|
+
pitchRangeStart,
|
|
15551
|
+
unitsPerRow: upr = UNITS_PER_SEMITONE
|
|
15552
|
+
} = renderConfig;
|
|
15553
|
+
const offset = renderer.getDrawOffset();
|
|
14996
15554
|
selectedNotes = active.core.getNotes().filter((note) => {
|
|
14997
15555
|
const logicalX = note.startStep * stepWidth;
|
|
14998
|
-
const yIndex = keyCount - 1 - (note.
|
|
15556
|
+
const yIndex = keyCount - 1 - (note.pitchUnits - pitchRangeStart) / upr;
|
|
14999
15557
|
const logicalY = yIndex * keyHeight;
|
|
15000
15558
|
const nx = logicalX - offset.x;
|
|
15001
15559
|
const ny = logicalY - offset.y;
|
|
@@ -15014,7 +15572,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
15014
15572
|
const orig = selectedOriginal.find((o) => o.id === note.id);
|
|
15015
15573
|
if (!orig) continue;
|
|
15016
15574
|
const newPitch = orig.pitch + deltaPitch;
|
|
15017
|
-
if (newPitch >=
|
|
15575
|
+
if (newPitch >= PITCH_RANGE_START && newPitch <= PITCH_RANGE_END)
|
|
15018
15576
|
active.core.moveNote(
|
|
15019
15577
|
note.id,
|
|
15020
15578
|
orig.startStep + snappedDelta,
|
|
@@ -15162,22 +15720,23 @@ var mountDAW = (target, options = {}) => {
|
|
|
15162
15720
|
refs.bgOpacityRow.classList.add("dtm-hidden");
|
|
15163
15721
|
}
|
|
15164
15722
|
refs.bgRemoveBtn.classList.toggle("dtm-hidden", !blob2);
|
|
15165
|
-
setBackgroundActive(!!blob2);
|
|
15723
|
+
renderer.setBackgroundActive(!!blob2);
|
|
15166
15724
|
redrawAll();
|
|
15167
15725
|
};
|
|
15168
15726
|
const setupCanvas = () => {
|
|
15169
15727
|
const w = refs.rollContainer.clientWidth || 800;
|
|
15170
15728
|
const h = refs.rollContainer.clientHeight || 450;
|
|
15171
|
-
|
|
15172
|
-
|
|
15729
|
+
renderer?.destroy();
|
|
15730
|
+
renderer = createRenderer(refs.wrapper, w, h, renderConfig);
|
|
15731
|
+
const gridCanvas = renderer.getGridCanvas();
|
|
15173
15732
|
gridCanvas.addEventListener("pointerdown", onGridPointerDown);
|
|
15174
15733
|
gridCanvas.addEventListener("dblclick", (event) => {
|
|
15175
15734
|
event.preventDefault();
|
|
15176
15735
|
if (isActiveLocked()) return;
|
|
15177
|
-
const { step, pitch } = getGridPosition(event);
|
|
15736
|
+
const { step, pitch } = renderer.getGridPosition(event);
|
|
15178
15737
|
const active = getActive();
|
|
15179
15738
|
const note = active.core.getNotes().find(
|
|
15180
|
-
(n) => n.
|
|
15739
|
+
(n) => n.pitchUnits === pitch && step >= n.startStep && step < n.startStep + n.durationSteps
|
|
15181
15740
|
);
|
|
15182
15741
|
if (note) active.core.deleteNoteById(note.id);
|
|
15183
15742
|
});
|
|
@@ -15195,7 +15754,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
15195
15754
|
0,
|
|
15196
15755
|
getMaxOffsetX()
|
|
15197
15756
|
);
|
|
15198
|
-
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
15757
|
+
renderer.setDrawOffset(currentOffsetX, currentOffsetY);
|
|
15199
15758
|
redrawAll();
|
|
15200
15759
|
},
|
|
15201
15760
|
{ passive: false }
|
|
@@ -15205,7 +15764,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
15205
15764
|
suppressClick = false;
|
|
15206
15765
|
}
|
|
15207
15766
|
});
|
|
15208
|
-
const headerCanvas = getHeaderCanvas();
|
|
15767
|
+
const headerCanvas = renderer.getHeaderCanvas();
|
|
15209
15768
|
headerCanvas.addEventListener("click", (event) => {
|
|
15210
15769
|
if (playbackState === "playing") return;
|
|
15211
15770
|
const rect = headerCanvas.getBoundingClientRect();
|
|
@@ -15221,11 +15780,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
15221
15780
|
}
|
|
15222
15781
|
redrawAll();
|
|
15223
15782
|
});
|
|
15224
|
-
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
15783
|
+
renderer.setDrawOffset(currentOffsetX, currentOffsetY);
|
|
15225
15784
|
redrawAll();
|
|
15226
15785
|
};
|
|
15227
15786
|
const applyZoomX = () => {
|
|
15228
|
-
const canvas = getGridCanvas();
|
|
15787
|
+
const canvas = renderer.getGridCanvas();
|
|
15229
15788
|
const centerStep = (currentOffsetX + canvas.width / 2) / renderConfig.stepWidth;
|
|
15230
15789
|
renderConfig.stepWidth = BASE_STEP_WIDTH * (zoomX * 2) / 100;
|
|
15231
15790
|
refs.zoomXLabel.textContent = `${zoomX}%`;
|
|
@@ -15234,11 +15793,11 @@ var mountDAW = (target, options = {}) => {
|
|
|
15234
15793
|
0,
|
|
15235
15794
|
getMaxOffsetX()
|
|
15236
15795
|
);
|
|
15237
|
-
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
15796
|
+
renderer.setDrawOffset(currentOffsetX, currentOffsetY);
|
|
15238
15797
|
redrawAll();
|
|
15239
15798
|
};
|
|
15240
15799
|
const applyZoomY = () => {
|
|
15241
|
-
const canvas = getGridCanvas();
|
|
15800
|
+
const canvas = renderer.getGridCanvas();
|
|
15242
15801
|
const centerKey = (currentOffsetY + canvas.height / 2) / renderConfig.keyHeight;
|
|
15243
15802
|
renderConfig.keyHeight = BASE_KEY_HEIGHT * zoomY / 100;
|
|
15244
15803
|
refs.zoomYLabel.textContent = `${zoomY}%`;
|
|
@@ -15247,7 +15806,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
15247
15806
|
0,
|
|
15248
15807
|
getMaxOffsetY()
|
|
15249
15808
|
);
|
|
15250
|
-
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
15809
|
+
renderer.setDrawOffset(currentOffsetX, currentOffsetY);
|
|
15251
15810
|
redrawAll();
|
|
15252
15811
|
};
|
|
15253
15812
|
const getViewState = () => ({
|
|
@@ -15259,7 +15818,14 @@ var mountDAW = (target, options = {}) => {
|
|
|
15259
15818
|
const notifyViewState = () => options.onViewStateChange?.(getViewState());
|
|
15260
15819
|
const dispatchNote = (trackId, pitch, trackVol, velocity, when, duration) => {
|
|
15261
15820
|
const volume = trackVol / 100 * (velocity / 127) * (masterVolume / 100);
|
|
15262
|
-
options.onPlayNote?.({
|
|
15821
|
+
options.onPlayNote?.({
|
|
15822
|
+
trackId,
|
|
15823
|
+
pitchUnits: pitch,
|
|
15824
|
+
velocity,
|
|
15825
|
+
volume,
|
|
15826
|
+
when,
|
|
15827
|
+
duration
|
|
15828
|
+
});
|
|
15263
15829
|
};
|
|
15264
15830
|
const sequencer = createSequencer({
|
|
15265
15831
|
getTracks: () => trackStates.map((t) => ({
|
|
@@ -15287,7 +15853,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
15287
15853
|
},
|
|
15288
15854
|
onTick: (step) => {
|
|
15289
15855
|
currentPlayStep = step;
|
|
15290
|
-
const canvas = getGridCanvas();
|
|
15856
|
+
const canvas = renderer.getGridCanvas();
|
|
15291
15857
|
const visibleSteps = canvas.width / renderConfig.stepWidth;
|
|
15292
15858
|
const threshold = currentOffsetX / renderConfig.stepWidth + visibleSteps - 4;
|
|
15293
15859
|
if (currentPlayStep > threshold) {
|
|
@@ -15297,7 +15863,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
15297
15863
|
0,
|
|
15298
15864
|
getMaxOffsetX()
|
|
15299
15865
|
);
|
|
15300
|
-
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
15866
|
+
renderer.setDrawOffset(currentOffsetX, currentOffsetY);
|
|
15301
15867
|
}
|
|
15302
15868
|
redrawAll();
|
|
15303
15869
|
},
|
|
@@ -15328,7 +15894,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
15328
15894
|
(a, b) => a.startStep - b.startStep
|
|
15329
15895
|
);
|
|
15330
15896
|
const gate = (lt.gate ?? DEFAULT_GATE) / 100;
|
|
15331
|
-
const semis = (lt.octave ?? 0) *
|
|
15897
|
+
const semis = (lt.octave ?? 0) * UNITS_PER_OCTAVE;
|
|
15332
15898
|
const count = Math.min(sorted.length, lt.syllables.length);
|
|
15333
15899
|
const notes = [];
|
|
15334
15900
|
for (let i2 = 0; i2 < count; i2++) {
|
|
@@ -15336,7 +15902,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
15336
15902
|
if (n.startStep < fromStep) continue;
|
|
15337
15903
|
notes.push({
|
|
15338
15904
|
syllable: lt.syllables[i2],
|
|
15339
|
-
pitch: n.
|
|
15905
|
+
pitch: n.pitchUnits + semis,
|
|
15340
15906
|
startSec: (n.startStep - fromStep) * secondsPerStep,
|
|
15341
15907
|
durationSec: n.durationSteps * secondsPerStep * gate
|
|
15342
15908
|
});
|
|
@@ -15379,13 +15945,13 @@ var mountDAW = (target, options = {}) => {
|
|
|
15379
15945
|
}
|
|
15380
15946
|
}
|
|
15381
15947
|
if (playbackState !== "paused") {
|
|
15382
|
-
const canvas = getGridCanvas();
|
|
15948
|
+
const canvas = renderer.getGridCanvas();
|
|
15383
15949
|
currentOffsetX = clamp5(
|
|
15384
15950
|
playStartStep * renderConfig.stepWidth - canvas.width * 0.5,
|
|
15385
15951
|
0,
|
|
15386
15952
|
getMaxOffsetX()
|
|
15387
15953
|
);
|
|
15388
|
-
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
15954
|
+
renderer.setDrawOffset(currentOffsetX, currentOffsetY);
|
|
15389
15955
|
}
|
|
15390
15956
|
playbackState = "playing";
|
|
15391
15957
|
sequencer.start(fromStep);
|
|
@@ -15471,17 +16037,38 @@ var mountDAW = (target, options = {}) => {
|
|
|
15471
16037
|
trackPillEls.clear();
|
|
15472
16038
|
for (const [i2, t] of trackStates.entries()) {
|
|
15473
16039
|
const [r, g, b] = t.config.color;
|
|
16040
|
+
const isActive = t.config.id === activeTrackId;
|
|
15474
16041
|
const btn = document.createElement("button");
|
|
15475
|
-
btn.className = `dtm-pill ${
|
|
16042
|
+
btn.className = `dtm-pill ${isActive ? "dtm-pill--active" : ""}`;
|
|
15476
16043
|
btn.style.setProperty("--dtm-pill-color", `rgb(${r},${g},${b})`);
|
|
15477
|
-
btn.title = t.config.name
|
|
16044
|
+
btn.title = `Track ${i2 + 1}: ${t.config.name}`;
|
|
16045
|
+
btn.setAttribute(
|
|
16046
|
+
"aria-label",
|
|
16047
|
+
`Track ${i2 + 1}: ${t.config.name}${isActive ? " (\u9078\u629E\u4E2D)" : ""}`
|
|
16048
|
+
);
|
|
15478
16049
|
btn.textContent = String(i2 + 1);
|
|
15479
16050
|
btn.addEventListener("click", () => switchTrack(t.config.id));
|
|
15480
16051
|
refs.trackTabs.appendChild(btn);
|
|
15481
16052
|
trackPillEls.set(t.config.id, btn);
|
|
15482
16053
|
}
|
|
15483
16054
|
const active = getActive();
|
|
16055
|
+
const activeIndex = trackStates.findIndex(
|
|
16056
|
+
(t) => t.config.id === activeTrackId
|
|
16057
|
+
);
|
|
16058
|
+
const [activeR, activeG, activeB] = active.config.color;
|
|
16059
|
+
const panelEl = refs.trackBody.closest(".dtm-panel");
|
|
16060
|
+
if (panelEl) {
|
|
16061
|
+
const summaryEl = panelEl.querySelector("summary");
|
|
16062
|
+
if (summaryEl) {
|
|
16063
|
+
summaryEl.textContent = `\u500B\u5225\u30C8\u30E9\u30C3\u30AF\u8A2D\u5B9A\uFF08Track ${activeIndex + 1}: ${active.config.name}\uFF09`;
|
|
16064
|
+
}
|
|
16065
|
+
}
|
|
15484
16066
|
refs.trackBody.innerHTML = `
|
|
16067
|
+
<div class="dtm-active-track-banner" style="--dtm-track-color: rgb(${activeR},${activeG},${activeB})">
|
|
16068
|
+
<span class="dtm-active-track-badge">TRACK ${activeIndex + 1}</span>
|
|
16069
|
+
<span class="dtm-active-track-name">${active.config.name}</span>
|
|
16070
|
+
<span class="dtm-active-track-pill">ACTIVE</span>
|
|
16071
|
+
</div>
|
|
15485
16072
|
<div class="dtm-row">
|
|
15486
16073
|
<span class="dtm-label">\u30D9\u30ED\u30B7\u30C6\u30A3</span>
|
|
15487
16074
|
<input type="range" class="dtm-range dtm-grow" data-dtm="track-vol" min="0" max="127" value="${active.volume}">
|
|
@@ -16351,6 +16938,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
16351
16938
|
fadeIn: Math.round(fadeInSec * 10),
|
|
16352
16939
|
fadeOut: Math.round(fadeOutSec * 10),
|
|
16353
16940
|
mode,
|
|
16941
|
+
edo: renderConfig.edo,
|
|
16354
16942
|
trackInstruments: trackInstMeta,
|
|
16355
16943
|
trackCompression: trackCompMeta,
|
|
16356
16944
|
trackWidth: trackWidthMeta,
|
|
@@ -16379,6 +16967,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
16379
16967
|
fadeIn: Math.round(fadeInSec * 10),
|
|
16380
16968
|
fadeOut: Math.round(fadeOutSec * 10),
|
|
16381
16969
|
mode,
|
|
16970
|
+
edo: renderConfig.edo,
|
|
16382
16971
|
trackInstruments: trackInstMeta,
|
|
16383
16972
|
trackCompression: trackCompMeta,
|
|
16384
16973
|
trackWidth: trackWidthMeta,
|
|
@@ -16518,19 +17107,19 @@ var mountDAW = (target, options = {}) => {
|
|
|
16518
17107
|
}
|
|
16519
17108
|
}
|
|
16520
17109
|
if (candidateNotes.length === 0) return null;
|
|
16521
|
-
const sum = candidateNotes.reduce((acc, note) => acc + note.
|
|
17110
|
+
const sum = candidateNotes.reduce((acc, note) => acc + note.pitchUnits, 0);
|
|
16522
17111
|
return Math.round(sum / candidateNotes.length);
|
|
16523
17112
|
};
|
|
16524
17113
|
const centerPitch = (pitch) => {
|
|
16525
|
-
const canvas = getGridCanvas();
|
|
16526
|
-
const yIndex = renderConfig.keyCount - 1 - (pitch - renderConfig.pitchRangeStart);
|
|
17114
|
+
const canvas = renderer.getGridCanvas();
|
|
17115
|
+
const yIndex = renderConfig.keyCount - 1 - (pitch - renderConfig.pitchRangeStart) / (renderConfig.unitsPerRow ?? UNITS_PER_SEMITONE);
|
|
16527
17116
|
const logicalY = yIndex * renderConfig.keyHeight;
|
|
16528
17117
|
currentOffsetY = clamp5(
|
|
16529
17118
|
logicalY - (canvas.height - renderConfig.keyHeight) / 2,
|
|
16530
17119
|
0,
|
|
16531
17120
|
getMaxOffsetY()
|
|
16532
17121
|
);
|
|
16533
|
-
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
17122
|
+
renderer.setDrawOffset(currentOffsetX, currentOffsetY);
|
|
16534
17123
|
};
|
|
16535
17124
|
const clearAll = () => {
|
|
16536
17125
|
for (const t of trackStates) {
|
|
@@ -16588,10 +17177,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
16588
17177
|
if (!meta.drumFont) applyDrumPatternFont(meta.drum);
|
|
16589
17178
|
}
|
|
16590
17179
|
if (meta.volume !== void 0) {
|
|
16591
|
-
|
|
16592
|
-
refs.masterVolume.value = String(meta.volume);
|
|
16593
|
-
refs.masterVolumeLabel.textContent = `${meta.volume}%`;
|
|
17180
|
+
applyMasterVolume(meta.volume);
|
|
16594
17181
|
}
|
|
17182
|
+
applyEdo(meta.edo ?? 12);
|
|
17183
|
+
refs.edoSelect.value = String(renderConfig.edo ?? 12);
|
|
16595
17184
|
if (meta.drumVolume !== void 0) {
|
|
16596
17185
|
drumVolume = meta.drumVolume;
|
|
16597
17186
|
refs.drumVolume.value = String(meta.drumVolume);
|
|
@@ -16756,7 +17345,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
16756
17345
|
if (applyActiveOnly && p.trackIndex !== activeTrackIndex) continue;
|
|
16757
17346
|
const t = trackStates[p.trackIndex];
|
|
16758
17347
|
if (!t) continue;
|
|
16759
|
-
t.core.addNote(p.startStep, p.
|
|
17348
|
+
t.core.addNote(p.startStep, p.pitchUnits, {
|
|
16760
17349
|
noteLengthSteps: p.durationSteps,
|
|
16761
17350
|
velocity: DEFAULT_VELOCITY
|
|
16762
17351
|
});
|
|
@@ -16773,7 +17362,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
16773
17362
|
if (firstPitch !== null) {
|
|
16774
17363
|
centerPitch(firstPitch);
|
|
16775
17364
|
} else {
|
|
16776
|
-
centerPitch(48);
|
|
17365
|
+
centerPitch(pitchV1ToUnits(48));
|
|
16777
17366
|
}
|
|
16778
17367
|
redrawAll();
|
|
16779
17368
|
updateTrackPanel();
|
|
@@ -16791,6 +17380,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
16791
17380
|
const chordTrack = trackStates.find((t) => t.config.id === "chord");
|
|
16792
17381
|
if (!chordTrack) return;
|
|
16793
17382
|
const placements = buildChordPlacements({
|
|
17383
|
+
// 和音は五度連鎖経由で音律の格子へ写す(31平均律では長3度が10度になる)
|
|
17384
|
+
edo: renderConfig.edo,
|
|
16794
17385
|
chordStr: active.savedChordInput,
|
|
16795
17386
|
patternType: active.savedChordPattern,
|
|
16796
17387
|
rootShift: active.savedChordRoot,
|
|
@@ -16800,7 +17391,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
16800
17391
|
chordTrack.core.clearNotesWithoutHistory();
|
|
16801
17392
|
chordTrack.core.beginBatch();
|
|
16802
17393
|
for (const p of placements) {
|
|
16803
|
-
chordTrack.core.addNote(p.startStep, p.
|
|
17394
|
+
chordTrack.core.addNote(p.startStep, p.pitchUnits, {
|
|
16804
17395
|
noteLengthSteps: Math.max(1, p.durationSteps),
|
|
16805
17396
|
velocity: p.velocity
|
|
16806
17397
|
});
|
|
@@ -16838,6 +17429,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
16838
17429
|
active.vocalOctaveUnison = "none";
|
|
16839
17430
|
} else {
|
|
16840
17431
|
clearAll();
|
|
17432
|
+
applyEdo(12);
|
|
17433
|
+
refs.edoSelect.value = String(renderConfig.edo ?? 12);
|
|
16841
17434
|
for (const t of trackStates) t.core.setLoadMode(true);
|
|
16842
17435
|
for (const t of trackStates) {
|
|
16843
17436
|
t.lyrics = "";
|
|
@@ -16864,7 +17457,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
16864
17457
|
if (applyActiveOnly && p.trackId !== activeTrackId) continue;
|
|
16865
17458
|
const t = trackStates.find((ts) => ts.config.id === p.trackId);
|
|
16866
17459
|
if (!t) continue;
|
|
16867
|
-
t.core.addNote(p.startStep, p.pitch, {
|
|
17460
|
+
t.core.addNote(p.startStep, snapToEdoGrid(pitchV1ToUnits(p.pitch)), {
|
|
16868
17461
|
noteLengthSteps: p.durationSteps,
|
|
16869
17462
|
velocity: p.velocity
|
|
16870
17463
|
});
|
|
@@ -16881,7 +17474,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
16881
17474
|
if (firstPitch !== null) {
|
|
16882
17475
|
centerPitch(firstPitch);
|
|
16883
17476
|
} else {
|
|
16884
|
-
centerPitch(48);
|
|
17477
|
+
centerPitch(pitchV1ToUnits(48));
|
|
16885
17478
|
}
|
|
16886
17479
|
redrawAll();
|
|
16887
17480
|
updateTrackPanel();
|
|
@@ -16945,18 +17538,21 @@ var mountDAW = (target, options = {}) => {
|
|
|
16945
17538
|
}
|
|
16946
17539
|
}, 30);
|
|
16947
17540
|
};
|
|
16948
|
-
const showConfirmModal = (message) => new Promise((resolve) => {
|
|
17541
|
+
const showConfirmModal = (message, opts) => new Promise((resolve) => {
|
|
17542
|
+
const title = opts?.title ?? "\u30E2\u30FC\u30C9\u306E\u78BA\u8A8D";
|
|
17543
|
+
const yes = opts?.yes ?? "\u306F\u3044\uFF08\u4E0A\u7D1A\u8005\u30E2\u30FC\u30C9\u306B\u5207\u308A\u66FF\u3048\u308B\uFF09";
|
|
17544
|
+
const no = opts?.no ?? "\u3044\u3044\u3048\uFF08\u3053\u306E\u307E\u307E\u8AAD\u307F\u8FBC\u3080\uFF09";
|
|
16949
17545
|
const overlay = document.createElement("div");
|
|
16950
17546
|
overlay.className = "dtm-modal-overlay";
|
|
16951
17547
|
overlay.innerHTML = `
|
|
16952
17548
|
<div class="dtm-modal">
|
|
16953
17549
|
<div class="dtm-modal-header">
|
|
16954
|
-
<span class="dtm-modal-title"
|
|
17550
|
+
<span class="dtm-modal-title">${title}</span>
|
|
16955
17551
|
</div>
|
|
16956
17552
|
<div class="dtm-modal-body"><p>${message}</p></div>
|
|
16957
17553
|
<div class="dtm-confirm-footer">
|
|
16958
|
-
<button class="dtm-btn dtm-btn--ghost dtm-confirm-no"
|
|
16959
|
-
<button class="dtm-btn dtm-btn--primary dtm-confirm-yes"
|
|
17554
|
+
<button class="dtm-btn dtm-btn--ghost dtm-confirm-no">${no}</button>
|
|
17555
|
+
<button class="dtm-btn dtm-btn--primary dtm-confirm-yes">${yes}</button>
|
|
16960
17556
|
</div>
|
|
16961
17557
|
</div>`;
|
|
16962
17558
|
const close = (result) => {
|
|
@@ -17064,10 +17660,36 @@ var mountDAW = (target, options = {}) => {
|
|
|
17064
17660
|
});
|
|
17065
17661
|
refs.decomposeChordToggle.addEventListener("change", notifyViewState);
|
|
17066
17662
|
refs.ignoreChordHeavyToggle.addEventListener("change", notifyViewState);
|
|
17663
|
+
refs.edoSelect.addEventListener("change", async () => {
|
|
17664
|
+
const next = Number.parseInt(refs.edoSelect.value, 10) === 31 ? 31 : 12;
|
|
17665
|
+
const prev = renderConfig.edo ?? 12;
|
|
17666
|
+
if (next === prev) return;
|
|
17667
|
+
const hasNotes = trackStates.some((t) => t.core.getNotes().length > 0);
|
|
17668
|
+
if (next === 12 && prev === 31 && hasNotes) {
|
|
17669
|
+
const okToDrop = await showConfirmModal(
|
|
17670
|
+
"31\u5E73\u5747\u5F8B\u304B\u308912\u5E73\u5747\u5F8B\u3078\u623B\u3059\u3068\u3001\u97F3\u7B26\u304C\u6700\u592748\u30BB\u30F3\u30C8\uFF08\u534A\u97F3\u306E\u7D04\u534A\u5206\uFF09\u52D5\u304D\u307E\u3059\u3002<br>12\u5E73\u5747\u5F8B\u306B\u7121\u3044\u97F3\uFF08\u4E2D\u7ACB3\u5EA6\u306A\u3069\uFF09\u306F\u8FD1\u3044\u97F3\u306B\u6F70\u308C\u3001<strong>\u5143\u306B\u306F\u623B\u305B\u307E\u305B\u3093</strong>\u3002<br>\u5207\u308A\u66FF\u3048\u307E\u3059\u304B\uFF1F",
|
|
17671
|
+
{
|
|
17672
|
+
title: "\u97F3\u5F8B\u306E\u78BA\u8A8D",
|
|
17673
|
+
yes: "\u5207\u308A\u66FF\u3048\u308B",
|
|
17674
|
+
no: "\u3084\u3081\u308B"
|
|
17675
|
+
}
|
|
17676
|
+
);
|
|
17677
|
+
if (!okToDrop) {
|
|
17678
|
+
refs.edoSelect.value = String(prev);
|
|
17679
|
+
return;
|
|
17680
|
+
}
|
|
17681
|
+
}
|
|
17682
|
+
applyEdo(next, { snap: true });
|
|
17683
|
+
const first = getFirstDetectedPitch();
|
|
17684
|
+
centerPitch(first ?? pitchV1ToUnits(48));
|
|
17685
|
+
redrawAll();
|
|
17686
|
+
updateUndoRedo();
|
|
17687
|
+
});
|
|
17688
|
+
refs.edoInfoBtn.addEventListener("click", () => {
|
|
17689
|
+
showModal("\u97F3\u5F8B\u306E\u89E3\u8AAC", EDO_INFO_HTML);
|
|
17690
|
+
});
|
|
17067
17691
|
refs.masterVolume.addEventListener("input", () => {
|
|
17068
|
-
|
|
17069
|
-
refs.masterVolumeLabel.textContent = `${masterVolume}%`;
|
|
17070
|
-
options.singingVoices?.setVolume(masterVolume / 100);
|
|
17692
|
+
applyMasterVolume(Number.parseInt(refs.masterVolume.value, 10) || 0);
|
|
17071
17693
|
});
|
|
17072
17694
|
refs.masterComp.addEventListener("input", () => {
|
|
17073
17695
|
masterCompression = Number.parseInt(refs.masterComp.value, 10) || 0;
|
|
@@ -17131,10 +17753,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
17131
17753
|
const scale = targetPeak / observedPeakMax;
|
|
17132
17754
|
const suggested = clamp5(Math.round(masterVolume * scale), 10, 100);
|
|
17133
17755
|
if (suggested !== masterVolume) {
|
|
17134
|
-
|
|
17135
|
-
refs.masterVolume.value = String(masterVolume);
|
|
17136
|
-
refs.masterVolumeLabel.textContent = `${masterVolume}%`;
|
|
17137
|
-
options.singingVoices?.setVolume(masterVolume / 100);
|
|
17756
|
+
applyMasterVolume(suggested);
|
|
17138
17757
|
}
|
|
17139
17758
|
observedPeakMax = 0;
|
|
17140
17759
|
observedPlayMs = 0;
|
|
@@ -17294,7 +17913,8 @@ var mountDAW = (target, options = {}) => {
|
|
|
17294
17913
|
generateRandomPattern(getActive().core, {
|
|
17295
17914
|
stepsPerBar: renderConfig.stepsPerBar,
|
|
17296
17915
|
startStep: playStartStep,
|
|
17297
|
-
pitchRangeStart: renderConfig.pitchRangeStart
|
|
17916
|
+
pitchRangeStart: renderConfig.pitchRangeStart,
|
|
17917
|
+
edo: renderConfig.edo
|
|
17298
17918
|
});
|
|
17299
17919
|
redrawAll();
|
|
17300
17920
|
});
|
|
@@ -17631,7 +18251,7 @@ var mountDAW = (target, options = {}) => {
|
|
|
17631
18251
|
id: 0,
|
|
17632
18252
|
startStep: p.startStep,
|
|
17633
18253
|
durationSteps: p.durationSteps,
|
|
17634
|
-
|
|
18254
|
+
pitchUnits: snapToEdoGrid(pitchV1ToUnits(p.pitch)),
|
|
17635
18255
|
velocity: p.velocity
|
|
17636
18256
|
}));
|
|
17637
18257
|
const mml = refCore.getMMLFromNotes(asNotes, tempo, 100).trim();
|
|
@@ -18086,10 +18706,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
18086
18706
|
const newStart = playStartStep + (note.startStep - minStart);
|
|
18087
18707
|
const newEnd = newStart + note.durationSteps;
|
|
18088
18708
|
const overlap = notes.some(
|
|
18089
|
-
(ex) => ex.
|
|
18709
|
+
(ex) => ex.pitchUnits === note.pitchUnits && newStart < ex.startStep + ex.durationSteps && newEnd > ex.startStep
|
|
18090
18710
|
);
|
|
18091
18711
|
if (!overlap)
|
|
18092
|
-
core.addNote(newStart, note.
|
|
18712
|
+
core.addNote(newStart, note.pitchUnits, {
|
|
18093
18713
|
noteLengthSteps: note.durationSteps,
|
|
18094
18714
|
velocity: note.velocity
|
|
18095
18715
|
});
|
|
@@ -18162,13 +18782,13 @@ var mountDAW = (target, options = {}) => {
|
|
|
18162
18782
|
pausedPlayStep = step;
|
|
18163
18783
|
currentPlayStep = step;
|
|
18164
18784
|
playbackState = "paused";
|
|
18165
|
-
const canvas = getGridCanvas();
|
|
18785
|
+
const canvas = renderer.getGridCanvas();
|
|
18166
18786
|
currentOffsetX = clamp5(
|
|
18167
18787
|
step * renderConfig.stepWidth - canvas.width * 0.5,
|
|
18168
18788
|
0,
|
|
18169
18789
|
getMaxOffsetX()
|
|
18170
18790
|
);
|
|
18171
|
-
setDrawOffset(currentOffsetX, currentOffsetY);
|
|
18791
|
+
renderer.setDrawOffset(currentOffsetX, currentOffsetY);
|
|
18172
18792
|
updateTransport();
|
|
18173
18793
|
redrawAll();
|
|
18174
18794
|
};
|
|
@@ -18236,16 +18856,10 @@ var mountDAW = (target, options = {}) => {
|
|
|
18236
18856
|
forcePauseAt,
|
|
18237
18857
|
setLoading,
|
|
18238
18858
|
setMasterVolume: (volume) => {
|
|
18239
|
-
|
|
18240
|
-
refs.masterVolume.value = String(masterVolume);
|
|
18241
|
-
refs.masterVolumeLabel.textContent = `${masterVolume}%`;
|
|
18242
|
-
options.singingVoices?.setVolume(masterVolume / 100);
|
|
18859
|
+
applyMasterVolume(volume);
|
|
18243
18860
|
},
|
|
18244
18861
|
setVolume: (volume) => {
|
|
18245
|
-
|
|
18246
|
-
refs.masterVolume.value = String(masterVolume);
|
|
18247
|
-
refs.masterVolumeLabel.textContent = `${masterVolume}%`;
|
|
18248
|
-
options.singingVoices?.setVolume(masterVolume / 100);
|
|
18862
|
+
applyMasterVolume(volume);
|
|
18249
18863
|
},
|
|
18250
18864
|
setDrumVolume: (volume) => {
|
|
18251
18865
|
drumVolume = clamp5(volume, 0, 100);
|
|
@@ -18288,15 +18902,19 @@ var mountDAW = (target, options = {}) => {
|
|
|
18288
18902
|
suppressPatch = true;
|
|
18289
18903
|
track.core.beginBatch();
|
|
18290
18904
|
for (const n of added) {
|
|
18291
|
-
const existing = track.core.getNotes().find(
|
|
18905
|
+
const existing = track.core.getNotes().find(
|
|
18906
|
+
(e) => e.startStep === n.startStep && e.pitchUnits === n.pitchUnits
|
|
18907
|
+
);
|
|
18292
18908
|
if (existing) track.core.deleteNoteById(existing.id);
|
|
18293
|
-
track.core.addNote(n.startStep, n.
|
|
18909
|
+
track.core.addNote(n.startStep, n.pitchUnits, {
|
|
18294
18910
|
noteLengthSteps: n.durationSteps,
|
|
18295
18911
|
velocity: n.velocity
|
|
18296
18912
|
});
|
|
18297
18913
|
}
|
|
18298
18914
|
for (const r of removed) {
|
|
18299
|
-
const note = track.core.getNotes().find(
|
|
18915
|
+
const note = track.core.getNotes().find(
|
|
18916
|
+
(n) => n.startStep === r.startStep && n.pitchUnits === r.pitchUnits
|
|
18917
|
+
);
|
|
18300
18918
|
if (note) track.core.deleteNoteById(note.id);
|
|
18301
18919
|
}
|
|
18302
18920
|
track.core.endBatch();
|
|
@@ -18338,9 +18956,9 @@ var mountDAW = (target, options = {}) => {
|
|
|
18338
18956
|
if (t.config.id === activeTrackId) updateTrackPanel();
|
|
18339
18957
|
},
|
|
18340
18958
|
noteToCanvas: (step, pitch) => {
|
|
18341
|
-
const canvas = getGridCanvas();
|
|
18959
|
+
const canvas = renderer.getGridCanvas();
|
|
18342
18960
|
const x2 = step * renderConfig.stepWidth - currentOffsetX;
|
|
18343
|
-
const y = (renderConfig.keyCount - 1 - pitch) * renderConfig.keyHeight - currentOffsetY;
|
|
18961
|
+
const y = (renderConfig.keyCount - 1 - (pitch - renderConfig.pitchRangeStart) / (renderConfig.unitsPerRow ?? UNITS_PER_SEMITONE)) * renderConfig.keyHeight - currentOffsetY;
|
|
18344
18962
|
const onScreen = x2 >= 0 && x2 <= canvas.width && y >= 0 && y <= canvas.height;
|
|
18345
18963
|
let side = null;
|
|
18346
18964
|
if (!onScreen) {
|
|
@@ -18404,7 +19022,7 @@ var playSingingMML = async (mml, options = {}) => {
|
|
|
18404
19022
|
id: id++,
|
|
18405
19023
|
startStep: p.startStep,
|
|
18406
19024
|
durationSteps: p.durationSteps,
|
|
18407
|
-
|
|
19025
|
+
pitchUnits: p.pitchUnits,
|
|
18408
19026
|
velocity: p.velocity
|
|
18409
19027
|
}));
|
|
18410
19028
|
return {
|
|
@@ -18430,7 +19048,7 @@ var playSingingMML = async (mml, options = {}) => {
|
|
|
18430
19048
|
(a, b) => a.startStep - b.startStep
|
|
18431
19049
|
);
|
|
18432
19050
|
const gate = (lt.gate ?? DEFAULT_GATE) / 100;
|
|
18433
|
-
const semis = (lt.octave ?? 0) *
|
|
19051
|
+
const semis = (lt.octave ?? 0) * UNITS_PER_OCTAVE;
|
|
18434
19052
|
const count = Math.min(sorted.length, lt.syllables.length);
|
|
18435
19053
|
const notes = [];
|
|
18436
19054
|
for (let i2 = 0; i2 < count; i2++) {
|
|
@@ -18438,7 +19056,7 @@ var playSingingMML = async (mml, options = {}) => {
|
|
|
18438
19056
|
if (n.startStep < fromStep) continue;
|
|
18439
19057
|
notes.push({
|
|
18440
19058
|
syllable: lt.syllables[i2],
|
|
18441
|
-
pitch: n.
|
|
19059
|
+
pitch: n.pitchUnits + semis,
|
|
18442
19060
|
startSec: (n.startStep - fromStep) * secondsPerStep,
|
|
18443
19061
|
durationSec: n.durationSteps * secondsPerStep * gate
|
|
18444
19062
|
});
|
|
@@ -18626,24 +19244,28 @@ var createPianoRoll = (options, handlers) => {
|
|
|
18626
19244
|
config,
|
|
18627
19245
|
noteLengthSteps = 1
|
|
18628
19246
|
} = options;
|
|
18629
|
-
|
|
19247
|
+
const renderer = createRenderer(mountTarget, width, height, config);
|
|
18630
19248
|
let currentNoteLengthSteps = noteLengthSteps;
|
|
18631
19249
|
let selectionRect = null;
|
|
18632
19250
|
let isSelecting = false;
|
|
18633
19251
|
let selectionStart = null;
|
|
18634
19252
|
let selectedNotes = [];
|
|
18635
19253
|
let copiedNotes = [];
|
|
18636
|
-
const core = new MMLCore(
|
|
18637
|
-
|
|
18638
|
-
|
|
18639
|
-
|
|
18640
|
-
|
|
18641
|
-
|
|
19254
|
+
const core = new MMLCore(
|
|
19255
|
+
{
|
|
19256
|
+
onMMLGenerated: handlers.onMMLGenerated,
|
|
19257
|
+
onNotesChanged: (notes) => {
|
|
19258
|
+
handlers.onNotesChanged(notes);
|
|
19259
|
+
}
|
|
19260
|
+
},
|
|
19261
|
+
80,
|
|
19262
|
+
() => config
|
|
19263
|
+
);
|
|
18642
19264
|
const getAddNoteOptions = () => ({
|
|
18643
19265
|
noteLengthSteps: currentNoteLengthSteps
|
|
18644
19266
|
});
|
|
18645
19267
|
let suppressClick = false;
|
|
18646
|
-
onClick((step, pitch) => {
|
|
19268
|
+
renderer.onClick((step, pitch) => {
|
|
18647
19269
|
if (suppressClick) {
|
|
18648
19270
|
suppressClick = false;
|
|
18649
19271
|
return;
|
|
@@ -18655,7 +19277,7 @@ var createPianoRoll = (options, handlers) => {
|
|
|
18655
19277
|
} else if (mode === "eraser") {
|
|
18656
19278
|
const notes = core.getNotes();
|
|
18657
19279
|
const note = notes.find(
|
|
18658
|
-
(n) => n.startStep <= step && step < n.startStep + n.durationSteps && n.
|
|
19280
|
+
(n) => n.startStep <= step && step < n.startStep + n.durationSteps && n.pitchUnits === pitch
|
|
18659
19281
|
);
|
|
18660
19282
|
if (note) {
|
|
18661
19283
|
core.deleteNoteById(note.id);
|
|
@@ -18663,17 +19285,23 @@ var createPianoRoll = (options, handlers) => {
|
|
|
18663
19285
|
}
|
|
18664
19286
|
}
|
|
18665
19287
|
});
|
|
18666
|
-
const gridCanvas = getGridCanvas();
|
|
19288
|
+
const gridCanvas = renderer.getGridCanvas();
|
|
18667
19289
|
const resizeHandleWidth = 6;
|
|
18668
19290
|
let dragState = null;
|
|
18669
19291
|
let hasDragged = false;
|
|
18670
19292
|
let lastPreviewPitch = null;
|
|
18671
19293
|
const findNoteAtPosition = (x2, y) => {
|
|
18672
|
-
const {
|
|
18673
|
-
|
|
19294
|
+
const {
|
|
19295
|
+
stepWidth,
|
|
19296
|
+
keyHeight,
|
|
19297
|
+
keyCount,
|
|
19298
|
+
pitchRangeStart,
|
|
19299
|
+
unitsPerRow: upr = UNITS_PER_SEMITONE
|
|
19300
|
+
} = renderer.getRenderConfig();
|
|
19301
|
+
const offset = renderer.getDrawOffset();
|
|
18674
19302
|
for (const note of core.getNotes()) {
|
|
18675
19303
|
const logicalX = note.startStep * stepWidth;
|
|
18676
|
-
const yIndex = keyCount - 1 - (note.
|
|
19304
|
+
const yIndex = keyCount - 1 - (note.pitchUnits - pitchRangeStart) / upr;
|
|
18677
19305
|
const logicalY = yIndex * keyHeight;
|
|
18678
19306
|
const w = note.durationSteps * stepWidth;
|
|
18679
19307
|
const h = keyHeight;
|
|
@@ -18687,7 +19315,7 @@ var createPianoRoll = (options, handlers) => {
|
|
|
18687
19315
|
};
|
|
18688
19316
|
const handlePointerMove = (e) => {
|
|
18689
19317
|
if (core.getToolMode() === "select" && isSelecting && selectionStart) {
|
|
18690
|
-
const { x: x2, y } = getGridPosition(e);
|
|
19318
|
+
const { x: x2, y } = renderer.getGridPosition(e);
|
|
18691
19319
|
const minX = Math.min(x2, selectionStart.x);
|
|
18692
19320
|
const minY = Math.min(y, selectionStart.y);
|
|
18693
19321
|
const width2 = Math.abs(x2 - selectionStart.x);
|
|
@@ -18699,7 +19327,7 @@ var createPianoRoll = (options, handlers) => {
|
|
|
18699
19327
|
}
|
|
18700
19328
|
if (!dragState) return;
|
|
18701
19329
|
hasDragged = true;
|
|
18702
|
-
const { step, pitch } = getGridPosition(e);
|
|
19330
|
+
const { step, pitch } = renderer.getGridPosition(e);
|
|
18703
19331
|
if (dragState.mode === "move") {
|
|
18704
19332
|
if (dragState.selectedNotes && dragState.selectedNotes.length > 0) {
|
|
18705
19333
|
const noteId = dragState.noteId;
|
|
@@ -18708,10 +19336,10 @@ var createPianoRoll = (options, handlers) => {
|
|
|
18708
19336
|
const nextStart2 = step - dragState.dragOffsetStep;
|
|
18709
19337
|
const nextPitch2 = pitch - dragState.dragOffsetPitch;
|
|
18710
19338
|
const stepDelta = nextStart2 - baseNote.startStep;
|
|
18711
|
-
const pitchDelta = nextPitch2 - baseNote.
|
|
19339
|
+
const pitchDelta = nextPitch2 - baseNote.pitchUnits;
|
|
18712
19340
|
for (const note of dragState.selectedNotes) {
|
|
18713
19341
|
const newStart = note.startStep + stepDelta;
|
|
18714
|
-
const newPitch = note.
|
|
19342
|
+
const newPitch = note.pitchUnits + pitchDelta;
|
|
18715
19343
|
core.moveNote(note.id, newStart, newPitch);
|
|
18716
19344
|
}
|
|
18717
19345
|
if (options.onPreviewSound && pitch !== lastPreviewPitch) {
|
|
@@ -18752,7 +19380,7 @@ var createPianoRoll = (options, handlers) => {
|
|
|
18752
19380
|
}
|
|
18753
19381
|
};
|
|
18754
19382
|
gridCanvas.addEventListener("pointerdown", (e) => {
|
|
18755
|
-
const { x: x2, y, step, pitch } = getGridPosition(e);
|
|
19383
|
+
const { x: x2, y, step, pitch } = renderer.getGridPosition(e);
|
|
18756
19384
|
const currentMode = core.getToolMode();
|
|
18757
19385
|
if (currentMode === "select") {
|
|
18758
19386
|
const clickedNote = findNoteAtPosition(x2, y);
|
|
@@ -18763,7 +19391,7 @@ var createPianoRoll = (options, handlers) => {
|
|
|
18763
19391
|
noteId: clickedNote.id,
|
|
18764
19392
|
mode: "move",
|
|
18765
19393
|
dragOffsetStep: step - clickedNote.startStep,
|
|
18766
|
-
dragOffsetPitch: pitch - clickedNote.
|
|
19394
|
+
dragOffsetPitch: pitch - clickedNote.pitchUnits,
|
|
18767
19395
|
startStep: clickedNote.startStep,
|
|
18768
19396
|
selectedNotes: notesInRect
|
|
18769
19397
|
// 複数選択ノートを保存
|
|
@@ -18781,10 +19409,16 @@ var createPianoRoll = (options, handlers) => {
|
|
|
18781
19409
|
}
|
|
18782
19410
|
const note = findNoteAtPosition(x2, y);
|
|
18783
19411
|
if (!note) return;
|
|
18784
|
-
const {
|
|
18785
|
-
|
|
19412
|
+
const {
|
|
19413
|
+
stepWidth,
|
|
19414
|
+
keyHeight,
|
|
19415
|
+
keyCount,
|
|
19416
|
+
pitchRangeStart,
|
|
19417
|
+
unitsPerRow: upr = UNITS_PER_SEMITONE
|
|
19418
|
+
} = renderer.getRenderConfig();
|
|
19419
|
+
const offset = renderer.getDrawOffset();
|
|
18786
19420
|
const logicalX = note.startStep * stepWidth;
|
|
18787
|
-
const yIndex = keyCount - 1 - (note.
|
|
19421
|
+
const yIndex = keyCount - 1 - (note.pitchUnits - pitchRangeStart) / upr;
|
|
18788
19422
|
const logicalY = yIndex * keyHeight;
|
|
18789
19423
|
const renderX = logicalX - offset.x;
|
|
18790
19424
|
const renderY = logicalY - offset.y;
|
|
@@ -18803,7 +19437,7 @@ var createPianoRoll = (options, handlers) => {
|
|
|
18803
19437
|
noteId: note.id,
|
|
18804
19438
|
mode: "move",
|
|
18805
19439
|
dragOffsetStep: step - note.startStep,
|
|
18806
|
-
dragOffsetPitch: pitch - note.
|
|
19440
|
+
dragOffsetPitch: pitch - note.pitchUnits,
|
|
18807
19441
|
startStep: note.startStep
|
|
18808
19442
|
};
|
|
18809
19443
|
});
|
|
@@ -18814,41 +19448,47 @@ var createPianoRoll = (options, handlers) => {
|
|
|
18814
19448
|
"wheel",
|
|
18815
19449
|
(e) => {
|
|
18816
19450
|
e.preventDefault();
|
|
18817
|
-
const configValues = getRenderConfig();
|
|
19451
|
+
const configValues = renderer.getRenderConfig();
|
|
18818
19452
|
const gridHeight = gridCanvas.height;
|
|
18819
19453
|
const maxOffsetY = Math.max(
|
|
18820
19454
|
0,
|
|
18821
19455
|
configValues.keyCount * configValues.keyHeight - gridHeight
|
|
18822
19456
|
);
|
|
18823
|
-
const currentOffset = getDrawOffset();
|
|
19457
|
+
const currentOffset = renderer.getDrawOffset();
|
|
18824
19458
|
const nextOffsetY = Math.min(
|
|
18825
19459
|
Math.max(currentOffset.y + e.deltaY, 0),
|
|
18826
19460
|
maxOffsetY
|
|
18827
19461
|
);
|
|
18828
|
-
setDrawOffset(currentOffset.x, nextOffsetY);
|
|
18829
|
-
drawGrid();
|
|
18830
|
-
drawNotes(core.getNotes());
|
|
19462
|
+
renderer.setDrawOffset(currentOffset.x, nextOffsetY);
|
|
19463
|
+
renderer.drawGrid();
|
|
19464
|
+
renderer.drawNotes(core.getNotes());
|
|
18831
19465
|
},
|
|
18832
19466
|
{ passive: false }
|
|
18833
19467
|
);
|
|
18834
19468
|
const redraw = () => {
|
|
18835
|
-
drawGrid();
|
|
18836
|
-
drawNotes(core.getNotes());
|
|
19469
|
+
renderer.drawGrid();
|
|
19470
|
+
renderer.drawNotes(core.getNotes());
|
|
18837
19471
|
if (core.getToolMode() === "select") {
|
|
18838
|
-
drawSelectionRect(selectionRect);
|
|
19472
|
+
renderer.drawSelectionRect(selectionRect);
|
|
18839
19473
|
if (selectedNotes.length > 0) {
|
|
18840
19474
|
const selectedIds = new Set(selectedNotes.map((n) => n.id));
|
|
18841
|
-
drawSelectedNotes(core.getNotes(), selectedIds);
|
|
19475
|
+
renderer.drawSelectedNotes(core.getNotes(), selectedIds);
|
|
18842
19476
|
}
|
|
18843
19477
|
}
|
|
18844
19478
|
};
|
|
18845
19479
|
const getNotesInRect = (rect) => {
|
|
18846
|
-
const {
|
|
18847
|
-
|
|
19480
|
+
const {
|
|
19481
|
+
stepWidth,
|
|
19482
|
+
keyHeight,
|
|
19483
|
+
keyCount,
|
|
19484
|
+
pitchRangeStart,
|
|
19485
|
+
unitsPerRow: upr = UNITS_PER_SEMITONE
|
|
19486
|
+
} = renderer.getRenderConfig();
|
|
19487
|
+
const offset = renderer.getDrawOffset();
|
|
18848
19488
|
const notes = [];
|
|
18849
19489
|
for (const note of core.getNotes()) {
|
|
18850
19490
|
const logicalX = note.startStep * stepWidth;
|
|
18851
|
-
const yIndex = keyCount - 1 - (note.
|
|
19491
|
+
const yIndex = keyCount - 1 - (note.pitchUnits - pitchRangeStart) / upr;
|
|
18852
19492
|
const logicalY = yIndex * keyHeight;
|
|
18853
19493
|
const noteRect = {
|
|
18854
19494
|
x: logicalX - offset.x,
|
|
@@ -18895,7 +19535,7 @@ var createPianoRoll = (options, handlers) => {
|
|
|
18895
19535
|
const minStart = Math.min(...copiedNotes.map((n) => n.startStep));
|
|
18896
19536
|
copiedNotes.forEach((note) => {
|
|
18897
19537
|
const newStep = startStep + (note.startStep - minStart);
|
|
18898
|
-
core.addNote(newStep, note.
|
|
19538
|
+
core.addNote(newStep, note.pitchUnits, {
|
|
18899
19539
|
noteLengthSteps: note.durationSteps,
|
|
18900
19540
|
velocity: note.velocity
|
|
18901
19541
|
});
|
|
@@ -19737,10 +20377,12 @@ var createDtmStudio = async (options = {}) => {
|
|
|
19737
20377
|
);
|
|
19738
20378
|
}
|
|
19739
20379
|
if (!sfInst) return;
|
|
20380
|
+
const { midi, detuneCents } = unitsToMidiDetune(e.pitchUnits);
|
|
19740
20381
|
sfInst.play({
|
|
19741
20382
|
ctx: audioCtx,
|
|
19742
20383
|
destination: getChannelStrip(e.trackId).input,
|
|
19743
|
-
pitch:
|
|
20384
|
+
pitch: midi,
|
|
20385
|
+
detuneCents,
|
|
19744
20386
|
volume: e.volume,
|
|
19745
20387
|
velocity: e.velocity,
|
|
19746
20388
|
when: e.when,
|
|
@@ -20056,10 +20698,12 @@ var createDtmStudio = async (options = {}) => {
|
|
|
20056
20698
|
);
|
|
20057
20699
|
}
|
|
20058
20700
|
if (!sfInst) return;
|
|
20701
|
+
const { midi, detuneCents } = unitsToMidiDetune(e.pitchUnits);
|
|
20059
20702
|
sfInst.play({
|
|
20060
20703
|
ctx: audioCtx,
|
|
20061
20704
|
destination: getChannelStrip(e.trackId).input,
|
|
20062
|
-
pitch:
|
|
20705
|
+
pitch: midi,
|
|
20706
|
+
detuneCents,
|
|
20063
20707
|
volume: e.volume,
|
|
20064
20708
|
velocity: e.velocity,
|
|
20065
20709
|
when: e.when,
|
|
@@ -20145,10 +20789,12 @@ var createDtmStudio = async (options = {}) => {
|
|
|
20145
20789
|
);
|
|
20146
20790
|
}
|
|
20147
20791
|
if (!sfInst) return;
|
|
20792
|
+
const { midi, detuneCents } = unitsToMidiDetune(e.pitchUnits);
|
|
20148
20793
|
sfInst.play({
|
|
20149
20794
|
ctx: audioCtx,
|
|
20150
20795
|
destination: getChannelStrip(e.trackId).input,
|
|
20151
|
-
pitch:
|
|
20796
|
+
pitch: midi,
|
|
20797
|
+
detuneCents,
|
|
20152
20798
|
volume: e.volume,
|
|
20153
20799
|
velocity: e.velocity,
|
|
20154
20800
|
when: e.when,
|
|
@@ -20227,10 +20873,12 @@ var createDtmStudio = async (options = {}) => {
|
|
|
20227
20873
|
);
|
|
20228
20874
|
}
|
|
20229
20875
|
if (!sfInst) return;
|
|
20876
|
+
const { midi, detuneCents } = unitsToMidiDetune(e.pitchUnits);
|
|
20230
20877
|
sfInst.play({
|
|
20231
20878
|
ctx: audioCtx,
|
|
20232
20879
|
destination: getChannelStrip(e.trackId).input,
|
|
20233
|
-
pitch:
|
|
20880
|
+
pitch: midi,
|
|
20881
|
+
detuneCents,
|
|
20234
20882
|
volume: e.volume,
|
|
20235
20883
|
velocity: e.velocity,
|
|
20236
20884
|
when: e.when,
|
|
@@ -20267,10 +20915,12 @@ var createDtmStudio = async (options = {}) => {
|
|
|
20267
20915
|
sfInst = resolveSoundFont(defaultPreset, role, "simple");
|
|
20268
20916
|
}
|
|
20269
20917
|
if (!sfInst) return;
|
|
20918
|
+
const { midi, detuneCents } = unitsToMidiDetune(e.pitchUnits);
|
|
20270
20919
|
sfInst.play({
|
|
20271
20920
|
ctx: audioCtx,
|
|
20272
20921
|
destination: getChannelStrip(e.trackId).input,
|
|
20273
|
-
pitch:
|
|
20922
|
+
pitch: midi,
|
|
20923
|
+
detuneCents,
|
|
20274
20924
|
volume: e.volume,
|
|
20275
20925
|
velocity: e.velocity,
|
|
20276
20926
|
when: e.when,
|
|
@@ -20293,10 +20943,12 @@ var createDtmStudio = async (options = {}) => {
|
|
|
20293
20943
|
if (audioCtx.state === "suspended") {
|
|
20294
20944
|
await audioCtx.resume();
|
|
20295
20945
|
}
|
|
20946
|
+
const { midi, detuneCents } = unitsToMidiDetune(options2.pitchUnits);
|
|
20296
20947
|
sfInst.play({
|
|
20297
20948
|
ctx: audioCtx,
|
|
20298
20949
|
destination: masterGain,
|
|
20299
|
-
pitch:
|
|
20950
|
+
pitch: midi,
|
|
20951
|
+
detuneCents,
|
|
20300
20952
|
volume: vol / 100,
|
|
20301
20953
|
when: 0,
|
|
20302
20954
|
duration: dur
|
|
@@ -20316,7 +20968,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
20316
20968
|
// 伴奏トラック
|
|
20317
20969
|
startStep: p.startStep,
|
|
20318
20970
|
durationSteps: p.durationSteps,
|
|
20319
|
-
|
|
20971
|
+
pitchUnits: p.pitchUnits,
|
|
20320
20972
|
velocity: p.velocity
|
|
20321
20973
|
}));
|
|
20322
20974
|
const playerPreset = defaultPreset;
|
|
@@ -20324,10 +20976,12 @@ var createDtmStudio = async (options = {}) => {
|
|
|
20324
20976
|
const playPlayerNote = (e) => {
|
|
20325
20977
|
const sfInst = resolveSoundFont(playerPreset, "chord");
|
|
20326
20978
|
if (!sfInst) return;
|
|
20979
|
+
const { midi, detuneCents } = unitsToMidiDetune(e.pitchUnits);
|
|
20327
20980
|
sfInst.play({
|
|
20328
20981
|
ctx: audioCtx,
|
|
20329
20982
|
destination: getChannelStrip("chord").input,
|
|
20330
|
-
pitch:
|
|
20983
|
+
pitch: midi,
|
|
20984
|
+
detuneCents,
|
|
20331
20985
|
volume: e.volume,
|
|
20332
20986
|
velocity: e.velocity,
|
|
20333
20987
|
when: e.when,
|
|
@@ -20466,8 +21120,12 @@ var createDtmStudio = async (options = {}) => {
|
|
|
20466
21120
|
};
|
|
20467
21121
|
// Annotate the CommonJS export names for ESM import in node:
|
|
20468
21122
|
0 && (module.exports = {
|
|
21123
|
+
A4_HZ,
|
|
21124
|
+
A4_UNITS,
|
|
21125
|
+
CENTS_PER_UNIT,
|
|
20469
21126
|
DAW_CSS,
|
|
20470
21127
|
DEFAULT_BPM,
|
|
21128
|
+
DEFAULT_EDO,
|
|
20471
21129
|
DEFAULT_GATE,
|
|
20472
21130
|
DEFAULT_PAN,
|
|
20473
21131
|
DEFAULT_PLAYBACK_VELOCITY,
|
|
@@ -20477,21 +21135,30 @@ var createDtmStudio = async (options = {}) => {
|
|
|
20477
21135
|
DRUM_FONT,
|
|
20478
21136
|
DRUM_KEYS,
|
|
20479
21137
|
DRUM_PATTERNS,
|
|
21138
|
+
EDO31_NAMES,
|
|
20480
21139
|
GM_INSTRUMENT_NAMES,
|
|
20481
21140
|
INSTRUMENT_PRESETS,
|
|
21141
|
+
KEY_COUNT,
|
|
20482
21142
|
KOE_BASE_URL,
|
|
20483
21143
|
KOE_VOICEBANKS,
|
|
20484
21144
|
KOE_VOICEBANK_LABELS,
|
|
20485
21145
|
KOE_VOICEBANK_TERMS,
|
|
20486
21146
|
LinkedList,
|
|
20487
21147
|
MAX_VOCAL_VOLUME,
|
|
21148
|
+
MICRO_STEP,
|
|
20488
21149
|
MMLCore,
|
|
20489
21150
|
MML_END_MARKER,
|
|
20490
21151
|
MidiSearchClient,
|
|
21152
|
+
PITCH_ENCODING_VERSION,
|
|
20491
21153
|
PITCH_MAP,
|
|
21154
|
+
PITCH_RANGE_END,
|
|
21155
|
+
PITCH_RANGE_START,
|
|
20492
21156
|
PREWARM_NOTES,
|
|
20493
21157
|
TRACKS_ADVANCED,
|
|
20494
21158
|
TRACKS_SIMPLE,
|
|
21159
|
+
UNITS_PER_EDO31_DEGREE,
|
|
21160
|
+
UNITS_PER_OCTAVE,
|
|
21161
|
+
UNITS_PER_SEMITONE,
|
|
20495
21162
|
VIBRATO_MIN_SEC,
|
|
20496
21163
|
VOICE_IMAGES,
|
|
20497
21164
|
VOICE_IMAGE_KEY,
|
|
@@ -20501,6 +21168,7 @@ var createDtmStudio = async (options = {}) => {
|
|
|
20501
21168
|
buildChordPlacements,
|
|
20502
21169
|
buildDrumPatternJson,
|
|
20503
21170
|
buildNameToKeyMapping,
|
|
21171
|
+
chromaticStep,
|
|
20504
21172
|
collectPitchTokens,
|
|
20505
21173
|
concatFloat32,
|
|
20506
21174
|
createAudioContext,
|
|
@@ -20509,19 +21177,13 @@ var createDtmStudio = async (options = {}) => {
|
|
|
20509
21177
|
createKoeVoice,
|
|
20510
21178
|
createLyricsConductor,
|
|
20511
21179
|
createPianoRoll,
|
|
21180
|
+
createRenderer,
|
|
20512
21181
|
createSequencer,
|
|
20513
21182
|
createSingingVoices,
|
|
20514
21183
|
createSynth,
|
|
20515
21184
|
createVoiceRegistry,
|
|
20516
21185
|
decodeMml,
|
|
20517
21186
|
decomposeToMonophonic,
|
|
20518
|
-
drawGrid,
|
|
20519
|
-
drawHeader,
|
|
20520
|
-
drawKeyboard,
|
|
20521
|
-
drawNoteLyrics,
|
|
20522
|
-
drawNotes,
|
|
20523
|
-
drawSelectedNotes,
|
|
20524
|
-
drawSelectionRect,
|
|
20525
21187
|
encodeMml,
|
|
20526
21188
|
encodeWavPCM16,
|
|
20527
21189
|
exportMIDI,
|
|
@@ -20529,36 +21191,34 @@ var createDtmStudio = async (options = {}) => {
|
|
|
20529
21191
|
extractMidiDrumPattern,
|
|
20530
21192
|
extractMidiPlacements,
|
|
20531
21193
|
extractMidiPlacementsByTrack,
|
|
21194
|
+
fifthToStep,
|
|
21195
|
+
fifthToUnits,
|
|
20532
21196
|
formatMmlMeta,
|
|
20533
21197
|
freqFromPitch,
|
|
20534
21198
|
generateRandomPattern,
|
|
20535
|
-
getDrawOffset,
|
|
20536
21199
|
getDrumPatternKeys,
|
|
20537
|
-
getGridCanvas,
|
|
20538
|
-
getGridContext,
|
|
20539
|
-
getGridPosition,
|
|
20540
|
-
getHeaderCanvas,
|
|
20541
21200
|
getMidiBPM,
|
|
20542
|
-
getRenderConfig,
|
|
20543
|
-
getXY,
|
|
20544
21201
|
icon,
|
|
20545
|
-
init,
|
|
20546
21202
|
injectStyles,
|
|
20547
21203
|
isChordHeavyTrack,
|
|
21204
|
+
isNaturalLetter,
|
|
20548
21205
|
isPlausibleMidiTranscription,
|
|
20549
21206
|
isValidHttpUrl,
|
|
21207
|
+
keyCountFor,
|
|
20550
21208
|
koeUrl,
|
|
21209
|
+
midiToUnits,
|
|
20551
21210
|
mountChordPlayer,
|
|
20552
21211
|
mountDAW,
|
|
20553
21212
|
mountMmlPlayer,
|
|
21213
|
+
naturalStep,
|
|
20554
21214
|
normalizeDrumPatterns,
|
|
20555
21215
|
normalizeLyrics,
|
|
20556
|
-
onClick,
|
|
20557
21216
|
panToStereo,
|
|
20558
21217
|
parseCustomVocals,
|
|
20559
21218
|
parseLyrics,
|
|
20560
21219
|
parseMML,
|
|
20561
21220
|
parseMmlMeta,
|
|
21221
|
+
pitchV1ToUnits,
|
|
20562
21222
|
playChords,
|
|
20563
21223
|
playMML,
|
|
20564
21224
|
playNote,
|
|
@@ -20566,13 +21226,18 @@ var createDtmStudio = async (options = {}) => {
|
|
|
20566
21226
|
playSingingMML,
|
|
20567
21227
|
resolveDrumPattern,
|
|
20568
21228
|
resolveLoopPoint,
|
|
20569
|
-
setBackgroundActive,
|
|
20570
|
-
setDrawOffset,
|
|
20571
21229
|
shiftNotes,
|
|
20572
21230
|
showLoadingOverlay,
|
|
21231
|
+
spellingToUnits,
|
|
20573
21232
|
stripCustomVocals,
|
|
20574
21233
|
stripLyrics,
|
|
20575
21234
|
stripMmlMeta,
|
|
20576
21235
|
transposeNotes,
|
|
21236
|
+
unitsPerRow,
|
|
21237
|
+
unitsPerStep,
|
|
21238
|
+
unitsToHz,
|
|
21239
|
+
unitsToMidi,
|
|
21240
|
+
unitsToMidiDetune,
|
|
21241
|
+
unitsToPitchV1,
|
|
20577
21242
|
vocalVolumeToGain
|
|
20578
21243
|
});
|