@waveform-playlist/core 12.5.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/README.md +86 -0
- package/dist/index.d.mts +158 -1
- package/dist/index.d.ts +158 -1
- package/dist/index.js +276 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +263 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# @waveform-playlist/core
|
|
2
|
+
|
|
3
|
+
Core types, interfaces and utilities for waveform-playlist — pure functions and TypeScript types with zero runtime dependencies.
|
|
4
|
+
|
|
5
|
+
Used by nearly every `@waveform-playlist/*` and `@dawcore/*` package (`engine`, `playout`, `media-element-playout`, `browser`, `ui-components`, `recording`, `annotations`, `spectrogram`, `midi`, `transport`, `dawcore`, `dawcore-spectrogram`, `dawcore-midi`, `webaudio-peaks`, `loaders`, and more). Most consumers get it transitively — install it directly only if you're building on the clip model, timeline math, or fade curves yourself.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Clip & track types** — `AudioClip`, `ClipTrack`, `Timeline`, `Fade`/`FadeType`, `Peaks`/`Bits`/`PeakData`, `WaveformDataObject`
|
|
10
|
+
- **Sample-based timeline math** — `createClip`/`createClipFromSeconds`/`createClipFromTicks`, clip queries (`getClipsInRange`, `clipsOverlap`, `findGaps`), and second-conversion helpers (`clipStartTime`, `clipEndTime`, `clipPixelWidth`)
|
|
11
|
+
- **Fade curves** — linear, exponential, logarithmic, and S-curve generators for native Web Audio `AudioParam` (no Tone.js dependency)
|
|
12
|
+
- **Decibel utilities** — gain ↔ dB ↔ normalized-0-1 conversions for meters and volume controls
|
|
13
|
+
- **Peaks generation** — real-time min/max peak extraction during recording
|
|
14
|
+
- **Musical time** — PPQN tick math, bar/beat conversion, time-signature meter detection, grid snapping
|
|
15
|
+
- **Keyboard shortcuts** — a framework-agnostic shortcut matcher shared by the React and Web Components layers
|
|
16
|
+
- **Spectrogram canvas-ID contract** — canonical build/parse helpers for the `${clipId}-ch${n}-chunk${m}` canvas ID format shared by the worker pool and rendering layers
|
|
17
|
+
- **HTTP range support probing** — detect whether an audio host supports byte-range seeking
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @waveform-playlist/core
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Zero dependencies — safe to install anywhere without pulling in Web Audio, React, or Tone.js.
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { createClipFromSeconds, clipStartTime, clipEndTime, gainToDb } from '@waveform-playlist/core';
|
|
31
|
+
|
|
32
|
+
// Build a clip from a decoded AudioBuffer, positioned at 2.5s on the timeline
|
|
33
|
+
const clip = createClipFromSeconds({
|
|
34
|
+
audioBuffer,
|
|
35
|
+
startTime: 2.5,
|
|
36
|
+
offset: 0,
|
|
37
|
+
fadeIn: { duration: 0.5, type: 'linear' },
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
clipStartTime(clip); // 2.5
|
|
41
|
+
clipEndTime(clip); // 2.5 + audioBuffer.duration
|
|
42
|
+
|
|
43
|
+
// Convert a linear gain value to dB for a Tone.js Volume node
|
|
44
|
+
gainToDb(0.5); // ≈ -6.02
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { buildSpectrogramCanvasId, parseSpectrogramCanvasId } from '@waveform-playlist/core';
|
|
49
|
+
|
|
50
|
+
const id = buildSpectrogramCanvasId({ clipId: 'clip-1', channelIndex: 0, chunkIndex: 3 });
|
|
51
|
+
// "clip-1-ch0-chunk3"
|
|
52
|
+
|
|
53
|
+
parseSpectrogramCanvasId(id);
|
|
54
|
+
// { clipId: 'clip-1', channelIndex: 0, chunkIndex: 3 }
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## API
|
|
58
|
+
|
|
59
|
+
| Module | Key exports |
|
|
60
|
+
|---|---|
|
|
61
|
+
| `types/` | `AudioClip`, `ClipTrack`, `Timeline`, `Track`, `Fade`, `FadeType`, `Peaks`, `Bits`, `PeakData`, `WaveformDataObject`, `SpectrogramConfig`, `RenderMode`, `ColorMapValue` |
|
|
62
|
+
| `clipTimeHelpers` | `clipStartTime`, `clipEndTime`, `clipOffsetTime`, `clipDurationTime`, `clipPixelWidth`, `trackChannelCount` |
|
|
63
|
+
| clip constructors (`types/clip`) | `createClip`, `createClipFromSeconds`, `createClipFromTicks`, `createTrack`, `createTimeline`, `getClipsInRange`, `getClipsAtSample`, `clipsOverlap`, `sortClipsByTime`, `findGaps` |
|
|
64
|
+
| `fades` | `applyFadeIn`, `applyFadeOut`, `generateCurve`, `linearCurve`, `exponentialCurve`, `logarithmicCurve`, `sCurveCurve` |
|
|
65
|
+
| `utils/dBUtils` | `gainToDb`, `dBToNormalized`, `normalizedToDb`, `gainToNormalized` |
|
|
66
|
+
| `utils/conversions` | `samplesToSeconds`, `secondsToSamples`, `samplesToPixels`, `pixelsToSamples`, `pixelsToSeconds`, `secondsToPixels` |
|
|
67
|
+
| `utils/beatsAndBars` | `PPQN`, `ticksPerBeat`, `ticksPerBar`, `ticksToSamples`, `samplesToTicks`, `snapToGrid` |
|
|
68
|
+
| `utils/musicalTicks` | `computeMusicalTicks`, `snapToTicks`, `snapTickToGrid`, `SnapTo`, `MusicalTick` |
|
|
69
|
+
| `utils/meterDetection` | `MeterEntry`, `detectMeterChanges` |
|
|
70
|
+
| `utils/peaksGenerator` | `generatePeaks`, `appendPeaks` |
|
|
71
|
+
| `utils/audioBufferUtils` | `concatenateAudioData`, `createAudioBuffer`, `appendToAudioBuffer`, `calculateDuration` |
|
|
72
|
+
| `utils/latency` | `resolveRecordingOffsetSamples` — override-vs-auto recording latency compensation (shared by React + dawcore) |
|
|
73
|
+
| `utils/carveClipRange` | `carveClipRange` — punch-in replace: carve a sample range out of a clip list (trim/remove/split) |
|
|
74
|
+
| `keyboard` | `KeyboardShortcut`, `handleKeyboardEvent`, `getShortcutLabel` |
|
|
75
|
+
| `spectrogramCanvasId` | `buildSpectrogramCanvasId`, `parseSpectrogramCanvasId`, `SpectrogramCanvasIdParts` |
|
|
76
|
+
| `probeRangeSupport` | `probeRangeSupport`, `RangeSupport` |
|
|
77
|
+
| `enumerateMicrophones` | `enumerateMicrophones`, `watchMicrophoneDevices`, `MicrophoneEnumeration` — permission-free device listing with `supported`/`hasLabels` flags (labels are browser-redacted pre-permission) |
|
|
78
|
+
| `constants` | `MAX_CANVAS_WIDTH` |
|
|
79
|
+
|
|
80
|
+
## Examples & Documentation
|
|
81
|
+
|
|
82
|
+
- Guides: [naomiaro.github.io/waveform-playlist](https://naomiaro.github.io/waveform-playlist/)
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
MIT
|
package/dist/index.d.mts
CHANGED
|
@@ -476,6 +476,10 @@ interface AnnotationData {
|
|
|
476
476
|
end: number;
|
|
477
477
|
lines: string[];
|
|
478
478
|
language?: string;
|
|
479
|
+
/** Musical position (ticks). Authoritative when BOTH tick fields are set —
|
|
480
|
+
* start/end seconds are then a derived cache (clip startTick pattern). */
|
|
481
|
+
startTick?: number;
|
|
482
|
+
endTick?: number;
|
|
479
483
|
}
|
|
480
484
|
/**
|
|
481
485
|
* Annotation format definition for parsing/serializing
|
|
@@ -775,6 +779,21 @@ declare const MIN_PIXELS_PER_UNIT = 8;
|
|
|
775
779
|
* correct across meter changes.
|
|
776
780
|
*/
|
|
777
781
|
declare function computeMusicalTicks(params: MusicalTickParams): MusicalTickData;
|
|
782
|
+
/**
|
|
783
|
+
* Convert an absolute tick to a 1-based bar.beat position, honoring meter
|
|
784
|
+
* changes. Bars/beats are counted per meter segment (the same walk
|
|
785
|
+
* `computeMusicalTicks` uses to build its `barOffset` accumulator). Beat is
|
|
786
|
+
* the integer beat the tick falls WITHIN (floor, 1-based) — a tick exactly on
|
|
787
|
+
* a bar line is beat 1.
|
|
788
|
+
*
|
|
789
|
+
* Guards: empty `meterEntries` → treat as 4/4 from tick 0. `ppqn <= 0` →
|
|
790
|
+
* returns `{ bar: 1, beat: 1 }` (division-by-zero guard, matches
|
|
791
|
+
* `computeMusicalTicks`'s ppqn guard).
|
|
792
|
+
*/
|
|
793
|
+
declare function ticksToBarBeat(tick: number, meterEntries: MeterEntry[], ppqn: number): {
|
|
794
|
+
bar: number;
|
|
795
|
+
beat: number;
|
|
796
|
+
};
|
|
778
797
|
/**
|
|
779
798
|
* Snaps a tick position to the nearest grid boundary defined by `snapTo`.
|
|
780
799
|
*
|
|
@@ -880,6 +899,37 @@ declare const SPECTROGRAM_DEFAULTS: Required<Omit<SpectrogramConfig, 'maxFrequen
|
|
|
880
899
|
/** Default color map when none is specified. */
|
|
881
900
|
declare const DEFAULT_SPECTROGRAM_COLOR_MAP: ColorMapValue;
|
|
882
901
|
|
|
902
|
+
/**
|
|
903
|
+
* Punch-in replace: carve a sample range out of a clip list.
|
|
904
|
+
*
|
|
905
|
+
* Used when a newly recorded clip lands at the playhead and must REPLACE any
|
|
906
|
+
* existing clip content between its start and end (issue #579): partial
|
|
907
|
+
* overlaps are trimmed, fully-covered clips are removed, and a clip that
|
|
908
|
+
* spans the whole range is split into two.
|
|
909
|
+
*/
|
|
910
|
+
|
|
911
|
+
/**
|
|
912
|
+
* Return a new clip list with the sample range [rangeStart, rangeEnd)
|
|
913
|
+
* removed from every clip that overlaps it.
|
|
914
|
+
*
|
|
915
|
+
* - Clips outside the range are returned by reference (untouched).
|
|
916
|
+
* - Clips fully inside the range are dropped.
|
|
917
|
+
* - A clip overlapping only the range's start keeps its head (right-trim).
|
|
918
|
+
* - A clip overlapping only the range's end keeps its tail: its start moves
|
|
919
|
+
* to rangeEnd and `offsetSamples` advances by the carved amount.
|
|
920
|
+
* - A clip containing the whole range splits into head + tail; the tail is a
|
|
921
|
+
* new clip (deterministic id `<original>-carve-<rangeEnd>`) sharing the
|
|
922
|
+
* same audioBuffer.
|
|
923
|
+
*
|
|
924
|
+
* Clips whose `startSample` changes lose their `startTick` — a sample-space
|
|
925
|
+
* carve cannot recompute ticks, and the engine re-enriches clips without
|
|
926
|
+
* `startTick` on ingestion (see AudioClip.startTick).
|
|
927
|
+
*
|
|
928
|
+
* Pure and immutable: input clips are never mutated. An empty or inverted
|
|
929
|
+
* range returns the input list unchanged.
|
|
930
|
+
*/
|
|
931
|
+
declare function carveClipRange(clips: readonly AudioClip[], rangeStart: number, rangeEnd: number): AudioClip[];
|
|
932
|
+
|
|
883
933
|
/** Clip start position in seconds */
|
|
884
934
|
declare function clipStartTime(clip: AudioClip): number;
|
|
885
935
|
/** Clip end position in seconds (start + duration) */
|
|
@@ -994,6 +1044,13 @@ interface KeyboardShortcut {
|
|
|
994
1044
|
description?: string;
|
|
995
1045
|
preventDefault?: boolean;
|
|
996
1046
|
}
|
|
1047
|
+
/** A key + modifier combination, without an action. Used for remapping maps. */
|
|
1048
|
+
type KeyBinding = Pick<KeyboardShortcut, 'key' | 'ctrlKey' | 'shiftKey' | 'metaKey' | 'altKey'>;
|
|
1049
|
+
/**
|
|
1050
|
+
* Does a keyboard event match a key binding?
|
|
1051
|
+
* `undefined` modifier = match any state; `false` = must NOT be pressed.
|
|
1052
|
+
*/
|
|
1053
|
+
declare function matchesKeyBinding(event: KeyboardEvent, binding: KeyBinding): boolean;
|
|
997
1054
|
/**
|
|
998
1055
|
* Handle a keyboard event against a list of shortcuts.
|
|
999
1056
|
* Pure function, no framework dependency.
|
|
@@ -1036,4 +1093,104 @@ type FetchLike = (url: string, init?: RequestInit) => Promise<Response>;
|
|
|
1036
1093
|
*/
|
|
1037
1094
|
declare function probeRangeSupport(url: string, fetchImpl?: FetchLike): Promise<RangeSupport>;
|
|
1038
1095
|
|
|
1039
|
-
|
|
1096
|
+
/**
|
|
1097
|
+
* Framework-agnostic microphone enumeration.
|
|
1098
|
+
*
|
|
1099
|
+
* Works without requesting a stream, but browsers redact device information
|
|
1100
|
+
* until microphone permission is granted: labels are empty everywhere, and
|
|
1101
|
+
* Chrome/Safari typically also redact deviceIds (often exposing a single
|
|
1102
|
+
* placeholder entry). The `hasLabels` flag tells consumers whether the list
|
|
1103
|
+
* is a usable picker or just a "microphones exist" signal — re-enumerate
|
|
1104
|
+
* after a successful getUserMedia to get the real list.
|
|
1105
|
+
*
|
|
1106
|
+
* Follows the probeRangeSupport pattern: the browser API is injectable for
|
|
1107
|
+
* tests and non-browser environments degrade gracefully.
|
|
1108
|
+
*/
|
|
1109
|
+
interface MicrophoneDeviceInfo {
|
|
1110
|
+
deviceId: string;
|
|
1111
|
+
/** Real device label, or a generated fallback name when redacted. */
|
|
1112
|
+
label: string;
|
|
1113
|
+
groupId: string;
|
|
1114
|
+
}
|
|
1115
|
+
interface MicrophoneEnumeration {
|
|
1116
|
+
/** False when enumeration is unavailable — SSR/Node, insecure context
|
|
1117
|
+
* (mediaDevices requires HTTPS or localhost), or a very old browser. */
|
|
1118
|
+
supported: boolean;
|
|
1119
|
+
/** True when the browser exposed real device labels. False before
|
|
1120
|
+
* microphone permission is granted (labels — and often deviceIds — are
|
|
1121
|
+
* redacted); the `devices` labels are then generated fallbacks. */
|
|
1122
|
+
hasLabels: boolean;
|
|
1123
|
+
devices: MicrophoneDeviceInfo[];
|
|
1124
|
+
}
|
|
1125
|
+
/**
|
|
1126
|
+
* List audio input devices without requesting a stream.
|
|
1127
|
+
*
|
|
1128
|
+
* @param mediaDevices - Injectable for tests; defaults to `navigator.mediaDevices`.
|
|
1129
|
+
*/
|
|
1130
|
+
declare function enumerateMicrophones(mediaDevices?: MediaDevices): Promise<MicrophoneEnumeration>;
|
|
1131
|
+
/**
|
|
1132
|
+
* Subscribe to the microphone device list: the listener fires once with the
|
|
1133
|
+
* initial enumeration and again on every `devicechange` (hot-plug, permission
|
|
1134
|
+
* grant in some browsers). Returns an unsubscribe function.
|
|
1135
|
+
*
|
|
1136
|
+
* Enumeration failures are logged and skipped — the subscription survives a
|
|
1137
|
+
* transient failure and delivers the next successful enumeration.
|
|
1138
|
+
*
|
|
1139
|
+
* @param mediaDevices - Injectable for tests; defaults to `navigator.mediaDevices`.
|
|
1140
|
+
*/
|
|
1141
|
+
declare function watchMicrophoneDevices(listener: (enumeration: MicrophoneEnumeration) => void, mediaDevices?: MediaDevices): () => void;
|
|
1142
|
+
|
|
1143
|
+
/** Edges within this distance (seconds) are considered "linked". */
|
|
1144
|
+
declare const LINK_THRESHOLD = 0.01;
|
|
1145
|
+
/** Minimum annotation duration (seconds) enforced by boundary edits. */
|
|
1146
|
+
declare const MIN_ANNOTATION_DURATION = 0.1;
|
|
1147
|
+
/** Link threshold for INTEGER TICK positions — closer than half a tick means equal. */
|
|
1148
|
+
declare const ANNOTATION_LINK_THRESHOLD_TICKS = 0.5;
|
|
1149
|
+
/** Minimum tick-annotation duration: a 128th note, floored at 1 tick. */
|
|
1150
|
+
declare function annotationMinDurationTicks(ppqn: number): number;
|
|
1151
|
+
/** Unit constants for a boundary-math run. Defaults are the seconds-domain values. */
|
|
1152
|
+
interface AnnotationBoundaryOptions {
|
|
1153
|
+
linkThreshold?: number;
|
|
1154
|
+
minDuration?: number;
|
|
1155
|
+
}
|
|
1156
|
+
interface AnnotationBoundaryUpdate {
|
|
1157
|
+
annotationIndex: number;
|
|
1158
|
+
newTime: number;
|
|
1159
|
+
isDraggingStart: boolean;
|
|
1160
|
+
annotations: AnnotationData[];
|
|
1161
|
+
duration: number;
|
|
1162
|
+
linkEndpoints: boolean;
|
|
1163
|
+
}
|
|
1164
|
+
/**
|
|
1165
|
+
* Pure boundary-update logic shared by the React annotations package and the
|
|
1166
|
+
* dawcore annotation web components. Handles linked endpoints (moving one edge
|
|
1167
|
+
* drags the linked neighbor edge, with cascade) and collision push-back.
|
|
1168
|
+
* Returns a NEW array; never mutates inputs.
|
|
1169
|
+
*/
|
|
1170
|
+
declare function updateAnnotationBoundaries(params: AnnotationBoundaryUpdate, options?: AnnotationBoundaryOptions): AnnotationData[];
|
|
1171
|
+
|
|
1172
|
+
type AnnotationShortcutAction = 'selectPrevious' | 'selectNext' | 'selectFirst' | 'selectLast' | 'clearSelection' | 'moveStartEarlier' | 'moveStartLater' | 'moveEndEarlier' | 'moveEndLater' | 'playActive';
|
|
1173
|
+
/** Key remapping map — null on the consumer means "use defaults". */
|
|
1174
|
+
interface AnnotationShortcutMap {
|
|
1175
|
+
selectPrevious?: KeyBinding;
|
|
1176
|
+
selectNext?: KeyBinding;
|
|
1177
|
+
selectFirst?: KeyBinding;
|
|
1178
|
+
selectLast?: KeyBinding;
|
|
1179
|
+
clearSelection?: KeyBinding;
|
|
1180
|
+
moveStartEarlier?: KeyBinding;
|
|
1181
|
+
moveStartLater?: KeyBinding;
|
|
1182
|
+
moveEndEarlier?: KeyBinding;
|
|
1183
|
+
moveEndLater?: KeyBinding;
|
|
1184
|
+
playActive?: KeyBinding;
|
|
1185
|
+
}
|
|
1186
|
+
declare const DEFAULT_ANNOTATION_SHORTCUTS: Record<AnnotationShortcutAction, KeyBinding[]>;
|
|
1187
|
+
/**
|
|
1188
|
+
* Flatten defaults + remap into a matchable list. A remapped action replaces
|
|
1189
|
+
* ALL of its default bindings with the single provided binding.
|
|
1190
|
+
*/
|
|
1191
|
+
declare function resolveAnnotationShortcuts(remap: AnnotationShortcutMap | null): Array<{
|
|
1192
|
+
action: AnnotationShortcutAction;
|
|
1193
|
+
binding: KeyBinding;
|
|
1194
|
+
}>;
|
|
1195
|
+
|
|
1196
|
+
export { ANNOTATION_LINK_THRESHOLD_TICKS, type AnnotationAction, type AnnotationActionOptions, type AnnotationBoundaryOptions, type AnnotationBoundaryUpdate, type AnnotationData, type AnnotationEventMap, type AnnotationFormat, type AnnotationListOptions, type AnnotationShortcutAction, type AnnotationShortcutMap, type AudioBuffer$1 as AudioBuffer, type AudioClip, type Bits, type ClipTrack, type ColorMapEntry, type ColorMapName, type ColorMapValue, type CreateClipOptions, type CreateClipOptionsSeconds, type CreateClipOptionsTicks, type CreateTrackOptions, DEFAULT_ANNOTATION_SHORTCUTS, DEFAULT_SPECTROGRAM_COLOR_MAP, type FFTSize, type Fade, type FadeConfig, type FadeType, type Gap, InteractionState, type KeyBinding, type KeyboardShortcut, LINK_THRESHOLD, MAX_CANVAS_WIDTH, MIN_ANNOTATION_DURATION, MIN_PIXELS_PER_UNIT, type MeterEntry, type MicrophoneDeviceInfo, type MicrophoneEnumeration, type MidiNoteData, type MusicalTick, type MusicalTickData, type MusicalTickParams, PPQN, type PeakData, type Peaks, type PlaylistConfig, type PlayoutState, type RangeSupport, type RenderAnnotationItemProps, type RenderMode, SPECTROGRAM_DEFAULTS, type SnapTo, type SpectrogramCanvasIdParts, type SpectrogramComputeConfig, type SpectrogramConfig, type SpectrogramData, type SpectrogramDisplayConfig, type TickType, type TimeSelection, type Timeline, type Track, type TrackEffectsFunction, type TrackSpectrogramOverrides, type WaveformConfig, type WaveformDataObject, type ZoomLevel, annotationMinDurationTicks, appendPeaks, appendToAudioBuffer, applyFadeIn, applyFadeOut, audibleLatencySamples, buildSpectrogramCanvasId, calculateDuration, carveClipRange, clipDurationTime, clipEndTime, clipOffsetTime, clipPixelWidth, clipStartTime, clipsOverlap, computeMusicalTicks, concatenateAudioData, createAudioBuffer, createClip, createClipFromSeconds, createClipFromTicks, createTimeline, createTrack, dBToNormalized, detectMeterChanges, enumerateMicrophones, exponentialCurve, findGaps, gainToDb, gainToNormalized, generateCurve, generatePeaks, getClipsAtSample, getClipsInRange, getShortcutLabel, handleKeyboardEvent, linearCurve, logarithmicCurve, matchesKeyBinding, normalizedToDb, parseSpectrogramCanvasId, pixelsToSamples, pixelsToSeconds, probeRangeSupport, resolveAnnotationShortcuts, resolveRecordingOffsetSamples, sCurveCurve, samplesToPixels, samplesToSeconds, samplesToTicks, secondsToPixels, secondsToSamples, snapTickToGrid, snapToGrid, snapToTicks, sortClipsByTime, ticksPerBar, ticksPerBeat, ticksToBarBeat, ticksToSamples, trackChannelCount, updateAnnotationBoundaries, watchMicrophoneDevices };
|
package/dist/index.d.ts
CHANGED
|
@@ -476,6 +476,10 @@ interface AnnotationData {
|
|
|
476
476
|
end: number;
|
|
477
477
|
lines: string[];
|
|
478
478
|
language?: string;
|
|
479
|
+
/** Musical position (ticks). Authoritative when BOTH tick fields are set —
|
|
480
|
+
* start/end seconds are then a derived cache (clip startTick pattern). */
|
|
481
|
+
startTick?: number;
|
|
482
|
+
endTick?: number;
|
|
479
483
|
}
|
|
480
484
|
/**
|
|
481
485
|
* Annotation format definition for parsing/serializing
|
|
@@ -775,6 +779,21 @@ declare const MIN_PIXELS_PER_UNIT = 8;
|
|
|
775
779
|
* correct across meter changes.
|
|
776
780
|
*/
|
|
777
781
|
declare function computeMusicalTicks(params: MusicalTickParams): MusicalTickData;
|
|
782
|
+
/**
|
|
783
|
+
* Convert an absolute tick to a 1-based bar.beat position, honoring meter
|
|
784
|
+
* changes. Bars/beats are counted per meter segment (the same walk
|
|
785
|
+
* `computeMusicalTicks` uses to build its `barOffset` accumulator). Beat is
|
|
786
|
+
* the integer beat the tick falls WITHIN (floor, 1-based) — a tick exactly on
|
|
787
|
+
* a bar line is beat 1.
|
|
788
|
+
*
|
|
789
|
+
* Guards: empty `meterEntries` → treat as 4/4 from tick 0. `ppqn <= 0` →
|
|
790
|
+
* returns `{ bar: 1, beat: 1 }` (division-by-zero guard, matches
|
|
791
|
+
* `computeMusicalTicks`'s ppqn guard).
|
|
792
|
+
*/
|
|
793
|
+
declare function ticksToBarBeat(tick: number, meterEntries: MeterEntry[], ppqn: number): {
|
|
794
|
+
bar: number;
|
|
795
|
+
beat: number;
|
|
796
|
+
};
|
|
778
797
|
/**
|
|
779
798
|
* Snaps a tick position to the nearest grid boundary defined by `snapTo`.
|
|
780
799
|
*
|
|
@@ -880,6 +899,37 @@ declare const SPECTROGRAM_DEFAULTS: Required<Omit<SpectrogramConfig, 'maxFrequen
|
|
|
880
899
|
/** Default color map when none is specified. */
|
|
881
900
|
declare const DEFAULT_SPECTROGRAM_COLOR_MAP: ColorMapValue;
|
|
882
901
|
|
|
902
|
+
/**
|
|
903
|
+
* Punch-in replace: carve a sample range out of a clip list.
|
|
904
|
+
*
|
|
905
|
+
* Used when a newly recorded clip lands at the playhead and must REPLACE any
|
|
906
|
+
* existing clip content between its start and end (issue #579): partial
|
|
907
|
+
* overlaps are trimmed, fully-covered clips are removed, and a clip that
|
|
908
|
+
* spans the whole range is split into two.
|
|
909
|
+
*/
|
|
910
|
+
|
|
911
|
+
/**
|
|
912
|
+
* Return a new clip list with the sample range [rangeStart, rangeEnd)
|
|
913
|
+
* removed from every clip that overlaps it.
|
|
914
|
+
*
|
|
915
|
+
* - Clips outside the range are returned by reference (untouched).
|
|
916
|
+
* - Clips fully inside the range are dropped.
|
|
917
|
+
* - A clip overlapping only the range's start keeps its head (right-trim).
|
|
918
|
+
* - A clip overlapping only the range's end keeps its tail: its start moves
|
|
919
|
+
* to rangeEnd and `offsetSamples` advances by the carved amount.
|
|
920
|
+
* - A clip containing the whole range splits into head + tail; the tail is a
|
|
921
|
+
* new clip (deterministic id `<original>-carve-<rangeEnd>`) sharing the
|
|
922
|
+
* same audioBuffer.
|
|
923
|
+
*
|
|
924
|
+
* Clips whose `startSample` changes lose their `startTick` — a sample-space
|
|
925
|
+
* carve cannot recompute ticks, and the engine re-enriches clips without
|
|
926
|
+
* `startTick` on ingestion (see AudioClip.startTick).
|
|
927
|
+
*
|
|
928
|
+
* Pure and immutable: input clips are never mutated. An empty or inverted
|
|
929
|
+
* range returns the input list unchanged.
|
|
930
|
+
*/
|
|
931
|
+
declare function carveClipRange(clips: readonly AudioClip[], rangeStart: number, rangeEnd: number): AudioClip[];
|
|
932
|
+
|
|
883
933
|
/** Clip start position in seconds */
|
|
884
934
|
declare function clipStartTime(clip: AudioClip): number;
|
|
885
935
|
/** Clip end position in seconds (start + duration) */
|
|
@@ -994,6 +1044,13 @@ interface KeyboardShortcut {
|
|
|
994
1044
|
description?: string;
|
|
995
1045
|
preventDefault?: boolean;
|
|
996
1046
|
}
|
|
1047
|
+
/** A key + modifier combination, without an action. Used for remapping maps. */
|
|
1048
|
+
type KeyBinding = Pick<KeyboardShortcut, 'key' | 'ctrlKey' | 'shiftKey' | 'metaKey' | 'altKey'>;
|
|
1049
|
+
/**
|
|
1050
|
+
* Does a keyboard event match a key binding?
|
|
1051
|
+
* `undefined` modifier = match any state; `false` = must NOT be pressed.
|
|
1052
|
+
*/
|
|
1053
|
+
declare function matchesKeyBinding(event: KeyboardEvent, binding: KeyBinding): boolean;
|
|
997
1054
|
/**
|
|
998
1055
|
* Handle a keyboard event against a list of shortcuts.
|
|
999
1056
|
* Pure function, no framework dependency.
|
|
@@ -1036,4 +1093,104 @@ type FetchLike = (url: string, init?: RequestInit) => Promise<Response>;
|
|
|
1036
1093
|
*/
|
|
1037
1094
|
declare function probeRangeSupport(url: string, fetchImpl?: FetchLike): Promise<RangeSupport>;
|
|
1038
1095
|
|
|
1039
|
-
|
|
1096
|
+
/**
|
|
1097
|
+
* Framework-agnostic microphone enumeration.
|
|
1098
|
+
*
|
|
1099
|
+
* Works without requesting a stream, but browsers redact device information
|
|
1100
|
+
* until microphone permission is granted: labels are empty everywhere, and
|
|
1101
|
+
* Chrome/Safari typically also redact deviceIds (often exposing a single
|
|
1102
|
+
* placeholder entry). The `hasLabels` flag tells consumers whether the list
|
|
1103
|
+
* is a usable picker or just a "microphones exist" signal — re-enumerate
|
|
1104
|
+
* after a successful getUserMedia to get the real list.
|
|
1105
|
+
*
|
|
1106
|
+
* Follows the probeRangeSupport pattern: the browser API is injectable for
|
|
1107
|
+
* tests and non-browser environments degrade gracefully.
|
|
1108
|
+
*/
|
|
1109
|
+
interface MicrophoneDeviceInfo {
|
|
1110
|
+
deviceId: string;
|
|
1111
|
+
/** Real device label, or a generated fallback name when redacted. */
|
|
1112
|
+
label: string;
|
|
1113
|
+
groupId: string;
|
|
1114
|
+
}
|
|
1115
|
+
interface MicrophoneEnumeration {
|
|
1116
|
+
/** False when enumeration is unavailable — SSR/Node, insecure context
|
|
1117
|
+
* (mediaDevices requires HTTPS or localhost), or a very old browser. */
|
|
1118
|
+
supported: boolean;
|
|
1119
|
+
/** True when the browser exposed real device labels. False before
|
|
1120
|
+
* microphone permission is granted (labels — and often deviceIds — are
|
|
1121
|
+
* redacted); the `devices` labels are then generated fallbacks. */
|
|
1122
|
+
hasLabels: boolean;
|
|
1123
|
+
devices: MicrophoneDeviceInfo[];
|
|
1124
|
+
}
|
|
1125
|
+
/**
|
|
1126
|
+
* List audio input devices without requesting a stream.
|
|
1127
|
+
*
|
|
1128
|
+
* @param mediaDevices - Injectable for tests; defaults to `navigator.mediaDevices`.
|
|
1129
|
+
*/
|
|
1130
|
+
declare function enumerateMicrophones(mediaDevices?: MediaDevices): Promise<MicrophoneEnumeration>;
|
|
1131
|
+
/**
|
|
1132
|
+
* Subscribe to the microphone device list: the listener fires once with the
|
|
1133
|
+
* initial enumeration and again on every `devicechange` (hot-plug, permission
|
|
1134
|
+
* grant in some browsers). Returns an unsubscribe function.
|
|
1135
|
+
*
|
|
1136
|
+
* Enumeration failures are logged and skipped — the subscription survives a
|
|
1137
|
+
* transient failure and delivers the next successful enumeration.
|
|
1138
|
+
*
|
|
1139
|
+
* @param mediaDevices - Injectable for tests; defaults to `navigator.mediaDevices`.
|
|
1140
|
+
*/
|
|
1141
|
+
declare function watchMicrophoneDevices(listener: (enumeration: MicrophoneEnumeration) => void, mediaDevices?: MediaDevices): () => void;
|
|
1142
|
+
|
|
1143
|
+
/** Edges within this distance (seconds) are considered "linked". */
|
|
1144
|
+
declare const LINK_THRESHOLD = 0.01;
|
|
1145
|
+
/** Minimum annotation duration (seconds) enforced by boundary edits. */
|
|
1146
|
+
declare const MIN_ANNOTATION_DURATION = 0.1;
|
|
1147
|
+
/** Link threshold for INTEGER TICK positions — closer than half a tick means equal. */
|
|
1148
|
+
declare const ANNOTATION_LINK_THRESHOLD_TICKS = 0.5;
|
|
1149
|
+
/** Minimum tick-annotation duration: a 128th note, floored at 1 tick. */
|
|
1150
|
+
declare function annotationMinDurationTicks(ppqn: number): number;
|
|
1151
|
+
/** Unit constants for a boundary-math run. Defaults are the seconds-domain values. */
|
|
1152
|
+
interface AnnotationBoundaryOptions {
|
|
1153
|
+
linkThreshold?: number;
|
|
1154
|
+
minDuration?: number;
|
|
1155
|
+
}
|
|
1156
|
+
interface AnnotationBoundaryUpdate {
|
|
1157
|
+
annotationIndex: number;
|
|
1158
|
+
newTime: number;
|
|
1159
|
+
isDraggingStart: boolean;
|
|
1160
|
+
annotations: AnnotationData[];
|
|
1161
|
+
duration: number;
|
|
1162
|
+
linkEndpoints: boolean;
|
|
1163
|
+
}
|
|
1164
|
+
/**
|
|
1165
|
+
* Pure boundary-update logic shared by the React annotations package and the
|
|
1166
|
+
* dawcore annotation web components. Handles linked endpoints (moving one edge
|
|
1167
|
+
* drags the linked neighbor edge, with cascade) and collision push-back.
|
|
1168
|
+
* Returns a NEW array; never mutates inputs.
|
|
1169
|
+
*/
|
|
1170
|
+
declare function updateAnnotationBoundaries(params: AnnotationBoundaryUpdate, options?: AnnotationBoundaryOptions): AnnotationData[];
|
|
1171
|
+
|
|
1172
|
+
type AnnotationShortcutAction = 'selectPrevious' | 'selectNext' | 'selectFirst' | 'selectLast' | 'clearSelection' | 'moveStartEarlier' | 'moveStartLater' | 'moveEndEarlier' | 'moveEndLater' | 'playActive';
|
|
1173
|
+
/** Key remapping map — null on the consumer means "use defaults". */
|
|
1174
|
+
interface AnnotationShortcutMap {
|
|
1175
|
+
selectPrevious?: KeyBinding;
|
|
1176
|
+
selectNext?: KeyBinding;
|
|
1177
|
+
selectFirst?: KeyBinding;
|
|
1178
|
+
selectLast?: KeyBinding;
|
|
1179
|
+
clearSelection?: KeyBinding;
|
|
1180
|
+
moveStartEarlier?: KeyBinding;
|
|
1181
|
+
moveStartLater?: KeyBinding;
|
|
1182
|
+
moveEndEarlier?: KeyBinding;
|
|
1183
|
+
moveEndLater?: KeyBinding;
|
|
1184
|
+
playActive?: KeyBinding;
|
|
1185
|
+
}
|
|
1186
|
+
declare const DEFAULT_ANNOTATION_SHORTCUTS: Record<AnnotationShortcutAction, KeyBinding[]>;
|
|
1187
|
+
/**
|
|
1188
|
+
* Flatten defaults + remap into a matchable list. A remapped action replaces
|
|
1189
|
+
* ALL of its default bindings with the single provided binding.
|
|
1190
|
+
*/
|
|
1191
|
+
declare function resolveAnnotationShortcuts(remap: AnnotationShortcutMap | null): Array<{
|
|
1192
|
+
action: AnnotationShortcutAction;
|
|
1193
|
+
binding: KeyBinding;
|
|
1194
|
+
}>;
|
|
1195
|
+
|
|
1196
|
+
export { ANNOTATION_LINK_THRESHOLD_TICKS, type AnnotationAction, type AnnotationActionOptions, type AnnotationBoundaryOptions, type AnnotationBoundaryUpdate, type AnnotationData, type AnnotationEventMap, type AnnotationFormat, type AnnotationListOptions, type AnnotationShortcutAction, type AnnotationShortcutMap, type AudioBuffer$1 as AudioBuffer, type AudioClip, type Bits, type ClipTrack, type ColorMapEntry, type ColorMapName, type ColorMapValue, type CreateClipOptions, type CreateClipOptionsSeconds, type CreateClipOptionsTicks, type CreateTrackOptions, DEFAULT_ANNOTATION_SHORTCUTS, DEFAULT_SPECTROGRAM_COLOR_MAP, type FFTSize, type Fade, type FadeConfig, type FadeType, type Gap, InteractionState, type KeyBinding, type KeyboardShortcut, LINK_THRESHOLD, MAX_CANVAS_WIDTH, MIN_ANNOTATION_DURATION, MIN_PIXELS_PER_UNIT, type MeterEntry, type MicrophoneDeviceInfo, type MicrophoneEnumeration, type MidiNoteData, type MusicalTick, type MusicalTickData, type MusicalTickParams, PPQN, type PeakData, type Peaks, type PlaylistConfig, type PlayoutState, type RangeSupport, type RenderAnnotationItemProps, type RenderMode, SPECTROGRAM_DEFAULTS, type SnapTo, type SpectrogramCanvasIdParts, type SpectrogramComputeConfig, type SpectrogramConfig, type SpectrogramData, type SpectrogramDisplayConfig, type TickType, type TimeSelection, type Timeline, type Track, type TrackEffectsFunction, type TrackSpectrogramOverrides, type WaveformConfig, type WaveformDataObject, type ZoomLevel, annotationMinDurationTicks, appendPeaks, appendToAudioBuffer, applyFadeIn, applyFadeOut, audibleLatencySamples, buildSpectrogramCanvasId, calculateDuration, carveClipRange, clipDurationTime, clipEndTime, clipOffsetTime, clipPixelWidth, clipStartTime, clipsOverlap, computeMusicalTicks, concatenateAudioData, createAudioBuffer, createClip, createClipFromSeconds, createClipFromTicks, createTimeline, createTrack, dBToNormalized, detectMeterChanges, enumerateMicrophones, exponentialCurve, findGaps, gainToDb, gainToNormalized, generateCurve, generatePeaks, getClipsAtSample, getClipsInRange, getShortcutLabel, handleKeyboardEvent, linearCurve, logarithmicCurve, matchesKeyBinding, normalizedToDb, parseSpectrogramCanvasId, pixelsToSamples, pixelsToSeconds, probeRangeSupport, resolveAnnotationShortcuts, resolveRecordingOffsetSamples, sCurveCurve, samplesToPixels, samplesToSeconds, samplesToTicks, secondsToPixels, secondsToSamples, snapTickToGrid, snapToGrid, snapToTicks, sortClipsByTime, ticksPerBar, ticksPerBeat, ticksToBarBeat, ticksToSamples, trackChannelCount, updateAnnotationBoundaries, watchMicrophoneDevices };
|