changeset 284:7932bbb7bacb

Toward frame -> stream processing. Need overlap-add.
author Chris Cannam
date Wed, 29 May 2013 22:59:23 +0100
parents e330ac62703b
children be39f21456a1
files yetilab/matrix.yeti yetilab/signal/window.yeti yetilab/stream/filter.yeti yetilab/stream/framer.yeti yetilab/stream/syntheticstream.yeti yetilab/stream/test/test_framer.yeti
diffstat 6 files changed, 103 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/yetilab/matrix.yeti	Wed May 29 17:32:58 2013 +0100
+++ b/yetilab/matrix.yeti	Wed May 29 22:59:23 2013 +0100
@@ -173,7 +173,7 @@
 
 zeroSizeMatrix () = zeroMatrix { rows = 0, columns = 0 };
 
-isZeroSize? m = (width m == 0 or height m == 0);
+empty?' m = (width m == 0 or height m == 0);
 
 generate f { rows, columns } =
     if rows < 1 or columns < 1 then zeroSizeMatrix ()
@@ -340,7 +340,7 @@
     esac);
 
 equal' comparator vecComparator m1 m2 =
-    if isZeroSize? m1 and isZeroSize? m2 then
+    if empty?' m1 and empty?' m2 then
         true
     elif size m1 != size m2 then 
         false
@@ -639,7 +639,7 @@
 
 //!!! doc this filter -- zero-size elts are ignored
 concat direction mm =
-    concat' direction (filter do mat: not (isZeroSize? mat) done mm);
+    concat' direction (filter do mat: not (empty?' mat) done mm);
 
 //!!! next two v. clumsy
 
@@ -727,7 +727,7 @@
     zeroMatrix,
     identityMatrix,
     zeroSizeMatrix,
-    isZeroSize?,
+    empty? = empty?',
     equal,
     equalUnder,
     transposed,
@@ -774,7 +774,7 @@
     zeroMatrix is { .rows is number, .columns is number } -> matrix, 
     identityMatrix is { .rows is number, .columns is number } -> matrix, 
     zeroSizeMatrix is () -> matrix,
-    isZeroSize? is matrix -> boolean,
+    empty? is matrix -> boolean,
     equal is matrix -> matrix -> boolean,
     equalUnder is (number -> number -> boolean) -> matrix -> matrix -> boolean,
     transposed is matrix -> matrix,
--- a/yetilab/signal/window.yeti	Wed May 29 17:32:58 2013 +0100
+++ b/yetilab/signal/window.yeti	Wed May 29 22:59:23 2013 +0100
@@ -85,6 +85,7 @@
     Bartlett (): bartlett sampling;
     esac);
 
+//!!! should use vector. but does anyone use this function anyway? would we use it in framer if it used vector?
 windowed windowFunc frames =
     case frames of
         []: frames;
--- a/yetilab/stream/filter.yeti	Wed May 29 17:32:58 2013 +0100
+++ b/yetilab/stream/filter.yeti	Wed May 29 22:59:23 2013 +0100
@@ -270,7 +270,7 @@
     s with 
     {
         get finished? () =
-            s.finished? and (mat.isZeroSize? history),
+            s.finished? and (mat.empty? history),
         get available () = 
             case s.available of
             Known n: Known (n + mat.width history);
--- a/yetilab/stream/framer.yeti	Wed May 29 17:32:58 2013 +0100
+++ b/yetilab/stream/framer.yeti	Wed May 29 22:59:23 2013 +0100
@@ -14,6 +14,7 @@
 mat = load yetilab.matrix;
 ch = load yetilab.stream.channels;
 syn = load yetilab.stream.syntheticstream;
+filt = load yetilab.stream.filter;
 
 blockList framesize stream =
     if stream.finished? then
@@ -58,33 +59,85 @@
             framesize (map \(vec.zeros framesize) [0..stream.channels-1]);
     fi;
 
-streamContiguous rate framesize fr =
-    if empty? frames then
-        syn.empty rate 1
-    else
-        var position = 0;
-        var frames = fr;
-        var fini = false;
-        channels = mat.height (head frames); // so we don't need to keep head ptr
+streamContiguous rate framesize frames =
+   (var remaining = frames;
+    var buffered = mat.zeroSizeMatrix ();
+    var position = 0;
+    channels = mat.height (head frames); // so we don't need to keep a head ptr
+    {
+        get position () = position,
+        get channels () = channels,
+        get sampleRate () = rate,
+        get finished? () = empty? remaining and mat.empty? buffered,
+        get available () = 
+            // Don't take length of frames -- we don't want to lose laziness.
+            // If the caller cares that much, they can measure frames themselves
+            if empty? remaining then
+                Known (mat.width buffered) 
+            else
+                Unknown ()
+            fi,
+        read count =
+           (framesFor samples acc =
+                if samples <= 0 or empty? remaining then
+                    acc
+                else
+                    this = head remaining;
+                    remaining := tail remaining;
+                    framesFor (samples - mat.width this) (acc ++ [this])
+                fi;
+            source = mat.concat (Horizontal ()) (framesFor count [buffered]);
+            toReturn = mat.columnSlice source 0 count;
+            position := position + mat.width toReturn;
+            buffered := mat.columnSlice source count (mat.width source);
+            toReturn),
+        close = \(),
+    });
+
+streamOverlapping rate framesize hop frames =
+   (var remaining = frames;
+    var buffered = mat.zeroSizeMatrix ();
+    var position = 0;
+    w = win.hann framesize;
+    channels = mat.height (head frames); // so we don't need to keep a head ptr
+    filt.delayedBy (- (framesize - hop))
         {
             get position () = position,
             get channels () = channels,
             get sampleRate () = rate,
-            get available () = if fini then Known 0 else Unknown () fi,
-            get finished? () = fini,
-                            
-                            
+            get finished? () = empty? remaining and mat.empty? buffered,
+            get available () = 
+                // Don't take length of frames: we don't want to lose
+                // laziness.  If the caller cares that much, they can
+                // measure frames themselves
+                if empty? remaining then
+                    Known (mat.width buffered) 
+                else
+                    Unknown ()
+                fi,
+            read count =
+               (framesFor samples acc =
+                    if samples <= 0 or empty? remaining then
+                        acc
+                    else
+                        this = mat.newMatrix (RowMajor ())
+                           (map (bf.multiply w) (mat.asRows (head remaining)));
+                        remaining := tail remaining;
+                        framesFor (samples - hop) (acc ++ [this])
+                    fi;
+                source = mat.concat (Horizontal ()) (framesFor count [buffered]);
+                toReturn = mat.columnSlice source 0 count;
+                buffered := mat.columnSlice source count (mat.width source);
+                toReturn),
             close = \(),
-        }
-    fi;
-
+        });
+    
 //!!! doc: convert frames back to a stream
 streamed rate { framesize, hop } frames =
     if framesize == hop then
         streamContiguous rate framesize frames
     else
