@@ -69,6 +69,94 @@ In-place complex forward FFT (unnormalized). Both `re` and `im` must be `Float64
6969
7070In-place complex inverse FFT (1/N normalized). Same signature as ` cfft ` .
7171
72+ ## STFT
73+
74+ ``` js
75+ import { stft , istft , stftBatch , stftStream , stftAnalysisStream } from ' fourier-transform/stft'
76+ ```
77+
78+ ### ` stft(signal, opts?) ` — analysis
79+
80+ Returns an array of frames, each with ` { re, im, mag, phase, time } ` .
81+
82+ - ` signal ` — ` Float32Array ` , ` Float64Array ` , or plain ` Array ` .
83+ - ` opts.frameSize ` — FFT size, power of 2. Default: ` 2048 ` .
84+ - ` opts.hopSize ` — hop between frames. Default: ` frameSize / 4 ` .
85+ - ` time ` is the sample index of the frame centre in the original signal.
86+ - Zero-padded by ` frameSize ` at front and back so edge samples are fully windowed.
87+
88+ ``` js
89+ const frames = stft (waveform, { frameSize: 2048 , hopSize: 512 })
90+ for (const f of frames ) {
91+ console .log (f .time , f .mag [100 ]) // time in samples, magnitude at bin 100
92+ }
93+ ```
94+
95+ ### ` istft(frames, opts?) ` — synthesis
96+
97+ Reconstructs a time-domain signal from STFT frames.
98+
99+ - ` frames ` — array of ` { mag, phase, time? } ` or ` { re, im, time? } ` objects.
100+ - ` opts.signalLength ` — expected output length. Inferred from last frame if omitted.
101+ - When inferred, the tail may include padding; pass ` signalLength ` for exact control.
102+ - Returns ` Float64Array ` .
103+ - If ` re ` /` im ` are present, they are used directly (no polar round-trip). Otherwise ` mag ` /` phase ` are converted to cartesian.
104+
105+ ``` js
106+ const recovered = istft (frames , { frameSize: 2048 , hopSize: 512 , signalLength: waveform .length })
107+ ```
108+
109+ ### ` stftBatch(data, process, opts?) ` — batch with callback
110+
111+ Processes each frame through a callback and overlap-adds the result.
112+
113+ - ` process(mag, phase, state, ctx) ` → ` { mag, phase } `
114+ - ` mag ` , ` phase ` — ` Float64Array(half + 1) `
115+ - ` state ` — persistent object across frames
116+ - ` ctx ` — ` { N, half, hop, anaHop, synHop, freqPerBin, frameStart, sampleRate, opts } `
117+ - ` ctx.frameStart ` — sample index of the frame start in the original signal. Negative at boundaries due to zero-padding.
118+ - ` ctx.opts ` — cloned copy of ` opts ` . Use this to pass custom parameters (e.g. ` ratio ` , ` ratioFn ` ) through to your process callback.
119+ - ` opts.anaHop ` — analysis hop (default: ` hopSize ` ).
120+ - ` opts.synHop ` — synthesis hop (default: ` hopSize ` ). When ` anaHop !== synHop ` , the output is time-stretched or compressed.
121+ - Returns ` Float32Array ` of length ` round(data.length * synHop / anaHop) ` (same as input when ` anaHop === synHop ` ).
122+
123+ ``` js
124+ const result = stftBatch (signal, (mag , phase , state , ctx ) => {
125+ // Simple spectral gate
126+ for (let k = 0 ; k < mag .length ; k++ ) if (mag[k] < 0.1 ) mag[k] = 0
127+ return { mag, phase }
128+ }, { frameSize: 2048 , hopSize: 512 })
129+ ```
130+
131+ ### ` stftStream(process, opts?) ` — streaming with callback
132+
133+ Streaming version of ` stftBatch ` . Returns ` { write(chunk), flush() } ` .
134+
135+ - Supports ` opts.anaHop ` / ` opts.synHop ` for time-stretching in streaming context.
136+
137+ ``` js
138+ const stream = stftStream ((mag , phase ) => ({ mag, phase }), { frameSize: 2048 })
139+
140+ for (const chunk of audioChunks) {
141+ const processed = stream .write (chunk)
142+ // emit processed...
143+ }
144+ const tail = stream .flush ()
145+ ```
146+
147+ ### ` stftAnalysisStream(opts?) ` — streaming analysis
148+
149+ Streaming version of ` stft ` . Returns ` { write(chunk), flush() } ` that emit frames.
150+
151+ - Supports ` opts.anaHop ` for non-uniform analysis spacing.
152+
153+ ``` js
154+ const stream = stftAnalysisStream ({ frameSize: 2048 , hopSize: 512 })
155+ const frames = stream .write (chunk1)
156+ frames .push (... stream .write (chunk2))
157+ frames .push (... stream .flush ())
158+ ```
159+
72160### View semantics
73161
74162` rfft ` , ` fft ` , and ` ifft ` return internal cached buffers by default. The next call with the same N overwrites the previous result. Pass an output buffer to keep results across calls:
0 commit comments