changeset 92:77d3292bbf12

Reorganise files
author Chris Cannam
date Wed, 20 Mar 2013 09:57:54 +0000
parents 6d6627fbbb78
children d0abc9afe608
files audiofile.yeti bin/yetilab block.yeti blockfuncs.yeti channels.yeti complex.yeti fft.yeti fmatrix.yeti framer.yeti fvector.yeti matrix.yeti playback.yeti stream.yeti syntheticstream.yeti test/all.yeti test/test.yeti test/test_blockfuncs.yeti test/test_complex.yeti test/test_fft.yeti test/test_framer.yeti test/test_fvector.yeti test/test_vamppost.yeti vamp.yeti vamppost.yeti vamprdf.yeti window.yeti yetilab yetilab/block/block.yeti yetilab/block/blockfuncs.yeti yetilab/block/complex.yeti yetilab/block/fmatrix.yeti yetilab/block/fvector.yeti yetilab/block/matrix.yeti yetilab/block/test/test_blockfuncs.yeti yetilab/block/test/test_complex.yeti yetilab/block/test/test_fvector.yeti yetilab/stream/audiofile.yeti yetilab/stream/channels.yeti yetilab/stream/framer.yeti yetilab/stream/playback.yeti yetilab/stream/stream.yeti yetilab/stream/syntheticstream.yeti yetilab/stream/test/test_framer.yeti yetilab/test/all.yeti yetilab/test/test.yeti yetilab/transform/fft.yeti yetilab/transform/test/test_fft.yeti yetilab/transform/window.yeti yetilab/vamp/test/test_vamppost.yeti yetilab/vamp/vamp.yeti yetilab/vamp/vamppost.yeti yetilab/vamp/vamprdf.yeti
diffstat 52 files changed, 2076 insertions(+), 2076 deletions(-) [+]
line wrap: on
line diff
--- a/audiofile.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +0,0 @@
-
-module audiofile;
-
-import javax.sound.sampled:
-     AudioSystem, AudioInputStream, AudioFormat, AudioFormat$Encoding,
-     UnsupportedAudioFileException;
-
-import java.io: File, IOException;
-
-import java.nio: ByteBuffer, ByteOrder;
-
-str = load stream;
-ch = load channels;
-block = load block;
-
-decode8u bytes doubles n is ~byte[] -> ~double[] -> number -> () =
-   (for [0..n-1] do i:
-       doubles[i] := (bytes[i] / 128.0) - 1.0;
-    done
-   );
-
-decode16s bytes doubles n is ~byte[] -> ~double[] -> number -> () =
-   (bb = ByteBuffer#wrap(bytes, 0, n * 2);
-    bb#order(ByteOrder#LITTLE_ENDIAN);
-    for [0..n-1] do i:
-       doubles[i] := bb#getShort(i*2) / 32768.0;
-    done
-   );
-
-decode32f bytes doubles n is ~byte[] -> ~double[] -> number -> () =
-   (bb = ByteBuffer#wrap(bytes, 0, n * 4);
-    bb#order(ByteOrder#LITTLE_ENDIAN);
-    for [0..n-1] do i:
-       doubles[i] := bb#getFloat(i*4);
-    done
-   );
-
-decodeFail () = 
-    throw new UnsupportedAudioFileException("File format not supported. Supported formats are 8-bit unsigned PCM, 16-bit signed little-endian PCM, or IEEE float");
-
-decode { format is ~AudioFormat } bytes doubles n = 
-   (if format#isBigEndian() then
-        decodeFail()
-    else
-        enc = format#getEncoding();
-        bits = format#getSampleSizeInBits();
-        if bits == 32 then
-            decode32f bytes doubles n;
-        elif bits == 16 and enc == AudioFormat$Encoding#PCM_SIGNED then
-            decode16s bytes doubles n;
-        elif bits == 8 and enc == AudioFormat$Encoding#PCM_UNSIGNED then
-            decode8u bytes doubles n;
-        else
-            decodeFail();
-        fi
-    fi);
-
-readInterleaved' { format is ~AudioFormat, stream is ~AudioInputStream } nframes =
-   (channels = format#getChannels();
-    bytesPerSample = format#getSampleSizeInBits() / 8;
-    bytes = new byte[nframes * channels * bytesPerSample];
-    bytesRead = stream#read(bytes);
-    if bytesRead <= 0 then block.zeros 0;
-    else
-        n = int(bytesRead / bytesPerSample);
-        doubles = new double[n];
-        decode { format } bytes doubles n;
-        block.block doubles;
-    fi;
-   );
-
-read' { format is ~AudioFormat, stream is ~AudioInputStream } n =
-   (b = readInterleaved' { format, stream } n;
-    channels = format#getChannels();
-    ch.deinterleaved channels b;
-   );
-
-readMono' { format is ~AudioFormat, stream is ~AudioInputStream } n =
-   (b = readInterleaved' { format, stream } n;
-    channels = format#getChannels();
-    ch.mixedDownFromInterleaved channels b;
-   );
-
-// Note, all this assumes the stream is non-blocking (i.e. available()
-// is to the end of file)
-
-available' { format is ~AudioFormat, stream is ~AudioInputStream } =
-    stream#available() / ((format#getSampleSizeInBits() / 8) * format#getChannels());
-
-close' { stream is ~AudioInputStream } =
-    stream#close();
-
-open name is string -> 'a = 
-    try
-        f = new File(name);
-        stream = AudioSystem#getAudioInputStream(f);
-        format = stream#getFormat();
-        len = available' { format, stream }; // at start of stream
-        Stream (str.stream {
-            stream,
-            format,
-            len,
-            rate = format#getSampleRate(),
-            channels = format#getChannels(),
-            get position () = len - available' { stream, format },
-            get available () = available' { stream, format },
-            read = read' { stream, format },
-            readInterleaved = readInterleaved' { stream, format },
-            readMono = readMono' { stream, format },
-            close () = close' { stream },
-        });
-    catch Exception e:
-        Error "Failed to open audio file \"\(name)\": \(e)";
-    yrt;
-
-{
-    open
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/yetilab	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,16 @@
+#!/bin/sh
+MYDIR=`dirname $0`
+YETII_DIR=../yetii
+YETI_LIBDIR=${YETI_LIBDIR:=../yeti}
+if [ ! -d "$YETI_LIBDIR" ]; then 
+    YETI_LIBDIR=../other/yeti
+fi
+JVAMP_DIR=${JVAMP_DIR:=../jvamp}
+YERTLE_DIR=${YERTLE_DIR:=../yertle}
+
+YETI_MODULE_SOURCE_PATH=${YETI_LIBDIR}/modules \
+    LD_LIBRARY_PATH=$YETII_DIR:$JVAMP_DIR:$LD_LIBRARY_PATH \
+    $JAVA_HOME/bin/java -classpath \
+        $YETII_DIR/yetii.jar:$YETI_LIBDIR/yeti.jar:$YETI_LIBDIR/yeti-lib.jar:$YETII_DIR/libreadline-java.jar:$JVAMP_DIR/jvamp.jar:$YERTLE_DIR/yertle.jar:./jtransforms-2.4.jar \
+    com.particularprograms.yetii "$@"
+
--- a/block.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-
-module block;
-
-vec = load fvector;
-
-typedef opaque block = ~double[];
-
-{
-zeros = vec.zeros,
-consts = vec.consts,
-ones = vec.ones,
-block v = v,
-data b = b,
-vector b = vec.copyOf b,
-floats = vec.floats,
-fromFloats ff = vec.fromFloats ff,
-fromList l = vec.vector l,
-list = vec.list,
-length = vec.length,
-equal = vec.equal,
-copyOf = vec.copyOf,
-rangeOf = vec.rangeOf,
-resizedTo = vec.resizedTo,
-concat = vec.concat,
-} as {
-zeros is number -> block,
-consts is number -> number -> block,
-ones is number -> block,
-block is ~double[] -> block,
-data is block -> ~double[],
-vector is block -> ~double[],
-floats is block -> ~float[],
-fromFloats is ~float[] -> block,
-fromList is list?<number> -> block,
-list is block -> list<number>,
-length is block -> number,
-equal is block -> block -> boolean,
-copyOf is block -> block,
-rangeOf is block -> number -> number -> block,
-resizedTo is number -> block -> block,
-concat is list?<block> -> block,
-}
-
-
-
--- a/blockfuncs.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-
-module blockfuncs;
-
-b = load block;
-
-sum' =
-    sum . b.list;
-
-mean bl =
-    case b.length bl of
-        0: 0;
-        len: sum' bl / len
-    esac;
-
-multiply b1 b2 =
-    b.fromList (map2 (*) (b.list b1) (b.list b2));
-
-divideBy n bl =
-    b.fromList (map (/ n) (b.list bl));
-
-sqr bl =
-    multiply bl bl;
-
-rms =
-    sqrt . mean . sqr;
-
-sqrt' =
-    b.fromList . (map sqrt) . b.list;
-
-fftshift bl =
-   (len = b.length bl;
-    half = int(len/2 + 0.5); // round up for odd-length sequences
-    b.concat [b.rangeOf bl half (len-half), b.rangeOf bl 0 half]);
-
-ifftshift bl =
-   (len = b.length bl;
-    half = int(len/2); // round down for odd-length sequences
-    b.concat [b.rangeOf bl half (len-half), b.rangeOf bl 0 half]);
-
-{
-sum = sum',
-mean,
-multiply, divideBy, sqr,
-sqrt = sqrt',
-rms,
-fftshift,
-ifftshift,
-}
-
-
-        
--- a/channels.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-
-module channels;
-
-vec = load fvector;
-block = load block;
-mat = load fmatrix;
-        
-interleaved m = 
-   ({ cols, rows } = mat.dimensions m;
-    v = vec.zeros (cols * rows);
-    for [0..rows-1] do row:
-        for [0..cols-1] do col:
-            v[col * rows + row] := m[row][col];
-        done;
-    done;
-    block.block v);
-
-deinterleaved rows b =
-   (v = block.data b;
-    mat.generate do row col:
-        v[rows * col + row]
-    done rows ((vec.length v) / rows));
-
-mixedDown m =
-   (if empty? m then block.zeros 0 else
-        { cols, rows } = mat.dimensions m;
-        v = vec.copyOf m[0];
-        for [1..rows-1] do row:
-            for [0..cols-1] do col:
-                v[col] := v[col] + m[row][col];
-            done;
-        done;
-        block.block v;
-    fi);
-
-mixedDownFromInterleaved rows b =
-   (v = block.data b;
-    cols = ((vec.length v) / rows);
-    v' = vec.zeros cols;
-    for [0..rows-1] do row:
-        for [0..cols-1] do col:
-            v'[col] := v'[col] + v[col * rows + row];
-        done;
-    done;
-    block.block v');
-
-mixedFromInterleavedTo targetRows rows b = 
-    if targetRows == rows then
-        b;
-    elif targetRows == 1 then
-        mixedDownFromInterleaved rows b;
-    else
-        v = block.data b;
-        cols = ((vec.length v) / rows);
-        v' = vec.zeros (cols * targetRows);
-        for [0..targetRows-1] do target:
-            for [0..cols-1] do col:
-                if target < rows then
-                    v'[col * targetRows + target] := v[col * rows + target];
-                elif rows == 1 then
-                    v'[col * targetRows + target] := v[col * rows];
-                fi
-            done
-        done;
-        block.block v';
-    fi;
-
-{
-    interleaved, deinterleaved,
-    mixedDown, mixedDownFromInterleaved, mixedFromInterleavedTo,
-}
-
--- a/complex.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +0,0 @@
-
-module complex;
-
-import java.lang: ClassCastException;
-
-class Cplx(double real, double imag)
-    int getReal()
-        real,
-    int getImag()
-        imag,
-    double getMagnitude()
-        sqrt (real * real + imag * imag),
-    double getAngle()
-        Math#atan2(imag, real),
-    String toString()
-        if real == int real and imag == int imag then
-            if imag < 0 then
-                " \(int real) - \(int (-imag))i"
-            else 
-                " \(int real) + \(int imag)i"
-            fi
-        else
-            if imag < 0 then
-                " \(real) - \((-imag))i"
-            else 
-                " \(real) + \(imag)i"
-            fi
-        fi,
-    int hashCode()
-        Double#valueOf(real)#hashCode() + Double#valueOf(imag)#hashCode(),
-    boolean equals(Object other)
-        try
-            c = other unsafely_as ~Cplx;
-            c#getReal() == real and c#getImag() == imag
-        catch ClassCastException:
-            false
-        yrt,
-end;
-
-typedef opaque cplx = ~Cplx;
-
-real c1 is ~Cplx -> number =
-    c1#getReal();
-
-imaginary c1 is ~Cplx -> number =
-    c1#getImag();
-
-complex re im is number -> number -> ~Cplx =
-    new Cplx(re, im);
-
-magnitude c is ~Cplx -> number =
-    c#getMagnitude();
-
-angle c is ~Cplx -> number =
-    c#getAngle();
-
-add c1 c2 is ~Cplx -> ~Cplx -> ~Cplx =
-    complex (real c1 + real c2) (imaginary c1 + imaginary c2);
-
-scale r c is number -> ~Cplx -> ~Cplx =
-    complex (r * real c) (r * imaginary c);
-
-{
-   real,
-   imaginary,
-   complex,
-   magnitude,
-   angle,
-   add,
-   scale,
-} as {
-   real is cplx -> number,
-   imaginary is cplx -> number,
-   complex is number -> number -> cplx,
-   magnitude is cplx -> number,
-   angle is cplx -> number,
-   add is cplx -> cplx -> cplx,
-   scale is number -> cplx -> cplx,
-}
-
--- a/fft.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-
-module fft;
-
-import edu.emory.mathcs.jtransforms.fft: DoubleFFT_1D;
-
-b = load block;
-vec = load fvector;
-complex = load complex;
-
-packedToComplex p =
-   (n = (vec.length p) / 2;
-    array
-       (map do i:
-            re = if i == n then p[1] else p[i*2] fi;
-            im = if i == 0 or i == n then 0 else p[i*2+1] fi;
-            complex.complex re im;
-        done [0..n]));
-
-complexToPacked arr =
-   (n = length arr;
-    v = vec.vector
-       (map do i:
-            ix = int (i/2);
-            if i == ix*2 then
-                complex.real arr[ix]
-            else 
-                complex.imaginary arr[ix] 
-            fi;
-            done [0..(n-1)*2-1]);
-    v[1] := complex.real arr[n-1];
-    v);
-
-realForward n = 
-   (d = new DoubleFFT_1D(n);
-    do bl:
-        v = b.vector bl;
-        d#realForward(v);
-        packedToComplex v;
-    done);
-
-realInverse n = 
-   (d = new DoubleFFT_1D(n);
-    do arr:
-        v = complexToPacked arr;
-        d#realInverse(v, true);
-        b.block v;
-    done);
-
-{
-realForward,
-realInverse,
-}
-
--- a/fmatrix.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-module fmatrix;
-
-// Basic matrices using primitive array of double as each row
-
-vec = load fvector;
-
-zeroMatrix rows cols = array (map \(vec.zeros cols) [1..rows]);
-
-generate f rows cols =
-   (m = zeroMatrix rows cols;
-    for [0..rows-1] do row:
-        for [0..cols-1] do col:
-            m[row][col] := f row col;
-        done;
-    done;
-    m);
-
-constMatrix n = generate do row col: n done;
-randomMatrix = generate do row col: Math#random() done;
-identityMatrix = constMatrix 1;
-
-width m = if length m > 0 then vec.length m[0] else 0 fi;
-cols = width;
-
-height m = length m;
-rows = height;
-
-dimensions m = { cols = width m, rows = height m };
-
-copyOf m = array (map vec.copyOf m);
-
-transposed m is array<~double[]> -> array<~double[]> = 
-    generate do row col: m[col][row] done (cols m) (rows m);
-
-{
-generate, constMatrix, randomMatrix, zeroMatrix, identityMatrix,
-width, cols, height, rows, dimensions,
-copyOf,
-transposed,
-}
-
--- a/framer.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-
-module framer;
-
-/**
- * Framer expresses a stream (or a file) as a lazy list of (possibly
- * overlapping) frames of mono data.
- */
-
-block = load block;
-bf = load blockfuncs;
-af = load audiofile;
-win = load window;
-fft = load fft;
-
-blockList framesize stream =
-    if stream.finished? then
-       (stream.close (); [] );
-    else
-        block.resizedTo framesize (stream.readMono framesize)
-            :. \(blockList framesize stream);
-    fi;
-
-overlappingBlockList size hop stream valid buffer =
-   (
-    b = stream.readMono hop;
-    obtained = block.length b;
-
-    // Retain framesize - hop samples from old buffer, add hop samples
-    // (zero-padded if necessary) just read
-    buffer = block.concat
-       [block.rangeOf buffer hop (size-hop),
-        block.resizedTo hop b];
-
-    // Number of "valid" elements (not tail-end zero-padding) left in buffer
-    remaining = valid - (hop - obtained);
-
-    if remaining <= 0 then
-        stream.close ();
-        [];
-    else
-        buffer :. \(overlappingBlockList size hop stream remaining buffer);
-    fi);
-
-frames { framesize, hop } stream =
-    if framesize == hop then
-        blockList framesize stream
-    else
-        overlappingBlockList framesize hop stream 
-            framesize (block.zeros framesize);
-    fi;
-
-windowedFrames { framesize, hop, window } stream =
-   (win = window framesize;
-    map (bf.multiply win) (frames { framesize, hop } stream));
-
-frequencyDomainFrames { framesize, hop } stream =
-   (f = fft.realForward framesize;
-    map f (windowedFrames { framesize, hop, window = win.hann } stream));
-
-{ 
-    frames,
-    windowedFrames,
-    frequencyDomainFrames,
-
-    framesOfFile parameters filename =
-        case af.open filename of
-        Stream s: Frames (frames parameters s);
-        Error e: Error e;
-        esac,
-
-    windowedFramesOfFile parameters filename = 
-        case af.open filename of
-        Stream s: Frames (windowedFrames parameters s);
-        Error e: Error e;
-        esac,
-
-    frequencyDomainFramesOfFile parameters filename = 
-        case af.open filename of
-        Stream s: Frames (frequencyDomainFrames parameters s);
-        Error e: Error e;
-        esac,
-}
-
--- a/fvector.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-module fvector;
-
-import java.util: Arrays;
-
-zeros n =
-    new double[n];
-
-consts m n =
-   (a = zeros n;
-    for [0..n-1] do i:
-        a[i] := m;
-    done;
-    a);
-
-ones = consts 1.0;
-
-vector l is list?<number> -> ~double[] =
-   (arr = array(l);
-    len = length arr;
-    v = zeros len;
-    for [0..len-1] do i:
-        v[i] := arr[i];
-    done;
-    v);
-
-list' a is ~double[] -> list<number> =
-    list a;
-
-length' =
-    length . list';
-
-floats a is ~double[] -> ~float[] =
-   (len = length' a;
-    f = new float[len];
-    for [0..len-1] do i:
-        f[i] := a[i];
-    done;
-    f);
-
-fromFloats ff is ~float[] -> ~double[] =
-   (len = length (list ff);
-    a = new double[len];
-    for [0..len-1] do i:
-        a[i] := ff[i];
-    done;
-    a);
-
-equal v1 v2 =
-    list' v1 == list' v2;
-
-copyOf v is ~double[] -> ~double[] =
-    Arrays#copyOf(v, list' v |> length);
-
-rangeOf v start len is ~double[] -> number -> number -> ~double[] =
-    Arrays#copyOfRange(v, start, start + len);
-
-resizedTo n v is number -> ~double[] -> ~double[] =
-    Arrays#copyOf(v, n);
-
-concat vv is list?<~double[]> -> ~double[] =
-   (len = sum (map length' vv);
-    vout = zeros len;
-    var base = 0;
-    for vv do v: 
-        vlen = length' v;
-        for [0..vlen-1] do i: vout[base + i] := v[i] done;
-        base := base + vlen;
-    done;
-    vout);
-
-{
-zeros, consts, ones,
-vector,
-length = length',
-list = list',
-floats, fromFloats,
-equal,
-copyOf, rangeOf, resizedTo,
-concat,
-}
-
--- a/matrix.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-module matrix;
-
-// Basic matrices using number type (rather than primitive arrays)
-
-zeros n = array(map \0 [1..n]);
-ones  n = array(map \1 [1..n]);
-
-generateMatrix f rows cols = array
-   (map do row: array
-       (map (f row) [0..cols-1])
-    done [0..rows-1]);
-
-constMatrix n = generateMatrix do row col: n done;
-
-randomMatrix = generateMatrix do row col: Math#random() done;
-
-zeroMatrix = constMatrix 0;
-identityMatrix = constMatrix 1;
-
-width m = if length m > 0 then length m[0] else 0 fi;
-
-height m = length m;
-
-dimensions m = { cols = width m, rows = height m };
-
-transposed m = array
-   (map do n: array
-       (map do a: a[n-1] done m)
-    done [1..width m]);
-
-interleaved m = array(concat(transposed m));
-
-deinterleaved rows v =
-    generateMatrix do row col:
-        v[rows * col + row]
-    done rows (length v / rows);
-
-{
-zeros, ones,
-generateMatrix, constMatrix, randomMatrix, zeroMatrix, identityMatrix,
-width, height, dimensions,
-transposed,
-interleaved, deinterleaved,
-}
-
--- a/playback.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-
-module playback;
-
-block = load block;
-fr = load framer;
-af = load audiofile;
-ch = load channels;
-
-import javax.sound.sampled:
-    AudioSystem, AudioFormat, AudioFormat$Encoding, SourceDataLine;
-
-import java.nio: ByteBuffer, ByteOrder;
-
-playBlock line b is ~SourceDataLine -> 'a -> () =
-   (len = block.length b;
-    samples = block.unblock b;
-    nb = len * 2;
-    bytes = new byte[nb];
-    bb = ByteBuffer#wrap(bytes, 0, nb);
-    bb#order(ByteOrder#LITTLE_ENDIAN);
-    sb = bb#asShortBuffer();
-    for [0..len-1] do i: sb#put(i, samples[i] * 32767.0); () done;
-    actual = line#write(bytes, 0, nb); 
-    ());
-
-play line blocks = for blocks (playBlock line);
-    
-open { rate, channels } = 
-   (format = new AudioFormat(AudioFormat$Encoding#PCM_SIGNED, rate, 16,
-                             channels, channels * 2, rate, false);
-    line = AudioSystem#getSourceDataLine(format);
-    line#open(format);
-    line#start();
-    {
-        line,
-        play = play line,
-        channels = line#getFormat()#getChannels(),
-        close () = (line#drain(); line#close()),
-    });
-
-playStream stream =
-   (line = open { rate = stream.sampleRate, channels = stream.channels };
-    blocksize = 10240;
-    not stream.finished? loop
-        line.play [(ch.mixedFromInterleavedTo line.channels stream.channels
-                    (stream.readInterleaved blocksize))];
-    line.close();
-    stream.close());
-
-playFile filename = playStream (af.open filename);
-
-{
-    open, play, playStream, playFile,
-}
-
--- a/stream.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-
-module stream;
-
-monoStream box =
-   (readAll' box = box.read (box.len - box.position);
-    {
-        get position () = box.position,
-        get channels () = 1,
-        get sampleRate () = box.rate,
-        get available () = box.len - box.position,
-        get finished? () = not (box.len > box.position),
-        read = box.read,
-        readInterleaved = box.read,
-        readMono = box.read,
-        readAll () = readAll' box,
-        readAllInterleaved () = readAll' box,
-        readAllMono () = readAll' box,
-        close = box.close,
-    });
-
-stream box = 
-   ({
-        get position () = box.position,
-        get channels () = box.channels,
-        get sampleRate () = box.rate,
-        get available () = box.len - box.position,
-        get finished? () = not (box.len > box.position),
-        read = box.read,
-        readInterleaved = box.readInterleaved,
-        readMono = box.readMono,
-        readAll () = box.read (box.len - box.position),
-        readAllInterleaved () = box.readInterleaved (box.len - box.position),
-        readAllMono () = box.readMono (box.len - box.position),
-        close = box.close,
-    });
-
-{ monoStream, stream }
-
--- a/syntheticstream.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-
-module syntheticstream;
-
-str = load stream;
-vec = load fvector;
-block = load block;
-
-generated rate generator seconds =
-    str.monoStream {
-        var position = 0,
-        len = int(seconds * rate + 0.5),
-        rate,
-        read count = 
-           (rc = min count (len - position);
-            result = vec.zeros rc;
-            for [0..rc-1] do i:
-                result[i] := generator ((position + i) / rate)
-            done;
-            position := position + rc;
-            block.block result),
-        close = \(),
-        };
-
-sinusoid rate freq seconds =
-    generated rate (sin . (* (freq / (2*pi * rate)))) seconds;
-
-whiteNoise rate seconds =
-    generated rate \((Math#random() * 2.0) - 1.0) seconds;
-
-precalculated rate data is number -> ~double[] -> 'a =
-   (n = vec.length data;
-    str.monoStream {
-        var position = 0,
-        len = n,
-        rate,
-        read count = 
-           (rc = min count (len - position);
-            result = vec.rangeOf data position rc;
-            position := position + rc;
-            block.block result),
-        close = \(),
-    });
-
-{
-    generated, precalculated, sinusoid, whiteNoise,
-}
-
--- a/test/all.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-
-program test.all;
-
-{ runTests } = load test.test;
-
-tests = [
-"fvector"    : load test.test_fvector,
-"framer"     : load test.test_framer,
-"blockfuncs" : load test.test_blockfuncs,
-"complex"    : load test.test_complex,
-"fft"        : load test.test_fft,
-"vamppost"   : load test.test_vamppost,
-];
-
-bad = sum (mapHash do name testHash: runTests name testHash done tests);
-
-if (bad > 0) then
-    println "\n** \(bad) test(s) failed!";
-else
-    ()
-fi
-
--- a/test/test.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-
-module test.test;
-
-vec = load fvector;
-ss = load syntheticstream;
-
-testStream n is number -> 'a  = ss.precalculated 1000 (vec.vector [1..n]);
-
-compare obtained expected =
-    if obtained == expected then
-        true;
-    else
-        println "** expected: \(expected)\n   obtained: \(obtained)";
-        false;
-    fi;
-
-select f = fold do r x: if f x then x::r else r fi done [];
-
-failedTests testHash =
-    select (!= "")
-       (mapHash do name f:
-            if f () then "" else
-                println "Test \(name) failed";
-                name;
-            fi 
-        done testHash);
-        
-runTests group testHash =
-   (failed = failedTests testHash;
-    good = (length testHash - length failed);
-    bad = length failed;
-    println "\(group): \(good)/\(good+bad) tests passed";
-    if not empty? failed then
-        println "\(group): Failed tests [\(bad)]: \(strJoin ' ' failed)";
-    fi;
-    bad);
-
-{
-    testStream, compare, failedTests, runTests
-}
-
--- a/test/test_blockfuncs.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-
-module test.test_blockfuncs;
-
-stdSqrt = sqrt;
-
-{ zeros, consts, ones, fromList, list } = load block;
-{ sum, mean, multiply, divideBy, sqr, sqrt, rms, fftshift, ifftshift } = load blockfuncs;
-{ compare } = load test.test;
-
-[
-
-"sum": \(
-    compare ((sum . zeros) 0) 0 and
-        compare ((sum . zeros) 5) 0 and
-        compare ((sum . ones) 5) 5 and
-        compare ((sum . fromList) [1,-2,3,0]) 2
-),
-
-"mean": \(
-    compare ((mean . zeros) 0) 0 and
-        compare ((mean . zeros) 5) 0 and
-        compare ((mean . ones) 5) 1 and
-        compare ((mean . fromList) [1,-2,3,0]) 0.5
-),
-
-"multiply": \(
-    compare (list (multiply (zeros 0) (ones 5))) [] and
-        compare (list (multiply (consts (-3) 4) (fromList [1,2,3]))) [-3,-6,-9]
-),
-
-"divideBy": \(
-    compare (list (divideBy 5 (ones 0))) [] and
-        compare (list (divideBy 5 (fromList [1,2,-3]))) [0.2,0.4,-0.6]
-),
-
-"sqr": \(
-    compare ((list . sqr . zeros) 0) [] and
-        compare ((list . sqr . ones) 5) [1,1,1,1,1] and
-        compare ((list . sqr . fromList) [0.5,-2,3,0]) [0.25,4,9,0]
-),
-
-"sqrt": \(
-    compare ((list . sqrt . zeros) 0) [] and
-        compare ((list . sqrt . ones) 5) [1,1,1,1,1] and
-        compare ((list . sqrt . fromList) [0.25,4,9,0]) [0.5,2,3,0]
-),
-
-"rms": \(
-    compare ((rms . zeros) 0) 0 and
-        compare ((rms . ones) 5) 1 and
-        compare ((rms . fromList) [-1,2,2]) (stdSqrt 3)
-),
-
-"fftshift": \(
-    compare ((list . fftshift . zeros) 0) [] and 
-        compare ((list . fftshift . fromList) [1,2,3,4]) [3,4,1,2] and
-        compare ((list . fftshift . fromList) [1,2,3,4,5]) [4,5,1,2,3]
-),
-
-"ifftshift": \(
-    compare ((list . ifftshift . zeros) 0) [] and 
-        compare ((list . ifftshift . fromList) [3,4,1,2]) [1,2,3,4] and
-        compare ((list . ifftshift . fromList) [4,5,1,2,3]) [1,2,3,4,5]
-),
-
-] is hash<string, () -> boolean>;
-
-
--- a/test/test_complex.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-module test.test_complex;
-
-{ real, imaginary, complex, magnitude, angle, add, scale }
-   = load complex;
-
-{ compare } = load test.test;
-
-[
-
-"complex": \( 
-    compare (complex 1 2) (complex 1 2) and
-        complex (-1) 2 != complex 1 2
-),
-
-"real": \(
-    compare (real (complex 3 2)) 3
-),
-
-"imaginary": \(
-    compare (imaginary (complex 3 4)) 4
-),
-
-"magnitude": \(
-    compare (magnitude (complex (-3) 4)) 5
-),
-
-"angle": \(
-    compare (angle (complex 1 0)) 0 and
-        compare (angle (complex 1 1)) (pi/4) and
-        compare (angle (complex 0 1)) (pi/2) and
-        compare (angle (complex (-1) 0)) pi and
-        compare (angle (complex 0 (-1))) (-pi/2)
-),
-
-"add": \(
-    compare (add (complex 2 3) (complex (-4) 5)) (complex (-2) 8)
-),
-
-"scale": \(
-    compare (scale 4 (complex 2 3)) (complex 8 12)
-),
-
-] is hash<string, () -> boolean>;
-
-
-
--- a/test/test_fft.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-
-module test.test_fft;
-
-{ realForward, realInverse } = load fft;
-{ list, fromList } = load block;
-{ complex } = load complex;
-
-{ compare } = load test.test;
-
-testFFT orig reals imags =
-   (out = realForward (length orig) (fromList orig);
-    back = realInverse (length orig) out;
-    compare out (array (map2 complex reals imags)) and compare orig (list back));
-
-[
-
-"dc": \(
-    testFFT [1,1,1,1] [4,0,0] [0,0,0];
-),
-
-"sine": \(
-    testFFT [0,1,0,-1] [0,0,0] [0,-2,0];
-),
-
-"cosine": \(
-    testFFT [1,0,-1,0] [0,2,0] [0,0,0];
-),
-
-"sineCosine": \(
-    testFFT [0.5,1,-0.5,-1] [0,1,0] [0,-2,0];
-),
-
-"nyquist": \(
-    testFFT [1,-1,1,-1] [0,0,4] [0,0,0];
-),
-
-"dirac": \(
-    testFFT [1,0,0,0] [1,1,1] [0,0,0] and
-        testFFT [0,1,0,0] [1,0,-1] [0,-1,0] and
-        testFFT [0,0,1,0] [1,-1,1] [0,0,0] and
-        testFFT [0,0,0,1] [1,0,-1] [0,1,0];
-),
-
-] is hash<string, () -> boolean>;
-
-
--- a/test/test_framer.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,94 +0,0 @@
-
-module test.test_framer;
-
-fr = load framer;
-block = load block;
-
-{ compare, testStream } = load test.test;
-
-[
-
-"framecount-2x2": \( 
-    fr = fr.frames { framesize = 2, hop = 2 } (testStream 2);
-    compare (length fr) 1
-),
-
-"framecount-2x3": \( 
-    fr = fr.frames { framesize = 2, hop = 2 } (testStream 3);
-    compare (length fr) 2
-),
-
-"framecount-2x4": \( 
-    fr = fr.frames { framesize = 2, hop = 2 } (testStream 4);
-    compare (length fr) 2
-),
-
-"framecount-2.1x0": \( 
-    fr = fr.frames { framesize = 2, hop = 1 } (testStream 0);
-    compare (length fr) 1
-),
-
-"framecount-2.1x1": \( 
-    fr = fr.frames { framesize = 2, hop = 1 } (testStream 1);
-    compare (length fr) 2
-),
-
-"framecount-2.1x2": \( 
-    fr = fr.frames { framesize = 2, hop = 1 } (testStream 2);
-    compare (length fr) 3
-),
-
-"framecount-2.1x3": \( 
-    fr = fr.frames { framesize = 2, hop = 1 } (testStream 3);
-    compare (length fr) 4
-),
-
-"framecount-4.1x4": \( 
-    fr = fr.frames { framesize = 4, hop = 1 } (testStream 4);
-    compare (length fr) 7
-),
-
-"framecount-4.3x4": \( 
-    fr = fr.frames { framesize = 4, hop = 3 } (testStream 4);
-    compare (length fr) 2 
-),
-
-"framecount-4.4x4": \( 
-    fr = fr.frames { framesize = 4, hop = 4 } (testStream 4);
-    compare (length fr) 1
-),
-
-"framecount-3.2x4": \(
-    fr = fr.frames { framesize = 3, hop = 2 } (testStream 4);
-    compare (length fr) 3
-),
-
-"frames-2x5": \( 
-    fr = fr.frames { framesize = 2, hop = 2 } (testStream 5);
-    expected = [ [1,2], [3,4], [5,0] ];
-    compare (map block.list fr) expected;
-),
-
-"frames-4.3x4": \( 
-    fr = fr.frames { framesize = 4, hop = 3 } (testStream 4);
-    expected = [ [0,1,2,3], [3,4,0,0] ];
-    compare (map block.list fr) expected;
-),
-
-"frames-3.2x4": \(
-    fr = fr.frames { framesize = 3, hop = 2 } (testStream 4);
-    expected = [ [0,1,2], [2,3,4], [4,0,0] ];
-    compare (map block.list fr) expected;
-),
-
-"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] ];
-    compare (map block.list fr) expected;
-),
-
-] is hash<string, () -> boolean>;
-
-
-
--- a/test/test_fvector.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,126 +0,0 @@
-
-module test.test_fvector;
-
-vec = load fvector;
-
-{ compare } = load test.test;
-
-[
-
-"zeros-empty": \(
-    v = vec.zeros 0;
-    compare (vec.length v) 0;
-),
-
-"zeros": \(
-    v = vec.zeros 3;
-    compare (vec.length v) 3 and
-        compare v[0] 0 and
-        compare v[1] 0 and
-        compare v[2] 0;
-),
-
-"consts-empty": \(
-    v = vec.consts 4 0;
-    compare (vec.length v) 0;
-),
-
-"consts": \(
-    v = vec.consts 4 3;
-    compare (vec.length v) 3 and
-        compare v[0] 4 and
-        compare v[1] 4 and
-        compare v[2] 4;
-),
-
-"ones-empty": \(
-    v = vec.ones 0;
-    compare (vec.length v) 0;
-),
-
-"ones": \(
-    v = vec.ones 3;
-    compare (vec.length v) 3 and
-        compare v[0] 1 and
-        compare v[1] 1 and
-        compare v[2] 1;
-),
-
-"from-list-empty": \(
-    v = vec.vector [];
-    compare (vec.length v) 0;
-),
-
-"from-list": \(
-    v = vec.vector [1,2,3,4];
-    compare (vec.length v) 4 and
-        compare v[0] 1 and
-        compare v[1] 2 and
-        compare v[2] 3 and
-        compare v[3] 4;
-),
-
-"equal-empty": \(
-    vec.equal (vec.vector []) (vec.vector [])
-),
-
-"equal": \(
-    v = vec.vector [1,1,1,1];
-    w = vec.ones 4;
-    w' = vec.zeros 4;
-    w'' = vec.ones 3;
-    vec.equal v w and not vec.equal v w' and not vec.equal v w'';
-),
-
-"copyOf-empty": \(
-    vec.equal (vec.vector []) (vec.copyOf (vec.vector []))
-),
-
-"copyOf": \(
-    v = vec.vector [1,2,3,4];
-    w = vec.copyOf v;
-    vec.equal v w and (
-        v[0] := 0; // check result is not aliasing inputs
-        not vec.equal v w
-    );
-),
-
-"rangeOf": \(
-    v = vec.vector [1,2,3,4];
-    vec.equal (vec.rangeOf v 0 4) v and (
-        vec.equal (vec.rangeOf v 2 2) (vec.vector [3,4])
-    )
-),
-
-"resizedTo": \(
-    vec.equal (vec.resizedTo 4 (vec.vector [])) (vec.zeros 4) and
-        vec.equal (vec.resizedTo 2 (vec.vector [1,2])) (vec.vector [1,2]) and
-        vec.equal (vec.resizedTo 3 (vec.vector [1,2])) (vec.vector [1,2,0]) and
-        vec.equal (vec.resizedTo 2 (vec.vector [1,2,3])) (vec.vector [1,2]);
-),
-
-"concat2": \(
-    v = vec.vector [1,2,3];
-    w = vec.vector [4,5,6];
-    x = vec.concat [v, w];
-    x' = vec.vector [1,2,3,4,5,6];
-    vec.equal x x' and
-       (v[0] := 0; // check result is not aliasing inputs
-        w[0] := 0;
-        vec.equal x x') and
-        vec.equal x' (vec.concat [x', vec.vector []]) and
-        vec.equal x' (vec.concat [vec.vector [], x'])
-),
-
-"concatn": \(
-    v = vec.vector [1,2,3];
-    w = vec.vector [4,5,6];
-    vec.equal (vec.concat []) (vec.zeros 0) and
-        vec.equal (vec.concat [v]) v and
-        vec.equal (vec.concat [v,w,v]) (vec.vector [1,2,3,4,5,6,1,2,3])
-),
-
-] is hash<string, () -> boolean>;
-
-
-
--- a/test/test_vamppost.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-module test.test_vamppost;
-
-vp = load vamppost;
-
-{ compare } = load test.test;
-
-untimed n = { timestamp = Untimed (), values = [n] };
-timed t n = { timestamp = Time t, values = [n] };
-
-testdata =  {
-    output = { sampleType = OneSamplePerStep () },
-    config = { sampleRate = 1000, stepSize = 100 },
-    features = [
-        [],
-        [ untimed 1 ],
-        [ untimed 1, untimed 2 ],
-        [],
-        [ timed 1.1 1, untimed 2, timed 4 3 ]
-    ]
-};
-
-[
-
-"fillOneSamplePerStep": \(
-    // All features returned within a single process call (i.e. within
-    // a single sub-list of the feature list) should be given the same
-    // timestamp; the timestamp increments according to the config
-    // step size between sub-lists
-    filled = vp.fillTimestamps
-       (testdata with { output = { sampleType = OneSamplePerStep () } });
-    compare filled [
-        timed 0.1 1 ,
-        timed 0.2 1, timed 0.2 2 ,
-        timed 0.4 1, timed 0.4 2, timed 0.4 3
-    ];
-),
-
-"fillFixedSampleRate": \(
-    // "If the output feature's hasTimestamp field is true, the host
-    // should read and use the output feature's timestamp. The host
-    // may round the timestamp according to the sample rate given in
-    // the output descriptor's sampleRate field [...] If
-    // [hasTimestamp] is false, its time will be implicitly calculated
-    // by incrementing the time of the previous feature according to
-    // the [output descriptor's] sample rate". Note that the time is
-    // based on that of the previous feature, not that of the previous
-    // process cycle (as is the case with OneSamplePerStep features).
-    filled = vp.fillTimestamps
-       (testdata with { output = { sampleType = FixedSampleRate 5 } });
-    compare filled [
-        timed 0 1 ,
-        timed 0.2 1, timed 0.4 2 ,
-        timed 1.0 1, timed 1.2 2, timed 4.0 3
-    ];
-),
-
-"fillVariableSampleRate": \(
-    // For VariableSampleRate outputs, the timestamps should always
-    // be left entirely alone by fillTimestamps -- it's an error for
-    // the plugin to return any features without valid timestamps,
-    // but it isn't the job of fillTimestamps to handle that error
-    filled = vp.fillTimestamps
-       (testdata with { output = { sampleType = VariableSampleRate 5 } });
-    compare filled [
-        untimed 1 ,
-        untimed 1, untimed 2 ,
-        timed 1.1 1, untimed 2, timed 4.0 3
-    ];
-),
-
-] is hash<string, () -> boolean>;
-
--- a/vamp.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,295 +0,0 @@
-module vamp;
-
-import org.vamp_plugins:
-       Plugin, Plugin$InputDomain,
-       PluginLoader, PluginLoader$AdapterFlags, PluginLoader$LoadFailedException,
-       ParameterDescriptor, OutputDescriptor, OutputDescriptor$SampleType,
-       RealTime, Feature;
-
-import java.lang: UnsatisfiedLinkError;
-
-import java.util: Map, List;
-
-block = load block;
-fr = load framer;
-af = load audiofile;
-vamprdf = load vamprdf;
-
-store = load yertle.store;
-
-realTime r is ~RealTime -> number = r#sec() + (r#nsec() / 1000000000);
-
-feature f is ~Feature -> 'a = {
-    timestamp = if f#hasTimestamp then Time (realTime f#timestamp) else Untimed () fi,
-    duration = if f#hasDuration then Time (realTime f#duration) else Untimed () fi,
-    values = f#values,
-    label = f#label,
-    };
-
-featureList fl is ~Object -> 'a =
-    if nullptr? fl then []
-    else
-        a = fl unsafely_as ~List;
-        result = array [];
-        itr = a#iterator();
-        itr#hasNext() loop (push result (feature (itr#next() unsafely_as ~Feature)));
-        list result
-    fi;
-
-featureSet fs is ~Map -> 'a =
-   (numberOf n is ~Object -> number = (n unsafely_as ~Integer)#intValue();
-    s = [:];
-    kk = list fs#keySet()#toArray();
-    for kk do k: s[numberOf k] := featureList fs#get(k) done;
-    s);
-
-getStandardStore =
-   (s = store.newRdfStore ();
-    var loaded = false;
-    \(synchronized s do:
-          if not loaded then
-            vamprdf.loadSystemVampRdf s;
-            loaded := true
-          fi;
-          s
-      done)
-    );
-
-getPluginPath () =
-   (try
-        map string PluginLoader#getInstance()#getPluginPath();
-    catch UnsatisfiedLinkError e:
-        eprintln "Warning: Unable to obtain plugin path:\n\(e)";
-        [];
-    yrt);
-
-listPlugins () =
-   (try
-        map string PluginLoader#getInstance()#listPlugins();
-    catch UnsatisfiedLinkError e:
-        eprintln "Warning: Unable to obtain plugin list:\n\(e)";
-        [];
-    yrt);
-   
-categoryOf key =
-    list PluginLoader#getInstance()#getPluginCategory(key);
-
-inputDomain d is ~Plugin$InputDomain -> 'a = 
-    if d == Plugin$InputDomain#FREQUENCY_DOMAIN then
-        FrequencyDomain ()
-    else
-        TimeDomain ()
-    fi;
-
-parameterDescriptor pd is ~ParameterDescriptor -> 'a = {
-    identifier = pd#identifier,
-    name = pd#name,
-    description = pd#description,
-    unit = pd#unit,
-    minValue = pd#minValue,
-    maxValue = pd#maxValue,
-    defaultValue = pd#defaultValue,
-    get quantize () = if pd#isQuantized then QuantizeStep pd#quantizeStep else Unquantized () fi,
-    valueNames = map string pd#valueNames
-    };
-
-sampleType t rate is ~OutputDescriptor$SampleType -> number -> 'a =
-    if t == OutputDescriptor$SampleType#OneSamplePerStep then
-        OneSamplePerStep ()
-    elif t == OutputDescriptor$SampleType#FixedSampleRate then
-        FixedSampleRate rate
-    else
-        VariableSampleRate rate
-    fi;
-
-structureOf rdfOutputData od is 'a -> ~OutputDescriptor -> 'b = 
-   (computes = case rdfOutputData of Some d: d.computes; None (): Unknown () esac;
-    s = getStandardStore ();
-    noteIRI = case s.expand "af:Note" of IRI iri: iri; _: "" esac;
-    if od#hasFixedBinCount and od#binCount == 0 then
-        Instants ();
-    elif od#hasDuration then
-        if computes != Unknown () then
-            if computes == Event noteIRI then Notes ();
-            else Regions ();
-            fi
-        elif od#hasFixedBinCount then
-            if od#binCount > 1 then Notes ();
-            elif od#unit == "Hz" or strIndexOf (strLower od#unit) "midi" 0 >= 0 then Notes ();
-            else Regions ();
-            fi
-        else
-            Unknown ();
-        fi
-    elif od#hasFixedBinCount and od#binCount == 1 then
-        case computes of
-        Event e:
-            if strEnds? e "Segment" then Segmentation ();
-            else Curve ();
-            fi;
-        _: Curve ();
-        esac;
-    elif od#hasFixedBinCount and
-         od#sampleType != OutputDescriptor$SampleType#VariableSampleRate then
-        Grid ();
-    else
-        Unknown ();
-    fi);
-
-outputDescriptor rdfOutputData od is 'a -> ~OutputDescriptor -> 'b = {
-    identifier = od#identifier,
-    name = od#name,
-    description = od#description,
-    get binCount () = if od#hasFixedBinCount then Fixed od#binCount else Variable () fi,
-    get valueExtents () = if od#hasKnownExtents then Known { min = od#minValue, max = od#maxValue } else Unknown () fi,
-    get valueQuantize () = if od#isQuantized then QuantizeStep od#quantizeStep else Unquantized () fi,
-    valueUnit = od#unit,
-    binNames = array (map string od#binNames),
-    sampleType = sampleType od#sampleType od#sampleRate,
-    hasDuration = od#hasDuration,
-    get computes () = case rdfOutputData of Some data: data.computes; None (): Unknown () esac,
-    get inferredStructure () = structureOf rdfOutputData od,
-    };
-
-plugin key p is string -> ~Plugin -> 'a =
-   (rdfData = vamprdf.pluginDataByKey (getStandardStore ()) key;
-    {
-    plugin = p,
-    key,
-    get apiVersion () = p#getVampApiVersion(),
-    get identifier () = p#getIdentifier(),
-    get name () = p#getName(),
-    get description () = p#getDescription(),
-    get maker () = p#getMaker(),
-    get copyright () = p#getCopyright(),
-    get version () = p#getPluginVersion(),
-    get category () = PluginLoader#getInstance()#getPluginCategory(key),
-    get hasRdfDescription () = (rdfData != None ()),
-    get infoURL () = case rdfData of Some data: data.infoURL; None (): "" esac,
-    get parameters () = array (map parameterDescriptor p#getParameterDescriptors()),
-    parameterValue identifier = p#getParameter(identifier),
-    setParameterValue identifier value = p#setParameter(identifier, value),
-    get programs () = array (map string p#getPrograms()),
-    get currentProgram () = p#getCurrentProgram(),
-    selectProgram pr = p#selectProgram(pr),
-    get inputDomain () = inputDomain p#getInputDomain(),
-    get preferredBlockSize () = p#getPreferredBlockSize(),
-    get preferredStepSize () = p#getPreferredStepSize(),
-    get minChannelCount () = p#getMinChannelCount(),
-    get maxChannelCount () = p#getMaxChannelCount(),
-    initialise { channels, hop, blockSize } = p#initialise(channels, hop, blockSize),
-    reset () = p#reset(),
-    get outputs () =
-        array case rdfData of
-        Some data: map2 outputDescriptor (map Some data.outputs) p#getOutputDescriptors();
-        None (): map (outputDescriptor (None ())) p#getOutputDescriptors();
-        esac,
-    process blocks time is 'a -> ~RealTime -> 'b = 
-        featureSet p#process((map block.floats blocks) as ~float[][], 0, time),
-    getRemainingFeatures () = featureSet p#getRemainingFeatures(),
-    dispose () = p#dispose(),
-    });
-
-featuresFromSet outputNo f = if outputNo in f then f[outputNo] else [] fi;
-
-outputNumberByName p name =
-   (outputs = p.outputs;
-    case find ((== name) . (.identifier)) outputs of
-    first::rest: index first outputs;
-    _: -1;
-    esac);
-
-loadPlugin rate key =
-    try
-        OK (plugin key 
-            PluginLoader#getInstance()#loadPlugin(key, rate,
-                PluginLoader$AdapterFlags#ADAPT_INPUT_DOMAIN +
-                PluginLoader$AdapterFlags#ADAPT_CHANNEL_COUNT))
-    catch PluginLoader$LoadFailedException _:
-        Error "Failed to load Vamp plugin with key \(key)"
-    yrt;
-
-processed { p, sampleRate, hop } frames count 
-    // I don't know why this type declaration is necessary. Without
-    // it, yeti records the return type as 'a and can't do anything
-    // useful with it (giving IncompatibleClassChangeError when
-    // e.g. accessing the values array)
-    is 'a -> 'b -> 'c ->
-        list<hash<number,
-                  list<{ timestamp is Time number | Untimed (), 
-                         duration  is Time number | Untimed (),
-                         label is string,
-                         values is ~float[]
-                       }>>> =
-    case frames of
-    frame::rest:
-        p.process [frame] RealTime#frame2RealTime(count, sampleRate)
-        :.
-        \(processed { p, sampleRate, hop } rest (count + hop));
-    _: 
-       (rf = p.getRemainingFeatures ();
-        p.dispose ();
-        [rf]);
-    esac;
-
-converted { p, sampleRate, hop } outputNo fl =
-    map (featuresFromSet outputNo) fl;
-
-returnErrorFrom p stream text = (p.dispose (); stream.close (); Error text);
-
-processWith key p outputNo stream =
-   (blockSize = p.preferredBlockSize;
-    stepSize = p.preferredStepSize;
-    channels = 1;
-    params = {
-        p, sampleRate = stream.sampleRate, channels = 1,
-        framesize = blockSize, blockSize, hop = stepSize
-    };
-    if p.initialise params then
-        OK {
-            key = key,
-            output = p.outputs[outputNo],
-            parameters = mapIntoHash id p.parameterValue
-               (map (.identifier) p.parameters),
-            config = {
-                channels, blockSize, stepSize,
-                sampleRate = stream.sampleRate
-            },
-            features = converted params outputNo
-               (processed params (fr.frames params stream) 0)
-        };
-        // If processing completed successfully, then p is
-        // disposed by processed and stream is closed by the
-        // framer
-    else
-        returnErrorFrom p stream "Failed to initialise plugin \(key) with channels = \(channels), blockSize = \(blockSize), stepSize = \(stepSize)";
-    fi);
-
-process key output stream =
-    case loadPlugin stream.sampleRate key of
-    OK p:
-        outputNo = outputNumberByName p output;
-        if outputNo >= 0 then
-            processWith key p outputNo stream
-        else
-            outputs = strJoin ", " (map (.identifier) p.outputs);
-            returnErrorFrom p stream "Plugin \(key) has no output named \(output) (outputs are \(outputs))"
-        fi;
-    Error e: Error e;
-    esac;
-
-processFile key output filename = 
-    case af.open filename of
-    Stream s: process key output s;
-    Error e: Error e;
-    esac;
-
-{
-get pluginPath = getPluginPath,
-get pluginKeys = listPlugins,
-loadPlugin,
-categoryOf,
-process,
-processFile,
-}
-
--- a/vamppost.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +0,0 @@
-module vamppost;
-
-fmat = load fmatrix;
-
-fillOneSamplePerStep config features =
-   (fill' n pending features =
-        case pending of
-        feature::rest:
-            stamped = feature with
-               { timestamp = Time ((n * config.stepSize) / config.sampleRate) };
-            stamped :. \(fill' n rest features);
-        _:
-            if empty? features then []
-            else fill' (n+1) (head features) (tail features);
-            fi;
-        esac;
-    fill' (-1) [] features);
-
-fillFixedSampleRate config rate features =
-   (eps = 0.0001;
-    fill' n pending features =
-        case pending of
-        feature::rest:
-            n = case feature.timestamp of
-                Untimed (): n + 1;
-                Time t: int (t * rate + eps);
-                esac;
-            stamped = feature with { timestamp = Time (n / rate) };
-            stamped :. \(fill' n rest features);
-        _:
-            if empty? features then []
-            else fill' n (head features) (tail features);
-            fi;
-        esac;
-    fill' (-1) [] features);
-
-fillTimestamps { output, config, features } =
-    case output.sampleType of
-    OneSamplePerStep ():
-        fillOneSamplePerStep config features;
-    FixedSampleRate rate:
-        fillFixedSampleRate config rate features;
-    VariableSampleRate _:
-        concat features;
-    esac;
-
-structureGrid binCount features =
-    if empty? features then
-        fmat.constMatrix binCount 0 0;
-    else
-        fmat.generate
-            do row col:
-                features[col].values[row];
-            done binCount (length features);
-    fi;
-
-structure data =
-   (type = data.output.inferredStructure;
-    features =
-        case type of
-        Grid (): concat data.features;
-        _: fillTimestamps data;
-        esac;
-    binCount = 
-        case data.output.binCount of
-        Fixed n: n;
-        _: 0;
-        esac;
-    case type of
-    Curve ():               // No duration, one value
-        Curve features;
-    Grid ():                // No duration, >1 value, not variable rate
-        Grid (structureGrid binCount (array features));
-    Instants ():            // Zero-valued features
-        Instants features;
-    Notes ():               // Duration, at least one value (pitch or freq)
-        Notes features;
-    Regions ():             // Duration, zero or more values
-        Regions features;
-    Segmentation ():        // No duration, one value, segment type in RDF
-        Segmentation features;
-    Unknown ():             // Other
-        Unknown features;
-    esac);
-
-postprocess data =
-    case data of
-    OK data:
-        structure data;
-    Error e:
-        Error e;
-    esac;
-
-{
-fillTimestamps,
-postprocess
-}
-
-
--- a/vamprdf.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,294 +0,0 @@
-
-module vamprdf;
-
-read = load yertle.read;
-{ newRdfStore } = load yertle.store;
-
-import java.io: File;
-
-import org.vamp_plugins: PluginLoader;
-
-import java.lang: UnsatisfiedLinkError;
-
-getPluginPath () =
-   (try map string PluginLoader#getInstance()#getPluginPath();
-    catch UnsatisfiedLinkError e: [];
-    yrt);
-
-systemVampRdfFiles () =
-    concat
-       (map do p:
-            map ((p ^ File#separator ^) . (.name))
-               (filter do entry:
-                    entry.file? and
-                       (lc = strLower entry.name;
-                        (strEnds? lc ".ttl") or
-                        (strEnds? lc ".n3") or
-                        (strEnds? lc ".nt"))
-                    done (listDirectory false p))
-            done (getPluginPath ()));
-
-addVampPrefixes store =
-   (store.addPrefix "vamp" "http://purl.org/ontology/vamp/";
-    store.addPrefix "dc" "http://purl.org/dc/elements/1.1/";
-    store.addPrefix "foaf" "http://xmlns.com/foaf/0.1/";
-    store.addPrefix "owl" "http://www.w3.org/2002/07/owl#";
-    store.addPrefix "af" "http://purl.org/ontology/af/");
-
-loadSystemVampRdf store =
-   (addVampPrefixes store;
-    for (systemVampRdfFiles ()) do file:
-        case read.loadTurtleFile store ("file://" ^ file) file of
-        OK (): ();
-        Error e: eprintln
-            "WARNING: Failed to load Vamp plugin RDF file \"\(file)\": \(e)";
-        esac
-    done);
-
-getGlobalPluginIndex () =
-    list (strSplit "\n" (fetchURL [ Timeout 10 ] (Handle getContents)
-                         "http://www.vamp-plugins.org/rdf/plugins/index.txt"));
-
-//!!! need to cache these retrievals
-parseGlobalVampRdf () =
-   (parse urls =
-        case urls of
-        url::rest:
-           (doc = fetchURL [ Timeout 10 ] (Handle getContents) url;
-            parsed = read.parseTurtleString url doc;
-            { url, parsed } :. \(parse rest));
-         _: [];
-        esac;
-    parse (getGlobalPluginIndex ()));
-
-loadGlobalVampRdf store =
-    for (parseGlobalVampRdf ()) do { url, parsed }:
-        case read.loadParsedTriples store parsed of
-        OK (): ();
-        Error e: eprintln "WARNING: Failed to load Vamp RDF from URL \(url): \(e)";
-        esac;
-    done;
-
-filterIRIsFromNodes nodes =
-    map do r: case r of IRI iri: iri; esac done
-       (filter do r: case r of IRI iri: true; _: false esac done nodes);
-
-subjects = map (.s);
-
-iriTypes =
-    map do t:
-        case t of
-        IRI iri: IRI iri;
-        Blank n: Blank n;
-        esac done;
-
-iriSubjects = iriTypes . subjects;
-
-allLibraryNodes store =
-    iriSubjects
-       (store.match {
-            s = Wildcard (),
-            p = Known (store.expand "a"),
-            o = Known (store.expand "vamp:PluginLibrary")
-            });
-
-allPluginNodes store =
-    iriSubjects
-       (store.match {
-            s = Wildcard (),
-            p = Known (store.expand "a"),
-            o = Known (store.expand "vamp:Plugin")
-            });
-
-pluginsWithId store id = 
-    iriTypes
-       (filter do pnode:
-        store.contains {
-            s = pnode,
-            p = store.expand "vamp:identifier",
-            o = Literal { value = id, type = "", language = "" }
-            }
-        done (allPluginNodes store));
-
-librariesWithId store id =
-    iriTypes
-       (filter do lnode:
-        store.contains {
-            s = lnode,
-            p = store.expand "vamp:identifier",
-            o = Literal { value = id, type = "", language = "" }
-            }
-        done (allLibraryNodes store));
-
-splitPluginKey key =
-   (bits = strSplit ":" key;
-    reversed = reverse bits;
-    soname = strJoin ":" (reverse (tail reversed));
-    identifier = head reversed;
-    { soname, identifier });
-
-pluginNodesByKey store key =
-   (case splitPluginKey key of { soname, identifier }:
-        candidatePlugins = pluginsWithId store identifier;
-        candidateLibraries = librariesWithId store soname;
-        filter do pnode:
-            any do lnode:
-                store.contains {
-                    s = lnode,
-                    p = store.expand "vamp:available_plugin",
-                    o = pnode
-                    }
-                done candidateLibraries
-            done candidatePlugins
-    esac);
-
-libraryNodeFor store pluginNode =
-    case store.match {
-        s = Wildcard (), p = Known (store.expand "vamp:available_plugin"), o = Known pluginNode
-        } of
-    { s = IRI iri }::others: Some (IRI iri);
-    { s = Blank n }::others: Some (Blank n);
-     _: None ();
-    esac;
-
-textProperty store subject name =
-    case store.match {
-        s = Known subject, p = Known (store.expand name), o = Wildcard ()
-        } of
-    { o = Literal { value = text } }::others: text;
-     _: "";
-    esac;
-
-iriProperty store subject name =
-    case store.match {
-        s = Known subject, p = Known (store.expand name), o = Wildcard ()
-        } of
-    { o = IRI iri }::others: IRI iri;
-     _: None ();
-    esac;
-
-nodeProperty store subject name =
-    case store.match {
-        s = Known subject, p = Known (store.expand name), o = Wildcard ()
-        } of
-    { o = IRI iri }::others: Some (IRI iri);
-    { o = Blank n }::others: Some (Blank n);
-     _: None ();
-    esac;
-
-inputDomainOf store pluginNode =
-   case store.match {
-        s = Known pluginNode, p = Known (store.expand "vamp:input_domain"), o = Wildcard ()
-        } of
-    { o = IRI iri }::others:
-        if IRI iri == store.expand "vamp:FrequencyDomain"
-        then FrequencyDomain ()
-        else TimeDomain ()
-        fi;
-     _: TimeDomain ();
-    esac;
-
-outputDescriptor store outputNode =
-   (tprop abbr = textProperty store outputNode abbr;
-    iprop abbr = iriProperty store outputNode abbr;
-    bprop abbr deflt =
-       (b = strLower (textProperty store outputNode abbr);
-        if b == "true" then true elif b == "false" then false else deflt fi);
-    nprop abbr =
-        try number (textProperty store outputNode abbr); catch Exception _: 0 yrt;
-    {
-        identifier = tprop "vamp:identifier",
-        name = tprop "dc:title",
-        description = tprop "dc:description",
-        rdfType = case iprop "a" of IRI iri: iri; _: "" esac,
-        valueUnit = tprop "vamp:unit",
-        binCount = 
-            if bprop "vamp:fixed_bin_count" false
-            then Known (nprop "vamp:bin_count")
-            else Unknown ()
-            fi,
-        computes =
-            case iprop "vamp:computes_event_type" of
-            IRI iri: Event iri;
-             _: case iprop "vamp:computes_signal_type" of
-                IRI iri: Signal iri;
-                 _: case iprop "vamp:computes_feature_type" of
-                    IRI iri: Feature iri;
-                     _: Unknown ();
-                    esac
-                esac
-            esac,
-        //!!! and some other properties
-    });
-
-pluginDataByNode store pluginNode =
-   (tprop abbr = textProperty store pluginNode abbr;
-    nprop abbr =
-        try number (textProperty store pluginNode abbr); catch Exception _: 0 yrt;
-    soname =
-        case libraryNodeFor store pluginNode of
-        None (): "";
-        Some n: textProperty store n "vamp:identifier";
-        esac;
-    {
-        pluginKey = soname ^ ":" ^ tprop "vamp:identifier",
-        soname,
-        apiVersion = nprop "vamp:vamp_API_version",
-        identifier = tprop "vamp:identifier",
-        name = tprop "dc:title",
-        description = tprop "dc:description",
-        maker =
-           (tmaker = tprop "foaf:maker";
-            if tmaker == "" then
-                case nodeProperty store pluginNode "foaf:maker" of
-                Some n: textProperty store n "foaf:name";
-                None (): "";
-                esac
-            else
-                tmaker
-            fi),
-        copyright = tprop "dc:rights",
-        version = tprop "owl:versionInfo",
-        category = tprop "vamp:category",
-        inputDomain = inputDomainOf store pluginNode,
-        infoURL =
-           (case iriProperty store pluginNode "foaf:page" of
-            IRI iri: iri;
-            None ():
-                case libraryNodeFor store pluginNode of
-                None (): "";
-                Some n:
-                    case iriProperty store n "foaf:page" of
-                    IRI iri: iri;
-                    None (): "";
-                    esac;
-                esac;
-            esac),
-        outputs = 
-           (matches = store.match { s = Known pluginNode,
-                                    p = Known (store.expand "vamp:output"), 
-                                    o = Wildcard () };
-            array (map do t:
-                       case t.o of
-                       IRI iri: outputDescriptor store (IRI iri);
-                       Blank n: outputDescriptor store (Blank n);
-                       esac
-                       done matches)),
-    });
-
-pluginDataByKey store key =
-    case pluginNodesByKey store key of
-    node::others: Some (pluginDataByNode store node);
-    _: None ()
-    esac;
-
-{
-loadSystemVampRdf,
-loadGlobalVampRdf,
-allPluginNodes,
-allLibraryNodes,
-pluginNodesByKey,
-pluginDataByNode,
-pluginDataByKey,
-}
-
--- a/window.yeti	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-
-module window;
-
-b = load block;
-bf = load blockfuncs;
-
-cosinewin a0 a1 a2 a3 n =
-    b.fromList
-       (map do i:
-            a0
-            - a1 * cos(2 * pi * i / n)
-            + a2 * cos(4 * pi * i / n)
-            - a2 * cos(6 * pi * i / n)
-            done [0..n-1]);
-                  
-hann = cosinewin 0.5 0.5 0.0 0.0;
-hamming = cosinewin 0.54 0.46 0.0 0.0;
-blackman = cosinewin 0.42 0.50 0.08 0.0;
-nuttall = cosinewin 0.3635819 0.4891775 0.1365995 0.0106411;
-blackmanHarris = cosinewin 0.35875 0.48829 0.14128 0.01168;
-boxcar = b.consts 0.5;
-
-bartlett n =
-    b.fromList
-       (m = n/2;
-        concat [
-            map do i:
-                i / m
-                done [0..m-1],
-            map do i:
-                1.0 - (i / m)
-                done [0..m-1]
-            ]);
-
-windowed windowFunc frames =
-    case frames of
-        []: frames;
-         _: (first = head frames;
-             window = windowFunc (b.length first);
-             map (bf.multiply window) frames);
-    esac;
-
-{
-cosinewin, hann, hamming, blackman, nuttall, blackmanHarris, boxcar, bartlett,
-windowed
-};
-
--- a/yetilab	Tue Mar 19 13:43:39 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-#!/bin/sh
-MYDIR=`dirname $0`
-YETII_DIR=../yetii
-YETI_LIBDIR=${YETI_LIBDIR:=../yeti}
-if [ ! -d "$YETI_LIBDIR" ]; then 
-    YETI_LIBDIR=../other/yeti
-fi
-JVAMP_DIR=${JVAMP_DIR:=../jvamp}
-YERTLE_DIR=${YERTLE_DIR:=../yertle}
-
-YETI_MODULE_SOURCE_PATH=${YETI_LIBDIR}/modules \
-    LD_LIBRARY_PATH=$YETII_DIR:$JVAMP_DIR:$LD_LIBRARY_PATH \
-    $JAVA_HOME/bin/java -classpath \
-        $YETII_DIR/yetii.jar:$YETI_LIBDIR/yeti.jar:$YETI_LIBDIR/yeti-lib.jar:$YETII_DIR/libreadline-java.jar:$JVAMP_DIR/jvamp.jar:$YERTLE_DIR/yertle.jar:./jtransforms-2.4.jar \
-    com.particularprograms.yetii "$@"
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/block/block.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,45 @@
+
+module block;
+
+vec = load fvector;
+
+typedef opaque block = ~double[];
+
+{
+zeros = vec.zeros,
+consts = vec.consts,
+ones = vec.ones,
+block v = v,
+data b = b,
+vector b = vec.copyOf b,
+floats = vec.floats,
+fromFloats ff = vec.fromFloats ff,
+fromList l = vec.vector l,
+list = vec.list,
+length = vec.length,
+equal = vec.equal,
+copyOf = vec.copyOf,
+rangeOf = vec.rangeOf,
+resizedTo = vec.resizedTo,
+concat = vec.concat,
+} as {
+zeros is number -> block,
+consts is number -> number -> block,
+ones is number -> block,
+block is ~double[] -> block,
+data is block -> ~double[],
+vector is block -> ~double[],
+floats is block -> ~float[],
+fromFloats is ~float[] -> block,
+fromList is list?<number> -> block,
+list is block -> list<number>,
+length is block -> number,
+equal is block -> block -> boolean,
+copyOf is block -> block,
+rangeOf is block -> number -> number -> block,
+resizedTo is number -> block -> block,
+concat is list?<block> -> block,
+}
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/block/blockfuncs.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,51 @@
+
+module blockfuncs;
+
+b = load block;
+
+sum' =
+    sum . b.list;
+
+mean bl =
+    case b.length bl of
+        0: 0;
+        len: sum' bl / len
+    esac;
+
+multiply b1 b2 =
+    b.fromList (map2 (*) (b.list b1) (b.list b2));
+
+divideBy n bl =
+    b.fromList (map (/ n) (b.list bl));
+
+sqr bl =
+    multiply bl bl;
+
+rms =
+    sqrt . mean . sqr;
+
+sqrt' =
+    b.fromList . (map sqrt) . b.list;
+
+fftshift bl =
+   (len = b.length bl;
+    half = int(len/2 + 0.5); // round up for odd-length sequences
+    b.concat [b.rangeOf bl half (len-half), b.rangeOf bl 0 half]);
+
+ifftshift bl =
+   (len = b.length bl;
+    half = int(len/2); // round down for odd-length sequences
+    b.concat [b.rangeOf bl half (len-half), b.rangeOf bl 0 half]);
+
+{
+sum = sum',
+mean,
+multiply, divideBy, sqr,
+sqrt = sqrt',
+rms,
+fftshift,
+ifftshift,
+}
+
+
+        
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/block/complex.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,80 @@
+
+module complex;
+
+import java.lang: ClassCastException;
+
+class Cplx(double real, double imag)
+    int getReal()
+        real,
+    int getImag()
+        imag,
+    double getMagnitude()
+        sqrt (real * real + imag * imag),
+    double getAngle()
+        Math#atan2(imag, real),
+    String toString()
+        if real == int real and imag == int imag then
+            if imag < 0 then
+                " \(int real) - \(int (-imag))i"
+            else 
+                " \(int real) + \(int imag)i"
+            fi
+        else
+            if imag < 0 then
+                " \(real) - \((-imag))i"
+            else 
+                " \(real) + \(imag)i"
+            fi
+        fi,
+    int hashCode()
+        Double#valueOf(real)#hashCode() + Double#valueOf(imag)#hashCode(),
+    boolean equals(Object other)
+        try
+            c = other unsafely_as ~Cplx;
+            c#getReal() == real and c#getImag() == imag
+        catch ClassCastException:
+            false
+        yrt,
+end;
+
+typedef opaque cplx = ~Cplx;
+
+real c1 is ~Cplx -> number =
+    c1#getReal();
+
+imaginary c1 is ~Cplx -> number =
+    c1#getImag();
+
+complex re im is number -> number -> ~Cplx =
+    new Cplx(re, im);
+
+magnitude c is ~Cplx -> number =
+    c#getMagnitude();
+
+angle c is ~Cplx -> number =
+    c#getAngle();
+
+add c1 c2 is ~Cplx -> ~Cplx -> ~Cplx =
+    complex (real c1 + real c2) (imaginary c1 + imaginary c2);
+
+scale r c is number -> ~Cplx -> ~Cplx =
+    complex (r * real c) (r * imaginary c);
+
+{
+   real,
+   imaginary,
+   complex,
+   magnitude,
+   angle,
+   add,
+   scale,
+} as {
+   real is cplx -> number,
+   imaginary is cplx -> number,
+   complex is number -> number -> cplx,
+   magnitude is cplx -> number,
+   angle is cplx -> number,
+   add is cplx -> cplx -> cplx,
+   scale is number -> cplx -> cplx,
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/block/fmatrix.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,41 @@
+module fmatrix;
+
+// Basic matrices using primitive array of double as each row
+
+vec = load fvector;
+
+zeroMatrix rows cols = array (map \(vec.zeros cols) [1..rows]);
+
+generate f rows cols =
+   (m = zeroMatrix rows cols;
+    for [0..rows-1] do row:
+        for [0..cols-1] do col:
+            m[row][col] := f row col;
+        done;
+    done;
+    m);
+
+constMatrix n = generate do row col: n done;
+randomMatrix = generate do row col: Math#random() done;
+identityMatrix = constMatrix 1;
+
+width m = if length m > 0 then vec.length m[0] else 0 fi;
+cols = width;
+
+height m = length m;
+rows = height;
+
+dimensions m = { cols = width m, rows = height m };
+
+copyOf m = array (map vec.copyOf m);
+
+transposed m is array<~double[]> -> array<~double[]> = 
+    generate do row col: m[col][row] done (cols m) (rows m);
+
+{
+generate, constMatrix, randomMatrix, zeroMatrix, identityMatrix,
+width, cols, height, rows, dimensions,
+copyOf,
+transposed,
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/block/fvector.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,81 @@
+module fvector;
+
+import java.util: Arrays;
+
+zeros n =
+    new double[n];
+
+consts m n =
+   (a = zeros n;
+    for [0..n-1] do i:
+        a[i] := m;
+    done;
+    a);
+
+ones = consts 1.0;
+
+vector l is list?<number> -> ~double[] =
+   (arr = array(l);
+    len = length arr;
+    v = zeros len;
+    for [0..len-1] do i:
+        v[i] := arr[i];
+    done;
+    v);
+
+list' a is ~double[] -> list<number> =
+    list a;
+
+length' =
+    length . list';
+
+floats a is ~double[] -> ~float[] =
+   (len = length' a;
+    f = new float[len];
+    for [0..len-1] do i:
+        f[i] := a[i];
+    done;
+    f);
+
+fromFloats ff is ~float[] -> ~double[] =
+   (len = length (list ff);
+    a = new double[len];
+    for [0..len-1] do i:
+        a[i] := ff[i];
+    done;
+    a);
+
+equal v1 v2 =
+    list' v1 == list' v2;
+
+copyOf v is ~double[] -> ~double[] =
+    Arrays#copyOf(v, list' v |> length);
+
+rangeOf v start len is ~double[] -> number -> number -> ~double[] =
+    Arrays#copyOfRange(v, start, start + len);
+
+resizedTo n v is number -> ~double[] -> ~double[] =
+    Arrays#copyOf(v, n);
+
+concat vv is list?<~double[]> -> ~double[] =
+   (len = sum (map length' vv);
+    vout = zeros len;
+    var base = 0;
+    for vv do v: 
+        vlen = length' v;
+        for [0..vlen-1] do i: vout[base + i] := v[i] done;
+        base := base + vlen;
+    done;
+    vout);
+
+{
+zeros, consts, ones,
+vector,
+length = length',
+list = list',
+floats, fromFloats,
+equal,
+copyOf, rangeOf, resizedTo,
+concat,
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/block/matrix.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,45 @@
+module matrix;
+
+// Basic matrices using number type (rather than primitive arrays)
+
+zeros n = array(map \0 [1..n]);
+ones  n = array(map \1 [1..n]);
+
+generateMatrix f rows cols = array
+   (map do row: array
+       (map (f row) [0..cols-1])
+    done [0..rows-1]);
+
+constMatrix n = generateMatrix do row col: n done;
+
+randomMatrix = generateMatrix do row col: Math#random() done;
+
+zeroMatrix = constMatrix 0;
+identityMatrix = constMatrix 1;
+
+width m = if length m > 0 then length m[0] else 0 fi;
+
+height m = length m;
+
+dimensions m = { cols = width m, rows = height m };
+
+transposed m = array
+   (map do n: array
+       (map do a: a[n-1] done m)
+    done [1..width m]);
+
+interleaved m = array(concat(transposed m));
+
+deinterleaved rows v =
+    generateMatrix do row col:
+        v[rows * col + row]
+    done rows (length v / rows);
+
+{
+zeros, ones,
+generateMatrix, constMatrix, randomMatrix, zeroMatrix, identityMatrix,
+width, height, dimensions,
+transposed,
+interleaved, deinterleaved,
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/block/test/test_blockfuncs.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,68 @@
+
+module test.test_blockfuncs;
+
+stdSqrt = sqrt;
+
+{ zeros, consts, ones, fromList, list } = load block;
+{ sum, mean, multiply, divideBy, sqr, sqrt, rms, fftshift, ifftshift } = load blockfuncs;
+{ compare } = load test.test;
+
+[
+
+"sum": \(
+    compare ((sum . zeros) 0) 0 and
+        compare ((sum . zeros) 5) 0 and
+        compare ((sum . ones) 5) 5 and
+        compare ((sum . fromList) [1,-2,3,0]) 2
+),
+
+"mean": \(
+    compare ((mean . zeros) 0) 0 and
+        compare ((mean . zeros) 5) 0 and
+        compare ((mean . ones) 5) 1 and
+        compare ((mean . fromList) [1,-2,3,0]) 0.5
+),
+
+"multiply": \(
+    compare (list (multiply (zeros 0) (ones 5))) [] and
+        compare (list (multiply (consts (-3) 4) (fromList [1,2,3]))) [-3,-6,-9]
+),
+
+"divideBy": \(
+    compare (list (divideBy 5 (ones 0))) [] and
+        compare (list (divideBy 5 (fromList [1,2,-3]))) [0.2,0.4,-0.6]
+),
+
+"sqr": \(
+    compare ((list . sqr . zeros) 0) [] and
+        compare ((list . sqr . ones) 5) [1,1,1,1,1] and
+        compare ((list . sqr . fromList) [0.5,-2,3,0]) [0.25,4,9,0]
+),
+
+"sqrt": \(
+    compare ((list . sqrt . zeros) 0) [] and
+        compare ((list . sqrt . ones) 5) [1,1,1,1,1] and
+        compare ((list . sqrt . fromList) [0.25,4,9,0]) [0.5,2,3,0]
+),
+
+"rms": \(
+    compare ((rms . zeros) 0) 0 and
+        compare ((rms . ones) 5) 1 and
+        compare ((rms . fromList) [-1,2,2]) (stdSqrt 3)
+),
+
+"fftshift": \(
+    compare ((list . fftshift . zeros) 0) [] and 
+        compare ((list . fftshift . fromList) [1,2,3,4]) [3,4,1,2] and
+        compare ((list . fftshift . fromList) [1,2,3,4,5]) [4,5,1,2,3]
+),
+
+"ifftshift": \(
+    compare ((list . ifftshift . zeros) 0) [] and 
+        compare ((list . ifftshift . fromList) [3,4,1,2]) [1,2,3,4] and
+        compare ((list . ifftshift . fromList) [4,5,1,2,3]) [1,2,3,4,5]
+),
+
+] is hash<string, () -> boolean>;
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/block/test/test_complex.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,46 @@
+module test.test_complex;
+
+{ real, imaginary, complex, magnitude, angle, add, scale }
+   = load complex;
+
+{ compare } = load test.test;
+
+[
+
+"complex": \( 
+    compare (complex 1 2) (complex 1 2) and
+        complex (-1) 2 != complex 1 2
+),
+
+"real": \(
+    compare (real (complex 3 2)) 3
+),
+
+"imaginary": \(
+    compare (imaginary (complex 3 4)) 4
+),
+
+"magnitude": \(
+    compare (magnitude (complex (-3) 4)) 5
+),
+
+"angle": \(
+    compare (angle (complex 1 0)) 0 and
+        compare (angle (complex 1 1)) (pi/4) and
+        compare (angle (complex 0 1)) (pi/2) and
+        compare (angle (complex (-1) 0)) pi and
+        compare (angle (complex 0 (-1))) (-pi/2)
+),
+
+"add": \(
+    compare (add (complex 2 3) (complex (-4) 5)) (complex (-2) 8)
+),
+
+"scale": \(
+    compare (scale 4 (complex 2 3)) (complex 8 12)
+),
+
+] is hash<string, () -> boolean>;
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/block/test/test_fvector.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,126 @@
+
+module test.test_fvector;
+
+vec = load fvector;
+
+{ compare } = load test.test;
+
+[
+
+"zeros-empty": \(
+    v = vec.zeros 0;
+    compare (vec.length v) 0;
+),
+
+"zeros": \(
+    v = vec.zeros 3;
+    compare (vec.length v) 3 and
+        compare v[0] 0 and
+        compare v[1] 0 and
+        compare v[2] 0;
+),
+
+"consts-empty": \(
+    v = vec.consts 4 0;
+    compare (vec.length v) 0;
+),
+
+"consts": \(
+    v = vec.consts 4 3;
+    compare (vec.length v) 3 and
+        compare v[0] 4 and
+        compare v[1] 4 and
+        compare v[2] 4;
+),
+
+"ones-empty": \(
+    v = vec.ones 0;
+    compare (vec.length v) 0;
+),
+
+"ones": \(
+    v = vec.ones 3;
+    compare (vec.length v) 3 and
+        compare v[0] 1 and
+        compare v[1] 1 and
+        compare v[2] 1;
+),
+
+"from-list-empty": \(
+    v = vec.vector [];
+    compare (vec.length v) 0;
+),
+
+"from-list": \(
+    v = vec.vector [1,2,3,4];
+    compare (vec.length v) 4 and
+        compare v[0] 1 and
+        compare v[1] 2 and
+        compare v[2] 3 and
+        compare v[3] 4;
+),
+
+"equal-empty": \(
+    vec.equal (vec.vector []) (vec.vector [])
+),
+
+"equal": \(
+    v = vec.vector [1,1,1,1];
+    w = vec.ones 4;
+    w' = vec.zeros 4;
+    w'' = vec.ones 3;
+    vec.equal v w and not vec.equal v w' and not vec.equal v w'';
+),
+
+"copyOf-empty": \(
+    vec.equal (vec.vector []) (vec.copyOf (vec.vector []))
+),
+
+"copyOf": \(
+    v = vec.vector [1,2,3,4];
+    w = vec.copyOf v;
+    vec.equal v w and (
+        v[0] := 0; // check result is not aliasing inputs
+        not vec.equal v w
+    );
+),
+
+"rangeOf": \(
+    v = vec.vector [1,2,3,4];
+    vec.equal (vec.rangeOf v 0 4) v and (
+        vec.equal (vec.rangeOf v 2 2) (vec.vector [3,4])
+    )
+),
+
+"resizedTo": \(
+    vec.equal (vec.resizedTo 4 (vec.vector [])) (vec.zeros 4) and
+        vec.equal (vec.resizedTo 2 (vec.vector [1,2])) (vec.vector [1,2]) and
+        vec.equal (vec.resizedTo 3 (vec.vector [1,2])) (vec.vector [1,2,0]) and
+        vec.equal (vec.resizedTo 2 (vec.vector [1,2,3])) (vec.vector [1,2]);
+),
+
+"concat2": \(
+    v = vec.vector [1,2,3];
+    w = vec.vector [4,5,6];
+    x = vec.concat [v, w];
+    x' = vec.vector [1,2,3,4,5,6];
+    vec.equal x x' and
+       (v[0] := 0; // check result is not aliasing inputs
+        w[0] := 0;
+        vec.equal x x') and
+        vec.equal x' (vec.concat [x', vec.vector []]) and
+        vec.equal x' (vec.concat [vec.vector [], x'])
+),
+
+"concatn": \(
+    v = vec.vector [1,2,3];
+    w = vec.vector [4,5,6];
+    vec.equal (vec.concat []) (vec.zeros 0) and
+        vec.equal (vec.concat [v]) v and
+        vec.equal (vec.concat [v,w,v]) (vec.vector [1,2,3,4,5,6,1,2,3])
+),
+
+] is hash<string, () -> boolean>;
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/stream/audiofile.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,119 @@
+
+module audiofile;
+
+import javax.sound.sampled:
+     AudioSystem, AudioInputStream, AudioFormat, AudioFormat$Encoding,
+     UnsupportedAudioFileException;
+
+import java.io: File, IOException;
+
+import java.nio: ByteBuffer, ByteOrder;
+
+str = load stream;
+ch = load channels;
+block = load block;
+
+decode8u bytes doubles n is ~byte[] -> ~double[] -> number -> () =
+   (for [0..n-1] do i:
+       doubles[i] := (bytes[i] / 128.0) - 1.0;
+    done
+   );
+
+decode16s bytes doubles n is ~byte[] -> ~double[] -> number -> () =
+   (bb = ByteBuffer#wrap(bytes, 0, n * 2);
+    bb#order(ByteOrder#LITTLE_ENDIAN);
+    for [0..n-1] do i:
+       doubles[i] := bb#getShort(i*2) / 32768.0;
+    done
+   );
+
+decode32f bytes doubles n is ~byte[] -> ~double[] -> number -> () =
+   (bb = ByteBuffer#wrap(bytes, 0, n * 4);
+    bb#order(ByteOrder#LITTLE_ENDIAN);
+    for [0..n-1] do i:
+       doubles[i] := bb#getFloat(i*4);
+    done
+   );
+
+decodeFail () = 
+    throw new UnsupportedAudioFileException("File format not supported. Supported formats are 8-bit unsigned PCM, 16-bit signed little-endian PCM, or IEEE float");
+
+decode { format is ~AudioFormat } bytes doubles n = 
+   (if format#isBigEndian() then
+        decodeFail()
+    else
+        enc = format#getEncoding();
+        bits = format#getSampleSizeInBits();
+        if bits == 32 then
+            decode32f bytes doubles n;
+        elif bits == 16 and enc == AudioFormat$Encoding#PCM_SIGNED then
+            decode16s bytes doubles n;
+        elif bits == 8 and enc == AudioFormat$Encoding#PCM_UNSIGNED then
+            decode8u bytes doubles n;
+        else
+            decodeFail();
+        fi
+    fi);
+
+readInterleaved' { format is ~AudioFormat, stream is ~AudioInputStream } nframes =
+   (channels = format#getChannels();
+    bytesPerSample = format#getSampleSizeInBits() / 8;
+    bytes = new byte[nframes * channels * bytesPerSample];
+    bytesRead = stream#read(bytes);
+    if bytesRead <= 0 then block.zeros 0;
+    else
+        n = int(bytesRead / bytesPerSample);
+        doubles = new double[n];
+        decode { format } bytes doubles n;
+        block.block doubles;
+    fi;
+   );
+
+read' { format is ~AudioFormat, stream is ~AudioInputStream } n =
+   (b = readInterleaved' { format, stream } n;
+    channels = format#getChannels();
+    ch.deinterleaved channels b;
+   );
+
+readMono' { format is ~AudioFormat, stream is ~AudioInputStream } n =
+   (b = readInterleaved' { format, stream } n;
+    channels = format#getChannels();
+    ch.mixedDownFromInterleaved channels b;
+   );
+
+// Note, all this assumes the stream is non-blocking (i.e. available()
+// is to the end of file)
+
+available' { format is ~AudioFormat, stream is ~AudioInputStream } =
+    stream#available() / ((format#getSampleSizeInBits() / 8) * format#getChannels());
+
+close' { stream is ~AudioInputStream } =
+    stream#close();
+
+open name is string -> 'a = 
+    try
+        f = new File(name);
+        stream = AudioSystem#getAudioInputStream(f);
+        format = stream#getFormat();
+        len = available' { format, stream }; // at start of stream
+        Stream (str.stream {
+            stream,
+            format,
+            len,
+            rate = format#getSampleRate(),
+            channels = format#getChannels(),
+            get position () = len - available' { stream, format },
+            get available () = available' { stream, format },
+            read = read' { stream, format },
+            readInterleaved = readInterleaved' { stream, format },
+            readMono = readMono' { stream, format },
+            close () = close' { stream },
+        });
+    catch Exception e:
+        Error "Failed to open audio file \"\(name)\": \(e)";
+    yrt;
+
+{
+    open
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/stream/channels.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,72 @@
+
+module channels;
+
+vec = load fvector;
+block = load block;
+mat = load fmatrix;
+        
+interleaved m = 
+   ({ cols, rows } = mat.dimensions m;
+    v = vec.zeros (cols * rows);
+    for [0..rows-1] do row:
+        for [0..cols-1] do col:
+            v[col * rows + row] := m[row][col];
+        done;
+    done;
+    block.block v);
+
+deinterleaved rows b =
+   (v = block.data b;
+    mat.generate do row col:
+        v[rows * col + row]
+    done rows ((vec.length v) / rows));
+
+mixedDown m =
+   (if empty? m then block.zeros 0 else
+        { cols, rows } = mat.dimensions m;
+        v = vec.copyOf m[0];
+        for [1..rows-1] do row:
+            for [0..cols-1] do col:
+                v[col] := v[col] + m[row][col];
+            done;
+        done;
+        block.block v;
+    fi);
+
+mixedDownFromInterleaved rows b =
+   (v = block.data b;
+    cols = ((vec.length v) / rows);
+    v' = vec.zeros cols;
+    for [0..rows-1] do row:
+        for [0..cols-1] do col:
+            v'[col] := v'[col] + v[col * rows + row];
+        done;
+    done;
+    block.block v');
+
+mixedFromInterleavedTo targetRows rows b = 
+    if targetRows == rows then
+        b;
+    elif targetRows == 1 then
+        mixedDownFromInterleaved rows b;
+    else
+        v = block.data b;
+        cols = ((vec.length v) / rows);
+        v' = vec.zeros (cols * targetRows);
+        for [0..targetRows-1] do target:
+            for [0..cols-1] do col:
+                if target < rows then
+                    v'[col * targetRows + target] := v[col * rows + target];
+                elif rows == 1 then
+                    v'[col * targetRows + target] := v[col * rows];
+                fi
+            done
+        done;
+        block.block v';
+    fi;
+
+{
+    interleaved, deinterleaved,
+    mixedDown, mixedDownFromInterleaved, mixedFromInterleavedTo,
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/stream/framer.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,83 @@
+
+module framer;
+
+/**
+ * Framer expresses a stream (or a file) as a lazy list of (possibly
+ * overlapping) frames of mono data.
+ */
+
+block = load block;
+bf = load blockfuncs;
+af = load audiofile;
+win = load window;
+fft = load fft;
+
+blockList framesize stream =
+    if stream.finished? then
+       (stream.close (); [] );
+    else
+        block.resizedTo framesize (stream.readMono framesize)
+            :. \(blockList framesize stream);
+    fi;
+
+overlappingBlockList size hop stream valid buffer =
+   (
+    b = stream.readMono hop;
+    obtained = block.length b;
+
+    // Retain framesize - hop samples from old buffer, add hop samples
+    // (zero-padded if necessary) just read
+    buffer = block.concat
+       [block.rangeOf buffer hop (size-hop),
+        block.resizedTo hop b];
+
+    // Number of "valid" elements (not tail-end zero-padding) left in buffer
+    remaining = valid - (hop - obtained);
+
+    if remaining <= 0 then
+        stream.close ();
+        [];
+    else
+        buffer :. \(overlappingBlockList size hop stream remaining buffer);
+    fi);
+
+frames { framesize, hop } stream =
+    if framesize == hop then
+        blockList framesize stream
+    else
+        overlappingBlockList framesize hop stream 
+            framesize (block.zeros framesize);
+    fi;
+
+windowedFrames { framesize, hop, window } stream =
+   (win = window framesize;
+    map (bf.multiply win) (frames { framesize, hop } stream));
+
+frequencyDomainFrames { framesize, hop } stream =
+   (f = fft.realForward framesize;
+    map f (windowedFrames { framesize, hop, window = win.hann } stream));
+
+{ 
+    frames,
+    windowedFrames,
+    frequencyDomainFrames,
+
+    framesOfFile parameters filename =
+        case af.open filename of
+        Stream s: Frames (frames parameters s);
+        Error e: Error e;
+        esac,
+
+    windowedFramesOfFile parameters filename = 
+        case af.open filename of
+        Stream s: Frames (windowedFrames parameters s);
+        Error e: Error e;
+        esac,
+
+    frequencyDomainFramesOfFile parameters filename = 
+        case af.open filename of
+        Stream s: Frames (frequencyDomainFrames parameters s);
+        Error e: Error e;
+        esac,
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/stream/playback.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,55 @@
+
+module playback;
+
+block = load block;
+fr = load framer;
+af = load audiofile;
+ch = load channels;
+
+import javax.sound.sampled:
+    AudioSystem, AudioFormat, AudioFormat$Encoding, SourceDataLine;
+
+import java.nio: ByteBuffer, ByteOrder;
+
+playBlock line b is ~SourceDataLine -> 'a -> () =
+   (len = block.length b;
+    samples = block.unblock b;
+    nb = len * 2;
+    bytes = new byte[nb];
+    bb = ByteBuffer#wrap(bytes, 0, nb);
+    bb#order(ByteOrder#LITTLE_ENDIAN);
+    sb = bb#asShortBuffer();
+    for [0..len-1] do i: sb#put(i, samples[i] * 32767.0); () done;
+    actual = line#write(bytes, 0, nb); 
+    ());
+
+play line blocks = for blocks (playBlock line);
+    
+open { rate, channels } = 
+   (format = new AudioFormat(AudioFormat$Encoding#PCM_SIGNED, rate, 16,
+                             channels, channels * 2, rate, false);
+    line = AudioSystem#getSourceDataLine(format);
+    line#open(format);
+    line#start();
+    {
+        line,
+        play = play line,
+        channels = line#getFormat()#getChannels(),
+        close () = (line#drain(); line#close()),
+    });
+
+playStream stream =
+   (line = open { rate = stream.sampleRate, channels = stream.channels };
+    blocksize = 10240;
+    not stream.finished? loop
+        line.play [(ch.mixedFromInterleavedTo line.channels stream.channels
+                    (stream.readInterleaved blocksize))];
+    line.close();
+    stream.close());
+
+playFile filename = playStream (af.open filename);
+
+{
+    open, play, playStream, playFile,
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/stream/stream.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,38 @@
+
+module stream;
+
+monoStream box =
+   (readAll' box = box.read (box.len - box.position);
+    {
+        get position () = box.position,
+        get channels () = 1,
+        get sampleRate () = box.rate,
+        get available () = box.len - box.position,
+        get finished? () = not (box.len > box.position),
+        read = box.read,
+        readInterleaved = box.read,
+        readMono = box.read,
+        readAll () = readAll' box,
+        readAllInterleaved () = readAll' box,
+        readAllMono () = readAll' box,
+        close = box.close,
+    });
+
+stream box = 
+   ({
+        get position () = box.position,
+        get channels () = box.channels,
+        get sampleRate () = box.rate,
+        get available () = box.len - box.position,
+        get finished? () = not (box.len > box.position),
+        read = box.read,
+        readInterleaved = box.readInterleaved,
+        readMono = box.readMono,
+        readAll () = box.read (box.len - box.position),
+        readAllInterleaved () = box.readInterleaved (box.len - box.position),
+        readAllMono () = box.readMono (box.len - box.position),
+        close = box.close,
+    });
+
+{ monoStream, stream }
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/stream/syntheticstream.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,47 @@
+
+module syntheticstream;
+
+str = load stream;
+vec = load fvector;
+block = load block;
+
+generated rate generator seconds =
+    str.monoStream {
+        var position = 0,
+        len = int(seconds * rate + 0.5),
+        rate,
+        read count = 
+           (rc = min count (len - position);
+            result = vec.zeros rc;
+            for [0..rc-1] do i:
+                result[i] := generator ((position + i) / rate)
+            done;
+            position := position + rc;
+            block.block result),
+        close = \(),
+        };
+
+sinusoid rate freq seconds =
+    generated rate (sin . (* (freq / (2*pi * rate)))) seconds;
+
+whiteNoise rate seconds =
+    generated rate \((Math#random() * 2.0) - 1.0) seconds;
+
+precalculated rate data is number -> ~double[] -> 'a =
+   (n = vec.length data;
+    str.monoStream {
+        var position = 0,
+        len = n,
+        rate,
+        read count = 
+           (rc = min count (len - position);
+            result = vec.rangeOf data position rc;
+            position := position + rc;
+            block.block result),
+        close = \(),
+    });
+
+{
+    generated, precalculated, sinusoid, whiteNoise,
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/stream/test/test_framer.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,94 @@
+
+module test.test_framer;
+
+fr = load framer;
+block = load block;
+
+{ compare, testStream } = load test.test;
+
+[
+
+"framecount-2x2": \( 
+    fr = fr.frames { framesize = 2, hop = 2 } (testStream 2);
+    compare (length fr) 1
+),
+
+"framecount-2x3": \( 
+    fr = fr.frames { framesize = 2, hop = 2 } (testStream 3);
+    compare (length fr) 2
+),
+
+"framecount-2x4": \( 
+    fr = fr.frames { framesize = 2, hop = 2 } (testStream 4);
+    compare (length fr) 2
+),
+
+"framecount-2.1x0": \( 
+    fr = fr.frames { framesize = 2, hop = 1 } (testStream 0);
+    compare (length fr) 1
+),
+
+"framecount-2.1x1": \( 
+    fr = fr.frames { framesize = 2, hop = 1 } (testStream 1);
+    compare (length fr) 2
+),
+
+"framecount-2.1x2": \( 
+    fr = fr.frames { framesize = 2, hop = 1 } (testStream 2);
+    compare (length fr) 3
+),
+
+"framecount-2.1x3": \( 
+    fr = fr.frames { framesize = 2, hop = 1 } (testStream 3);
+    compare (length fr) 4
+),
+
+"framecount-4.1x4": \( 
+    fr = fr.frames { framesize = 4, hop = 1 } (testStream 4);
+    compare (length fr) 7
+),
+
+"framecount-4.3x4": \( 
+    fr = fr.frames { framesize = 4, hop = 3 } (testStream 4);
+    compare (length fr) 2 
+),
+
+"framecount-4.4x4": \( 
+    fr = fr.frames { framesize = 4, hop = 4 } (testStream 4);
+    compare (length fr) 1
+),
+
+"framecount-3.2x4": \(
+    fr = fr.frames { framesize = 3, hop = 2 } (testStream 4);
+    compare (length fr) 3
+),
+
+"frames-2x5": \( 
+    fr = fr.frames { framesize = 2, hop = 2 } (testStream 5);
+    expected = [ [1,2], [3,4], [5,0] ];
+    compare (map block.list fr) expected;
+),
+
+"frames-4.3x4": \( 
+    fr = fr.frames { framesize = 4, hop = 3 } (testStream 4);
+    expected = [ [0,1,2,3], [3,4,0,0] ];
+    compare (map block.list fr) expected;
+),
+
+"frames-3.2x4": \(
+    fr = fr.frames { framesize = 3, hop = 2 } (testStream 4);
+    expected = [ [0,1,2], [2,3,4], [4,0,0] ];
+    compare (map block.list fr) expected;
+),
+
+"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] ];
+    compare (map block.list fr) expected;
+),
+
+] is hash<string, () -> boolean>;
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/test/all.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,22 @@
+
+program test.all;
+
+{ runTests } = load test.test;
+
+tests = [
+"fvector"    : load test.test_fvector,
+"framer"     : load test.test_framer,
+"blockfuncs" : load test.test_blockfuncs,
+"complex"    : load test.test_complex,
+"fft"        : load test.test_fft,
+"vamppost"   : load test.test_vamppost,
+];
+
+bad = sum (mapHash do name testHash: runTests name testHash done tests);
+
+if (bad > 0) then
+    println "\n** \(bad) test(s) failed!";
+else
+    ()
+fi
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/test/test.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,41 @@
+
+module test.test;
+
+vec = load fvector;
+ss = load syntheticstream;
+
+testStream n is number -> 'a  = ss.precalculated 1000 (vec.vector [1..n]);
+
+compare obtained expected =
+    if obtained == expected then
+        true;
+    else
+        println "** expected: \(expected)\n   obtained: \(obtained)";
+        false;
+    fi;
+
+select f = fold do r x: if f x then x::r else r fi done [];
+
+failedTests testHash =
+    select (!= "")
+       (mapHash do name f:
+            if f () then "" else
+                println "Test \(name) failed";
+                name;
+            fi 
+        done testHash);
+        
+runTests group testHash =
+   (failed = failedTests testHash;
+    good = (length testHash - length failed);
+    bad = length failed;
+    println "\(group): \(good)/\(good+bad) tests passed";
+    if not empty? failed then
+        println "\(group): Failed tests [\(bad)]: \(strJoin ' ' failed)";
+    fi;
+    bad);
+
+{
+    testStream, compare, failedTests, runTests
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/transform/fft.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,53 @@
+
+module fft;
+
+import edu.emory.mathcs.jtransforms.fft: DoubleFFT_1D;
+
+b = load block;
+vec = load fvector;
+complex = load complex;
+
+packedToComplex p =
+   (n = (vec.length p) / 2;
+    array
+       (map do i:
+            re = if i == n then p[1] else p[i*2] fi;
+            im = if i == 0 or i == n then 0 else p[i*2+1] fi;
+            complex.complex re im;
+        done [0..n]));
+
+complexToPacked arr =
+   (n = length arr;
+    v = vec.vector
+       (map do i:
+            ix = int (i/2);
+            if i == ix*2 then
+                complex.real arr[ix]
+            else 
+                complex.imaginary arr[ix] 
+            fi;
+            done [0..(n-1)*2-1]);
+    v[1] := complex.real arr[n-1];
+    v);
+
+realForward n = 
+   (d = new DoubleFFT_1D(n);
+    do bl:
+        v = b.vector bl;
+        d#realForward(v);
+        packedToComplex v;
+    done);
+
+realInverse n = 
+   (d = new DoubleFFT_1D(n);
+    do arr:
+        v = complexToPacked arr;
+        d#realInverse(v, true);
+        b.block v;
+    done);
+
+{
+realForward,
+realInverse,
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/transform/test/test_fft.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,46 @@
+
+module test.test_fft;
+
+{ realForward, realInverse } = load fft;
+{ list, fromList } = load block;
+{ complex } = load complex;
+
+{ compare } = load test.test;
+
+testFFT orig reals imags =
+   (out = realForward (length orig) (fromList orig);
+    back = realInverse (length orig) out;
+    compare out (array (map2 complex reals imags)) and compare orig (list back));
+
+[
+
+"dc": \(
+    testFFT [1,1,1,1] [4,0,0] [0,0,0];
+),
+
+"sine": \(
+    testFFT [0,1,0,-1] [0,0,0] [0,-2,0];
+),
+
+"cosine": \(
+    testFFT [1,0,-1,0] [0,2,0] [0,0,0];
+),
+
+"sineCosine": \(
+    testFFT [0.5,1,-0.5,-1] [0,1,0] [0,-2,0];
+),
+
+"nyquist": \(
+    testFFT [1,-1,1,-1] [0,0,4] [0,0,0];
+),
+
+"dirac": \(
+    testFFT [1,0,0,0] [1,1,1] [0,0,0] and
+        testFFT [0,1,0,0] [1,0,-1] [0,-1,0] and
+        testFFT [0,0,1,0] [1,-1,1] [0,0,0] and
+        testFFT [0,0,0,1] [1,0,-1] [0,1,0];
+),
+
+] is hash<string, () -> boolean>;
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/transform/window.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,47 @@
+
+module window;
+
+b = load block;
+bf = load blockfuncs;
+
+cosinewin a0 a1 a2 a3 n =
+    b.fromList
+       (map do i:
+            a0
+            - a1 * cos(2 * pi * i / n)
+            + a2 * cos(4 * pi * i / n)
+            - a2 * cos(6 * pi * i / n)
+            done [0..n-1]);
+                  
+hann = cosinewin 0.5 0.5 0.0 0.0;
+hamming = cosinewin 0.54 0.46 0.0 0.0;
+blackman = cosinewin 0.42 0.50 0.08 0.0;
+nuttall = cosinewin 0.3635819 0.4891775 0.1365995 0.0106411;
+blackmanHarris = cosinewin 0.35875 0.48829 0.14128 0.01168;
+boxcar = b.consts 0.5;
+
+bartlett n =
+    b.fromList
+       (m = n/2;
+        concat [
+            map do i:
+                i / m
+                done [0..m-1],
+            map do i:
+                1.0 - (i / m)
+                done [0..m-1]
+            ]);
+
+windowed windowFunc frames =
+    case frames of
+        []: frames;
+         _: (first = head frames;
+             window = windowFunc (b.length first);
+             map (bf.multiply window) frames);
+    esac;
+
+{
+cosinewin, hann, hamming, blackman, nuttall, blackmanHarris, boxcar, bartlett,
+windowed
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/vamp/test/test_vamppost.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,72 @@
+module test.test_vamppost;
+
+vp = load vamppost;
+
+{ compare } = load test.test;
+
+untimed n = { timestamp = Untimed (), values = [n] };
+timed t n = { timestamp = Time t, values = [n] };
+
+testdata =  {
+    output = { sampleType = OneSamplePerStep () },
+    config = { sampleRate = 1000, stepSize = 100 },
+    features = [
+        [],
+        [ untimed 1 ],
+        [ untimed 1, untimed 2 ],
+        [],
+        [ timed 1.1 1, untimed 2, timed 4 3 ]
+    ]
+};
+
+[
+
+"fillOneSamplePerStep": \(
+    // All features returned within a single process call (i.e. within
+    // a single sub-list of the feature list) should be given the same
+    // timestamp; the timestamp increments according to the config
+    // step size between sub-lists
+    filled = vp.fillTimestamps
+       (testdata with { output = { sampleType = OneSamplePerStep () } });
+    compare filled [
+        timed 0.1 1 ,
+        timed 0.2 1, timed 0.2 2 ,
+        timed 0.4 1, timed 0.4 2, timed 0.4 3
+    ];
+),
+
+"fillFixedSampleRate": \(
+    // "If the output feature's hasTimestamp field is true, the host
+    // should read and use the output feature's timestamp. The host
+    // may round the timestamp according to the sample rate given in
+    // the output descriptor's sampleRate field [...] If
+    // [hasTimestamp] is false, its time will be implicitly calculated
+    // by incrementing the time of the previous feature according to
+    // the [output descriptor's] sample rate". Note that the time is
+    // based on that of the previous feature, not that of the previous
+    // process cycle (as is the case with OneSamplePerStep features).
+    filled = vp.fillTimestamps
+       (testdata with { output = { sampleType = FixedSampleRate 5 } });
+    compare filled [
+        timed 0 1 ,
+        timed 0.2 1, timed 0.4 2 ,
+        timed 1.0 1, timed 1.2 2, timed 4.0 3
+    ];
+),
+
+"fillVariableSampleRate": \(
+    // For VariableSampleRate outputs, the timestamps should always
+    // be left entirely alone by fillTimestamps -- it's an error for
+    // the plugin to return any features without valid timestamps,
+    // but it isn't the job of fillTimestamps to handle that error
+    filled = vp.fillTimestamps
+       (testdata with { output = { sampleType = VariableSampleRate 5 } });
+    compare filled [
+        untimed 1 ,
+        untimed 1, untimed 2 ,
+        timed 1.1 1, untimed 2, timed 4.0 3
+    ];
+),
+
+] is hash<string, () -> boolean>;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/vamp/vamp.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,295 @@
+module vamp;
+
+import org.vamp_plugins:
+       Plugin, Plugin$InputDomain,
+       PluginLoader, PluginLoader$AdapterFlags, PluginLoader$LoadFailedException,
+       ParameterDescriptor, OutputDescriptor, OutputDescriptor$SampleType,
+       RealTime, Feature;
+
+import java.lang: UnsatisfiedLinkError;
+
+import java.util: Map, List;
+
+block = load block;
+fr = load framer;
+af = load audiofile;
+vamprdf = load vamprdf;
+
+store = load yertle.store;
+
+realTime r is ~RealTime -> number = r#sec() + (r#nsec() / 1000000000);
+
+feature f is ~Feature -> 'a = {
+    timestamp = if f#hasTimestamp then Time (realTime f#timestamp) else Untimed () fi,
+    duration = if f#hasDuration then Time (realTime f#duration) else Untimed () fi,
+    values = f#values,
+    label = f#label,
+    };
+
+featureList fl is ~Object -> 'a =
+    if nullptr? fl then []
+    else
+        a = fl unsafely_as ~List;
+        result = array [];
+        itr = a#iterator();
+        itr#hasNext() loop (push result (feature (itr#next() unsafely_as ~Feature)));
+        list result
+    fi;
+
+featureSet fs is ~Map -> 'a =
+   (numberOf n is ~Object -> number = (n unsafely_as ~Integer)#intValue();
+    s = [:];
+    kk = list fs#keySet()#toArray();
+    for kk do k: s[numberOf k] := featureList fs#get(k) done;
+    s);
+
+getStandardStore =
+   (s = store.newRdfStore ();
+    var loaded = false;
+    \(synchronized s do:
+          if not loaded then
+            vamprdf.loadSystemVampRdf s;
+            loaded := true
+          fi;
+          s
+      done)
+    );
+
+getPluginPath () =
+   (try
+        map string PluginLoader#getInstance()#getPluginPath();
+    catch UnsatisfiedLinkError e:
+        eprintln "Warning: Unable to obtain plugin path:\n\(e)";
+        [];
+    yrt);
+
+listPlugins () =
+   (try
+        map string PluginLoader#getInstance()#listPlugins();
+    catch UnsatisfiedLinkError e:
+        eprintln "Warning: Unable to obtain plugin list:\n\(e)";
+        [];
+    yrt);
+   
+categoryOf key =
+    list PluginLoader#getInstance()#getPluginCategory(key);
+
+inputDomain d is ~Plugin$InputDomain -> 'a = 
+    if d == Plugin$InputDomain#FREQUENCY_DOMAIN then
+        FrequencyDomain ()
+    else
+        TimeDomain ()
+    fi;
+
+parameterDescriptor pd is ~ParameterDescriptor -> 'a = {
+    identifier = pd#identifier,
+    name = pd#name,
+    description = pd#description,
+    unit = pd#unit,
+    minValue = pd#minValue,
+    maxValue = pd#maxValue,
+    defaultValue = pd#defaultValue,
+    get quantize () = if pd#isQuantized then QuantizeStep pd#quantizeStep else Unquantized () fi,
+    valueNames = map string pd#valueNames
+    };
+
+sampleType t rate is ~OutputDescriptor$SampleType -> number -> 'a =
+    if t == OutputDescriptor$SampleType#OneSamplePerStep then
+        OneSamplePerStep ()
+    elif t == OutputDescriptor$SampleType#FixedSampleRate then
+        FixedSampleRate rate
+    else
+        VariableSampleRate rate
+    fi;
+
+structureOf rdfOutputData od is 'a -> ~OutputDescriptor -> 'b = 
+   (computes = case rdfOutputData of Some d: d.computes; None (): Unknown () esac;
+    s = getStandardStore ();
+    noteIRI = case s.expand "af:Note" of IRI iri: iri; _: "" esac;
+    if od#hasFixedBinCount and od#binCount == 0 then
+        Instants ();
+    elif od#hasDuration then
+        if computes != Unknown () then
+            if computes == Event noteIRI then Notes ();
+            else Regions ();
+            fi
+        elif od#hasFixedBinCount then
+            if od#binCount > 1 then Notes ();
+            elif od#unit == "Hz" or strIndexOf (strLower od#unit) "midi" 0 >= 0 then Notes ();
+            else Regions ();
+            fi
+        else
+            Unknown ();
+        fi
+    elif od#hasFixedBinCount and od#binCount == 1 then
+        case computes of
+        Event e:
+            if strEnds? e "Segment" then Segmentation ();
+            else Curve ();
+            fi;
+        _: Curve ();
+        esac;
+    elif od#hasFixedBinCount and
+         od#sampleType != OutputDescriptor$SampleType#VariableSampleRate then
+        Grid ();
+    else
+        Unknown ();
+    fi);
+
+outputDescriptor rdfOutputData od is 'a -> ~OutputDescriptor -> 'b = {
+    identifier = od#identifier,
+    name = od#name,
+    description = od#description,
+    get binCount () = if od#hasFixedBinCount then Fixed od#binCount else Variable () fi,
+    get valueExtents () = if od#hasKnownExtents then Known { min = od#minValue, max = od#maxValue } else Unknown () fi,
+    get valueQuantize () = if od#isQuantized then QuantizeStep od#quantizeStep else Unquantized () fi,
+    valueUnit = od#unit,
+    binNames = array (map string od#binNames),
+    sampleType = sampleType od#sampleType od#sampleRate,
+    hasDuration = od#hasDuration,
+    get computes () = case rdfOutputData of Some data: data.computes; None (): Unknown () esac,
+    get inferredStructure () = structureOf rdfOutputData od,
+    };
+
+plugin key p is string -> ~Plugin -> 'a =
+   (rdfData = vamprdf.pluginDataByKey (getStandardStore ()) key;
+    {
+    plugin = p,
+    key,
+    get apiVersion () = p#getVampApiVersion(),
+    get identifier () = p#getIdentifier(),
+    get name () = p#getName(),
+    get description () = p#getDescription(),
+    get maker () = p#getMaker(),
+    get copyright () = p#getCopyright(),
+    get version () = p#getPluginVersion(),
+    get category () = PluginLoader#getInstance()#getPluginCategory(key),
+    get hasRdfDescription () = (rdfData != None ()),
+    get infoURL () = case rdfData of Some data: data.infoURL; None (): "" esac,
+    get parameters () = array (map parameterDescriptor p#getParameterDescriptors()),
+    parameterValue identifier = p#getParameter(identifier),
+    setParameterValue identifier value = p#setParameter(identifier, value),
+    get programs () = array (map string p#getPrograms()),
+    get currentProgram () = p#getCurrentProgram(),
+    selectProgram pr = p#selectProgram(pr),
+    get inputDomain () = inputDomain p#getInputDomain(),
+    get preferredBlockSize () = p#getPreferredBlockSize(),
+    get preferredStepSize () = p#getPreferredStepSize(),
+    get minChannelCount () = p#getMinChannelCount(),
+    get maxChannelCount () = p#getMaxChannelCount(),
+    initialise { channels, hop, blockSize } = p#initialise(channels, hop, blockSize),
+    reset () = p#reset(),
+    get outputs () =
+        array case rdfData of
+        Some data: map2 outputDescriptor (map Some data.outputs) p#getOutputDescriptors();
+        None (): map (outputDescriptor (None ())) p#getOutputDescriptors();
+        esac,
+    process blocks time is 'a -> ~RealTime -> 'b = 
+        featureSet p#process((map block.floats blocks) as ~float[][], 0, time),
+    getRemainingFeatures () = featureSet p#getRemainingFeatures(),
+    dispose () = p#dispose(),
+    });
+
+featuresFromSet outputNo f = if outputNo in f then f[outputNo] else [] fi;
+
+outputNumberByName p name =
+   (outputs = p.outputs;
+    case find ((== name) . (.identifier)) outputs of
+    first::rest: index first outputs;
+    _: -1;
+    esac);
+
+loadPlugin rate key =
+    try
+        OK (plugin key 
+            PluginLoader#getInstance()#loadPlugin(key, rate,
+                PluginLoader$AdapterFlags#ADAPT_INPUT_DOMAIN +
+                PluginLoader$AdapterFlags#ADAPT_CHANNEL_COUNT))
+    catch PluginLoader$LoadFailedException _:
+        Error "Failed to load Vamp plugin with key \(key)"
+    yrt;
+
+processed { p, sampleRate, hop } frames count 
+    // I don't know why this type declaration is necessary. Without
+    // it, yeti records the return type as 'a and can't do anything
+    // useful with it (giving IncompatibleClassChangeError when
+    // e.g. accessing the values array)
+    is 'a -> 'b -> 'c ->
+        list<hash<number,
+                  list<{ timestamp is Time number | Untimed (), 
+                         duration  is Time number | Untimed (),
+                         label is string,
+                         values is ~float[]
+                       }>>> =
+    case frames of
+    frame::rest:
+        p.process [frame] RealTime#frame2RealTime(count, sampleRate)
+        :.
+        \(processed { p, sampleRate, hop } rest (count + hop));
+    _: 
+       (rf = p.getRemainingFeatures ();
+        p.dispose ();
+        [rf]);
+    esac;
+
+converted { p, sampleRate, hop } outputNo fl =
+    map (featuresFromSet outputNo) fl;
+
+returnErrorFrom p stream text = (p.dispose (); stream.close (); Error text);
+
+processWith key p outputNo stream =
+   (blockSize = p.preferredBlockSize;
+    stepSize = p.preferredStepSize;
+    channels = 1;
+    params = {
+        p, sampleRate = stream.sampleRate, channels = 1,
+        framesize = blockSize, blockSize, hop = stepSize
+    };
+    if p.initialise params then
+        OK {
+            key = key,
+            output = p.outputs[outputNo],
+            parameters = mapIntoHash id p.parameterValue
+               (map (.identifier) p.parameters),
+            config = {
+                channels, blockSize, stepSize,
+                sampleRate = stream.sampleRate
+            },
+            features = converted params outputNo
+               (processed params (fr.frames params stream) 0)
+        };
+        // If processing completed successfully, then p is
+        // disposed by processed and stream is closed by the
+        // framer
+    else
+        returnErrorFrom p stream "Failed to initialise plugin \(key) with channels = \(channels), blockSize = \(blockSize), stepSize = \(stepSize)";
+    fi);
+
+process key output stream =
+    case loadPlugin stream.sampleRate key of
+    OK p:
+        outputNo = outputNumberByName p output;
+        if outputNo >= 0 then
+            processWith key p outputNo stream
+        else
+            outputs = strJoin ", " (map (.identifier) p.outputs);
+            returnErrorFrom p stream "Plugin \(key) has no output named \(output) (outputs are \(outputs))"
+        fi;
+    Error e: Error e;
+    esac;
+
+processFile key output filename = 
+    case af.open filename of
+    Stream s: process key output s;
+    Error e: Error e;
+    esac;
+
+{
+get pluginPath = getPluginPath,
+get pluginKeys = listPlugins,
+loadPlugin,
+categoryOf,
+process,
+processFile,
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/vamp/vamppost.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,99 @@
+module vamppost;
+
+fmat = load fmatrix;
+
+fillOneSamplePerStep config features =
+   (fill' n pending features =
+        case pending of
+        feature::rest:
+            stamped = feature with
+               { timestamp = Time ((n * config.stepSize) / config.sampleRate) };
+            stamped :. \(fill' n rest features);
+        _:
+            if empty? features then []
+            else fill' (n+1) (head features) (tail features);
+            fi;
+        esac;
+    fill' (-1) [] features);
+
+fillFixedSampleRate config rate features =
+   (eps = 0.0001;
+    fill' n pending features =
+        case pending of
+        feature::rest:
+            n = case feature.timestamp of
+                Untimed (): n + 1;
+                Time t: int (t * rate + eps);
+                esac;
+            stamped = feature with { timestamp = Time (n / rate) };
+            stamped :. \(fill' n rest features);
+        _:
+            if empty? features then []
+            else fill' n (head features) (tail features);
+            fi;
+        esac;
+    fill' (-1) [] features);
+
+fillTimestamps { output, config, features } =
+    case output.sampleType of
+    OneSamplePerStep ():
+        fillOneSamplePerStep config features;
+    FixedSampleRate rate:
+        fillFixedSampleRate config rate features;
+    VariableSampleRate _:
+        concat features;
+    esac;
+
+structureGrid binCount features =
+    if empty? features then
+        fmat.constMatrix binCount 0 0;
+    else
+        fmat.generate
+            do row col:
+                features[col].values[row];
+            done binCount (length features);
+    fi;
+
+structure data =
+   (type = data.output.inferredStructure;
+    features =
+        case type of
+        Grid (): concat data.features;
+        _: fillTimestamps data;
+        esac;
+    binCount = 
+        case data.output.binCount of
+        Fixed n: n;
+        _: 0;
+        esac;
+    case type of
+    Curve ():               // No duration, one value
+        Curve features;
+    Grid ():                // No duration, >1 value, not variable rate
+        Grid (structureGrid binCount (array features));
+    Instants ():            // Zero-valued features
+        Instants features;
+    Notes ():               // Duration, at least one value (pitch or freq)
+        Notes features;
+    Regions ():             // Duration, zero or more values
+        Regions features;
+    Segmentation ():        // No duration, one value, segment type in RDF
+        Segmentation features;
+    Unknown ():             // Other
+        Unknown features;
+    esac);
+
+postprocess data =
+    case data of
+    OK data:
+        structure data;
+    Error e:
+        Error e;
+    esac;
+
+{
+fillTimestamps,
+postprocess
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/vamp/vamprdf.yeti	Wed Mar 20 09:57:54 2013 +0000
@@ -0,0 +1,294 @@
+
+module vamprdf;
+
+read = load yertle.read;
+{ newRdfStore } = load yertle.store;
+
+import java.io: File;
+
+import org.vamp_plugins: PluginLoader;
+
+import java.lang: UnsatisfiedLinkError;
+
+getPluginPath () =
+   (try map string PluginLoader#getInstance()#getPluginPath();
+    catch UnsatisfiedLinkError e: [];
+    yrt);
+
+systemVampRdfFiles () =
+    concat
+       (map do p:
+            map ((p ^ File#separator ^) . (.name))
+               (filter do entry:
+                    entry.file? and
+                       (lc = strLower entry.name;
+                        (strEnds? lc ".ttl") or
+                        (strEnds? lc ".n3") or
+                        (strEnds? lc ".nt"))
+                    done (listDirectory false p))
+            done (getPluginPath ()));
+
+addVampPrefixes store =
+   (store.addPrefix "vamp" "http://purl.org/ontology/vamp/";
+    store.addPrefix "dc" "http://purl.org/dc/elements/1.1/";
+    store.addPrefix "foaf" "http://xmlns.com/foaf/0.1/";
+    store.addPrefix "owl" "http://www.w3.org/2002/07/owl#";
+    store.addPrefix "af" "http://purl.org/ontology/af/");
+
+loadSystemVampRdf store =
+   (addVampPrefixes store;
+    for (systemVampRdfFiles ()) do file:
+        case read.loadTurtleFile store ("file://" ^ file) file of
+        OK (): ();
+        Error e: eprintln
+            "WARNING: Failed to load Vamp plugin RDF file \"\(file)\": \(e)";
+        esac
+    done);
+
+getGlobalPluginIndex () =
+    list (strSplit "\n" (fetchURL [ Timeout 10 ] (Handle getContents)
+                         "http://www.vamp-plugins.org/rdf/plugins/index.txt"));
+
+//!!! need to cache these retrievals
+parseGlobalVampRdf () =
+   (parse urls =
+        case urls of
+        url::rest:
+           (doc = fetchURL [ Timeout 10 ] (Handle getContents) url;
+            parsed = read.parseTurtleString url doc;
+            { url, parsed } :. \(parse rest));
+         _: [];
+        esac;
+    parse (getGlobalPluginIndex ()));
+
+loadGlobalVampRdf store =
+    for (parseGlobalVampRdf ()) do { url, parsed }:
+        case read.loadParsedTriples store parsed of
+        OK (): ();
+        Error e: eprintln "WARNING: Failed to load Vamp RDF from URL \(url): \(e)";
+        esac;
+    done;
+
+filterIRIsFromNodes nodes =
+    map do r: case r of IRI iri: iri; esac done
+       (filter do r: case r of IRI iri: true; _: false esac done nodes);
+
+subjects = map (.s);
+
+iriTypes =
+    map do t:
+        case t of
+        IRI iri: IRI iri;
+        Blank n: Blank n;
+        esac done;
+
+iriSubjects = iriTypes . subjects;
+
+allLibraryNodes store =
+    iriSubjects
+       (store.match {
+            s = Wildcard (),
+            p = Known (store.expand "a"),
+            o = Known (store.expand "vamp:PluginLibrary")
+            });
+
+allPluginNodes store =
+    iriSubjects
+       (store.match {
+            s = Wildcard (),
+            p = Known (store.expand "a"),
+            o = Known (store.expand "vamp:Plugin")
+            });
+
+pluginsWithId store id = 
+    iriTypes
+       (filter do pnode:
+        store.contains {
+            s = pnode,
+            p = store.expand "vamp:identifier",
+            o = Literal { value = id, type = "", language = "" }
+            }
+        done (allPluginNodes store));
+
+librariesWithId store id =
+    iriTypes
+       (filter do lnode:
+        store.contains {
+            s = lnode,
+            p = store.expand "vamp:identifier",
+            o = Literal { value = id, type = "", language = "" }
+            }
+        done (allLibraryNodes store));
+
+splitPluginKey key =
+   (bits = strSplit ":" key;
+    reversed = reverse bits;
+    soname = strJoin ":" (reverse (tail reversed));
+    identifier = head reversed;
+    { soname, identifier });
+
+pluginNodesByKey store key =
+   (case splitPluginKey key of { soname, identifier }:
+        candidatePlugins = pluginsWithId store identifier;
+        candidateLibraries = librariesWithId store soname;
+        filter do pnode:
+            any do lnode:
+                store.contains {
+                    s = lnode,
+                    p = store.expand "vamp:available_plugin",
+                    o = pnode
+                    }
+                done candidateLibraries
+            done candidatePlugins
+    esac);
+
+libraryNodeFor store pluginNode =
+    case store.match {
+        s = Wildcard (), p = Known (store.expand "vamp:available_plugin"), o = Known pluginNode
+        } of
+    { s = IRI iri }::others: Some (IRI iri);
+    { s = Blank n }::others: Some (Blank n);
+     _: None ();
+    esac;
+
+textProperty store subject name =
+    case store.match {
+        s = Known subject, p = Known (store.expand name), o = Wildcard ()
+        } of
+    { o = Literal { value = text } }::others: text;
+     _: "";
+    esac;
+
+iriProperty store subject name =
+    case store.match {
+        s = Known subject, p = Known (store.expand name), o = Wildcard ()
+        } of
+    { o = IRI iri }::others: IRI iri;
+     _: None ();
+    esac;
+
+nodeProperty store subject name =
+    case store.match {
+        s = Known subject, p = Known (store.expand name), o = Wildcard ()
+        } of
+    { o = IRI iri }::others: Some (IRI iri);
+    { o = Blank n }::others: Some (Blank n);
+     _: None ();
+    esac;
+
+inputDomainOf store pluginNode =
+   case store.match {
+        s = Known pluginNode, p = Known (store.expand "vamp:input_domain"), o = Wildcard ()
+        } of
+    { o = IRI iri }::others:
+        if IRI iri == store.expand "vamp:FrequencyDomain"
+        then FrequencyDomain ()
+        else TimeDomain ()
+        fi;
+     _: TimeDomain ();
+    esac;
+
+outputDescriptor store outputNode =
+   (tprop abbr = textProperty store outputNode abbr;
+    iprop abbr = iriProperty store outputNode abbr;
+    bprop abbr deflt =
+       (b = strLower (textProperty store outputNode abbr);
+        if b == "true" then true elif b == "false" then false else deflt fi);
+    nprop abbr =
+        try number (textProperty store outputNode abbr); catch Exception _: 0 yrt;
+    {
+        identifier = tprop "vamp:identifier",
+        name = tprop "dc:title",
+        description = tprop "dc:description",
+        rdfType = case iprop "a" of IRI iri: iri; _: "" esac,
+        valueUnit = tprop "vamp:unit",
+        binCount = 
+            if bprop "vamp:fixed_bin_count" false
+            then Known (nprop "vamp:bin_count")
+            else Unknown ()
+            fi,
+        computes =
+            case iprop "vamp:computes_event_type" of
+            IRI iri: Event iri;
+             _: case iprop "vamp:computes_signal_type" of
+                IRI iri: Signal iri;
+                 _: case iprop "vamp:computes_feature_type" of
+                    IRI iri: Feature iri;
+                     _: Unknown ();
+                    esac
+                esac
+            esac,
+        //!!! and some other properties
+    });
+
+pluginDataByNode store pluginNode =
+   (tprop abbr = textProperty store pluginNode abbr;
+    nprop abbr =
+        try number (textProperty store pluginNode abbr); catch Exception _: 0 yrt;
+    soname =
+        case libraryNodeFor store pluginNode of
+        None (): "";
+        Some n: textProperty store n "vamp:identifier";
+        esac;
+    {
+        pluginKey = soname ^ ":" ^ tprop "vamp:identifier",
+        soname,
+        apiVersion = nprop "vamp:vamp_API_version",
+        identifier = tprop "vamp:identifier",
+        name = tprop "dc:title",
+        description = tprop "dc:description",
+        maker =
+           (tmaker = tprop "foaf:maker";
+            if tmaker == "" then
+                case nodeProperty store pluginNode "foaf:maker" of
+                Some n: textProperty store n "foaf:name";
+                None (): "";
+                esac
+            else
+                tmaker
+            fi),
+        copyright = tprop "dc:rights",
+        version = tprop "owl:versionInfo",
+        category = tprop "vamp:category",
+        inputDomain = inputDomainOf store pluginNode,
+        infoURL =
+           (case iriProperty store pluginNode "foaf:page" of
+            IRI iri: iri;
+            None ():
+                case libraryNodeFor store pluginNode of
+                None (): "";
+                Some n:
+                    case iriProperty store n "foaf:page" of
+                    IRI iri: iri;
+                    None (): "";
+                    esac;
+                esac;
+            esac),
+        outputs = 
+           (matches = store.match { s = Known pluginNode,
+                                    p = Known (store.expand "vamp:output"), 
+                                    o = Wildcard () };
+            array (map do t:
+                       case t.o of
+                       IRI iri: outputDescriptor store (IRI iri);
+                       Blank n: outputDescriptor store (Blank n);
+                       esac
+                       done matches)),
+    });
+
+pluginDataByKey store key =
+    case pluginNodesByKey store key of
+    node::others: Some (pluginDataByNode store node);
+    _: None ()
+    esac;
+
+{
+loadSystemVampRdf,
+loadGlobalVampRdf,
+allPluginNodes,
+allLibraryNodes,
+pluginNodesByKey,
+pluginDataByNode,
+pluginDataByKey,
+}
+