-        //!!! OLA
-        syn.empty rate 1;
+        streamOverlapping rate framesize hop frames
     fi;
 
 monoFrames params stream =
@@ -115,5 +168,7 @@
 
     frequencyDomainFramesOfFile parameters filename = 
         frequencyDomainFrames parameters (af.open filename),
+
+    streamed,
 }
 
--- a/yetilab/stream/syntheticstream.yeti	Wed May 29 17:32:58 2013 +0100
+++ b/yetilab/stream/syntheticstream.yeti	Wed May 29 22:59:23 2013 +0100
@@ -3,6 +3,7 @@
 
 ch = load yetilab.stream.channels;
 vec = load yetilab.vector;
+mat = load yetilab.matrix;
 
 load yetilab.vector.type;
 load yetilab.stream.type;
@@ -69,7 +70,7 @@
     sinusoid is number -> number -> stream, 
     whiteNoise is number -> stream,
     silent is number -> stream,
-    empty = number -> number -> stream,
+    empty is number -> number -> stream,
 }
 
 
--- a/yetilab/stream/test/test_framer.yeti	Wed May 29 17:32:58 2013 +0100
+++ b/yetilab/stream/test/test_framer.yeti	Wed May 29 22:59:23 2013 +0100
@@ -8,12 +8,27 @@
 
 { compare, compareUsing } = load yetilab.test.test;
 
-testStream n is number -> 'a  = syn.precalculated 1000 (vec.fromList [1..n]);
+rate = 10;
+
+testStream n is number -> 'a  = syn.precalculated rate (vec.fromList [1..n]);
 
 compareFrames frames1 frames2 =
     all id (map2 do f1 f2: compareUsing mat.equal f1 f2 done frames1
        (map (mat.newRowVector . vec.fromList) frames2));
 
+testFrames parameters length expected =
+   (f = fr.frames parameters (testStream length);
+    str = fr.streamed rate parameters f;
+    sz = parameters.framesize;
+    ts = testStream length;
+    compareFrames f expected and
+        compareUsing mat.equal
+           (str.read 2) (ts.read 2) and
+        compareUsing mat.equal
+           (str.read (length - 2)) (ts.read (length - 2)) and
+        compare str.position length and
+        compare str.available (Known (sz - (length % sz))));
+
 [
 
 "framecount-2x2": \( 
@@ -72,28 +87,21 @@
 ),
 
 "frames-2x5": \( 
-    fr = fr.frames { framesize = 2, hop = 2 } (testStream 5);
-    expected = [ [1,2], [3,4], [5,0] ];
-    compareFrames fr expected;
+    testFrames { framesize = 2, hop = 2 } 5 [ [1,2], [3,4], [5,0] ];
 ),
 
 "frames-4.3x4": \( 
-    fr = fr.frames { framesize = 4, hop = 3 } (testStream 4);
-    expected = [ [0,1,2,3], [3,4,0,0] ];
-    compareFrames fr expected;
+    testFrames { framesize = 4, hop = 3 } 4 [ [0,1,2,3], [3,4,0,0] ];
 ),
 
 "frames-3.2x4": \(
-    fr = fr.frames { framesize = 3, hop = 2 } (testStream 4);
-    expected = [ [0,1,2], [2,3,4], [4,0,0] ];
-    compareFrames fr expected;
+    testFrames { framesize = 3, hop = 2 } 4 [ [0,1,2], [2,3,4], [4,0,0] ];
 ),
 
 "frames-3.1x6": \(
-    fr = fr.frames { framesize = 3, hop = 1 } (testStream 6);
-    expected = [ [0,0,1], [0,1,2], [1,2,3], [2,3,4],
-                 [3,4,5], [4,5,6], [5,6,0], [6,0,0] ];
-    compareFrames fr expected;
+    testFrames { framesize = 3, hop = 1 } 6
+        [ [0,0,1], [0,1,2], [1,2,3], [2,3,4],
+          [3,4,5], [4,5,6], [5,6,0], [6,0,0] ];
 ),
 
 ] is hash<string, () -> boolean>;