/** * playback/ops.js — buffer-level loop playback operations (Wave 6). * * These are the loop-speed / gap / reverse operations that previously lived * inline in the demo's AudioAnalyzer component, hoisted verbatim (same math, * same edge behavior) into the library as pure functions. * * Universal-runtime contract: * - Inputs are AudioBuffer-shaped objects: { numberOfChannels, length, * sampleRate, getChannelData(channel) }. Real AudioBuffers qualify. * - No DOM, no window, no AudioContext. Output buffers are allocated via an * injectable `createBuffer(numberOfChannels, length, sampleRate)` factory; * the default factory returns a plain AudioBuffer-shaped object backed by * Float32Arrays (Node/worker safe). Browser callers that need a genuine * AudioBuffer pass `{ createBuffer: (ch, len, sr) => ctx.createBuffer(ch, len, sr) }`. * - Loop descriptors are normalized: { start, end } with 0 <= start < end <= 1. * - Invalid input throws immediately (API law 6) — no silent fallbacks. */ /** * Default pure buffer factory: an AudioBuffer-shaped object backed by * Float32Array channels. Safe in Node, workers, and browsers. */ export function createBufferLike(numberOfChannels: any, length: any, sampleRate: any): { numberOfChannels: any; length: any; sampleRate: any; duration: number; getChannelData(channel: any): any; }; /** * Half speed (time stretch) a loop section. The loop region is stretched to * 2x its length (linear interpolation), so the returned buffer is LONGER by * one loop length. Content before/after the loop is preserved. * * @param {AudioBuffer|Object} audioBuffer * @param {{start:number, end:number}} loopData - normalized loop * @param {{createBuffer?: Function}} [opts] * @returns {Object} new buffer (length = old length + loopLength) */ export function halfSpeedLoop(audioBuffer: AudioBuffer | any, loopData: { start: number; end: number; }, opts?: { createBuffer?: Function; }): any; /** * Half speed quantz: time-stretch the loop content at half speed but MASK it * to the original loop window, so the track length never changes. Only the * first half of the loop's source material is heard (the "hidden half" can be * revealed with revealHiddenHalf / revealFirstHalf). * * @returns {Object} new buffer (same length as input) */ export function halfSpeedQuantzLoop(audioBuffer: any, loopData: any, opts?: {}): any; /** * Double speed quantz — gapless: compress the loop content at 2x speed into * half the space and shift everything after it left to close the gap. The * returned buffer is SHORTER. * * @returns {{buffer: Object, newLoopEnd: number}} new buffer + the new * normalized loop end (loop start is unchanged) */ export function doubleSpeedQuantzLoop(audioBuffer: any, loopData: any, opts?: {}): { buffer: any; newLoopEnd: number; }; /** * Double speed unquantz: compress the loop content at 2x speed IN PLACE * (track length unchanged), leaving either a natural glitch tail or — in * fractal mode when there is not enough room after the loop — writing only * the first half of the window so previously-layered content is preserved * (the matryoshka effect). * * @param {{fractal?: boolean, createBuffer?: Function}} [opts] * @returns {Object} new buffer (same length as input) */ export function doubleSpeedUnquantzLoop(audioBuffer: any, loopData: any, opts?: { fractal?: boolean; createBuffer?: Function; }): any; /** * Detect a gap (silence across all channels) after the loop end. * * @param {{silenceThreshold?: number, minGapSize?: number}} [opts] * @returns {{start:number, end:number, size:number}|null} sample-indexed gap, * or null when no qualifying gap exists (an honest "nothing found", not an * error). */ export function detectGap(audioBuffer: any, loopData: any, opts?: { silenceThreshold?: number; minGapSize?: number; }): { start: number; end: number; size: number; } | null; /** * Close a detected gap by shifting the audio after it left. * The normalized loop end is preserved as-is (legacy demo behavior). * * @returns {{buffer: Object, newLoopEnd: number, gapSize: number}|null} * null when no gap was detected. */ export function closeGapLeft(audioBuffer: any, loopData: any, opts?: {}): { buffer: any; newLoopEnd: number; gapSize: number; } | null; /** * Close a detected gap by removing it and rescaling the loop end to the * shorter buffer (content after the gap keeps its absolute position relative * to the loop end). * * @returns {{buffer: Object, newLoopEnd: number, gapSize: number}|null} */ export function closeGapRight(audioBuffer: any, loopData: any, opts?: {}): { buffer: any; newLoopEnd: number; gapSize: number; } | null; /** * Reverse a sample range of a buffer WITHOUT mutating the input * (copy-then-reverse; contrast with the in-place core reverseBufferSection). * * @param {number} startSample - inclusive start of the section * @param {number} endSample - exclusive end of the section * @returns {Object} new buffer (same length as input) */ export function reverseSection(audioBuffer: any, startSample: number, endSample: number, opts?: {}): any; /** * Reveal the "hidden" second half of a half-speed-quantz'd loop: replace the * loop window with the SECOND half of the original loop content, half-speed * stretched (optionally reversed). Used by the demo's nudge toggle. * * @param {Object} currentBuffer - the processed buffer currently playing * @param {Object} originalBuffer - the unprocessed source buffer * @param {{start:number, end:number}} loopData - normalized loop * @param {{reverse?: boolean, createBuffer?: Function}} [opts] * @returns {Object} new buffer (same length as currentBuffer) */ export function revealHiddenHalf(currentBuffer: any, originalBuffer: any, loopData: { start: number; end: number; }, opts?: { reverse?: boolean; createBuffer?: Function; }): any; /** * Reveal the FIRST half of a half-speed-quantz'd loop (counterpart of * revealHiddenHalf; toggles the nudge back). * * @returns {Object} new buffer (same length as currentBuffer) */ export function revealFirstHalf(currentBuffer: any, originalBuffer: any, loopData: any, opts?: {}): any;