/** * The per-signal header, and where each signal's bytes live inside a data record. * * Layer 2. Sole owner of `recordByteOffset` arithmetic. * * The per-signal header is FIELD-MAJOR: all `ns` labels, then all `ns` transducer types, and so * on for ten fields, `ns * 256` bytes in total. It is NOT one 256-byte struct per signal, and * reading it as one is the single most common EDF bug — it happens to produce plausible output * for a one-signal file, which is exactly how it survives a first test. * * address(field, i) = 256 + ns * SIGNAL_FIELD_BLOCK_OFFSETS[field] + i * WIDTHS[field] * * Inside a data record the signals are stored back to back in signal order, each contributing * `samplesPerRecord * bytesPerSample` bytes. Only the first of those comes from this file. * `bytesPerSample` is 2 or 3 and is decided by `header/variant.ts` from the version block — it is * the whole difference between EDF and BDF, and it is per-file rather than per-signal, which is * why it arrives here on `input.variant` rather than being read out of a signal field. This said * "both numbers come from this file and nowhere else" until 0.6.60, which sent a reader tracing * the record layout to one file when it takes two. */ import { SIGNAL_FIELD_WIDTHS } from '../constants.js'; import { type DiagnosticSink } from '../diagnostics/collector.js'; import type { EdfSignal } from '../types.js'; import type { EdfVariantInfo } from './variant.js'; /** The ten per-signal fields, in file order. */ export type SignalFieldName = keyof typeof SIGNAL_FIELD_WIDTHS; /** * Everything about one signal that does not depend on the resolved record count. * * The record count is resolved AFTER the per-signal blocks — recovering it needs the record * size, which needs `samplesPerRecord` — so `sampleCount` is the one field that cannot be filled * in here. `buildSignals` adds it once the count is known. */ export type SignalDraft = Omit; /** * The whole header block plus what the fixed part already established. The signal fields are * FIELD-MAJOR — every label, then every transducer — so a signal cannot be parsed from a slice of * its own, and the count has to be known before any of them can be located. */ export interface SignalHeaderInput { /** At least `256 * (signalCount + 1)` bytes; the caller has already refused anything less. */ readonly headerBytes: Uint8Array; readonly signalCount: number; readonly variant: EdfVariantInfo; /** May be 0, in which case no signal gets a sample rate. Never divided by. */ readonly recordDurationSeconds: number; } /** * Every per-signal field, plus the two things only a whole pass can know: the record's byte * length, and which signals are annotations. Both are sums over all signals, so returning them * here is what stops each caller re-walking the array to work them out. */ export interface ParsedSignalHeaders { readonly signals: readonly SignalDraft[]; /** `bytesPerSample * SUM(samplesPerRecord)`. May be 0; the caller decides that is fatal. */ readonly recordByteLength: number; readonly dataSignalIndices: readonly number[]; readonly annotationSignalIndices: readonly number[]; } /** * The byte offset of one per-signal field, for one signal. * * Plain arithmetic on plain numbers: a 512-signal header is 131 KB, and nothing here is ever * near 2^31, but the same expression is what every offset in the file is built on and `|0` has * no business anywhere in it. */ export declare function signalFieldOffset(field: SignalFieldName, signalCount: number, signalIndex: number): number; /** * Parse the whole per-signal header. * * Check order within one signal is fixed and deliberate: samples per record (the offsets depend * on it), then the digital range, then the physical range, then the range checks, then the * annotation-signal conformance, then the scale. Cross-signal checks — duplicate labels, then a * missing EDF+ marker — run once at the end, when every label is known. */ export declare function parseSignalHeaders(input: SignalHeaderInput, sink: DiagnosticSink): ParsedSignalHeaders; /** The drafts, completed with the sample count the resolved record count implies. */ export declare function buildSignals(drafts: readonly SignalDraft[], recordCount: number): readonly EdfSignal[]; //# sourceMappingURL=signals.d.ts.map