@onjmin/dtm 2.0.3 → 2.1.0

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.
@@ -517,12 +517,60 @@
517
517
  var VIBRATO_RATE_HZ = 5.5;
518
518
  var VIBRATO_DEPTH_CENTS = 35;
519
519
  var VIBRATO_FADE_MS = 150;
520
- var vibratoPitchCurve = (baseHz, preMs) => (tMs) => {
521
- const sinceOnset = tMs - preMs;
522
- if (sinceOnset <= 0) return baseHz;
523
- const depth = VIBRATO_DEPTH_CENTS * Math.min(1, sinceOnset / VIBRATO_FADE_MS);
524
- const cents = depth * Math.sin(2 * Math.PI * VIBRATO_RATE_HZ * sinceOnset / 1e3);
525
- return baseHz * 2 ** (cents / 1200);
520
+ var vibratoRatio = (sinceOnsetMs) => {
521
+ if (sinceOnsetMs <= 0) return 1;
522
+ const depth = VIBRATO_DEPTH_CENTS * Math.min(1, sinceOnsetMs / VIBRATO_FADE_MS);
523
+ const cents = depth * Math.sin(2 * Math.PI * VIBRATO_RATE_HZ * sinceOnsetMs / 1e3);
524
+ return 2 ** (cents / 1200);
525
+ };
526
+
527
+ // src/pitch-curve.ts
528
+ var STEP_GLIDE_MS = 15;
529
+ var PORTAMENTO_MS = 70;
530
+ var MIN_GLIDE_MS = 4;
531
+ var unitsToHz = (units) => 440 * 2 ** ((units - 2139) / 372);
532
+ var smoothstep = (u) => u * u * (3 - 2 * u);
533
+ var toKeyframes = (segments) => {
534
+ const kf = [];
535
+ let prevAtMs = 0;
536
+ for (const seg of segments) {
537
+ const atMs = seg.atSec * 1e3;
538
+ const glideMs = Math.max(
539
+ MIN_GLIDE_MS,
540
+ Math.min(
541
+ seg.portamento ? PORTAMENTO_MS : STEP_GLIDE_MS,
542
+ (atMs - prevAtMs) * 0.5
543
+ )
544
+ );
545
+ kf.push({
546
+ hz: unitsToHz(seg.pitch),
547
+ startMs: Math.max(0, atMs - glideMs / 2),
548
+ glideMs
549
+ });
550
+ prevAtMs = atMs;
551
+ }
552
+ return kf;
553
+ };
554
+ var pitchCurveFor = (baseHz, segments, preMs, vibrato) => {
555
+ const kf = segments?.length ? toKeyframes(segments) : null;
556
+ if (!kf && !vibrato) return baseHz;
557
+ return (tMs) => {
558
+ const sinceOnset = tMs - preMs;
559
+ let hz = baseHz;
560
+ if (kf && sinceOnset > 0) {
561
+ for (const k of kf) {
562
+ if (sinceOnset >= k.startMs + k.glideMs) {
563
+ hz = k.hz;
564
+ continue;
565
+ }
566
+ if (sinceOnset <= k.startMs) break;
567
+ const u = smoothstep((sinceOnset - k.startMs) / k.glideMs);
568
+ hz = hz * (k.hz / hz) ** u;
569
+ break;
570
+ }
571
+ }
572
+ return vibrato ? hz * vibratoRatio(sinceOnset) : hz;
573
+ };
526
574
  };
527
575
 
528
576
  // src/voice-worker-types.ts
@@ -567,7 +615,7 @@
567
615
  const consonantMs = (consonantPcm.length - xfade / 2) / sampleRate * 1e3;
568
616
  return { pcm, preMs: consonantMs, consonantMs };
569
617
  };
570
- var renderComposite = async (consonantAlias, vowelAlias, pitch, durationMs, vibrato, gender, breathiness, tension) => {
618
+ var renderComposite = async (consonantAlias, vowelAlias, pitch, durationMs, vibrato, gender, breathiness, tension, pitchSegments) => {
571
619
  if (!worldline) return null;
572
620
  const [consonantPcm, vowelPcm] = await Promise.all([
573
621
  getPcm(consonantAlias),
@@ -579,7 +627,7 @@
579
627
  const targetHz = unitsToFreq(pitch);
580
628
  const audio = worldline.renderNote({
581
629
  pcm: spliced.pcm,
582
- pitch: vibrato ? vibratoPitchCurve(targetHz, spliced.preMs) : targetHz,
630
+ pitch: pitchCurveFor(targetHz, pitchSegments, spliced.preMs, !!vibrato),
583
631
  durationMs,
584
632
  preMs: spliced.preMs,
585
633
  consonantMs: spliced.consonantMs,
@@ -589,7 +637,7 @@
589
637
  });
590
638
  return audio ? { pcm: audio, preSec: spliced.preMs / 1e3, rate: 1 } : null;
591
639
  };
592
- var renderAlias = async (alias, pitch, durationMs, vibrato, gender, breathiness, tension) => {
640
+ var renderAlias = async (alias, pitch, durationMs, vibrato, gender, breathiness, tension, pitchSegments) => {
593
641
  if (!bank) return null;
594
642
  const composite = unpackCompositeAlias(alias);
595
643
  if (composite) {
@@ -601,7 +649,8 @@
601
649
  vibrato,
602
650
  gender,
603
651
  breathiness,
604
- tension
652
+ tension,
653
+ pitchSegments
605
654
  );
606
655
  }
607
656
  const pcm = await getPcm(alias);
@@ -612,7 +661,7 @@
612
661
  if (worldline) {
613
662
  const audio = worldline.renderNote({
614
663
  pcm,
615
- pitch: vibrato ? vibratoPitchCurve(targetHz, lead.preMs) : targetHz,
664
+ pitch: pitchCurveFor(targetHz, pitchSegments, lead.preMs, !!vibrato),
616
665
  durationMs,
617
666
  ...lead,
618
667
  gender,
@@ -657,7 +706,8 @@
657
706
  vibrato,
658
707
  gender,
659
708
  breathiness,
660
- tension
709
+ tension,
710
+ pitchSegments
661
711
  } = msg;
662
712
  try {
663
713
  const out = await renderAlias(
@@ -667,7 +717,8 @@
667
717
  vibrato,
668
718
  gender,
669
719
  breathiness,
670
- tension
720
+ tension,
721
+ pitchSegments
671
722
  );
672
723
  if (out) {
673
724
  wself.postMessage(
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "author": "onjmin",
3
3
  "license": "MIT",
4
4
  "name": "@onjmin/dtm",
5
- "version": "2.0.3",
5
+ "version": "2.1.0",
6
6
  "description": "MMLを中間言語に用いた、モバイルファーストなDAW / ピアノロール打ち込みコンポーネント",
7
7
  "homepage": "https://onjmin.github.io/dtm",
8
8
  "repository": {
@@ -62,6 +62,7 @@
62
62
  "dev": "rimraf dist && tsup && biome format src --write && tsx demo/server.ts",
63
63
  "build": "rimraf dist && tsup && biome format src --write",
64
64
  "check": "biome check src --write",
65
+ "test": "tsx scripts/check-compose.ts",
65
66
  "patch": "pnpm version patch",
66
67
  "minor": "pnpm version minor",
67
68
  "major": "pnpm version major",