Chris@8
|
1
|
Chris@93
|
2 module yetilab.stream.framer;
|
Chris@8
|
3
|
Chris@25
|
4 /**
|
Chris@25
|
5 * Framer expresses a stream (or a file) as a lazy list of (possibly
|
Chris@158
|
6 * overlapping) frames of data.
|
Chris@25
|
7 */
|
Chris@25
|
8
|
Chris@93
|
9 block = load yetilab.block.block;
|
Chris@93
|
10 bf = load yetilab.block.blockfuncs;
|
Chris@93
|
11 af = load yetilab.stream.audiofile;
|
Chris@93
|
12 win = load yetilab.transform.window;
|
Chris@93
|
13 fft = load yetilab.transform.fft;
|
Chris@158
|
14 mat = load yetilab.matrix.matrix;
|
Chris@158
|
15 ch = load yetilab.stream.channels;
|
Chris@8
|
16
|
Chris@32
|
17 blockList framesize stream =
|
Chris@14
|
18 if stream.finished? then
|
Chris@158
|
19 stream.close ();
|
Chris@158
|
20 [ mat.zeroMatrix { rows = stream.channels, columns = 0 } ]
|
Chris@13
|
21 else
|
Chris@158
|
22 mat.resizedTo { rows = stream.channels, columns = framesize }
|
Chris@158
|
23 (stream.read framesize)
|
Chris@32
|
24 :. \(blockList framesize stream);
|
Chris@13
|
25 fi;
|
Chris@13
|
26
|
Chris@47
|
27 overlappingBlockList size hop stream valid buffer =
|
Chris@29
|
28 (
|
Chris@158
|
29 m = stream.read hop;
|
Chris@158
|
30 obtained = mat.width m;
|
Chris@23
|
31
|
Chris@32
|
32 // Retain framesize - hop samples from old buffer, add hop samples
|
Chris@29
|
33 // (zero-padded if necessary) just read
|
Chris@158
|
34 buffer = map2
|
Chris@158
|
35 do buf row:
|
Chris@158
|
36 block.concat
|
Chris@158
|
37 [block.rangeOf buf hop (size-hop),
|
Chris@158
|
38 block.resizedTo hop (m.getRow row)];
|
Chris@158
|
39 done buffer [0..stream.channels-1];
|
Chris@23
|
40
|
Chris@23
|
41 // Number of "valid" elements (not tail-end zero-padding) left in buffer
|
Chris@24
|
42 remaining = valid - (hop - obtained);
|
Chris@23
|
43
|
Chris@23
|
44 if remaining <= 0 then
|
Chris@23
|
45 stream.close ();
|
Chris@23
|
46 [];
|
Chris@12
|
47 else
|
Chris@158
|
48 mat.newMatrix (RowMajor ()) (map block.list buffer)
|
Chris@158
|
49 :. \(overlappingBlockList size hop stream remaining buffer);
|
Chris@23
|
50 fi);
|
Chris@14
|
51
|
Chris@32
|
52 frames { framesize, hop } stream =
|
Chris@32
|
53 if framesize == hop then
|
Chris@32
|
54 blockList framesize stream
|
Chris@30
|
55 else
|
Chris@32
|
56 overlappingBlockList framesize hop stream
|
Chris@158
|
57 framesize (map \(block.zeros framesize) [0..stream.channels-1]);
|
Chris@30
|
58 fi;
|
Chris@14
|
59
|
Chris@158
|
60 monoFrames params stream =
|
Chris@158
|
61 map ch.mixedDown (frames params stream);
|
Chris@158
|
62
|
Chris@49
|
63 windowedFrames { framesize, hop, window } stream =
|
Chris@49
|
64 (win = window framesize;
|
Chris@158
|
65 map (bf.multiply win) (monoFrames { framesize, hop } stream));
|
Chris@49
|
66
|
Chris@49
|
67 frequencyDomainFrames { framesize, hop } stream =
|
Chris@49
|
68 (f = fft.realForward framesize;
|
Chris@49
|
69 map f (windowedFrames { framesize, hop, window = win.hann } stream));
|
Chris@23
|
70
|
Chris@11
|
71 {
|
Chris@30
|
72 frames,
|
Chris@158
|
73 monoFrames,
|
Chris@49
|
74 windowedFrames,
|
Chris@49
|
75 frequencyDomainFrames,
|
Chris@49
|
76
|
Chris@49
|
77 framesOfFile parameters filename =
|
Chris@146
|
78 frames parameters (af.open filename),
|
Chris@49
|
79
|
Chris@158
|
80 monoFramesOfFile parameters filename =
|
Chris@158
|
81 monoFrames parameters (af.open filename),
|
Chris@158
|
82
|
Chris@49
|
83 windowedFramesOfFile parameters filename =
|
Chris@146
|
84 windowedFrames parameters (af.open filename),
|
Chris@49
|
85
|
Chris@49
|
86 frequencyDomainFramesOfFile parameters filename =
|
Chris@146
|
87 frequencyDomainFrames parameters (af.open filename),
|
Chris@11
|
88 }
|
Chris@8
|
89
|