@waveform-playlist/core 12.6.0 → 12.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +80 -1
- package/dist/index.d.ts +80 -1
- package/dist/index.js +177 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +168 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -456,6 +456,37 @@ function computeMusicalTicks(params) {
|
|
|
456
456
|
};
|
|
457
457
|
return result;
|
|
458
458
|
}
|
|
459
|
+
function ticksToBarBeat(tick, meterEntries, ppqn) {
|
|
460
|
+
if (ppqn <= 0) return { bar: 1, beat: 1 };
|
|
461
|
+
const entries = meterEntries.length > 0 ? meterEntries : [{ tick: 0, numerator: 4, denominator: 4 }];
|
|
462
|
+
let barOffset = 0;
|
|
463
|
+
for (let i = 0; i < entries.length; i++) {
|
|
464
|
+
const meter = entries[i];
|
|
465
|
+
const segmentStart = meter.tick;
|
|
466
|
+
const segmentEnd = i + 1 < entries.length ? entries[i + 1].tick : Number.MAX_SAFE_INTEGER;
|
|
467
|
+
const ts2 = [meter.numerator, meter.denominator];
|
|
468
|
+
const tpBar2 = ticksPerBar(ts2, ppqn);
|
|
469
|
+
const tpBeat2 = ticksPerBeat(ts2, ppqn);
|
|
470
|
+
if (tick >= segmentStart && tick < segmentEnd) {
|
|
471
|
+
const offset2 = tick - segmentStart;
|
|
472
|
+
const barIndexInSegment2 = Math.floor(offset2 / tpBar2);
|
|
473
|
+
const beatInBar2 = Math.floor(offset2 % tpBar2 / tpBeat2);
|
|
474
|
+
return { bar: barOffset + barIndexInSegment2 + 1, beat: beatInBar2 + 1 };
|
|
475
|
+
}
|
|
476
|
+
if (segmentEnd !== Number.MAX_SAFE_INTEGER) {
|
|
477
|
+
const segmentLen = segmentEnd - segmentStart;
|
|
478
|
+
barOffset += Math.floor(segmentLen / tpBar2);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
const first = entries[0];
|
|
482
|
+
const ts = [first.numerator, first.denominator];
|
|
483
|
+
const tpBar = ticksPerBar(ts, ppqn);
|
|
484
|
+
const tpBeat = ticksPerBeat(ts, ppqn);
|
|
485
|
+
const offset = tick - first.tick;
|
|
486
|
+
const barIndexInSegment = Math.floor(offset / tpBar);
|
|
487
|
+
const beatInBar = Math.floor(offset % tpBar / tpBeat);
|
|
488
|
+
return { bar: barIndexInSegment + 1, beat: beatInBar + 1 };
|
|
489
|
+
}
|
|
459
490
|
function snapTickToGrid(tick, snapTo, meterEntries, ppqn = 960) {
|
|
460
491
|
if (snapTo === "off") return tick;
|
|
461
492
|
let meter = meterEntries[0] ?? { tick: 0, numerator: 4, denominator: 4 };
|
|
@@ -800,6 +831,14 @@ function applyFadeOut(param, startTime, duration, type = "linear", startValue =
|
|
|
800
831
|
}
|
|
801
832
|
|
|
802
833
|
// src/keyboard.ts
|
|
834
|
+
function matchesKeyBinding(event, binding) {
|
|
835
|
+
const keyMatch = event.key.toLowerCase() === binding.key.toLowerCase() || event.key === binding.key;
|
|
836
|
+
const ctrlMatch = binding.ctrlKey === void 0 || event.ctrlKey === binding.ctrlKey;
|
|
837
|
+
const shiftMatch = binding.shiftKey === void 0 || event.shiftKey === binding.shiftKey;
|
|
838
|
+
const metaMatch = binding.metaKey === void 0 || event.metaKey === binding.metaKey;
|
|
839
|
+
const altMatch = binding.altKey === void 0 || event.altKey === binding.altKey;
|
|
840
|
+
return keyMatch && ctrlMatch && shiftMatch && metaMatch && altMatch;
|
|
841
|
+
}
|
|
803
842
|
function handleKeyboardEvent(event, shortcuts, enabled) {
|
|
804
843
|
if (!enabled) return;
|
|
805
844
|
if (event.repeat) return;
|
|
@@ -807,14 +846,7 @@ function handleKeyboardEvent(event, shortcuts, enabled) {
|
|
|
807
846
|
if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable) {
|
|
808
847
|
return;
|
|
809
848
|
}
|
|
810
|
-
const matchingShortcut = shortcuts.find((shortcut) =>
|
|
811
|
-
const keyMatch = event.key.toLowerCase() === shortcut.key.toLowerCase() || event.key === shortcut.key;
|
|
812
|
-
const ctrlMatch = shortcut.ctrlKey === void 0 || event.ctrlKey === shortcut.ctrlKey;
|
|
813
|
-
const shiftMatch = shortcut.shiftKey === void 0 || event.shiftKey === shortcut.shiftKey;
|
|
814
|
-
const metaMatch = shortcut.metaKey === void 0 || event.metaKey === shortcut.metaKey;
|
|
815
|
-
const altMatch = shortcut.altKey === void 0 || event.altKey === shortcut.altKey;
|
|
816
|
-
return keyMatch && ctrlMatch && shiftMatch && metaMatch && altMatch;
|
|
817
|
-
});
|
|
849
|
+
const matchingShortcut = shortcuts.find((shortcut) => matchesKeyBinding(event, shortcut));
|
|
818
850
|
if (matchingShortcut) {
|
|
819
851
|
if (matchingShortcut.preventDefault !== false) {
|
|
820
852
|
event.preventDefault();
|
|
@@ -915,13 +947,137 @@ function watchMicrophoneDevices(listener, mediaDevices) {
|
|
|
915
947
|
md.removeEventListener("devicechange", deliver);
|
|
916
948
|
};
|
|
917
949
|
}
|
|
950
|
+
|
|
951
|
+
// src/annotations/boundaries.ts
|
|
952
|
+
var LINK_THRESHOLD = 0.01;
|
|
953
|
+
var MIN_ANNOTATION_DURATION = 0.1;
|
|
954
|
+
var ANNOTATION_LINK_THRESHOLD_TICKS = 0.5;
|
|
955
|
+
function annotationMinDurationTicks(ppqn) {
|
|
956
|
+
return Math.max(1, Math.round(ppqn / 32));
|
|
957
|
+
}
|
|
958
|
+
function updateAnnotationBoundaries(params, options = {}) {
|
|
959
|
+
const { annotationIndex, newTime, isDraggingStart, annotations, duration, linkEndpoints } = params;
|
|
960
|
+
const linkThreshold = options.linkThreshold ?? LINK_THRESHOLD;
|
|
961
|
+
const minDuration = options.minDuration ?? MIN_ANNOTATION_DURATION;
|
|
962
|
+
const updatedAnnotations = [...annotations];
|
|
963
|
+
const annotation = annotations[annotationIndex];
|
|
964
|
+
if (isDraggingStart) {
|
|
965
|
+
const constrainedStart = Math.min(annotation.end - minDuration, Math.max(0, newTime));
|
|
966
|
+
const delta = constrainedStart - annotation.start;
|
|
967
|
+
updatedAnnotations[annotationIndex] = { ...annotation, start: constrainedStart };
|
|
968
|
+
if (linkEndpoints && annotationIndex > 0) {
|
|
969
|
+
const prevAnnotation = updatedAnnotations[annotationIndex - 1];
|
|
970
|
+
if (Math.abs(prevAnnotation.end - annotation.start) < linkThreshold) {
|
|
971
|
+
updatedAnnotations[annotationIndex - 1] = {
|
|
972
|
+
...prevAnnotation,
|
|
973
|
+
end: Math.max(prevAnnotation.start + minDuration, prevAnnotation.end + delta)
|
|
974
|
+
};
|
|
975
|
+
} else if (constrainedStart <= prevAnnotation.end) {
|
|
976
|
+
updatedAnnotations[annotationIndex] = {
|
|
977
|
+
...updatedAnnotations[annotationIndex],
|
|
978
|
+
start: prevAnnotation.end
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
} else if (!linkEndpoints && annotationIndex > 0 && constrainedStart < updatedAnnotations[annotationIndex - 1].end) {
|
|
982
|
+
updatedAnnotations[annotationIndex - 1] = {
|
|
983
|
+
...updatedAnnotations[annotationIndex - 1],
|
|
984
|
+
end: constrainedStart
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
} else {
|
|
988
|
+
const constrainedEnd = Math.max(annotation.start + minDuration, Math.min(newTime, duration));
|
|
989
|
+
const delta = constrainedEnd - annotation.end;
|
|
990
|
+
updatedAnnotations[annotationIndex] = { ...annotation, end: constrainedEnd };
|
|
991
|
+
if (linkEndpoints && annotationIndex < updatedAnnotations.length - 1) {
|
|
992
|
+
const nextAnnotation = updatedAnnotations[annotationIndex + 1];
|
|
993
|
+
if (Math.abs(nextAnnotation.start - annotation.end) < linkThreshold) {
|
|
994
|
+
const newStart = nextAnnotation.start + delta;
|
|
995
|
+
updatedAnnotations[annotationIndex + 1] = {
|
|
996
|
+
...nextAnnotation,
|
|
997
|
+
start: Math.min(nextAnnotation.end - minDuration, newStart)
|
|
998
|
+
};
|
|
999
|
+
let currentIndex = annotationIndex + 1;
|
|
1000
|
+
while (currentIndex < updatedAnnotations.length - 1) {
|
|
1001
|
+
const current = updatedAnnotations[currentIndex];
|
|
1002
|
+
const next = updatedAnnotations[currentIndex + 1];
|
|
1003
|
+
if (Math.abs(next.start - current.end) < linkThreshold) {
|
|
1004
|
+
const nextDelta = current.end - annotations[currentIndex].end;
|
|
1005
|
+
updatedAnnotations[currentIndex + 1] = {
|
|
1006
|
+
...next,
|
|
1007
|
+
start: Math.min(next.end - minDuration, next.start + nextDelta)
|
|
1008
|
+
};
|
|
1009
|
+
currentIndex++;
|
|
1010
|
+
} else {
|
|
1011
|
+
break;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
} else if (constrainedEnd >= nextAnnotation.start) {
|
|
1015
|
+
updatedAnnotations[annotationIndex] = {
|
|
1016
|
+
...updatedAnnotations[annotationIndex],
|
|
1017
|
+
end: nextAnnotation.start
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
} else if (!linkEndpoints && annotationIndex < updatedAnnotations.length - 1 && constrainedEnd > updatedAnnotations[annotationIndex + 1].start) {
|
|
1021
|
+
const nextAnnotation = updatedAnnotations[annotationIndex + 1];
|
|
1022
|
+
updatedAnnotations[annotationIndex + 1] = { ...nextAnnotation, start: constrainedEnd };
|
|
1023
|
+
let currentIndex = annotationIndex + 1;
|
|
1024
|
+
while (currentIndex < updatedAnnotations.length - 1) {
|
|
1025
|
+
const current = updatedAnnotations[currentIndex];
|
|
1026
|
+
const next = updatedAnnotations[currentIndex + 1];
|
|
1027
|
+
if (current.end > next.start) {
|
|
1028
|
+
updatedAnnotations[currentIndex + 1] = { ...next, start: current.end };
|
|
1029
|
+
currentIndex++;
|
|
1030
|
+
} else {
|
|
1031
|
+
break;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
return updatedAnnotations;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
// src/annotations/shortcuts.ts
|
|
1040
|
+
var noMods = { ctrlKey: false, metaKey: false };
|
|
1041
|
+
var DEFAULT_ANNOTATION_SHORTCUTS = {
|
|
1042
|
+
selectPrevious: [
|
|
1043
|
+
{ key: "ArrowUp", ...noMods },
|
|
1044
|
+
{ key: "ArrowLeft", ...noMods }
|
|
1045
|
+
],
|
|
1046
|
+
selectNext: [
|
|
1047
|
+
{ key: "ArrowDown", ...noMods },
|
|
1048
|
+
{ key: "ArrowRight", ...noMods }
|
|
1049
|
+
],
|
|
1050
|
+
selectFirst: [{ key: "Home", ...noMods }],
|
|
1051
|
+
selectLast: [{ key: "End", ...noMods }],
|
|
1052
|
+
clearSelection: [{ key: "Escape", ...noMods }],
|
|
1053
|
+
moveStartEarlier: [{ key: "[", ...noMods }],
|
|
1054
|
+
moveStartLater: [{ key: "]", ...noMods }],
|
|
1055
|
+
// '{' / '}' are what event.key reports for Shift+[ / Shift+] — no explicit
|
|
1056
|
+
// shiftKey needed; the key value itself encodes it.
|
|
1057
|
+
moveEndEarlier: [{ key: "{", ...noMods }],
|
|
1058
|
+
moveEndLater: [{ key: "}", ...noMods }],
|
|
1059
|
+
playActive: [{ key: "Enter", ...noMods }]
|
|
1060
|
+
};
|
|
1061
|
+
var ALL_ACTIONS = Object.keys(DEFAULT_ANNOTATION_SHORTCUTS);
|
|
1062
|
+
function resolveAnnotationShortcuts(remap) {
|
|
1063
|
+
return ALL_ACTIONS.flatMap((action) => {
|
|
1064
|
+
const override = remap?.[action];
|
|
1065
|
+
const bindings = override ? [override] : DEFAULT_ANNOTATION_SHORTCUTS[action];
|
|
1066
|
+
return bindings.map((binding) => ({ action, binding }));
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
918
1069
|
export {
|
|
1070
|
+
ANNOTATION_LINK_THRESHOLD_TICKS,
|
|
1071
|
+
DEFAULT_ANNOTATION_SHORTCUTS,
|
|
919
1072
|
DEFAULT_SPECTROGRAM_COLOR_MAP,
|
|
920
1073
|
InteractionState,
|
|
1074
|
+
LINK_THRESHOLD,
|
|
921
1075
|
MAX_CANVAS_WIDTH,
|
|
1076
|
+
MIN_ANNOTATION_DURATION,
|
|
922
1077
|
MIN_PIXELS_PER_UNIT,
|
|
923
1078
|
PPQN,
|
|
924
1079
|
SPECTROGRAM_DEFAULTS,
|
|
1080
|
+
annotationMinDurationTicks,
|
|
925
1081
|
appendPeaks,
|
|
926
1082
|
appendToAudioBuffer,
|
|
927
1083
|
applyFadeIn,
|
|
@@ -959,11 +1115,13 @@ export {
|
|
|
959
1115
|
handleKeyboardEvent,
|
|
960
1116
|
linearCurve,
|
|
961
1117
|
logarithmicCurve,
|
|
1118
|
+
matchesKeyBinding,
|
|
962
1119
|
normalizedToDb,
|
|
963
1120
|
parseSpectrogramCanvasId,
|
|
964
1121
|
pixelsToSamples,
|
|
965
1122
|
pixelsToSeconds,
|
|
966
1123
|
probeRangeSupport,
|
|
1124
|
+
resolveAnnotationShortcuts,
|
|
967
1125
|
resolveRecordingOffsetSamples,
|
|
968
1126
|
sCurveCurve,
|
|
969
1127
|
samplesToPixels,
|
|
@@ -977,8 +1135,10 @@ export {
|
|
|
977
1135
|
sortClipsByTime,
|
|
978
1136
|
ticksPerBar,
|
|
979
1137
|
ticksPerBeat,
|
|
1138
|
+
ticksToBarBeat,
|
|
980
1139
|
ticksToSamples,
|
|
981
1140
|
trackChannelCount,
|
|
1141
|
+
updateAnnotationBoundaries,
|
|
982
1142
|
watchMicrophoneDevices
|
|
983
1143
|
};
|
|
984
1144
|
//# sourceMappingURL=index.mjs.map
|