/** * Querying annotations that are already in hand. * * Layer 7, and pure: nothing here reads. `readAnnotations` returns every event in a record range, * and the next thing a caller does is almost always narrow it — to a time window, to a label, to * the stages of a hypnogram. * * Doing that by hand goes wrong in one specific way. The obvious filter is * `a.onsetSecondsFromFirstRecord >= from && ... < to`, and those are float64 seconds converted * from exact tick counts. An onset stored as `+30.0000001` and a bound of `30.0000001` need not * compare equal once both have been through a division by 10,000,000. Every comparison here is on * ticks, which are exact, and the bounds are converted to ticks once. * * The ticks compared are `onsetTicksFromFirstRecord`, not `onsetTicks`. The window is in the same * seconds `resolveTimeWindow` and `readWindow` take, and those put `t = 0` at the start of record * 0; `onsetTicks` is on the header's timebase, which sits up to a second earlier when the file * declares a sub-second start offset in record 0's timekeeping TAL. Comparing against the wrong * one puts events in the neighbouring window on exactly the files that bother to state their * offset. */ import type { EdfAnnotation, EdfAnnotationWindow } from './types.js'; /** * The annotations that overlap a time window, in the recording's own timebase. * * Overlap, not containment: an annotation with a duration counts when any part of it falls in the * window, so a 30-second sleep epoch is returned for a window inside it. A zero-duration event * counts when its onset is in `[startSeconds, startSeconds + durationSeconds)` — half-open, so * adjacent windows partition the recording without double-counting the boundary. */ export declare function filterAnnotationsByTime(annotations: readonly EdfAnnotation[], window: EdfAnnotationWindow): readonly EdfAnnotation[]; /** * The annotations whose text matches. * * A string matches the text VERBATIM, because annotation vocabularies are controlled — * `Sleep stage W` is a fixed token, and a substring match on `W` would also catch `Sleep stage * REM` in files that spell it `W/REM`. Pass a predicate or a RegExp when you want something * looser; edfcore does not guess which you meant. * * Verbatim means verbatim, in both directions: `annotation.text` is the TAL's bytes as written and * is never trimmed (`api-types.md` says so of the field itself), so an event a scorer spelled * `'Sleep stage W '` is not matched by `'Sleep stage W'`, and a query with its own stray space * matches nothing. This docblock used to say "the exact trimmed text", which is neither what this * function does nor what the field holds, and the failure it describes is silent: a padded * vocabulary returns an empty list rather than an error (corrected in 0.3.51). For such a file, * pass the predicate that says what you mean — `(text) => text.trim() === label`. */ export declare function filterAnnotationsByText(annotations: readonly EdfAnnotation[], match: string | RegExp | ((text: string) => boolean)): readonly EdfAnnotation[]; /** * Counts annotations by their exact text, most frequent first. * * The first thing worth knowing about an unfamiliar scoring file: which labels it uses and how * often. Ties keep insertion order, so the output is deterministic for a given input. */ export declare function countAnnotationsByText(annotations: readonly EdfAnnotation[]): ReadonlyArray<{ readonly text: string; readonly count: number; }>; /** * The annotations covering an instant. * * A viewer with a cursor asks this on every mouse move. The window form works — a zero-length * window — except that `filterAnnotationsByTime` returns nothing for a non-positive duration, so * the obvious call returns an empty list at every position. This is the instant form: an * annotation covers `t` when `onset <= t < onset + duration`, and a zero-duration event covers * only its own onset. */ export declare function annotationsAt(annotations: readonly EdfAnnotation[], seconds: number): readonly EdfAnnotation[]; //# sourceMappingURL=annotations-query.d.ts.map