Chris@8: Chris@93: module yetilab.stream.framer; Chris@8: Chris@25: /** Chris@25: * Framer expresses a stream (or a file) as a lazy list of (possibly Chris@158: * overlapping) frames of data. Chris@25: */ Chris@25: Chris@222: vec = load yetilab.vector.vector; Chris@222: bf = load yetilab.vector.blockfuncs; Chris@93: af = load yetilab.stream.audiofile; Chris@93: win = load yetilab.transform.window; Chris@93: fft = load yetilab.transform.fft; Chris@158: mat = load yetilab.matrix.matrix; Chris@158: ch = load yetilab.stream.channels; Chris@8: Chris@32: blockList framesize stream = Chris@14: if stream.finished? then Chris@158: stream.close (); Chris@160: [] Chris@13: else Chris@158: mat.resizedTo { rows = stream.channels, columns = framesize } Chris@158: (stream.read framesize) Chris@32: :. \(blockList framesize stream); Chris@13: fi; Chris@13: Chris@47: overlappingBlockList size hop stream valid buffer = Chris@29: ( Chris@158: m = stream.read hop; Chris@158: obtained = mat.width m; Chris@23: Chris@32: // Retain framesize - hop samples from old buffer, add hop samples Chris@29: // (zero-padded if necessary) just read Chris@158: buffer = map2 Chris@158: do buf row: Chris@218: vec.concat Chris@218: [vec.rangeOf hop (size-hop) buf, Chris@218: vec.resizedTo hop (mat.getRow row m)]; Chris@158: done buffer [0..stream.channels-1]; Chris@23: Chris@23: // Number of "valid" elements (not tail-end zero-padding) left in buffer Chris@24: remaining = valid - (hop - obtained); Chris@23: Chris@23: if remaining <= 0 then Chris@23: stream.close (); Chris@23: []; Chris@12: else Chris@163: mat.newMatrix (RowMajor ()) buffer Chris@158: :. \(overlappingBlockList size hop stream remaining buffer); Chris@23: fi); Chris@14: Chris@32: frames { framesize, hop } stream = Chris@32: if framesize == hop then Chris@32: blockList framesize stream Chris@30: else Chris@32: overlappingBlockList framesize hop stream Chris@218: framesize (map \(vec.zeros framesize) [0..stream.channels-1]); Chris@30: fi; Chris@14: Chris@158: monoFrames params stream = Chris@158: map ch.mixedDown (frames params stream); Chris@158: Chris@49: windowedFrames { framesize, hop, window } stream = Chris@49: (win = window framesize; Chris@158: map (bf.multiply win) (monoFrames { framesize, hop } stream)); Chris@49: Chris@49: frequencyDomainFrames { framesize, hop } stream = Chris@49: (f = fft.realForward framesize; Chris@49: map f (windowedFrames { framesize, hop, window = win.hann } stream)); Chris@23: Chris@11: { Chris@30: frames, Chris@158: monoFrames, Chris@49: windowedFrames, Chris@49: frequencyDomainFrames, Chris@49: Chris@49: framesOfFile parameters filename = Chris@146: frames parameters (af.open filename), Chris@49: Chris@158: monoFramesOfFile parameters filename = Chris@158: monoFrames parameters (af.open filename), Chris@158: Chris@49: windowedFramesOfFile parameters filename = Chris@146: windowedFrames parameters (af.open filename), Chris@49: Chris@49: frequencyDomainFramesOfFile parameters filename = Chris@146: frequencyDomainFrames parameters (af.open filename), Chris@11: } Chris@8: