# HG changeset patch
# User Chris Cannam
# Date 1379936135 -3600
# Node ID 20903ee537197496b47356df4cabc8a1631cc83c
# Parent 4b104ef8d1101408e5985a37e54e4dfa83e6727e
Use Ayr init option to preload May modules; move code from may/ to src/ to avoid Yeti compiler doing on-the-fly recompilation on load
diff -r 4b104ef8d110 -r 20903ee53719 .hgsubstate
--- a/.hgsubstate Mon Sep 16 10:56:19 2013 +0100
+++ b/.hgsubstate Mon Sep 23 12:35:35 2013 +0100
@@ -1,1 +1,1 @@
-79aabcffd9a15ab6f74487964657fc2bc683cf4f ext
+43584910bba1d16db21b2ce6d8feafe87c9361fa ext
diff -r 4b104ef8d110 -r 20903ee53719 bin/may
--- a/bin/may Mon Sep 16 10:56:19 2013 +0100
+++ b/bin/may Mon Sep 23 12:35:35 2013 +0100
@@ -1,5 +1,7 @@
#!/bin/sh
+MAY_INIT_MODULES="may.vector:may.matrix:may.complex:may.plot:may.vamp"
+
ARCH=unknown
case `uname -a` in
@@ -32,5 +34,5 @@
YETI_MODULE_SOURCE_PATH=${YETI_LIBDIR}/modules \
LD_LIBRARY_PATH=$SODIR:$LD_LIBRARY_PATH \
- $JAVA_HOME/bin/java $JAVA_OPTS -classpath "$CLASSPATH" com.particularprograms.ayr "$@"
+ $JAVA_HOME/bin/java $JAVA_OPTS -classpath "$CLASSPATH" com.particularprograms.ayr -init "$MAY_INIT_MODULES" "$@"
diff -r 4b104ef8d110 -r 20903ee53719 build.xml
--- a/build.xml Mon Sep 16 10:56:19 2013 +0100
+++ b/build.xml Mon Sep 23 12:35:35 2013 +0100
@@ -26,8 +26,8 @@
-
+
@@ -41,7 +41,7 @@
classname="yeti.lang.compiler.yeti"
fork="true">
-
+
diff -r 4b104ef8d110 -r 20903ee53719 may/complex.yeti
--- a/may/complex.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,112 +0,0 @@
-
-module may.complex;
-
-load may.vector.type;
-load may.complex.type;
-
-vec = load may.vector;
-
-import java.lang: ClassCastException;
-
-class Cplx(double real, double imag)
- double getReal()
- real,
- double 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;
-
-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();
-
-sum' cc is list?<~Cplx> -> ~Cplx =
- complex (sum (map real cc)) (sum (map imaginary cc));
-
-add c1 c2 is ~Cplx -> ~Cplx -> ~Cplx =
- complex (real c1 + real c2) (imaginary c1 + imaginary c2);
-
-multiply c1 c2 is ~Cplx -> ~Cplx -> ~Cplx =
- (a = real c1;
- b = imaginary c1;
- c = real c2;
- d = imaginary c2;
- complex (a * c - b * d) (b * c + a * d)); //!!! need units
-
-scale r c is number -> ~Cplx -> ~Cplx =
- complex (r * real c) (r * imaginary c);
-
-zeros n is number -> array<~Cplx> =
- array (map \(complex 0 0) [1..n]);
-
-magnitudes cc is list?<~Cplx> -> vector =
- vec.fromList (map magnitude cc);
-
-angles cc is list?<~Cplx> -> vector =
- vec.fromList (map angle cc);
-
-{
- real,
- imaginary,
- complex,
- magnitude,
- angle,
- sum = sum',
- add,
- multiply,
- scale,
- zeros,
- magnitudes,
- angles,
-} as {
- real is cplx -> number,
- imaginary is cplx -> number,
- complex is number -> number -> cplx,
- magnitude is cplx -> number,
- angle is cplx -> number,
- sum is list? -> cplx,
- add is cplx -> cplx -> cplx,
- multiply is cplx -> cplx -> cplx,
- scale is number -> cplx -> cplx,
- zeros is number -> array,
- magnitudes is list? -> vector,
- angles is list? -> vector,
-}
-
diff -r 4b104ef8d110 -r 20903ee53719 may/complex/test/test_complex.yeti
--- a/may/complex/test/test_complex.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-module may.complex.test.test_complex;
-
-{ real, imaginary, complex, magnitude, angle, sum, scale, zeros, magnitudes, angles }
- = load may.complex;
-
-{ compare } = load may.test.test;
-
-vec = load may.vector;
-
-[
-
-"complex": \(
- compare (complex 1 2) (complex 1 2) and
- complex (-1) 2 != complex 1 2
-),
-
-"real": \(
- compare (real (complex 3 2)) 3 and
- compare (real (complex 3.3 2.2)) 3.3
-),
-
-"imaginary": \(
- compare (imaginary (complex 3 4)) 4 and
- compare (imaginary (complex 3 (-4.1))) (-4.1)
-),
-
-"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)
-),
-
-"sum": \(
- compare (sum [complex 2 3, complex (-4) 5]) (complex (-2) 8)
-),
-
-"scale": \(
- compare (scale 4 (complex 2 3)) (complex 8 12)
-),
-
-"zeros": \(
- compare (zeros 0) (array []) and
- compare (zeros 3) (array [complex 0 0, complex 0 0, complex 0 0])
-),
-
-"magnitudes": \(
- compare (vec.list (magnitudes [ complex (-3) 4, complex 4 3, complex 0 0 ]))
- [ 5, 5, 0 ] and
- compare (vec.list (magnitudes (array []))) []
-),
-
-"angles": \(
- compare (vec.list (angles [ complex 1 0, complex (-1) 0, complex 0 (-1) ]))
- [ 0, pi, -pi/2 ] and
- compare (vec.list (angles (array []))) []
-),
-
-
-] is hash boolean>;
-
-
-
diff -r 4b104ef8d110 -r 20903ee53719 may/complex/type.yeti
--- a/may/complex/type.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-
-module may.complex.type;
-
-typedef opaque cplx = ~may.Cplx;
-
-();
-
diff -r 4b104ef8d110 -r 20903ee53719 may/feature/feature.yeti
--- a/may/feature/feature.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-
-module may.feature.feature;
-
-vec = load may.vector;
-fr = load may.stream.framer;
-
-// Utility functions for feature extractors
-
-magdiff frame1 frame2 =
- sum (map2 do a b: abs(a - b) done (vec.list frame1) (vec.list frame2));
-
-emptyFrameFor frames =
- vec.zeros
- if empty? frames then 0
- else vec.length (head frames)
- fi;
-
-features featureFunc frames =
- (featuresOf prev frames =
- case frames of
- frame::rest: featureFunc prev frame :. \(featuresOf frame rest);
- _: [];
- esac;
- featuresOf (emptyFrameFor frames) frames);
-
-{ magdiff, emptyFrameFor, features };
-
-
-
diff -r 4b104ef8d110 -r 20903ee53719 may/feature/specdiff.yeti
--- a/may/feature/specdiff.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-
-module may.feature.specdiff;
-
-vec = load may.vector;
-fr = load may.stream.framer;
-cplx = load may.complex;
-
-load may.feature.feature;
-
-specdiff frames =
- features magdiff (map cplx.magnitudes frames);
-
-specdiffOfFile parameters filename =
- specdiff (fr.frequencyDomainFramesOfFile parameters filename);
-
-{
- specdiff,
- specdiffOfFile,
-}
-
diff -r 4b104ef8d110 -r 20903ee53719 may/feature/test/test_features.yeti
--- a/may/feature/test/test_features.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-
diff -r 4b104ef8d110 -r 20903ee53719 may/matrix.yeti
--- a/may/matrix.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,842 +0,0 @@
-
-/**
- * Matrices. A matrix is a two-dimensional (NxM) container of
- * double-precision floating point values.
- *
- * A matrix may be dense or sparse.
- *
- * A dense matrix (the default) is just a series of vectors, making up
- * the matrix "grid". The values may be stored in either column-major
- * order, in which case the series consists of one vector for each
- * column in the matrix, or row-major order, in which case the series
- * consists of one vector for each row. The default is column-major.
- *
- * A sparse matrix has a more complex representation in which only the
- * non-zero values are stored. This is typically used for matrices
- * containing sparse data, that is, data in which most of the values
- * are zero: using a sparse representation is more efficient than a
- * dense one (in both time and memory) if the matrix is very large but
- * contains a relatively low proportion of non-zero values. Like dense
- * matrices, sparse ones may be column-major or row-major.
- *
- * The choice of dense or sparse, row- or column-major is a question
- * of efficiency alone. All functions in this module should return the
- * same results regardless of how the matrices they operate on are
- * represented. However, differences in performance can be very large
- * and it is often worth converting matrices to a different storage
- * format if you know they can be more efficiently manipulated that
- * way. For example, multiplying two matrices is fastest if the first
- * is in column-major and the second in row-major order.
- *
- * Use the isRowMajor? and isSparse? functions to query the storage
- * format of a matrix; use the flipped function to convert between
- * column-major and row-major storage; and use toSparse and toDense to
- * convert between sparse and dense storage.
- *
- * Note that the matrix representation does not take into account
- * different forms of zero-width or zero-height matrix. All matrices
- * of zero width or height are equal to each other, and all are equal
- * to the zero-sized matrix.
- */
-
-module may.matrix;
-
-vec = load may.vector;
-bf = load may.vector.blockfuncs;
-
-load may.vector.type;
-load may.matrix.type;
-
-size m =
- case m of
- DenseRows r:
- major = length r;
- {
- rows = major,
- columns = if major > 0 then vec.length r[0] else 0 fi,
- };
- DenseCols c:
- major = length c;
- {
- rows = if major > 0 then vec.length c[0] else 0 fi,
- columns = major,
- };
- SparseCSR { values, indices, pointers, extent }:
- {
- rows = (length pointers) - 1,
- columns = extent
- };
- SparseCSC { values, indices, pointers, extent }:
- {
- rows = extent,
- columns = (length pointers) - 1
- };
- esac;
-
-width m = (size m).columns;
-height m = (size m).rows;
-
-nonZeroValues m =
- (nz d =
- sum
- (map do v:
- sum (map do n: if n == 0 then 0 else 1 fi done (vec.list v))
- done d);
- case m of
- DenseRows d: nz d;
- DenseCols d: nz d;
- SparseCSR d: vec.length d.values;
- SparseCSC d: vec.length d.values;
- esac);
-
-density m =
- ({ rows, columns } = size m;
- cells = rows * columns;
- (nonZeroValues m) / cells);
-
-sparseSlice n d =
- (start = d.pointers[n];
- end = d.pointers[n+1];
- {
- values = vec.slice d.values start end,
- indices = slice d.indices start end,
- });
-
-nonEmptySlices d =
- (ne = array [];
- for [0..length d.pointers - 2] do i:
- if d.pointers[i] != d.pointers[i+1] then
- push ne i
- fi
- done;
- ne);
-
-fromSlice n m d =
- (slice = sparseSlice n d;
- var v = 0;
- for [0..length slice.indices - 1] do i:
- if slice.indices[i] == m then
- v := vec.at slice.values i;
- fi
- done;
- v);
-
-filledSlice n d =
- (slice = sparseSlice n d;
- dslice = new double[d.extent];
- for [0..length slice.indices - 1] do i:
- dslice[slice.indices[i]] := vec.at slice.values i;
- done;
- vec.vector dslice);
-
-at' m row col =
- case m of
- DenseRows rows: r = rows[row]; vec.at r col;
- DenseCols cols: c = cols[col]; vec.at c row;
- SparseCSR data: fromSlice row col data;
- SparseCSC data: fromSlice col row data;
- esac;
-
-getColumn j m =
- case m of
- DenseCols cols: cols[j];
- SparseCSC data: filledSlice j data;
- _: vec.fromList (map do i: at' m i j done [0..height m - 1]);
- esac;
-
-getRow i m =
- case m of
- DenseRows rows: rows[i];
- SparseCSR data: filledSlice i data;
- _: vec.fromList (map do j: at' m i j done [0..width m - 1]);
- esac;
-
-asRows m =
- map do i: getRow i m done [0 .. (height m) - 1];
-
-asColumns m =
- map do i: getColumn i m done [0 .. (width m) - 1];
-
-isRowMajor? m =
- case m of
- DenseRows _: true;
- DenseCols _: false;
- SparseCSR _: true;
- SparseCSC _: false;
- esac;
-
-isSparse? m =
- case m of
- DenseRows _: false;
- DenseCols _: false;
- SparseCSR _: true;
- SparseCSC _: true;
- esac;
-
-typeOf m =
- if isRowMajor? m then RowMajor ()
- else ColumnMajor ()
- fi;
-
-flippedTypeOf m =
- if isRowMajor? m then ColumnMajor ()
- else RowMajor ()
- fi;
-
-newColumnMajorStorage { rows, columns } =
- if rows < 1 then array []
- else array (map \(vec.zeros rows) [1..columns])
- fi;
-
-zeroMatrix { rows, columns } =
- DenseCols (newColumnMajorStorage { rows, columns });
-
-zeroMatrixWithTypeOf m { rows, columns } =
- if isRowMajor? m then
- DenseRows (newColumnMajorStorage { rows = columns, columns = rows });
- else
- DenseCols (newColumnMajorStorage { rows, columns });
- fi;
-
-zeroSizeMatrix () = zeroMatrix { rows = 0, columns = 0 };
-
-empty?' m = (width m == 0 or height m == 0);
-
-generate f { rows, columns } =
- if rows < 1 or columns < 1 then zeroSizeMatrix ()
- else
- m = array (map \(new double[rows]) [1..columns]);
- for [0..columns-1] do col:
- for [0..rows-1] do row:
- m[col][row] := f row col;
- done;
- done;
- DenseCols (array (map vec.vector m))
- fi;
-
-swapij =
- map do { i, j, v }: { i = j, j = i, v } done;
-
-//!!! should use { row = , column = , value = } instead of i, j, v?
-enumerateSparse m =
- (enumerate { values, indices, pointers } =
- concat
- (map do i:
- start = pointers[i];
- end = pointers[i+1];
- map2 do j v: { i, j, v } done
- (slice indices start end)
- (vec.list (vec.slice values start end))
- done [0..length pointers - 2]);
- case m of
- SparseCSC d: swapij (enumerate d);
- SparseCSR d: enumerate d;
- _: [];
- esac);
-
-enumerateDense m =
- (enumerate d =
- concat
- (map do i:
- vv = d[i];
- map2 do j v: { i, j, v } done
- [0..vec.length vv - 1]
- (vec.list vv);
- done [0..length d - 1]);
- case m of
- DenseCols c: swapij (enumerate c);
- DenseRows r: enumerate r;
- _: [];
- esac);
-
-enumerate m =
- if isSparse? m then enumerateSparse m else enumerateDense m fi;
-
-// Make a sparse matrix from entries whose i, j values are known to be
-// within range
-makeSparse type size data =
- (isRow = case type of RowMajor (): true; ColumnMajor (): false esac;
- ordered =
- sortBy do a b:
- if a.maj == b.maj then a.min < b.min else a.maj < b.maj fi
- done
- (map
- if isRow then
- do { i, j, v }: { maj = i, min = j, v } done;
- else
- do { i, j, v }: { maj = j, min = i, v } done;
- fi
- (filter do d: d.v != 0 done data));
- tagger = if isRow then SparseCSR else SparseCSC fi;
- majorSize = if isRow then size.rows else size.columns fi;
- minorSize = if isRow then size.columns else size.rows fi;
- pointers = array [0];
- setArrayCapacity pointers (size.rows + 1);
- fillPointers n i data =
- if n < majorSize then
- case data of
- d::rest:
- (for [n..d-1] \(push pointers i);
- fillPointers d (i+1) rest);
- _:
- for [n..majorSize-1] \(push pointers i);
- esac;
- fi;
- fillPointers 0 0 (map (.maj) ordered);
- tagger {
- values = vec.fromList (map (.v) ordered),
- indices = array (map (.min) ordered),
- pointers,
- extent = minorSize,
- });
-
-// Make a sparse matrix from entries that may contain out-of-range
-// cells which need to be filtered out. This is the public API for
-// makeSparse and is also used to discard out-of-range cells from
-// resizedTo.
-newSparseMatrix type size data =
- makeSparse type size
- (filter
- do { i, j, v }:
- i == int i and i >= 0 and i < size.rows and
- j == int j and j >= 0 and j < size.columns
- done data);
-
-toSparse m =
- if isSparse? m then m
- else
- makeSparse (typeOf m) (size m) (enumerateDense m);
- fi;
-
-toDense m =
- if not (isSparse? m) then m
- elif isRowMajor? m then
- DenseRows (array (map do row: getRow row m done [0..height m - 1]));
- else
- DenseCols (array (map do col: getColumn col m done [0..width m - 1]));
- fi;
-
-constMatrix n = generate do row col: n done;
-randomMatrix = generate do row col: Math#random() done;
-identityMatrix = constMatrix 1;
-
-transposed m =
- case m of
- DenseRows d: DenseCols d;
- DenseCols d: DenseRows d;
- SparseCSR d: SparseCSC d;
- SparseCSC d: SparseCSR d;
- esac;
-
-flipped m =
- if isSparse? m then
- makeSparse (flippedTypeOf m) (size m) (enumerateSparse m)
- else
- if isRowMajor? m then
- generate do row col: at' m row col done (size m);
- else
- transposed
- (generate do row col: at' m col row done
- { rows = (width m), columns = (height m) });
- fi
- fi;
-
-toRowMajor m =
- if isRowMajor? m then m else flipped m fi;
-
-toColumnMajor m =
- if not isRowMajor? m then m else flipped m fi;
-
-equal'' comparator vecComparator m1 m2 =
- // Prerequisite: m1 and m2 have same sparse-p and storage order
- (compareVecLists vv1 vv2 = all id (map2 vecComparator vv1 vv2);
- compareSparse d1 d2 =
- d1.extent == d2.extent and
- vecComparator d1.values d2.values and
- d1.indices == d2.indices and
- d1.pointers == d2.pointers;
- case m1 of
- DenseRows d1:
- case m2 of DenseRows d2: compareVecLists d1 d2; _: false; esac;
- DenseCols d1:
- case m2 of DenseCols d2: compareVecLists d1 d2; _: false; esac;
- SparseCSR d1:
- case m2 of SparseCSR d2: compareSparse d1 d2; _: false; esac;
- SparseCSC d1:
- case m2 of SparseCSC d2: compareSparse d1 d2; _: false; esac;
- esac);
-
-equal' comparator vecComparator m1 m2 =
- if empty?' m1 and empty?' m2 then
- true
- elif size m1 != size m2 then
- false
- elif isRowMajor? m1 != isRowMajor? m2 then
- equal' comparator vecComparator (flipped m1) m2;
- elif isSparse? m1 != isSparse? m2 then
- if isSparse? m1 then
- equal' comparator vecComparator m1 (toSparse m2)
- else
- equal' comparator vecComparator (toSparse m1) m2
- fi
- else
- equal'' comparator vecComparator m1 m2
- fi;
-
-// Compare matrices using the given comparator for individual cells.
-// Note that matrices with different storage order but the same
-// contents are equal, although comparing them is slow.
-//!!! Document the fact that sparse matrices can only be equal if they
-// have the same set of non-zero cells (regardless of comparator used)
-equalUnder comparator =
- equal' comparator (vec.equalUnder comparator);
-
-equal =
- equal' (==) vec.equal;
-
-newMatrix type data = //!!! NB does not copy data
- (tagger = case type of RowMajor (): DenseRows; ColumnMajor (): DenseCols esac;
- if empty? data or vec.empty? (head data)
- then zeroSizeMatrix ()
- else tagger (array data)
- fi);
-
-newRowVector data = //!!! NB does not copy data
- DenseRows (array [data]);
-
-newColumnVector data = //!!! NB does not copy data
- DenseCols (array [data]);
-
-denseLinearOp op m1 m2 =
- if isRowMajor? m1 then
- newMatrix (typeOf m1)
- (map2 do c1 c2: op c1 c2 done (asRows m1) (asRows m2));
- else
- newMatrix (typeOf m1)
- (map2 do c1 c2: op c1 c2 done (asColumns m1) (asColumns m2));
- fi;
-
-sparseSumOrDifference op m1 m2 =
- (h = [:];
- for (enumerate m1) do { i, j, v }:
- if not (i in h) then h[i] := [:] fi;
- h[i][j] := v;
- done;
- for (enumerate m2) do { i, j, v }:
- if not (i in h) then h[i] := [:] fi;
- if j in h[i] then h[i][j] := op h[i][j] v;
- else h[i][j] := op 0 v;
- fi;
- done;
- entries = concat
- (map do i:
- kk = keys h[i];
- map2 do j v: { i, j, v } done kk (map (at h[i]) kk)
- done (keys h));
- makeSparse (typeOf m1) (size m1) entries);
-
-sum' m1 m2 =
- if (size m1) != (size m2)
- then failWith "Matrices are not the same size: \(size m1), \(size m2)";
- elif isSparse? m1 and isSparse? m2 then
- sparseSumOrDifference (+) m1 m2;
- else
- add2 v1 v2 = bf.add [v1,v2];
- denseLinearOp add2 m1 m2;
- fi;
-
-difference m1 m2 =
- if (size m1) != (size m2)
- then failWith "Matrices are not the same size: \(size m1), \(size m2)";
- elif isSparse? m1 and isSparse? m2 then
- sparseSumOrDifference (-) m1 m2;
- else
- denseLinearOp bf.subtract m1 m2;
- fi;
-
-scaled factor m =
- if isSparse? m then
- makeSparse (typeOf m) (size m)
- (map do { i, j, v }: { i, j, v = factor * v } done (enumerate m))
- elif isRowMajor? m then
- newMatrix (typeOf m) (map (bf.scaled factor) (asRows m));
- else
- newMatrix (typeOf m) (map (bf.scaled factor) (asColumns m));
- fi;
-
-abs' m =
- if isSparse? m then
- makeSparse (typeOf m) (size m)
- (map do { i, j, v }: { i, j, v = abs v } done (enumerate m))
- elif isRowMajor? m then
- newMatrix (typeOf m) (map bf.abs (asRows m));
- else
- newMatrix (typeOf m) (map bf.abs (asColumns m));
- fi;
-
-filter' f m =
- if isSparse? m then
- makeSparse (typeOf m) (size m)
- (map do { i, j, v }: { i, j, v = if f v then v else 0 fi } done
- (enumerate m))
- else
- vfilter = vec.fromList . (map do i: if f i then i else 0 fi done) . vec.list;
- if isRowMajor? m then
- newMatrix (typeOf m) (map vfilter (asRows m));
- else
- newMatrix (typeOf m) (map vfilter (asColumns m));
- fi;
- fi;
-
-sparseProductLeft size m1 m2 =
- ({ values, indices, pointers } = case m1 of
- SparseCSR d: d;
- SparseCSC d: d;
- _: failWith "sparseProductLeft called for non-sparse m1";
- esac;
- rows = isRowMajor? m1;
- data = array (map \(new double[size.rows]) [1..size.columns]);
- for [0..size.columns - 1] do j':
- c = getColumn j' m2;
- var p = 0;
- for [0..length indices - 1] do ix:
- ix == pointers[p+1] loop (p := p + 1);
- i = if rows then p else indices[ix] fi;
- j = if rows then indices[ix] else p fi;
- data[j'][i] := data[j'][i] + (vec.at values ix) * (vec.at c j);
- done;
- done;
- DenseCols (array (map vec.vector (list data))));
-
-sparseProductRight size m1 m2 =
- ({ values, indices, pointers } = case m2 of
- SparseCSR d: d;
- SparseCSC d: d;
- _: failWith "sparseProductLeft called for non-sparse m1";
- esac;
- rows = isRowMajor? m2;
- data = array (map \(new double[size.columns]) [1..size.rows]);
- for [0..size.rows - 1] do i':
- r = getRow i' m1;
- var p = 0;
- for [0..length indices - 1] do ix:
- ix == pointers[p+1] loop (p := p + 1);
- i = if rows then p else indices[ix] fi;
- j = if rows then indices[ix] else p fi;
- data[i'][j] := data[i'][j] + (vec.at values ix) * (vec.at r i);
- done;
- done;
- DenseRows (array (map vec.vector (list data))));
-
-sparseProduct size m1 m2 =
- case m2 of
- SparseCSC d:
- ({ values, indices, pointers } = case m1 of
- SparseCSR d1: d1;
- SparseCSC d1: d1;
- _: failWith "sparseProduct called for non-sparse matrices";
- esac;
- rows = isRowMajor? m1;
- var p = 0;
- pindices = new int[length indices];
- for [0..length indices - 1] do ix:
- ix == pointers[p+1] loop (p := p + 1);
- pindices[ix] := p;
- done;
- entries =
- (map do j':
- cs = sparseSlice j' d;
- hin = mapIntoHash
- (at cs.indices) (vec.at cs.values)
- [0..length cs.indices - 1];
- hout = [:];
- for [0..length indices - 1] do ix:
- i = if rows then pindices[ix] else indices[ix] fi;
- j = if rows then indices[ix] else pindices[ix] fi;
- if j in hin then
- p = (vec.at values ix) * hin[j];
- hout[i] := p + (if i in hout then hout[i] else 0 fi);
- fi;
- done;
- map do i:
- { i, j = j', v = hout[i] }
- done (keys hout);
- done (nonEmptySlices d));
- makeSparse (ColumnMajor ()) size (concat entries));
- SparseCSR _:
- sparseProduct size m1 (flipped m2);
- _: failWith "sparseProduct called for non-sparse matrices";
- esac;
-
-denseProduct size m1 m2 =
- (data = array (map \(new double[size.rows]) [1..size.columns]);
- for [0..size.rows - 1] do i:
- row = getRow i m1;
- for [0..size.columns - 1] do j:
- data[j][i] := bf.sum (bf.multiply row (getColumn j m2));
- done;
- done;
- DenseCols (array (map vec.vector (list data))));
-
-product m1 m2 =
- if (size m1).columns != (size m2).rows
- then failWith "Matrix dimensions incompatible: \(size m1), \(size m2) (\((size m1).columns) != \((size m2).rows))";
- else
- size = { rows = (size m1).rows, columns = (size m2).columns };
- if isSparse? m1 then
- if isSparse? m2 then
- sparseProduct size m1 m2
- else
- sparseProductLeft size m1 m2
- fi
- elif isSparse? m2 then
- sparseProductRight size m1 m2
- else
- denseProduct size m1 m2
- fi;
- fi;
-
-entryWiseProduct m1 m2 = // or element-wise, or Hadamard product
-//!!! todo: faster, sparse version, units
- if (size m1) != (size m2)
- then failWith "Matrices are not the same size: \(size m1), \(size m2)";
- else generate do row col: at' m1 row col * at' m2 row col done (size m1);
- fi;
-
-concatAgainstGrain tagger getter counter mm =
- (n = counter (size (head mm));
- tagger (array
- (map do i:
- vec.concat (map (getter i) mm)
- done [0..n-1])));
-
-concatWithGrain tagger getter counter mm =
- tagger (array
- (concat
- (map do m:
- n = counter (size m);
- map do i: getter i m done [0..n-1]
- done mm)));
-
-sparseConcat direction first mm =
- (dimension d f = if direction == d then sum (map f mm) else f first fi;
- rows = dimension (Vertical ()) height;
- columns = dimension (Horizontal ()) width;
- entries ioff joff ui uj mm acc =
- case mm of
- m::rest:
- entries
- (ioff + ui * height m)
- (joff + uj * width m)
- ui uj rest
- ((map do { i, j, v }: { i = i + ioff, j = j + joff, v }
- done (enumerate m)) ++ acc);
- _: acc;
- esac;
- makeSparse (typeOf first) { rows, columns }
- if direction == Vertical () then entries 0 0 1 0 mm []
- else entries 0 0 0 1 mm [] fi);
-
-checkDimensionsFor direction first mm =
- (counter = if direction == Horizontal () then (.rows) else (.columns) fi;
- n = counter (size first);
- if not (all id (map do m: counter (size m) == n done mm)) then
- failWith "Matrix dimensions incompatible for concat (found \(map do m: counter (size m) done mm) not all of which are \(n))";
- fi);
-
-concat' direction mm = //!!! doc: storage order is taken from first matrix in sequence; concat is obviously not lazy (unlike std module)
- case length mm of
- 0: zeroSizeMatrix ();
- 1: head mm;
- _:
- first = head mm;
- checkDimensionsFor direction first mm;
- if all isSparse? mm then
- sparseConcat direction first mm
- else
- row = isRowMajor? first;
- // horizontal, row-major: against grain with rows
- // horizontal, col-major: with grain with cols
- // vertical, row-major: with grain with rows
- // vertical, col-major: against grain with cols
- case direction of
- Horizontal ():
- if row then concatAgainstGrain DenseRows getRow (.rows) mm;
- else concatWithGrain DenseCols getColumn (.columns) mm;
- fi;
- Vertical ():
- if row then concatWithGrain DenseRows getRow (.rows) mm;
- else concatAgainstGrain DenseCols getColumn (.columns) mm;
- fi;
- esac;
- fi;
- esac;
-
-//!!! doc this filter -- zero-size elts are ignored
-concat direction mm =
- concat' direction (filter do mat: not (empty?' mat) done mm);
-
-//!!! next two v. clumsy
-
-//!!! doc note: argument order chosen for consistency with std module slice
-//!!! NB always returns dense matrix, should have sparse version
-rowSlice m start end = //!!! doc: storage order same as input
- if start < 0 then rowSlice m 0 end
- elif start > height m then rowSlice m (height m) end
- else
- if end < start then rowSlice m start start
- elif end > height m then rowSlice m start (height m)
- else
- if isRowMajor? m then
- DenseRows (array (map ((flip getRow) m) [start .. end - 1]))
- else
- DenseCols (array (map do v: vec.slice v start end done (asColumns m)))
- fi;
- fi;
- fi;
-
-//!!! doc note: argument order chosen for consistency with std module slice
-//!!! NB always returns dense matrix, should have sparse version
-columnSlice m start end = //!!! doc: storage order same as input
- if start < 0 then columnSlice m 0 end
- elif start > width m then columnSlice m (width m) end
- else
- if end < start then columnSlice m start start
- elif end > width m then columnSlice m start (width m)
- else
- if not isRowMajor? m then
- DenseCols (array (map ((flip getColumn) m) [start .. end - 1]))
- else
- DenseRows (array (map do v: vec.slice v start end done (asRows m)))
- fi;
- fi;
- fi;
-
-resizedTo newsize m =
- (if newsize == (size m) then
- m
- elif isSparse? m then
- // don't call makeSparse directly: want to discard
- // out-of-range cells
- newSparseMatrix (typeOf m) newsize (enumerateSparse m)
- elif (height m) == 0 or (width m) == 0 then
- zeroMatrixWithTypeOf m newsize;
- else
- growrows = newsize.rows - (height m);
- growcols = newsize.columns - (width m);
- rowm = isRowMajor? m;
- resizedTo newsize
- if rowm and growrows < 0 then
- rowSlice m 0 newsize.rows
- elif (not rowm) and growcols < 0 then
- columnSlice m 0 newsize.columns
- elif growrows < 0 then
- rowSlice m 0 newsize.rows
- elif growcols < 0 then
- columnSlice m 0 newsize.columns
- else
- if growrows > 0 then
- concat (Vertical ())
- [m, zeroMatrixWithTypeOf m ((size m) with { rows = growrows })]
- else
- concat (Horizontal ())
- [m, zeroMatrixWithTypeOf m ((size m) with { columns = growcols })]
- fi
- fi
- fi);
-
-{
- size,
- width,
- height,
- density,
- nonZeroValues,
- at = at',
- getColumn,
- getRow,
- isRowMajor?,
- isSparse?,
- generate,
- constMatrix,
- randomMatrix,
- zeroMatrix,
- identityMatrix,
- zeroSizeMatrix,
- empty? = empty?',
- equal,
- equalUnder,
- transposed,
- flipped,
- toRowMajor,
- toColumnMajor,
- toSparse,
- toDense,
- scaled,
- resizedTo,
- asRows,
- asColumns,
- sum = sum',
- difference,
- abs = abs',
- filter = filter',
- product,
- entryWiseProduct,
- concat,
- rowSlice,
- columnSlice,
- newMatrix,
- newRowVector,
- newColumnVector,
- newSparseMatrix,
- enumerate
-}
-as
-{
-//!!! check whether these are right to be .selector rather than just selector
-
- size is matrix -> { .rows is number, .columns is number },
- width is matrix -> number,
- height is matrix -> number,
- density is matrix -> number,
- nonZeroValues is matrix -> number,
- at is matrix -> number -> number -> number,
- getColumn is number -> matrix -> vector,
- getRow is number -> matrix -> vector,
- isRowMajor? is matrix -> boolean,
- isSparse? is matrix -> boolean,
- generate is (number -> number -> number) -> { .rows is number, .columns is number } -> matrix,
- constMatrix is number -> { .rows is number, .columns is number } -> matrix,
- randomMatrix is { .rows is number, .columns is number } -> matrix,
- zeroMatrix is { .rows is number, .columns is number } -> matrix,
- identityMatrix is { .rows is number, .columns is number } -> matrix,
- zeroSizeMatrix is () -> matrix,
- empty? is matrix -> boolean,
- equal is matrix -> matrix -> boolean,
- equalUnder is (number -> number -> boolean) -> matrix -> matrix -> boolean,
- transposed is matrix -> matrix,
- flipped is matrix -> matrix,
- toRowMajor is matrix -> matrix,
- toColumnMajor is matrix -> matrix,
- toSparse is matrix -> matrix,
- toDense is matrix -> matrix,
- scaled is number -> matrix -> matrix,
- thresholded is number -> matrix -> matrix,
- resizedTo is { .rows is number, .columns is number } -> matrix -> matrix,
- asRows is matrix -> list,
- asColumns is matrix -> list,
- sum is matrix -> matrix -> matrix,
- difference is matrix -> matrix -> matrix,
- abs is matrix -> matrix,
- filter is (number -> boolean) -> matrix -> matrix,
- product is matrix -> matrix -> matrix,
- entryWiseProduct is matrix -> matrix -> matrix,
- concat is (Horizontal () | Vertical ()) -> list -> matrix,
- rowSlice is matrix -> number -> number -> matrix,
- columnSlice is matrix -> number -> number -> matrix,
- newMatrix is (ColumnMajor () | RowMajor ()) -> list -> matrix,
- newRowVector is vector -> matrix,
- newColumnVector is vector -> matrix,
- newSparseMatrix is (ColumnMajor () | RowMajor ()) -> { .rows is number, .columns is number } -> list<{ .i is number, .j is number, .v is number }> -> matrix,
- enumerate is matrix -> list<{ .i is number, .j is number, .v is number }>
-}
-
diff -r 4b104ef8d110 -r 20903ee53719 may/matrix/test/speedtest.yeti
--- a/may/matrix/test/speedtest.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,218 +0,0 @@
-
-program may.matrix.test.speedtest;
-
-mat = load may.matrix;
-vec = load may.vector;
-
-{ compare, compareUsing, time } = load may.test.test;
-
-norec time f = time "" f;
-
-compareMatrices = compareUsing mat.equal;
-
-makeMatrices sz sparsity =
- (print "Making \(sz) * \(sz) random matrix...";
- m = time \(mat.randomMatrix { rows = sz, columns = sz });
- makeSparse () =
- (print "Making \(sparsity * 100)% sparse version (as dense matrix)...";
- t = time \(mat.filter (> sparsity) m);
- println "Reported density: \(mat.density t) (non-zero values: \(mat.nonZeroValues t))";
- print "Converting to sparse matrix...";
- s = time \(mat.toSparse t);
- println "Reported density: \(mat.density s) (non-zero values: \(mat.nonZeroValues s))";
- s);
- s = makeSparse ();
- println "Making types:";
- print "Col-major dense...";
- cmd = time \(mat.toColumnMajor m);
- print "Row-major dense...";
- rmd = time \(mat.toRowMajor m);
- print "Col-major sparse...";
- cms = time \(mat.toColumnMajor s);
- print "Row-major sparse...";
- rms = time \(mat.toRowMajor s);
- println "";
- { cmd, rmd, cms, rms });
-
-println "\nR * M multiplies:\n";
-
-sz = 2000;
-sparsity = 0.95;
-
-{ cmd, rmd, cms, rms } = makeMatrices sz sparsity;
-
-row = mat.newRowVector (vec.fromList (map \(Math#random()) [1..sz]));
-col = mat.newColumnVector (vec.fromList (map \(Math#random()) [1..sz]));
-
-print "R * CMD... ";
-a = (time \(mat.product row cmd));
-
-print "R * RMD... ";
-b = (time \(mat.product row rmd));
-
-print "R * CMS... ";
-c = (time \(mat.product row cms));
-
-print "R * RMS... ";
-d = (time \(mat.product row rms));
-
-println "\nChecking results: \(compareMatrices a b) \(compareMatrices c d)";
-
-println "\nM * C multiplies:\n";
-
-print "CMD * C... ";
-a = (time \(mat.product cmd col));
-
-print "RMD * C... ";
-b = (time \(mat.product rmd col));
-
-print "CMS * C... ";
-c = (time \(mat.product cms col));
-
-print "RMS * C... ";
-d = (time \(mat.product rms col));
-
-println "\nChecking results: \(compareMatrices a b) \(compareMatrices c d)";
-
-reportOn m =
- (print " ";
- println "isSparse: \(mat.isSparse? m), density \(mat.density m)");
-
-println "\nM * M multiplies (and a few sums):\n";
-
-sz = 500;
-
-{ cmd, rmd, cms, rms } = makeMatrices sz sparsity;
-
-print "CMS * CMD... ";
-reportOn (time \(mat.product cms cmd));
-
-print "CMS * RMD... ";
-reportOn (time \(mat.product cms rmd));
-
-print "RMS * CMD... ";
-reportOn (time \(mat.product rms cmd));
-
-print "RMS * RMD... ";
-reportOn (time \(mat.product rms rmd));
-
-println "";
-
-print "CMD * CMS... ";
-reportOn (time \(mat.product cmd cms));
-
-print "CMD * RMS... ";
-reportOn (time \(mat.product cmd rms));
-
-print "RMD * CMS... ";
-reportOn (time \(mat.product rmd cms));
-
-print "RMD * RMS... ";
-reportOn (time \(mat.product rmd rms));
-
-println "";
-
-print "CMS * CMS... ";
-reportOn (time \(mat.product cms cms));
-
-print "CMS * RMS... ";
-reportOn (time \(mat.product cms rms));
-
-print "RMS * CMS... ";
-reportOn (time \(mat.product rms cms));
-
-print "RMS * RMS... ";
-reportOn (time \(mat.product rms rms));
-
-println "";
-
-print "CMD + CMD... ";
-reportOn (time \(mat.sum cmd cmd));
-
-print "CMD + RMD... ";
-reportOn (time \(mat.sum cmd rmd));
-
-print "RMD + CMD... ";
-reportOn (time \(mat.sum rmd cmd));
-
-print "RMD + RMD... ";
-reportOn (time \(mat.sum rmd rmd));
-
-println "";
-
-print "CMS + CMS... ";
-reportOn (time \(mat.sum cms cms));
-
-print "CMS + RMS... ";
-reportOn (time \(mat.sum cms rms));
-
-print "RMS + CMS... ";
-reportOn (time \(mat.sum rms cms));
-
-print "RMS + RMS... ";
-reportOn (time \(mat.sum rms rms));
-
-println "";
-
-print "CMD * CMD... ";
-reportOn (time \(mat.product cmd cmd));
-
-print "CMD * RMD... ";
-reportOn (time \(mat.product cmd rmd));
-
-print "RMD * CMD... ";
-reportOn (time \(mat.product rmd cmd));
-
-print "RMD * RMD... ";
-reportOn (time \(mat.product rmd rmd));
-
-println "\nLarge sparse M * M multiplies and adds:\n";
-
-sz = 5000000;
-nnz = 10000;
-
-print "Calculating \(nnz) non-zero entry records...";
-entries = time \(e = map \({ i = int (Math#random() * sz),
- j = int (Math#random() * sz),
- v = Math#random() }) [1..nnz];
- \() (length e); // make sure list non-lazy for timing purposes
- e);
-
-print "Making \(sz) * \(sz) random matrix with \(nnz) entries...";
-rms = time \(mat.newSparseMatrix (RowMajor ()) { rows = sz, columns = sz }
- entries);
-println "Reported density: \(mat.density rms) (non-zero values: \(mat.nonZeroValues rms))";
-
-print "Making col-major copy...";
-cms = time \(mat.toColumnMajor rms);
-println "Reported density: \(mat.density cms) (non-zero values: \(mat.nonZeroValues cms))";
-
-println "";
-
-print "CMS * CMS... ";
-reportOn (time \(mat.product cms cms));
-
-print "CMS * RMS... ";
-reportOn (time \(mat.product cms rms));
-
-print "RMS * CMS... ";
-reportOn (time \(mat.product rms cms));
-
-print "RMS * RMS... ";
-reportOn (time \(mat.product rms rms));
-
-println "";
-
-print "CMS + CMS... ";
-reportOn (time \(mat.sum cms cms));
-
-print "CMS + RMS... ";
-reportOn (time \(mat.sum cms rms));
-
-print "RMS + CMS... ";
-reportOn (time \(mat.sum rms cms));
-
-print "RMS + RMS... ";
-reportOn (time \(mat.sum rms rms));
-
-();
diff -r 4b104ef8d110 -r 20903ee53719 may/matrix/test/test_matrix.yeti
--- a/may/matrix/test/test_matrix.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,575 +0,0 @@
-
-module may.matrix.test.test_matrix;
-
-mat = load may.matrix;
-vec = load may.vector;
-
-load may.vector.type;
-load may.matrix.type;
-
-import yeti.lang: FailureException;
-
-{ compare, compareUsing } = load may.test.test;
-
-compareMatrices = compareUsing mat.equal;
-
-makeTests name flipper =
- (constMatrix n s = flipper (mat.constMatrix n s);
- zeroMatrix s = flipper (mat.zeroMatrix s);
- randomMatrix s = flipper (mat.randomMatrix s);
- identityMatrix s = flipper (mat.identityMatrix s);
- generate f s = flipper (mat.generate f s);
- newMatrix t d = flipper (mat.newMatrix t (map vec.fromList d));
-[
-
-"constMatrixEmpty-\(name)": \(
- m = constMatrix 2 { rows = 0, columns = 0 };
- compare (mat.size m) { columns = 0, rows = 0 }
-),
-
-"constMatrixEmpty2-\(name)": \(
- compare (mat.size (constMatrix 2 { rows = 0, columns = 4 })) { columns = 0, rows = 0 } and
- compare (mat.size (constMatrix 2 { rows = 4, columns = 0 })) { columns = 0, rows = 0 }
-),
-
-"constMatrix-\(name)": \(
- m = constMatrix 2 { rows = 3, columns = 4 };
- compare (mat.size m) { columns = 4, rows = 3 } and
- all id (map do row: compare (vec.list (mat.getRow row m)) [2,2,2,2] done [0..2]) and
- all id (map do col: compare (vec.list (mat.getColumn col m)) [2,2,2] done [0..3])
-),
-
-"randomMatrixEmpty-\(name)": \(
- m = randomMatrix { rows = 0, columns = 0 };
- compare (mat.size m) { columns = 0, rows = 0 }
-),
-
-"randomMatrix-\(name)": \(
- m = randomMatrix { rows = 3, columns = 4 };
- compare (mat.size m) { columns = 4, rows = 3 }
-),
-
-"zeroMatrixEmpty-\(name)": \(
- m = zeroMatrix { rows = 0, columns = 0 };
- compare (mat.size m) { columns = 0, rows = 0 }
-),
-
-"zeroMatrix-\(name)": \(
- m = zeroMatrix { rows = 3, columns = 4 };
- compare (mat.size m) { columns = 4, rows = 3 } and
- all id (map do row: compare (vec.list (mat.getRow row m)) [0,0,0,0] done [0..2]) and
- all id (map do col: compare (vec.list (mat.getColumn col m)) [0,0,0] done [0..3])
-),
-
-"identityMatrixEmpty-\(name)": \(
- m = identityMatrix { rows = 0, columns = 0 };
- compare (mat.size m) { columns = 0, rows = 0 }
-),
-
-"identityMatrix-\(name)": \(
- m = identityMatrix { rows = 3, columns = 4 };
- compare (mat.size m) { columns = 4, rows = 3 } and
- all id (map do row: compare (vec.list (mat.getRow row m)) [1,1,1,1] done [0..2]) and
- all id (map do col: compare (vec.list (mat.getColumn col m)) [1,1,1] done [0..3])
-),
-
-"generateEmpty-\(name)": \(
- m = generate do row col: 0 done { rows = 0, columns = 0 };
- compare (mat.size m) { columns = 0, rows = 0 }
-),
-
-"generate-\(name)": \(
- m = generate do row col: row * 10 + col done { rows = 2, columns = 3 };
- compare (vec.list (mat.getRow 0 m)) [0,1,2] and
- compare (vec.list (mat.getRow 1 m)) [10,11,12]
-),
-
-"widthAndHeight-\(name)": \(
- m = constMatrix 2 { rows = 3, columns = 4 };
- compare (mat.size m) { columns = mat.width m, rows = mat.height m }
-),
-
-"equal-\(name)": \(
- m = newMatrix (ColumnMajor ()) [[1,4],[0,5],[3,6]];
- n = m;
- p = newMatrix (RowMajor ()) [[1,0,3],[4,5,6]];
- q = newMatrix (ColumnMajor ()) [[1,0,3],[4,5,6]];
- r = newMatrix (ColumnMajor ()) [[1,4],[0,5]];
- compareMatrices m n and
- compareMatrices m p and
- compareMatrices n p and
- not mat.equal m q and
- not mat.equal m r
-),
-
-"equalUnder-\(name)": \(
- p = newMatrix (ColumnMajor ()) [[1,2,3],[4,5,6]];
- q = newMatrix (ColumnMajor ()) [[1,2,3],[4,5,6]];
- r = newMatrix (ColumnMajor ()) [[4,3,1],[3,1,2]];
- s = newMatrix (ColumnMajor ()) [[1,4,5],[6,7,8]];
- t = newMatrix (ColumnMajor ()) [[1,4,5],[6,7,9]];
- mat.equalUnder (==) p p and
- mat.equalUnder (==) p q and
- mat.equalUnder (!=) p r and
- mat.equalUnder do a b: a % 2 == b % 2 done p s and
- not mat.equalUnder do a b: a % 2 == b % 2 done p t
-),
-
-"at-\(name)": \(
- generator row col = row * 10 + col;
- m = generate generator { rows = 2, columns = 3 };
- all id
- (map do row: all id
- (map do col: mat.at m row col == generator row col done [0..2])
- done [0..1])
-),
-
-"transposedEmpty-\(name)": \(
- compare (mat.size (mat.transposed (constMatrix 2 { rows = 0, columns = 0 }))) { columns = 0, rows = 0 } and
- compare (mat.size (mat.transposed (constMatrix 2 { rows = 0, columns = 4 }))) { columns = 0, rows = 0 } and
- compare (mat.size (mat.transposed (constMatrix 2 { rows = 4, columns = 0 }))) { columns = 0, rows = 0 }
-),
-
-"transposedSize-\(name)": \(
- compare (mat.size (mat.transposed (constMatrix 2 { rows = 3, columns = 4 }))) { columns = 3, rows = 4 }
-),
-
-"transposed-\(name)": \(
- generator row col = row * 10 + col;
- m = generate generator { rows = 2, columns = 3 };
- m' = mat.transposed m;
- all id
- (map do row: all id
- // like at test, but with col/row flipped
- (map do col: mat.at m' col row == generator row col done [0..2])
- done [0..1])
-),
-
-"transposed-back-\(name)": \(
- m = newMatrix (ColumnMajor ()) [[1,4],[2,5],[3,6]];
- compareMatrices m (mat.transposed (mat.transposed m)) and
- not mat.equal m (mat.transposed m);
-),
-
-"flipped-\(name)": \(
- m = newMatrix (ColumnMajor ()) [[1,4],[0,5],[3,6]];
- m' = mat.flipped m;
- m'' = newMatrix (RowMajor ()) [[1,0,3],[4,5,6]];
- compareMatrices m m' and compareMatrices m m'' and compareMatrices m' m'';
-),
-
-"flipped-back-\(name)": \(
- m = newMatrix (ColumnMajor ()) [[1,4],[0,5],[3,6]];
- compareMatrices m (mat.flipped (mat.flipped m));
-),
-
-"flipped-empty-\(name)": \(
- m = constMatrix 2 { rows = 0, columns = 4 };
- compareMatrices (mat.flipped m) (mat.flipped (constMatrix 0 { rows = 0, columns = 0 }));
-),
-
-"toRowMajor-\(name)": \(
- m = newMatrix (ColumnMajor ()) [[1,4],[0,5],[3,6]];
- m' = mat.toRowMajor m;
- m'' = newMatrix (RowMajor ()) [[1,0,3],[4,5,6]];
- m''' = mat.toRowMajor m'';
- compareMatrices m m' and compareMatrices m m'' and compareMatrices m' m''
- and compareMatrices m m''';
-),
-
-"toColumnMajor-\(name)": \(
- m = newMatrix (RowMajor ()) [[1,4],[0,5],[3,6]];
- m' = mat.toColumnMajor m;
- m'' = newMatrix (ColumnMajor ()) [[1,0,3],[4,5,6]];
- m''' = mat.toColumnMajor m'';
- compareMatrices m m' and compareMatrices m m'' and compareMatrices m' m''
- and compareMatrices m m''';
-),
-
-"scaled-\(name)": \(
- compareMatrices
- (mat.scaled 0.5 (constMatrix 2 { rows = 3, columns = 4 }))
- (constMatrix 1 { rows = 3, columns = 4 }) and
- compareMatrices
- (mat.scaled 0.5 (constMatrix (-3) { rows = 3, columns = 4 }))
- (constMatrix (-1.5) { rows = 3, columns = 4 }) and
- compareMatrices
- (mat.scaled 0.5 (constMatrix 2 { rows = 0, columns = 2 }))
- (constMatrix 5 { rows = 0, columns = 0 })
-),
-
-"sum-\(name)": \(
- compareMatrices
- (mat.sum (constMatrix 2 { rows = 3, columns = 4 })
- (constMatrix 1 { rows = 3, columns = 4 }))
- (constMatrix 3 { rows = 3, columns = 4 })
-),
-
-"sumFail-\(name)": \(
- try
- \() (mat.sum (constMatrix 2 { rows = 3, columns = 4 })
- (constMatrix 1 { rows = 3, columns = 5 }));
- false;
- catch FailureException e:
- true
- yrt
-),
-
-"sparseSum-\(name)": \(
- s = mat.newSparseMatrix (ColumnMajor ()) { rows = 2, columns = 3 } [
- { i = 0, j = 0, v = 1 },
- { i = 0, j = 2, v = 2 },
- { i = 1, j = 1, v = 4 },
- ];
- t = mat.newSparseMatrix (ColumnMajor ()) { rows = 2, columns = 3 } [
- { i = 0, j = 1, v = 7 },
- { i = 1, j = 0, v = 5 },
- { i = 1, j = 1, v = -4 }, // NB this means [1,1] -> 0, sparse zero
- ];
- tot = mat.sum s t;
- mat.isSparse? tot and
- compareMatrices tot (mat.sum (mat.toDense s) t) and
- compareMatrices tot (mat.sum (mat.toDense s) (mat.toDense t)) and
- compareMatrices tot (mat.sum s (mat.toDense t)) and
- compareMatrices tot
- (mat.newSparseMatrix (RowMajor ()) { rows = 2, columns = 3 } [
- { i = 0, j = 0, v = 1 },
- { i = 0, j = 1, v = 7 },
- { i = 0, j = 2, v = 2 },
- { i = 1, j = 0, v = 5 },
- ]) and
- compare (mat.density tot) (4/6)
-),
-
-"difference-\(name)": \(
- compareMatrices
- (mat.difference (constMatrix 2 { rows = 3, columns = 4 })
- (constMatrix 1 { rows = 3, columns = 4 }))
- (constMatrix 1 { rows = 3, columns = 4 })
-),
-
-"differenceFail-\(name)": \(
- try
- \() (mat.difference (constMatrix 2 { rows = 3, columns = 4 })
- (constMatrix 1 { rows = 3, columns = 5 }));
- false;
- catch FailureException e:
- true
- yrt
-),
-
-"sparseDifference-\(name)": \(
- s = mat.newSparseMatrix (ColumnMajor ()) { rows = 2, columns = 3 } [
- { i = 0, j = 0, v = 1 },
- { i = 0, j = 2, v = 2 },
- { i = 1, j = 1, v = 4 },
- ];
- t = mat.newSparseMatrix (ColumnMajor ()) { rows = 2, columns = 3 } [
- { i = 0, j = 1, v = 7 },
- { i = 1, j = 0, v = 5 },
- { i = 1, j = 1, v = 6 },
- ];
- diff = mat.difference s t;
- mat.isSparse? diff and
- compareMatrices diff (mat.difference (mat.toDense s) t) and
- compareMatrices diff (mat.difference (mat.toDense s) (mat.toDense t)) and
- compareMatrices diff (mat.difference s (mat.toDense t)) and
- compareMatrices diff
- (mat.newSparseMatrix (RowMajor ()) { rows = 2, columns = 3 } [
- { i = 0, j = 0, v = 1 },
- { i = 0, j = 1, v = -7 },
- { i = 0, j = 2, v = 2 },
- { i = 1, j = 0, v = -5 },
- { i = 1, j = 1, v = -2 },
- ])
-),
-
-"abs-\(name)": \(
- compareMatrices
- (mat.abs (newMatrix (ColumnMajor ()) [[-1,4],[2,-5],[-3,0]]))
- (newMatrix (ColumnMajor ()) [[1,4],[2,5],[3,0]])
-),
-
-"product-\(name)": \(
- compareMatrices
- (mat.product (constMatrix 2 { rows = 4, columns = 2 })
- (constMatrix 3 { rows = 2, columns = 3 }))
- (constMatrix 12 { rows = 4, columns = 3 }) and
- compareMatrices
- (mat.product (newMatrix (ColumnMajor ()) [[1,4],[2,5],[3,6]])
- (newMatrix (ColumnMajor ()) [[7,9,11],[8,10,12]]))
- (newMatrix (ColumnMajor ()) [[58,139],[64,154]])
-),
-
-"sparseProduct-\(name)": \(
- s = mat.newSparseMatrix (ColumnMajor ()) { rows = 2, columns = 3 } [
- { i = 0, j = 0, v = 1 },
- { i = 0, j = 2, v = 2 },
- { i = 1, j = 1, v = 4 },
- ];
- t = mat.newSparseMatrix (ColumnMajor ()) { rows = 3, columns = 2 } [
- { i = 0, j = 1, v = 7 },
- { i = 1, j = 0, v = 5 },
- { i = 2, j = 0, v = 6 },
- ];
- prod = mat.product s t;
- mat.isSparse? prod and
- compareMatrices prod (mat.product (mat.toDense s) t) and
- compareMatrices prod (mat.product (mat.toDense s) (mat.toDense t)) and
- compareMatrices prod (mat.product s (mat.toDense t)) and
- compareMatrices prod
- (mat.newSparseMatrix (RowMajor ()) { rows = 2, columns = 2 } [
- { i = 0, j = 0, v = 12 },
- { i = 0, j = 1, v = 7 },
- { i = 1, j = 0, v = 20 },
- ])
-),
-
-"productFail-\(name)": \(
- try
- \() (mat.product (constMatrix 2 { rows = 4, columns = 2 })
- (constMatrix 3 { rows = 3, columns = 2 }));
- false;
- catch FailureException e:
- true
- yrt
-),
-
-"resizedTo-\(name)": \(
- compareMatrices
- (mat.resizedTo { rows = 2, columns = 2 }
- (newMatrix (ColumnMajor ()) [[1,4],[2,5],[3,6]]))
- (newMatrix (ColumnMajor ()) [[1,4],[2,5]]) and
- compareMatrices
- (mat.resizedTo { rows = 3, columns = 4 }
- (newMatrix (ColumnMajor ()) [[1,4],[2,5],[3,6]]))
- (newMatrix (ColumnMajor ()) [[1,4,0],[2,5,0],[3,6,0],[0,0,0]]) and
- compareMatrices
- (mat.resizedTo { rows = 1, columns = 1 }
- (newMatrix (ColumnMajor ()) [[1,4],[2,5],[3,6]]))
- (newMatrix (RowMajor ()) [[1]]) and
- compareMatrices
- (mat.resizedTo { rows = 2, columns = 3 }
- (mat.zeroSizeMatrix ()))
- (newMatrix (RowMajor ()) [[0,0,0],[0,0,0]]) and
- mat.isSparse?
- (mat.resizedTo { rows = 1, columns = 1 }
- (mat.toSparse (newMatrix (ColumnMajor ()) [[1,4],[2,5],[3,6]])))
-),
-
-"zeroSizeMatrix-\(name)": \(
- compareMatrices
- (mat.zeroSizeMatrix ())
- (newMatrix (ColumnMajor ()) []) and
- compareMatrices
- (mat.zeroSizeMatrix ())
- (newMatrix (ColumnMajor ()) [[]]) and
- compareMatrices
- (newMatrix (ColumnMajor ()) [[]])
- (newMatrix (RowMajor ()) [[]]) and
- compareMatrices
- (mat.zeroSizeMatrix ())
- (mat.newSparseMatrix (ColumnMajor ()) { rows = 0, columns = 1 } []) and
- compareMatrices
- (mat.zeroSizeMatrix ())
- (mat.newSparseMatrix (ColumnMajor ()) { rows = 1, columns = 0 } [])
-),
-
-"asRows-\(name)": \(
- compare
- (map vec.list
- (mat.asRows (newMatrix (ColumnMajor ()) [[1,4],[0,5],[3,6]])))
- [[1,0,3],[4,5,6]];
-),
-
-"asColumns-\(name)": \(
- compare
- (map vec.list
- (mat.asColumns (newMatrix (ColumnMajor ()) [[1,4],[0,5],[3,6]])))
- [[1,4],[0,5],[3,6]];
-),
-
-"concat-horiz-\(name)": \(
- compareMatrices
- (mat.concat (Horizontal ())
- [(newMatrix (ColumnMajor ()) [[1,4],[0,5]]),
- (newMatrix (RowMajor ()) [[3],[6]])])
- (newMatrix (ColumnMajor ()) [[1,4],[0,5],[3,6]])
-),
-
-"concatEmpty-horiz-\(name)": \(
- compareMatrices
- (mat.concat (Horizontal ())
- [(newMatrix (ColumnMajor ()) [[]]),
- (newMatrix (RowMajor ()) [[]]),
- (mat.zeroSizeMatrix ())])
- (mat.zeroSizeMatrix ());
-),
-
-"sparseConcat-horiz-\(name)": \(
- s = mat.concat (Horizontal ())
- [mat.toSparse (newMatrix (ColumnMajor ()) [[1,4],[0,5]]),
- mat.toSparse (newMatrix (RowMajor ()) [[3],[6]])];
- compareMatrices s (newMatrix (ColumnMajor ()) [[1,4],[0,5],[3,6]]) and
- compare (mat.isSparse? s) true and
- compare (mat.density s) (5/6)
-),
-
-"concatFail-horiz-\(name)": \(
- try
- \() (mat.concat (Horizontal ())
- [(newMatrix (ColumnMajor ()) [[1,4],[0,5]]),
- (newMatrix (ColumnMajor ()) [[3],[6]])]);
- false
- catch FailureException e:
- true
- yrt
-),
-
-"concat-vert-\(name)": \(
- compareMatrices
- (mat.concat (Vertical ())
- [(newMatrix (ColumnMajor ()) [[1,4],[0,5]]),
- (newMatrix (RowMajor ()) [[3,6]])])
- (newMatrix (ColumnMajor ()) [[1,4,3],[0,5,6]])
-),
-
-"sparseConcat-vert-\(name)": \(
- s = mat.concat (Vertical ())
- [mat.toSparse (newMatrix (ColumnMajor ()) [[1,4],[0,5]]),
- mat.toSparse (newMatrix (RowMajor ()) [[3,6]])];
- compareMatrices s (newMatrix (ColumnMajor ()) [[1,4,3],[0,5,6]]) and
- compare (mat.isSparse? s) true and
- compare (mat.density s) (5/6)
-),
-
-"concatFail-vert-\(name)": \(
- try
- \() (mat.concat (Vertical ())
- [(newMatrix (ColumnMajor ()) [[1,4],[0,5]]),
- (newMatrix (RowMajor ()) [[3],[6]])]);
- false
- catch FailureException e:
- true
- yrt
-),
-
-"rowSlice-\(name)": \(
- compareMatrices
- (mat.rowSlice (newMatrix (RowMajor ()) [[1,0],[3,4],[0,6],[7,8]]) 1 3)
- (newMatrix (RowMajor ()) [[3,4],[0,6]]) and
- compareMatrices
- (mat.rowSlice (newMatrix (RowMajor ()) [[1,0],[3,4],[0,6],[7,8]]) 3 6)
- (newMatrix (RowMajor ()) [[7,8]])
-),
-
-"columnSlice-\(name)": \(
- compareMatrices
- (mat.columnSlice (newMatrix (RowMajor ()) [[1,0,3,4],[0,6,7,8]]) 1 3)
- (newMatrix (RowMajor ()) [[0,3],[6,7]]) and
- compareMatrices
- (mat.columnSlice (newMatrix (RowMajor ()) [[1,0,3,4],[0,6,7,8]]) 2 5)
- (newMatrix (RowMajor ()) [[3,4],[7,8]])
-),
-
-"density-\(name)": \(
- compare (mat.density (newMatrix (ColumnMajor ()) [[1,2,0],[0,5,0]])) (3/6) and
- compare (mat.density (newMatrix (ColumnMajor ()) [[1,2,3],[4,5,6]])) (6/6) and
- compare (mat.density (newMatrix (ColumnMajor ()) [[0,0,0],[0,0,0]])) 0
-),
-
-"nonZeroValues-\(name)": \(
- compare (mat.nonZeroValues (newMatrix (ColumnMajor ()) [[1,2,0],[0,5,0]])) 3 and
- compare (mat.nonZeroValues (newMatrix (ColumnMajor ()) [[1,2,3],[4,5,6]])) 6 and
- compare (mat.nonZeroValues (newMatrix (ColumnMajor ()) [[0,0,0],[0,0,0]])) 0
-),
-
-"toSparse-\(name)": \(
- m = newMatrix (ColumnMajor ()) [[1,2,0],[-1,-4,6],[0,0,3]];
- compareMatrices (mat.toSparse m) m and
- compareMatrices (mat.toDense (mat.toSparse m)) m and
- compare (mat.density (mat.toSparse m)) (6/9)
-),
-
-"toDense-\(name)": \(
- m = newMatrix (ColumnMajor ()) [[1,2,0],[-1,-4,6],[0,0,3]];
- compareMatrices (mat.toDense m) m and
- compareMatrices (mat.toSparse (mat.toDense m)) m
-),
-
-"filter-\(name)": \(
- m = newMatrix (ColumnMajor ()) [[1,2,0],[-1,-4,6],[0,0,3]];
- compareMatrices
- (mat.filter (> 2) m)
- (newMatrix (ColumnMajor ()) [[0,0,0],[0,0,6],[0,0,3]]) and
- compare (mat.density (mat.filter (> 2) m)) (2/9)
-),
-
-"newSparseMatrix-\(name)": \(
- s = mat.newSparseMatrix (ColumnMajor ()) { rows = 2, columns = 3 } [
- { i = 0, j = 0, v = 1 },
- { i = 0, j = 2, v = 2 },
- { i = 1, j = 1, v = 4 },
- ];
- // If there are zeros in the entries list, they should not end up
- // in the sparse data
- t = mat.newSparseMatrix (ColumnMajor ()) { rows = 2, columns = 3 } [
- { i = 0, j = 0, v = 1 },
- { i = 0, j = 2, v = 0 },
- { i = 1, j = 1, v = 4 },
- ];
- // Any out-of-range or non-integer i, j should be ignored too
- u = mat.newSparseMatrix (ColumnMajor ()) { rows = 2, columns = 3 } [
- { i = -1, j = 0, v = 1 },
- { i = 0, j = 4, v = 3 },
- { i = 1, j = 1.5, v = 4 },
- ];
- compare (mat.density s) (3/6) and
- compare (mat.density t) (2/6) and
- compareMatrices s (newMatrix (RowMajor ()) [[1,0,2],[0,4,0]]) and
- compareMatrices t (newMatrix (RowMajor ()) [[1,0,0],[0,4,0]]) and
- compareMatrices u (newMatrix (RowMajor ()) [[0,0,0],[0,0,0]])
-),
-
-"enumerate-\(name)": \(
- m = newMatrix (ColumnMajor ()) [[1,2,0],[-1,-4,6],[0,0,3]];
- all = [
- { i = 0, j = 0, v = 1 },
- { i = 1, j = 0, v = 2 },
- { i = 2, j = 0, v = 0 },
- { i = 0, j = 1, v = -1 },
- { i = 1, j = 1, v = -4 },
- { i = 2, j = 1, v = 6 },
- { i = 0, j = 2, v = 0 },
- { i = 1, j = 2, v = 0 },
- { i = 2, j = 2, v = 3 },
- ];
- sortEntries =
- sortBy do a b:
- if a.i == b.i then a.j < b.j else a.i < b.i fi
- done;
- compare
- (sortEntries (mat.enumerate m))
- (sortEntries
- (if mat.isSparse? m then filter do d: d.v != 0 done all else all fi));
-),
-
-]);
-
-colhash = makeTests "column-dense" id;
-rowhash = makeTests "row-dense" mat.flipped;
-sparsecolhash = makeTests "column-sparse" mat.toSparse;
-
-// there are two possible orders for constructing a sparse row-major
-// matrix from a dense col-major one, so test them both:
-sparserowhash1 = makeTests "row-sparse-a" (mat.toSparse . mat.flipped);
-sparserowhash2 = makeTests "row-sparse-b" (mat.flipped . mat.toSparse);
-
-all = [:];
-for [ colhash, rowhash, sparsecolhash, sparserowhash1, sparserowhash2 ] do h:
- for (keys h) do k: all[k] := h[k] done;
-done;
-
-all is hash boolean>;
-
-
diff -r 4b104ef8d110 -r 20903ee53719 may/matrix/type.yeti
--- a/may/matrix/type.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-
-module may.matrix.type;
-
-load may.vector.type;
-
-typedef opaque matrix =
- DenseRows. array | // array of rows
- DenseCols. array | // array of columns
- SparseCSR. {
- .values is vector,
- .indices is array, // column index of each value
- .pointers is array, // offset of first value in each row
- .extent is number // max possible index + 1, i.e. number of columns
- } |
- SparseCSC. {
- .values is vector,
- .indices is array, // row index of each value
- .pointers is array, // offset of first value in each column
- .extent is number // max pointers index + 1, i.e. number of rows
- };
-
-();
-
diff -r 4b104ef8d110 -r 20903ee53719 may/plot.yeti
--- a/may/plot.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,132 +0,0 @@
-module may.plot;
-
-vec = load may.vector;
-mat = load may.matrix;
-
-{ distinctColour } = load may.plot.colour;
-
-import org.jzy3d.plot3d.builder: Mapper;
-import org.jzy3d.maths: Range, Coord3d;
-import org.jzy3d.chart: Chart, ChartLauncher;
-import org.jzy3d.plot3d.builder: Builder;
-import org.jzy3d.plot3d.builder.concrete: OrthonormalGrid;
-import org.jzy3d.colors.colormaps: ColorMapRainbow;
-import org.jzy3d.colors: ColorMapper, Color;
-import org.jzy3d.plot3d.rendering.canvas: Quality;
-import org.jzy3d.plot3d.rendering.view.modes: ViewPositionMode;
-import org.jzy3d.plot3d.primitives: FlatLine2d, Point;
-
-newMatrixMapper matrix =
- (class MMapper extends Mapper
- double f(double x, double y)
- result = mat.at matrix y x;
- println "f(\(x),\(y)) -> \(result)";
- result
- end;
- new MMapper());
-
-newMatrixLogMapper matrix =
- (class MMapper extends Mapper
- double f(double x, double y)
- ln (mat.at matrix y x)
- end;
- new MMapper());
-
-newMapper mapFunction =
- (class FMapper extends Mapper
- double f(double x, double y)
- mapFunction x y
- end;
- new FMapper());
-
-plotMatrix chart colour matrix is ~Chart -> ~Color -> 'a -> () =
- (mapper = newMatrixMapper matrix;
- size = mat.size matrix;
- //!!! doesn't work if either rows or columns is 1
- xrange = new Range(0, size.columns - 1);
- yrange = new Range(0, size.rows - 1);
- grid = new OrthonormalGrid(xrange, size.columns, yrange, size.rows);
- println "Matrix size: \(size)";
- surface = Builder#buildOrthonormal(grid, mapper); //??? big?
- println "Z Bounds: \(surface#getBounds()#getZmin()) -> \(surface#getBounds()#getZmax())";
- surface#setFaceDisplayed(true);
- surface#setWireframeDisplayed(true);
- surface#setWireframeColor(colour);
- chart#getScene()#getGraph()#add(surface);
- ());
-
-plotCurve chart colour depth curve is ~Chart -> ~Color -> number -> 'a -> () =
- (scene = chart#getScene();
- xx = map (.time) curve;
- yy = map (.value) curve;
- line = new FlatLine2d(xx as ~float[], yy as ~float[], depth);
- line#setWireframeDisplayed(true);
- line#setWireframeColor(colour);
- line#setWireframeWidth(2);
- line#setFaceDisplayed(false);
- scene#add(line);
- chart#getView()#setViewPoint(new Coord3d(0, 0, 0));
-/*
- axes = chart#getAxeLayout();
- axes#setXAxeLabelDisplayed(false);
- axes#setYAxeLabelDisplayed(false);
- axes#setZAxeLabelDisplayed(true);
- axes#setZAxeLabel("unit goes here"); //!!!
- axes#setYTickLabelDisplayed(false);
-*/
- ());
-
-plotSeries chart colour depth { start, step, values } is ~Chart -> ~Color -> number -> 'a -> () =
- (scene = chart#getScene();
- xx = map do i: start + step * i done [0..length values - 1];
- yy = list values;
- line = new FlatLine2d(xx as ~float[], yy as ~float[], depth);
- line#setWireframeDisplayed(true);
- line#setWireframeColor(colour);
- line#setWireframeWidth(2);
- line#setFaceDisplayed(false);
- scene#add(line);
- chart#getView()#setViewPoint(new Coord3d(0, 0, 0));
-/*
- axes = chart#getAxeLayout();
- axes#setXAxeLabelDisplayed(false);
- axes#setYAxeLabelDisplayed(false);
- axes#setZAxeLabelDisplayed(true);
- axes#setZAxeLabel("unit goes here"); //!!!
- axes#setYTickLabelDisplayed(false);
-*/
- ());
-
-plot structures =
- (chart = new Chart(Quality#Nicest);
- var j = 0;
- for structures do s:
- colour = distinctColour j;
- case s of
- Grid matrix:
- plotMatrix chart colour matrix;
- Curve curve:
- plotCurve chart colour j curve;
- Series series:
- plotSeries chart colour j series;
- Vector vector:
- plotSeries chart colour j
- { start = 0, step = 1, values = vec.list vector };
- other:
- failWith "Unable to plot \(other)";
- esac;
- j := j + 1;
- done;
- ChartLauncher#openChart(chart);
- chart);
-
-{
- newMatrixMapper,
- newMatrixLogMapper,
- newMapper,
- plotMatrix,
- plotCurve,
- plotSeries,
- plot,
-}
-
diff -r 4b104ef8d110 -r 20903ee53719 may/plot/chart.yeti
--- a/may/plot/chart.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,241 +0,0 @@
-module may.plot.chart;
-
-{ distinctColour } = load may.plot.colour;
-
-import org.jzy3d.plot3d.text.drawable: DrawableTextBillboard, DrawableTextBitmap;
-import org.jzy3d.maths: Range, Coord3d;
-import org.jzy3d.plot3d.primitives: Shape, HistogramBar, FlatLine2d, Polygon, Quad, Point;
-import org.jzy3d.plot3d.primitives.axes.layout.providers: StaticTickProvider, RegularTickProvider;
-import org.jzy3d.plot3d.primitives.axes.layout.renderers: ITickRenderer, TickLabelMap, IntegerTickRenderer;
-import org.jzy3d.chart: Chart, ChartLauncher;
-import org.jzy3d.plot3d.builder: Builder;
-import org.jzy3d.colors: Color;
-import org.jzy3d.plot3d.rendering.canvas: Quality;
-import org.jzy3d.plot3d.rendering.view.modes: ViewPositionMode;
-
-import javax.imageio: ImageIO;
-
-import java.io: File;
-
-newPercentTickRenderer () =
- (f v = " \(int (v * 100))%";
- class PercentageTickRenderer extends ITickRenderer
- String format(double value) f value,
- String format(float value) f value
- end;
- new PercentageTickRenderer());
-
-newPaddedIntTickRenderer () =
- (f v = " \(int (v + 0.5))";
- class PaddedIntTickRenderer extends ITickRenderer
- String format(double value) f value,
- String format(float value) f value
- end;
- new PaddedIntTickRenderer());
-
-parseOptions options defaultKeys defaultXKeys =
- (parsed = {
- var keys = array (sort defaultKeys),
- var labels = [:],
- var animated = false,
- var normalised = false,
- var unit = "",
- var xkeys = array (sort defaultXKeys),
- var saveTo = "",
- var display = true,
- };
- for options
- \case of
- Keys kk: parsed.keys := array kk;
- XKeys xk: parsed.xkeys := array xk;
- Animated a: parsed.animated := a;
- Normalised n: parsed.normalised := n;
- Unit u: parsed.unit := u;
- Labels ll: parsed.labels := ll;
- SaveTo file: parsed.saveTo := file;
- Display d: parsed.display := d;
- esac;
- if empty? parsed.labels then
- parsed.labels := mapIntoHash id id parsed.keys
- fi;
- parsed);
-
-newChart opts =
- (quality = Quality#Fastest;
- quality#setAnimated(opts.animated);
-// if opts.display then
- new Chart(quality);
-// else
-// new Chart(quality, "offscreen,640,640");
-// fi);
- );
-
-showChart opts chart is 'a -> ~Chart -> () =
- (if opts.display then
- \() ChartLauncher#openChart(chart);
- else
- \() ChartLauncher#openStaticChart(chart);
- fi;
- if opts.saveTo != "" then
- \() chart#screenshot(opts.saveTo);
- fi);
-
-plotBarChart options values =
- (opts = parseOptions options (keys values) [];
- chart = newChart opts;
- var n = length opts.keys;
- scene = chart#getScene();
- ticks = new double[n];
- tickLabels = new TickLabelMap();
- var i = 0;
- var x = n - i - 1;
- total = sum (map do k: if k in values then values[k] else 0 fi done opts.keys);
- for opts.keys do k:
- bar = new HistogramBar();
- v = if k in values then values[k] else 0 fi;
- v = if opts.normalised and total > 0 then v / total else v fi;
- bar#setData(new Coord3d(x, 0, 0), v, 0.45, distinctColour i);
- bar#setWireframeDisplayed(false);
- scene#add(bar);
- ticks[i] := i;
- tickLabels#register(x, opts.labels[k]);
- i := i + 1;
- x := x - 1;
- done;
- chart#getView()#setViewPoint(new Coord3d(pi/2, 0, 0));
- axes = chart#getAxeLayout();
- axes#setXAxeLabelDisplayed(false);
- axes#setYAxeLabelDisplayed(false);
- axes#setZAxeLabelDisplayed(true);
- if opts.normalised then
- axes#setZAxeLabel("");
- axes#setZTickRenderer(newPercentTickRenderer ());
- else
- axes#setZAxeLabel(opts.unit);
- fi;
- axes#setXTickProvider(new StaticTickProvider(ticks));
- axes#setXTickRenderer(tickLabels);
- axes#setYTickLabelDisplayed(false);
- showChart opts chart);
-
-plotLines options values =
- (opts = parseOptions options (keys values) (keys values[head (keys values)]);
- chart = newChart opts;
- scene = chart#getScene();
- n = length opts.xkeys;
- var z = 0;
- for opts.keys do k:
- v = values[k];
- x = new float[n];
- y = new float[n];
- var i = 0;
- for opts.xkeys do xk:
- x[i] := i;
- y[i] := if xk in v then v[xk] else 0 fi;
- i := i + 1;
- done;
- line = new FlatLine2d(x, y, z);
- line#setWireframeDisplayed(true);
- line#setWireframeColor(distinctColour z);
- line#setWireframeWidth(2);
- line#setFaceDisplayed(false);
- scene#add(line);
- z := z + 1;
- done;
- chart#getView()#setViewPoint(new Coord3d(0, 0, 0));
- axes = chart#getAxeLayout();
- axes#setXAxeLabelDisplayed(false);
- axes#setYAxeLabelDisplayed(false);
- axes#setZAxeLabelDisplayed(true);
- axes#setZAxeLabel(opts.unit);
- axes#setYTickLabelDisplayed(false);
- showChart opts chart);
-
-stack keys xkeys values normalised =
- (stacked = mapIntoHash id \(mapIntoHash id \{ y0 = 0, y1 = 0 } xkeys) keys;
- prev = mapIntoHash id \0 xkeys;
- valueOf k xk =
- if k in values and xk in values[k]
- then values[k][xk] else 0
- fi;
- for xkeys do xk:
- total = sum (map do k: valueOf k xk done keys);
- for keys do k:
- value =
- if normalised and total > 0
- then (valueOf k xk) / total
- else (valueOf k xk)
- fi;
- height = prev[xk] + value;
- stacked[k][xk] := { y0 = prev[xk], y1 = height };
- prev[xk] := height;
- done;
- done;
- stacked);
-
-newRect x y0 y1 z colour is number -> number -> number -> number -> ~Color -> 'a =
- (poly = new Quad();
- poly#add(new Point(new Coord3d(x + 0.5, z, y0)));
- poly#add(new Point(new Coord3d(x + 0.5, z, y1)));
- poly#add(new Point(new Coord3d(x - 0.5, z, y1)));
- poly#add(new Point(new Coord3d(x - 0.5, z, y0)));
- poly#setWireframeDisplayed(true);
- poly#setWireframeColor(colour);
- poly#setFaceDisplayed(true);
- poly#setColor(colour);
- poly);
-
-plotStacked options values =
- (opts = parseOptions options (keys values) (keys values[head (keys values)]);
- chart = newChart opts;
- scene = chart#getScene();
- stacked = stack opts.keys opts.xkeys values opts.normalised;
- var z = 0;
- var ty = 0;
- nxk = length opts.xkeys;
- xticks = new double[nxk];
- xtickLabels = new TickLabelMap();
- for [0..nxk - 1] do x:
- xticks[x] := x;
- k = opts.xkeys[x];
- xtickLabels#register(x, if k in opts.labels then opts.labels[k] else k fi);
- done;
- for opts.keys do k:
- ranges = stacked[k];
- c = distinctColour z;
- for [0..nxk - 1] do x:
- xk = opts.xkeys[x];
- rect = newRect x ranges[xk].y0 ranges[xk].y1 z c;
- scene#add(rect);
- done;
- text = new DrawableTextBitmap(opts.labels[k], new Coord3d(-(nxk/5 + 0.5), z, ty), c);
- scene#add(text);
- z := z - 1;
- ty := ty + 0.1;
- done;
- chart#getView()#setViewPoint(new Coord3d(-pi/2, 0, 0));
- axes = chart#getAxeLayout();
- axes#setXAxeLabelDisplayed(false);
- if nxk < 10 then
- axes#setXTickProvider(new StaticTickProvider(xticks));
- fi;
- axes#setXTickRenderer(xtickLabels);
- axes#setYAxeLabelDisplayed(false);
- axes#setZAxeLabelDisplayed(true);
- if opts.normalised then
- axes#setZAxeLabel("");
- axes#setZTickRenderer(newPercentTickRenderer ());
- else
- axes#setZAxeLabel(opts.unit);
- axes#setZTickRenderer(newPaddedIntTickRenderer ());
- fi;
- axes#setYTickLabelDisplayed(false);
- showChart opts chart);
-
-{
- plotBarChart,
- plotLines,
- stack,
- plotStacked,
-}
-
diff -r 4b104ef8d110 -r 20903ee53719 may/plot/colour.yeti
--- a/may/plot/colour.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-module may.plot.colour;
-
-import org.jzy3d.colors: Color;
-
-distinctColours = array [
- { r = 82, g = 126, b = 154 }, // dark steel blue
- { r = 161, g = 54, b = 2 }, // red
- { r = 207, g = 228, b = 148 }, // grey-green
- { r = 21, g = 183, b = 197 }, // light blue
- { r = 251, g = 116, b = 43 }, // light red
- { r = 200, g = 125, b = 234 }, // light purple
- { r = 126, g = 33, b = 28 }, // dried blood!
- { r = 188, g = 13, b = 207 }, // mid purple
-];
-
-distinctColour n =
- if n < 0
- then distinctColour (-n)
- else
- rgb = distinctColours[n % (length distinctColours)];
- new Color(rgb.r / 255.0, rgb.g / 255.0, rgb.b / 255.0);
- fi;
-
-{
- distinctColour
-};
-
diff -r 4b104ef8d110 -r 20903ee53719 may/plot/test/test_plot.yeti
--- a/may/plot/test/test_plot.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-module may.plot.test.test_plot;
-
-ch = load may.plot.chart;
-
-{ compare } = load may.test.test;
-
-[
-
-"stack": \(
- compare
- (ch.stack
- [ "Conrad", "Alice", "Bob" ]
- [ "Jan", "Feb", "Mar" ]
- [ "Alice": [ "Jan": 3, "Mar": 2 ],
- "Bob": [ "Jan": 0, "Feb": 1, "Mar": 4 ],
- "Conrad": [ "Feb": 2, "Mar": 1 ] ]
- false)
- [ "Conrad": [ "Jan": { y0 = 0, y1 = 0 },
- "Feb": { y0 = 0, y1 = 2 },
- "Mar": { y0 = 0, y1 = 1 } ],
- "Alice": [ "Jan": { y0 = 0, y1 = 3 },
- "Feb": { y0 = 2, y1 = 2 },
- "Mar": { y0 = 1, y1 = 3 } ],
- "Bob": [ "Jan": { y0 = 3, y1 = 3 },
- "Feb": { y0 = 2, y1 = 3 },
- "Mar": { y0 = 3, y1 = 7 } ] ]
-),
-
-"stack-normalised": \(
- compare
- (ch.stack
- [ "Conrad", "Alice", "Bob" ]
- [ "Jan", "Feb", "Mar" ]
- [ "Alice": [ "Jan": 3, "Mar": 2 ],
- "Bob": [ "Jan": 0, "Feb": 1, "Mar": 4 ],
- "Conrad": [ "Feb": 2, "Mar": 1 ] ]
- true)
- [ "Conrad": [ "Jan": { y0 = 0, y1 = 0 },
- "Feb": { y0 = 0, y1 = 2/3 },
- "Mar": { y0 = 0, y1 = 1/7 } ],
- "Alice": [ "Jan": { y0 = 0, y1 = 1 },
- "Feb": { y0 = 2/3, y1 = 2/3 },
- "Mar": { y0 = 1/7, y1 = 3/7 } ],
- "Bob": [ "Jan": { y0 = 1, y1 = 1 },
- "Feb": { y0 = 2/3, y1 = 1 },
- "Mar": { y0 = 3/7, y1 = 1 } ] ]
-),
-
-] is hash boolean>;
-
diff -r 4b104ef8d110 -r 20903ee53719 may/signal/autocorrelation.yeti
--- a/may/signal/autocorrelation.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-
-module may.signal.autocorrelation;
-
-acf len series =
- (a = array series;
- map do i:
- sum (map do j:
- a[j] * a[j-i]
- done [i..length a - 1])
- done [0..len-1]);
-
-acfNormalised len series =
- (n = length series;
- map2 do v i: if n == i then 0 else v / (n - i) fi done
- (acf len series) [0..len-1]);
-
-acfUnityNormalised len series =
- (a = acfNormalised len series;
- max = head (sortBy (>) a);
- map (/ max) a);
-
-{
- acf,
- acfNormalised,
- acfUnityNormalised,
-}
-
diff -r 4b104ef8d110 -r 20903ee53719 may/signal/test/test_signal.yeti
--- a/may/signal/test/test_signal.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-module may.signal.test.test_signal;
-
-{ acf, acfNormalised, acfUnityNormalised } = load may.signal.autocorrelation;
-
-{ compare } = load may.test.test;
-
-[
-
-"unnormalised": \(
- compare (acf 12 (array [1,0,0, 1,0,0, 1,0,0, 1,0,0]))
- [4,0,0, 3,0,0, 2,0,0, 1,0,0 ];
-),
-
-"normalised": \(
- compare (acfNormalised 9 (array [1,0,0, 1,0,0, 1,0,0, 1,0,0]))
- [4/12,0,0, 3/9,0,0, 2/6,0,0 ];
-),
-
-"normalisedUnity": \(
- compare (acfUnityNormalised 9 (array [1,0,0, 1,0,0, 1,0,0, 1,0,0]))
- [1,0,0, 1,0,0, 1,0,0 ];
-),
-
-] is hash boolean>;
diff -r 4b104ef8d110 -r 20903ee53719 may/signal/test/test_window.yeti
--- a/may/signal/test/test_window.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,192 +0,0 @@
-
-module may.signal.test.test_window;
-
-win = load may.signal.window;
-vec = load may.vector;
-
-{ compare, compareUsing } = load may.test.test;
-
-functions = [
- Hann () : win.hann,
- Hamming () : win.hamming,
- Blackman () : win.blackman,
- Nuttall () : win.nuttall,
- BlackmanNuttall () : win.blackmanNuttall,
- BlackmanHarris () : win.blackmanHarris,
- Boxcar () : win.boxcar,
- Bartlett () : win.bartlett,
-];
-
-close aa bb = all id (map2 do a b: (abs (a - b) < 0.0001) done aa bb);
-
-isSymmetric a =
- (len = (vec.length a);
- b = if len % 2 == 0 then
- half = vec.slice a 0 (len/2);
- vec.concat [half, vec.reverse half];
- else
- half = vec.slice a 0 (int (len/2));
- mid = vec.slice a (int (len/2)) (int (len/2) + 1);
- vec.concat [half, mid, vec.reverse half];
- fi;
- compareUsing close (vec.list a) (vec.list b));
-
-[
-
-"windowFunction": \(
- all id (map do type:
- f = functions[type];
- a = f 10;
- b = win.windowFunction type [ Symmetric false ] 10;
- compareUsing close (vec.list a) (vec.list b)
- or (eprintln "** failed window type: \(type)"; false)
- done (keys functions));
-),
-
-"symmetric-even": \(
- len = 10;
- all id (map do type:
- f = win.windowFunction type [ Symmetric true ];
- v = f len;
- (compare (vec.length v) len and isSymmetric v) or
- (eprintln "** failed window type: \(type)"; false);
- done (keys functions));
-),
-
-"symmetric-odd": \(
- len = 11;
- all id (map do type:
- f = win.windowFunction type [ Symmetric true ];
- v = f len;
- (compare (vec.length v) len and isSymmetric v) or
- (eprintln "** failed window type: \(type)"; false);
- done (keys functions));
-),
-
-"periodic-even": \(
- // We can't actually test whether a function is periodic, given
- // only one cycle of it! But we can make sure that all but the
- // first sample is symmetric, which is what a symmetric window
- // becomes when generated in periodic mode
- len = 10;
- all id (map do type:
- f = win.windowFunction type [ Symmetric false ];
- v = f len;
- (compare (vec.length v) len and isSymmetric (vec.slice v 1 len)) or
- (eprintln "** failed window type: \(type)"; false);
- done (keys functions));
-),
-
-"periodic-odd": \(
- len = 11;
- all id (map do type:
- f = win.windowFunction type [ Symmetric false ];
- v = f len;
- (compare (vec.length v) len and isSymmetric (vec.slice v 1 len)) or
- (eprintln "** failed window type: \(type)"; false);
- done (keys functions));
-),
-
-"bartlett-periodic": \(
- compare (vec.list (win.bartlett 1)) [1] and
- compare (vec.list (win.bartlett 2)) [0,1] and
- compare (vec.list (win.bartlett 3)) [0,2/3,2/3] and
- compare (vec.list (win.bartlett 4)) [0,1/2,1,1/2]
-),
-
-"bartlett-symmetric": \(
- b = win.windowFunction (Bartlett ()) [ Symmetric true ];
- compare (vec.list (b 1)) [1] and
- compare (vec.list (b 2)) [0,0] and
- compare (vec.list (b 3)) [0,1,0] and
- compare (vec.list (b 4)) [0,2/3,2/3,0] and
- compare (vec.list (b 5)) [0,1/2,1,1/2,0]
-),
-
-"hann": \(
- compareUsing close (vec.list (win.hann 10)) [
- 0, 0.0955, 0.3455, 0.6545, 0.9045,
- 1.0000, 0.9045, 0.6545, 0.3455, 0.0955,
- ] and
- compareUsing close
- (vec.list (win.windowFunction (Hann ()) [ Symmetric true ] 10)) [
- 0, 0.1170, 0.4132, 0.7500, 0.9698,
- 0.9698, 0.7500, 0.4132, 0.1170, 0,
- ]
-),
-
-"hamming": \(
- compareUsing close (vec.list (win.hamming 10)) [
- 0.0800, 0.1679, 0.3979, 0.6821, 0.9121,
- 1.0000, 0.9121, 0.6821, 0.3979, 0.1679,
- ] and
- compareUsing close
- (vec.list (win.windowFunction (Hamming ()) [ Symmetric true ] 10)) [
- 0.0800, 0.1876, 0.4601, 0.7700, 0.9723,
- 0.9723, 0.7700, 0.4601, 0.1876, 0.0800,
- ]
-),
-
-"blackman": \(
- compareUsing close (vec.list (win.blackman 10)) [
- 0, 0.0402, 0.2008, 0.5098, 0.8492,
- 1.0000, 0.8492, 0.5098, 0.2008, 0.0402,
- ] and
- compareUsing close
- (vec.list (win.windowFunction (Blackman ()) [ Symmetric true ] 10)) [
- 0, 0.0509, 0.2580, 0.6300, 0.9511,
- 0.9511, 0.6300, 0.2580, 0.0509, 0,
- ]
-),
-
-"blackmanHarris": \(
- compareUsing close (vec.list (win.blackmanHarris 10)) [
- 0.0001, 0.0110, 0.1030, 0.3859, 0.7938,
- 1.0000, 0.7938, 0.3859, 0.1030, 0.0110,
- ] and
- compareUsing close
- (vec.list (win.windowFunction (BlackmanHarris ()) [ Symmetric true ] 10)) [
- 0.0001, 0.0151, 0.1470, 0.5206, 0.9317,
- 0.9317, 0.5206, 0.1470, 0.0151, 0.0001,
- ]
-),
-
-"dirac": \(
- compareUsing close (vec.list (win.dirac 1)) [ 1 ] and
- compareUsing close (vec.list (win.dirac 5)) [ 0, 0, 1, 0, 0 ] and
- compareUsing close (vec.list (win.dirac 6)) [ 0, 0, 0, 1, 0, 0 ]
-),
-
-"sinc": \(
- compareUsing close (vec.list (win.sinc 1 5)) [ 0, 0, 1, 0, 0 ] and
- compareUsing close (vec.list (win.sinc 2 5)) [ 0, 0, 1, 0, 0 ] and
- compareUsing close (vec.list (win.sinc 4 7)) [ -0.2122, 0, 0.6366, 1, 0.6366, 0, -0.2122 ] and
- compareUsing close (vec.list (win.sinc 4 6)) [ -0.2122, 0, 0.6366, 1, 0.6366, 0 ]
-),
-
-"kaiser": \(
- compareUsing close (vec.list (win.kaiser 4 10)) [
- 0.0885, 0.2943, 0.5644, 0.8216, 0.9789,
- 0.9789, 0.8216, 0.5644, 0.2943, 0.0885
- ] and
- compareUsing close (vec.list (win.kaiser 2.5 11)) [
- 0.3040, 0.5005, 0.6929, 0.8546, 0.9622,
- 1.0000, 0.9622, 0.8546, 0.6929, 0.5005, 0.3040
- ]
-),
-
-"degenerate": \(
- all id (map do type:
- f = functions[type];
- periodic = f;
- symmetric = win.windowFunction type [ Symmetric true ];
- (compare (vec.list (periodic 0)) [] and
- compare (vec.list (periodic 1)) [1] and
- compare (vec.list (symmetric 0)) [] and
- compare (vec.list (symmetric 1)) [1])
- or (eprintln "** failed window type: \(type)"; false)
- done (keys functions));
-),
-
-] is hash boolean>;
-
diff -r 4b104ef8d110 -r 20903ee53719 may/signal/window.yeti
--- a/may/signal/window.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,191 +0,0 @@
-module may.signal.window;
-
-vec = load may.vector;
-bf = load may.vector.blockfuncs;
-
-cosineWindowSymmetric a0 a1 a2 a3 n =
- (n1 = n - 1;
- vec.fromList
- (map do i:
- a0
- - a1 * cos(2 * pi * i / n1)
- + a2 * cos(4 * pi * i / n1)
- - a3 * cos(6 * pi * i / n1)
- done [0..n1]));
-
-cosineWindowPeriodic a0 a1 a2 a3 n =
- (vec.fromList
- (map do i:
- a0
- - a1 * cos(2 * pi * i / n)
- + a2 * cos(4 * pi * i / n)
- - a3 * cos(6 * pi * i / n)
- done [0..n-1]));
-
-cosineWindow a0 a1 a2 a3 sampling n =
- if n < 2 then vec.ones n
- else
- case sampling of
- Symmetric (): cosineWindowSymmetric;
- Periodic (): cosineWindowPeriodic;
- esac a0 a1 a2 a3 n;
- fi;
-
-bartlettSymmetric n =
- if n < 2 then vec.ones n
- else
- vec.fromList
- (n1 = n - 1;
- h = int (n1 / 2);
- concat [
- map do i:
- 2 * i / n1
- done [0..h],
- map do i:
- 2 - (2 * i / n1)
- done [h+1..n1]
- ]);
- fi;
-
-bartlettPeriodic n =
- if n < 2 then vec.ones n
- else
- vec.slice (bartlettSymmetric (n+1)) 0 n;
- fi;
-
-bartlett sampling =
- case sampling of
- Symmetric (): bartlettSymmetric;
- Periodic (): bartlettPeriodic;
- esac;
-
-hann = cosineWindow 0.5 0.5 0.0 0.0;
-hamming = cosineWindow 0.54 0.46 0.0 0.0;
-blackman = cosineWindow 0.42 0.50 0.08 0.0;
-nuttall = cosineWindow 0.355768 0.487396 0.144232 0.012604;
-blackmanNuttall = cosineWindow 0.3635819 0.4891775 0.1365995 0.0106411;
-blackmanHarris = cosineWindow 0.35875 0.48829 0.14128 0.01168;
-
-boxcar n = vec.ones n;
-
-/**
- * Vector of size n with the "middle" sample equal to 1 and all others
- * equal to 0. The middle sample is sample (n-1)/2 for odd n or n/2+1
- * for even n.
- */
-dirac n =
- if n < 2 then vec.ones n
- else
- n0 = if n % 2 == 0 then n/2 else (n-1)/2 fi;
- vec.concat [ vec.zeros n0, vec.ones 1, vec.zeros n0 ]
- fi;
-
-/**
- * Make a vector of size n containing the values of sinc(x) with
- * x=0 in the middle, i.e. at sample (n-1)/2 for odd n or n/2+1 for
- * even n, such that the distance from -pi to pi (the point at
- * which the sinc function first crosses zero, for negative and
- * positive arguments respectively) is p samples. p does not have
- * to be an integer.
- */
-sinc p n =
- if n < 2 then vec.ones n
- else
- n0 = if n % 2 == 0 then n/2 else (n-1)/2 fi;
- n1 = if n % 2 == 0 then n/2 else (n+1)/2 fi;
- half = 1 :: map do i: x = i * 2*pi / p; sin(x) / x done [1..n/2];
- vec.fromList ((take n0 (reverse half)) ++ (take n1 half));
- fi;
-
-kaiser beta n =
- (bes0 x =
- (fact x = fold do x y: x*y done 1 [1..x]; // x!
- ipow a b = fold do x _: x*a done 1 [1..b]; // a^b where b∈ℕ
- square x = x*x;
- term x i =
- case i of
- 0: 1;
- _: (ipow (x/2) (i*2)) / (square (fact i));
- esac;
- sum (map (term x) [0..20]));
- denominator = bes0 beta;
- vec.fromList
- (map do i:
- k = 2*i / (n-1) - 1;
- bes0 (beta * sqrt (1 - k*k)) / denominator;
- done [0..n-1]));
-
-/**
- Kaiser window with sidelobe attenuation of 𝛼 dB and window length n
-*/
-kaiserForAttenuation alpha n =
- (beta =
- if alpha > 50 then 0.1102 * (alpha - 8.7)
- elif alpha > 21 then 0.5842 * Math#pow(alpha - 21, 0.4) + 0.07886 * (alpha - 21)
- else 0
- fi;
- kaiser beta n);
-
-/**
- Kaiser window with sidelobe attenuation of alpha dB and transition
- bandwidth of (tw * samplerate) / (2 * pi)
-*/
-kaiserForTransitionLength alpha tw =
- (m = if alpha > 21
- then Math#ceil((alpha - 7.95) / (2.285 * tw))
- else Math#ceil(5.79 / tw)
- fi;
- kaiserForAttenuation alpha (m+1));
-
-/**
- Kaiser window with sidelobe attenuation of alpha dB and transition
- bandwidth of tbw Hz at the given sampleRate
-*/
-kaiserForBandwidth alpha tbw samplerate =
- kaiserForTransitionLength alpha ((tbw * 2 * pi) / samplerate);
-
-windowFunction type options =
- (var sampling = Periodic ();
- var beta = 4;
- for options \case of
- Symmetric s: if s then sampling := Symmetric () fi;
- Beta b: beta := b;
- esac;
- case type of
- Hann (): hann sampling;
- Hamming (): hamming sampling;
- Blackman (): blackman sampling;
- Nuttall (): nuttall sampling;
- BlackmanNuttall (): blackmanNuttall sampling;
- BlackmanHarris (): blackmanHarris sampling;
- Boxcar (): boxcar;
- Bartlett (): bartlett sampling;
- Kaiser (): kaiser beta;
- esac);
-
-//!!! should use vector. but does anyone use this function anyway? would we use it in framer if it used vector?
-windowed windowFunc frames =
- case frames of
- []: frames;
- _: (first = head frames;
- window = windowFunc (vec.length first);
- map (bf.multiply window) frames);
- esac;
-
-{
-cosineWindow,
-hann = hann (Periodic ()),
-hamming = hamming (Periodic ()),
-blackman = blackman (Periodic ()),
-nuttall = nuttall (Periodic ()),
-blackmanNuttall = blackmanNuttall (Periodic ()),
-blackmanHarris = blackmanHarris (Periodic ()),
-boxcar,
-bartlett = bartlett (Periodic ()),
-dirac,
-sinc,
-kaiser, kaiserForAttenuation, kaiserForTransitionLength, kaiserForBandwidth,
-windowFunction,
-windowed
-};
-
diff -r 4b104ef8d110 -r 20903ee53719 may/stream/audiofile.yeti
--- a/may/stream/audiofile.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,165 +0,0 @@
-
-module may.stream.audiofile;
-
-load may.stream.type;
-
-import javax.sound.sampled:
- AudioSystem, AudioInputStream, AudioFormat, AudioFormat$Encoding,
- AudioFileFormat, AudioFileFormat$Type,
- TargetDataLine, LineListener, Line, Line$Info, Control, Control$Type,
- UnsupportedAudioFileException;
-
-import java.io: File, IOException;
-
-import java.nio: ByteBuffer, ByteOrder;
-
-ch = load may.stream.channels;
-vec = load may.vector;
-mat = load may.matrix;
-
-{ decode } = load may.stream.format;
-
-readInterleaved' { format is ~AudioFormat, aistream is ~AudioInputStream } nframes =
- (channels = format#getChannels();
- bytesPerSample = format#getSampleSizeInBits() / 8;
- bytes = new byte[nframes * channels * bytesPerSample];
- bytesRead = aistream#read(bytes);
- if bytesRead <= 0 then vec.zeros 0;
- else
- n = int(bytesRead / bytesPerSample);
- doubles = new double[n];
- decode { format } bytes doubles n;
- vec.vector doubles;
- fi;
- );
-
-read' { format is ~AudioFormat, aistream is ~AudioInputStream } n =
- (b = readInterleaved' { format, aistream } n;
- channels = format#getChannels();
- ch.deinterleaved channels b;
- );
-
-readMono' { format is ~AudioFormat, aistream is ~AudioInputStream } n =
- (b = readInterleaved' { format, aistream } n;
- channels = format#getChannels();
- ch.deinterleaved 1 (ch.mixedDownFromInterleaved channels b);
- );
-
-// Note, all this assumes aistream is non-blocking (i.e. available()
-// is to the end of file). Our stream interface does support
-// indefinite and infinite streams, but audiofile doesn't yet.
-
-available' { format is ~AudioFormat, aistream is ~AudioInputStream } =
- aistream#available() / ((format#getSampleSizeInBits() / 8) * format#getChannels());
-
-close' { aistream is ~AudioInputStream } =
- aistream#close();
-
-openWithReader reader ch name is 'a -> number -> string -> stream =
- (f = new File(name);
- aistream = AudioSystem#getAudioInputStream(f);
- format = aistream#getFormat();
- len = available' { format, aistream }; // at start of stream
- syncd = synchronized aistream;
- {
- get position () = syncd \(len - available' { aistream, format }),
- get channels () = if ch == 0 then format#getChannels() else ch fi,
- get sampleRate () = format#getSampleRate(),
- get available () = syncd \(Known (available' { aistream, format })),
- get finished? () = syncd \(not (aistream#available() > 0)),
- read = syncd \(reader { aistream, format }),
- close () = syncd \(close' { aistream }),
- });
-
-/**
- * Open the named audio file and return a stream object for
- * reading from it. May throw UnsupportedAudioFileException
- * or IOException.
- */
-open = openWithReader read' 0;
-
-/**
- * Open the named audio file and return a stream object that
- * reads mono samples from it, using
- * may.stream.channels.mixedDown to mix channels as necessary.
- * May throw UnsupportedAudioFileException or IOException.
- */
-openMono = openWithReader readMono' 1;
-
-/**
- * Open an audio file with the given name and write all available
- * samples to it from the given stream, returning the number of
- * sample frames written. The stream must have finite length.
- *
- * Example:
- * : str = audiofile.open "in.wav";
- * : n = audiofile.write str "out.wav";
- */
-write str name is stream -> string -> number =
- (var tot = 0;
- case str.available of
- Infinite (): failWith "Cannot write infinite stream to file";
- _: ()
- esac;
- class StreamAdapter extends TargetDataLine
- int read(byte[] bytes, int off, int len)
- (bb = ByteBuffer#wrap(bytes, off, len);
- bb#order(ByteOrder#LITTLE_ENDIAN);
- m = str.read (len / (str.channels * 2));
- tot := mat.width m;
- for [0..mat.width m - 1] do i:
- for [0..str.channels - 1] do c:
- v = int (32767 * mat.at m c i);
- v = if v < -32768 then -32768
- elif v > 32767 then 32767
- else v fi;
- \() bb#putShort((i * str.channels + c) * 2, v);
- done
- done;
- str.channels * 2 * mat.width m),
- int available()
- case str.available of
- Known n: str.channels * n * 2;
- other: str.channels * 1024; //!!!???
- esac,
- AudioFormat getFormat()
- new AudioFormat(str.sampleRate, 16, str.channels, true, false),
- void open(),
- void open(AudioFormat format),
- void open(AudioFormat format, int bufferSize),
- void close(),
- void drain(),
- void flush(),
- boolean isOpen() true,
- boolean isActive() true,
- boolean isRunning() true,
- int getFramePosition() str.position,
- long getLongFramePosition() str.position,
- long getMicrosecondPosition() 0,
- int getBufferSize() 0,
- float getLevel() 1.0,
- void start(),
- void stop(),
- void addLineListener(LineListener ll),
- void removeLineListener(LineListener ll),
- Control getControl(Control$Type c),
- Control[] getControls(),
- Line$Info getLineInfo(),
- boolean isControlSupported(Control$Type c) false,
- end;
- f = new File(name);
- n = AudioSystem#write(new AudioInputStream(new StreamAdapter()),
- AudioFileFormat$Type#WAVE, f);
- str.close ();
- n); //!!! this is wrong, should be number of frames (is number of bytes?)
-
-{
- open,
- openMono,
- write,
-} as {
- open is string -> stream,
- openMono is string -> stream,
- write is stream -> string -> number,
-}
-
diff -r 4b104ef8d110 -r 20903ee53719 may/stream/channels.yeti
--- a/may/stream/channels.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,116 +0,0 @@
-
-module may.stream.channels;
-
-vec = load may.vector;
-mat = load may.matrix;
-bf = load may.vector.blockfuncs;
-
-load may.vector.type;
-
-//!!! "internal" vector function to retrieve data for read-only
-// purposes without copying
-//!!! need to have this in some internal-use module
-raw =
- (raw' v is ~double[] -> ~double[] = v;
- raw' as vector -> ~double[]);
-
-interleaved m =
- ({ columns, rows } = (mat.size m);
- if rows == 1 then
- mat.getRow 0 m
- else
- v = new double[columns * rows];
- for [0..rows-1] do row:
- for [0..columns-1] do col:
- v[col * rows + row] := mat.at m row col;
- done;
- done;
- vec.vector v;
- fi);
-
-deinterleaved channels b =
- if channels == 1 then
- mat.newRowVector b
- else
- rows = (vec.length b) / channels;
- vv = array (map \(new double[rows]) [0..channels-1]);
- v = raw b;
- for [0..rows-1] do row:
- for [0..channels-1] do col:
- vv[col][row] := v[channels * row + col];
- done
- done;
- mat.newMatrix (RowMajor ()) (map vec.vector vv);
- fi;
-
-mixedDown m = //!!!doc: average, not sum
- ({ columns, rows } = (mat.size m);
- if rows < 1 or columns < 1 then
- vec.zeros 0
- elif rows == 1 then
- mat.getRow 0 m
- else
- bf.divideBy rows (bf.add (mat.asRows m));
- fi);
-
-mixedDownFromInterleaved channels b =
- if channels == 1 then
- b;
- else
- columns = ((vec.length b) / channels);
- v = raw b;
- v' = new double[columns];
- for [0..channels-1] do row:
- for [0..columns-1] do col:
- v'[col] := v'[col] + v[col * channels + row];
- done;
- done;
- bf.divideBy channels (vec.vector v');
- fi;
-
-mixedFromInterleavedTo targetChannels channels b =
- if targetChannels == channels then
- b;
- elif targetChannels == 1 then
- mixedDownFromInterleaved channels b;
- else
- columns = ((vec.length b) / channels);
- v = raw b;
- v' = new double[columns * targetChannels];
- for [0..targetChannels-1] do target:
- for [0..columns-1] do col:
- if target < channels then
- v'[col * targetChannels + target] := v[col * channels + target];
- elif channels == 1 and target == 1 then
- v'[col * targetChannels + target] := v[col * channels];
- fi
- done
- done;
- vec.vector v';
- fi;
-
-mixedTo targetChannels m =
- if targetChannels == mat.height m then // n -> n: pass unmodified
- m
- elif targetChannels == 1 then // n -> 1: mix down
- deinterleaved 1 (mixedDown m)
- elif mat.height m == 1 then // 1 -> n: copy to all channels
- mat.newMatrix (RowMajor ()) (map \(mat.getRow 0 m) [1..targetChannels])
- else // n -> m: truncate or add zeros
- mat.resizedTo { rows = targetChannels, columns = mat.width m } m
- fi;
-
-mixedAndInterleavedTo targetChannels m =
- if targetChannels == 1 then
- mixedDown m
- else
- interleaved (mixedTo targetChannels m);
- fi;
-
-//!!! some of these names are terrible
-{
- interleaved, deinterleaved,
- mixedDown, mixedDownFromInterleaved,
- mixedFromInterleavedTo, mixedTo, mixedAndInterleavedTo
-}
-
diff -r 4b104ef8d110 -r 20903ee53719 may/stream/filter.yeti
--- a/may/stream/filter.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,570 +0,0 @@
-
-module may.stream.filter;
-
-mat = load may.matrix;
-ch = load may.stream.channels;
-vec = load may.vector;
-bf = load may.vector.blockfuncs;
-fr = load may.stream.framer;
-win = load may.signal.window;
-fft = load may.transform.fft;
-syn = load may.stream.syntheticstream;
-cplx = load may.complex;
-
-load may.stream.type;
-load may.vector.type;
-load may.matrix.type;
-
-//!!! todo: synchronized for everything with state assignment
-
-minDurationOf d1 d2 =
- case d1 of
- Known a:
- case d2 of
- Known b: Known (min a b);
- Unknown (): Unknown ();
- Infinite (): Known a;
- esac;
- Unknown ():
- case d2 of
- Known b: Known b;
- Unknown (): Unknown ();
- Infinite (): Unknown ();
- esac;
- Infinite ():
- d2;
- esac;
-
-withDuration nsamples s = //!!! should nsamples be a time in seconds? (no)
- (var pos = 0;
- s with
- {
- get position () = pos,
- get available () = Known (nsamples - pos),
- get finished? () = not (nsamples > pos),
- read count =
- (n = min count (nsamples - pos);
- pos := pos + n;
- if not s.finished? then
- mat.resizedTo { rows = s.channels, columns = n } (s.read n);
- else
- mat.zeroMatrix { columns = n, rows = s.channels }
- fi),
- });
-
-delayedBy nsamples s = //!!! doc: nsamples may be -ve
- (var prepos = 0;
- zeros n = mat.toRowMajor
- (prepos := prepos + n;
- mat.zeroMatrix { rows = s.channels, columns = n });
- delay =
- if nsamples < 0 then
- \0 (s.read (-nsamples));
- else
- nsamples
- fi;
- {
- get position () =
- if prepos < delay then prepos
- elif s.position + nsamples < 0 then 0
- else s.position + nsamples
- fi,
- get channels () = s.channels,
- get sampleRate () = s.sampleRate,
- get available () =
- case s.available of
- Known a: Known (a + delay - prepos);
- other: other
- esac,
- get finished? () = (prepos >= delay) and s.finished?,
- read count =
- if prepos >= delay then s.read count
- elif prepos + count < delay then zeros count
- else
- nleft = delay - prepos;
- left = zeros nleft;
- right = s.read (count - nleft);
- mat.concat (Horizontal ()) [left, right];
- fi,
- close = s.close
- });
-
-scaledBy factor s =
- s with
- {
- read count = mat.scaled factor (s.read count);
- };
-
-inverted s = //!!! todo: test
- s with
- {
- read count =
- (m = s.read count;
- if mat.empty? m then m
- else mat.difference (mat.zeroMatrix (mat.size m)) m
- fi)
- };
-
-//!!! poor name, confusion with mixed, but consistent with channels.yeti
-mixedTo targetChannels s =
- s with
- {
- get channels () = targetChannels,
- read count = ch.mixedTo targetChannels (s.read count),
- };
-
-//!!! what should happen if we mix or multiplex a finite-length and an
-//infinite-length stream? or even two streams with differing finite
-//lengths? write tests for this. At the moment the resulting stream
-//has duration equal to the shortest source stream and finishes as
-//soon as any one of them finishes
-
-sum' streams =
- (mix m1 m2 =
- (sz = { rows = max (mat.height m1) (mat.height m2),
- columns = min (mat.width m1) (mat.width m2) };
- if sz.columns == 0 then
- mat.zeroSizeMatrix ()
- else
- mat.sum (mat.resizedTo sz m1) (mat.resizedTo sz m2);
- fi);
- {
- get position () = head (sort (map (.position) streams)),
- get channels () = head (sortBy (>) (map (.channels) streams)),
- get sampleRate () = (head streams).sampleRate, //!!! document this
- get available () =
- fold do dur s: minDurationOf dur s.available done (Infinite ()) streams,
- get finished? () = any id (map (.finished?) streams),
- read count =
- (readTo acc ss count =
- case ss of
- first::rest:
- part = first.read count;
- case acc of
- Some m:
- readTo (Some (mix m part)) rest count;
- None ():
- readTo (Some part) rest count;
- esac;
- _: acc;
- esac;
- case readTo (None ()) streams count of
- None (): mat.zeroSizeMatrix ();
- Some m: m;
- esac),
- close () = for streams do s: s.close() done,
- });
-
-mean streams = //!!! todo: test
- scaledBy (1 / (length streams)) (sum' streams);
-
-difference s1 s2 = //!!! todo: test
- sum' [ s1, inverted s2 ];
-
-multiplexed streams =
- {
- get position () = head (sort (map (.position) streams)), // can differ after EOS
- get channels () = sum (map (.channels) streams),
- get sampleRate () = (head streams).sampleRate, //!!! document this
- get available () =
- fold do dur s: minDurationOf dur s.available done (Infinite ()) streams,
- get finished? () = any id (map (.finished?) streams),
- read count =
- (outs = map do s: s.read count done streams;
- minlen = head (sort (map mat.width outs));
- outs = map do m:
- mat.resizedTo { rows = mat.height m, columns = minlen } m
- done outs;
- mat.concat (Vertical ()) outs
- ),
- close () = for streams do s: s.close() done,
- };
-
-repeated s =
- // There is no way to reset a stream (as in principle, they might
- // be "live") so we can't read from the same stream repeatedly --
- // we have to cache its output and then repeat that. This is a
- // little tricky to do efficiently without knowing how long the
- // stream is (in general) or how much is going to be requested at
- // a time.
- if s.available == Infinite () then s
- else
- var pos = 0;
- var cache = mat.zeroSizeMatrix ();
- chunks = array [];
- cachedPartsFor count =
- (start = pos % (mat.width cache);
- avail = (mat.width cache) - start;
- if avail >= count then
- pos := pos + count;
- [mat.columnSlice cache start (start + count)]
- else
- pos := pos + avail;
- mat.columnSlice cache start (start + avail) ::
- cachedPartsFor (count - avail);
- fi);
- readFromCache count =
- (if (mat.width cache) == 0 then
- cache := mat.concat (Horizontal ()) (list chunks);
- clearArray chunks;
- fi;
- if (mat.width cache) == 0 then
- cache
- else
- mat.concat (Horizontal ()) (cachedPartsFor count);
- fi);
- s with
- {
- get position () = pos,
- get available () = Infinite (),
- get finished? () = false,
- read count =
- if s.finished? then
- readFromCache count
- else
- part = s.read count;
- len = (mat.width part);
- push chunks part;
- pos := pos + len;
- if len == count then part
- else
- mat.concat (Horizontal ())
- [part, readFromCache (count - len)];
- fi;
- fi,
- }
- fi;
-
-duplicated copies s =
-//!!! doc fact that original s cannot be used independently of this afterward
-// (so maybe name is misleading?)
- array if copies < 2 then map \s [1..copies];
- else
- pos = [:];
- lowtide () = head (sort (map (at pos) (keys pos)));
- var hightide = 0;
- var cache = mat.zeroSizeMatrix ();
- syncd = synchronized pos;
- advance i n =
- (formerLow = lowtide ();
- pos[i] := pos[i] + n;
- encroachment = lowtide () - formerLow;
- if encroachment > 0 then
- cache := mat.columnSlice cache encroachment (mat.width cache);
- fi);
- map do instance:
- pos[instance] := 0;
- {
- get position () = syncd \(pos[instance]),
- get channels () = syncd \(s.channels),
- get sampleRate () = syncd \(s.sampleRate),
- get available () = syncd
- \(case s.available of
- Known av: Known (av + (hightide - pos[instance]));
- other: other;
- esac),
- get finished? () = syncd
- \(if not s.finished? then false
- else pos[instance] >= hightide
- fi),
- read count = syncd
- \(ready = hightide - pos[instance];
- if s.finished? and ready <= 0
- then mat.zeroSizeMatrix ()
- else
- if count > ready then
- more = s.read (count - ready);
- cache := mat.concat (Horizontal ()) [cache, more];
- hightide := hightide + (mat.width more);
- fi;
- offset = pos[instance] - (lowtide ());
- chunk = mat.columnSlice cache offset (offset + count);
- advance instance (mat.width chunk);
- chunk;
- fi),
- close () = syncd
- \(delete pos instance;
- if empty? pos then
- s.close ()
- fi),
- }
- done [1..copies];
- fi;
-
-zeroPaddedFreqFrames framesize channels =
- // take a multi-channel stream, convert to a series of list of
- // complex frequency-domain frames calculated from input padded to
- // framesize*2, in which each frame contains all channels one
- // after another (not interleaved) [because we lack a complex
- // matrix type]
- (forwardTransform = fft.realForward (framesize * 2);
- do stream:
- padded =
- (map (mat.resizedTo { rows = channels, columns = framesize * 2 })
- (fr.frames { framesize, hop = framesize } stream));
- map do fr: concat (map forwardTransform (mat.asRows fr)) done padded;
- done);
-
-doFastConvolve irframes sigframes =
- (var history = [];
- irlen = length irframes;
- n = length (head irframes);
- map do sigf:
- history := take irlen (sigf::history);
- fold do cc1 cc2: map2 cplx.add cc1 cc2 done
- (list (cplx.zeros n))
- (map2 do irf histf:
- map2 cplx.multiply irf histf;
- done irframes history);
- done sigframes);
-
-splitInto n fr =
- (parts = splitAt n fr;
- if empty? parts.snd then [parts.fst]
- else parts.fst :: splitInto n parts.snd;
- fi);
-
-fastConvolvedWith ir framesize s =
- // prerequisite: ir and s have same number of channels
- (framer = zeroPaddedFreqFrames framesize (mat.height ir);
- ch = s.channels;
- irfr = framer (syn.precalculated 1 ir); // rate arg is irrelevant here
- sigfr = framer s;
- inverseTransform = fft.realInverse (framesize * 2);
- extended = sigfr ++
- map do _: list (cplx.zeros (ch * (framesize + 1))) done
- [1..length irfr-1];
- cframes = doFastConvolve irfr extended;
- rframes = (mat.zeroMatrix { rows = ch, columns = framesize * 2}) ::
- map do fr:
- mat.newMatrix (RowMajor ())
- (map inverseTransform (splitInto (framesize+1) fr))
- done cframes;
- fr.streamOverlapping s.sampleRate
- { framesize = framesize * 2, hop = framesize, window = win.boxcar }
- rframes;
-);
-
-plainConvolvedWith ir s =
- // prerequisite: ir and s have same number of channels
- (var history = mat.toRowMajor
- (mat.zeroMatrix { rows = s.channels, columns = mat.width ir - 1 });
- s with
- {
- get finished? () =
- s.finished? and (mat.empty? history),
- get available () =
- case s.available of
- Known n: Known (n + mat.width history);
- other: other;
- esac,
- read count =
- (// Example: The IR is four samples long; we have three
- // samples in history; two samples are available to read
- // before the stream runs out. That means we can return
- // up to five samples. Caller requests 6.
- signal = s.read count; // -> two samples
- siglen = mat.width signal; // -> 2
- histlen = mat.width history; // -> 3
- convlen = min count (siglen + histlen); // -> 5
- input = mat.resizedTo { rows = s.channels, columns = convlen }
- signal; // example input now 5 samples, of which 2 are signal
- output = array (map \(new double[convlen]) [1..s.channels]);
- for [0..s.channels - 1] do ch:
- for [0..mat.width input - 1] do i:
- for [0..mat.width ir - 1] do j:
- v =
- if i >= j then
- mat.at input ch (i - j)
- else
- ix = mat.width ir + i - j - 1;
- if ix >= histlen then
- 0
- else
- mat.at history ch ix
- fi
- fi;
- output[ch][i] := output[ch][i] + v * (mat.at ir ch j);
- done;
- done;
- done;
- // Remove from history a number of samples equal to the
- // number returned; add to it a number equal to the number
- // read from source
- extended = mat.concat (Horizontal ()) [history, signal]; // -> 5
- newlen = (histlen + siglen) - convlen; // -> 0
- w = mat.width extended;
- history := mat.columnSlice extended (w - newlen) w;
- mat.newMatrix (RowMajor ()) (map vec.vector output)),
- });
-
-nextPowerOfTwo n =
- (nextPowerOfTwo' p n =
- if p >= n then p
- else nextPowerOfTwo' (p * 2) n
- fi;
- nextPowerOfTwo' 1 n);
-
-//!!! todo: partial application so as to preprocess ir (in fast convolution case)
-convolvedWith options ir s = //!!! cheap mono thing here
- (if mat.height ir != s.channels then
- failWith "Signal stream and IR must have same number of channels (\(s.channels) != \(mat.height ir))"
- fi;
- var type = Fast ();
- var framesize = nextPowerOfTwo (mat.width ir);
- for options \case of
- Fast s: if s then type := Fast () else type := Plain () fi;
- Framesize n: framesize := nextPowerOfTwo n;
- esac;
- case type of
- Fast ():
- fastConvolvedWith ir framesize s;
- Plain ():
- plainConvolvedWith ir s;
- esac);
-
-/**
- Produce a sinc window of width equal to zc zero crossings per half,
- with n samples from peak to first zero crossing, multiplied by a
- Kaiser window with attenuation 𝛼 dB
- */
-kaiserSincWindow zc n 𝛼 =
- (sw = win.sinc (n*2) (n*zc*2 + 1);
- kw = win.kaiserForAttenuation 𝛼 (vec.length sw);
- bf.multiply sw kw);
-
-bandpassed f0 f1 attenuation bandwidth s =
- (rate = s.sampleRate;
- kw = win.kaiserForBandwidth attenuation bandwidth rate;
- filterLength = vec.length kw;
- // First arg to sinc is the complete cycle length for the cutoff frequency
- idealFor freq =
- bf.scaled (2 * freq / rate)
- (win.sinc (rate / freq) filterLength);
- idealBandpass =
- if f1 < rate/2 then
- if f0 > 0 then bf.subtract (idealFor f1) (idealFor f0)
- else idealFor f1
- fi
- else
- if f0 > 0 then bf.subtract (win.dirac filterLength) (idealFor f0)
- else win.dirac filterLength;
- fi;
- fi;
- filter = bf.multiply idealBandpass kw;
- filtered = convolvedWith [Framesize 1024]
- (mat.newMatrix (RowMajor ()) (map \filter [1..s.channels]))
- s;
- delayedBy (- (int (filterLength / 2))) filtered);
-
-lowpassed f attenuation bandwidth s =
- bandpassed 0 f attenuation bandwidth s;
-
-highpassed f attenuation bandwidth s =
- bandpassed f (s.sampleRate/2) attenuation bandwidth s;
-
-spaced mult s = //!!! mult must be an integer [how to enforce this??]
- (spaceToNext pos = case (pos % mult) of 0: 0; n: (mult - n) esac;
- readWithoutPadding n =
- (toRead = int Math#ceil(n / mult);
- source = s.read toRead;
- targetWidth = min n (mult * mat.width source);
- mat.newMatrix (ColumnMajor ())
- (map do i:
- if i % mult == 0 then
- mat.getColumn (int (i / mult)) source
- else
- vec.zeros s.channels
- fi
- done [0..targetWidth-1]));
- var pos = 0;
- s with
- {
- get position () = pos,
- get available () =
- case s.available of
- Known n: Known ((spaceToNext pos) + (n * mult));
- other: other
- esac,
- read n =
- (sp = spaceToNext pos;
- result = mat.toRowMajor
- (mat.concat (Horizontal ())
- [mat.zeroMatrix { rows = s.channels, columns = min sp n },
- readWithoutPadding (max (n - sp) 0)]);
- pos := pos + mat.width result;
- result),
- });
-
-interpolated factor s = //!!! factor must be an integer [how to enforce this??]
- if factor == 1 then s
- else
- nzc = 13;
- attenuation = 80;
- filter = kaiserSincWindow nzc factor attenuation;
- out = delayedBy (- (nzc * factor))
- (convolvedWith [Framesize 1024]
- (mat.newMatrix (RowMajor ()) (map \filter [1..s.channels]))
- (spaced factor s));
- out with { get sampleRate () = s.sampleRate * factor };
- fi;
-
-picked frac s = //!!! frac must be an integer [how to enforce this??]
- s with
- {
- get position () = int Math#ceil(s.position / frac),
- get available () =
- case s.available of
- Known n: Known (int Math#ceil(n / frac));
- other: other
- esac,
- read n =
- (m = s.read (n*frac);
- obtained = int Math#ceil((mat.width m) / frac);
- mat.flipped
- (mat.newMatrix (ColumnMajor ())
- (map do c: mat.getColumn (c*frac) m done [0..obtained-1])))
- };
-
-decimated factor s = //!!! factor must be an integer [how to enforce this??]
- if factor == 1 then s
- else
- nzc = 13;
- attenuation = 80;
- filter = kaiserSincWindow nzc factor attenuation;
- filtered =
- (convolvedWith [Framesize 1024]
- (mat.newMatrix (RowMajor ()) (map \filter [1..s.channels])) s);
- out = scaledBy (1/factor)
- (delayedBy (- nzc)
- (picked factor filtered));
- out with { get sampleRate () = s.sampleRate / factor };
- fi;
-
-gcd a b = (c = a % b; if c == 0 then b else gcd b c fi);
-
-//!!! slow -- can't in this form be used for e.g. 44100 -> 48000 conversion
-resampledTo rate s =
- (g = gcd rate s.sampleRate;
- println "orig = \(s.sampleRate), target = \(rate), g = \(g), up = \(rate/g), down = \(s.sampleRate/g)";
- decimated (s.sampleRate / g) (interpolated (rate/g) s));
-
-{
- withDuration is number -> stream -> stream,
- delayedBy is number -> stream -> stream,
- scaledBy is number -> stream -> stream,
- inverted is stream -> stream,
- mixedTo is number -> stream -> stream,
- sum is list -> stream = sum',
- mean is list -> stream,
- difference is stream -> stream -> stream,
- multiplexed is list -> stream,
- repeated is stream -> stream,
- duplicated is number -> stream -> array,
- convolvedWith is list -> matrix -> stream -> stream,
- kaiserSincWindow,
- lowpassed, bandpassed, highpassed,
- spaced, interpolated, decimated, picked,
- resampledTo,
-}
-
diff -r 4b104ef8d110 -r 20903ee53719 may/stream/format.yeti
--- a/may/stream/format.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-
-module may.stream.format;
-
-import java.nio: ByteBuffer, ByteOrder;
-
-import javax.sound.sampled:
- AudioFormat, AudioFormat$Encoding,
- UnsupportedAudioFileException;
-
-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("Audio 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);
-
-{
- decode
-}
-
diff -r 4b104ef8d110 -r 20903ee53719 may/stream/framer.yeti
--- a/may/stream/framer.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,214 +0,0 @@
-
-module may.stream.framer;
-
-/**
- * Framer expresses a stream (or a file) as a lazy list of (possibly
- * overlapping) frames of data.
- */
-
-vec = load may.vector;
-bf = load may.vector.blockfuncs;
-af = load may.stream.audiofile;
-win = load may.signal.window;
-fft = load may.transform.fft;
-mat = load may.matrix;
-ch = load may.stream.channels;
-
-load may.stream.type;
-load may.matrix.type;
-load may.vector.type;
-load may.complex.type;
-
-//!!! todo: synchronized for everything with state assignment
-
-blockList framesize stream =
- if stream.finished? then
- stream.close ();
- []
- else
- mat.resizedTo { rows = stream.channels, columns = framesize }
- (stream.read framesize)
- :. \(blockList framesize stream);
- fi;
-
-overlappingBlockList size hop stream valid buffer =
- (
- m = stream.read hop;
- obtained = mat.width m;
-
- // Retain framesize - hop samples from old buffer, add hop samples
- // (zero-padded if necessary) just read
- buffer = map2
- do buf row:
- vec.concat
- [vec.slice buf hop size,
- vec.resizedTo hop (mat.getRow row m)];
- done buffer [0..stream.channels-1];
-
- // Number of "valid" elements (not tail-end zero-padding) left in buffer
- remaining = valid - (hop - obtained);
-
- if remaining <= 0 then
- stream.close ();
- [];
- else
- mat.newMatrix (RowMajor ()) 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 (map \(vec.zeros framesize) [0..stream.channels-1]);
- fi;
-
-streamContiguous rate framesize frames =
- (var remaining = frames;
- var buffered = mat.zeroSizeMatrix ();
- var position = 0;
- channels = mat.height (head frames); // so we don't need to keep a head ptr
- {
- get position () = position,
- get channels () = channels,
- get sampleRate () = rate,
- get finished? () = empty? remaining and mat.empty? buffered,
- get available () =
- // Don't take length of frames -- we don't want to lose laziness.
- // If the caller cares that much, they can measure frames themselves
- if empty? remaining then
- Known (mat.width buffered)
- else
- Unknown ()
- fi,
- read count =
- (framesFor samples acc =
- if samples <= 0 or empty? remaining then
- reverse acc
- else
- this = head remaining;
- remaining := tail remaining;
- framesFor (samples - mat.width this) (this :: acc)
- fi;
- source = mat.concat (Horizontal ()) (framesFor count [buffered]);
- toReturn = mat.columnSlice source 0 count;
- position := position + mat.width toReturn;
- buffered := mat.columnSlice source count (mat.width source);
- toReturn),
- close = \(),
- });
-
-overlapAdd overlap frames =
- (ola fr pending acc =
- case fr of
- first::rest:
- (w = mat.width pending;
- pre = mat.columnSlice pending 0 (w - overlap);
- added = mat.sum first
- (mat.resizedTo (mat.size first)
- (mat.columnSlice pending (w - overlap) w));
- ola rest added (pre::acc));
- _:
- reverse (pending::acc);
- esac;
- case frames of
- first::rest:
- mat.concat (Horizontal ()) (ola rest first []);
- _:
- mat.zeroSizeMatrix ();
- esac);
-
-streamOverlapping rate { framesize, hop, window } frames =
- (var remaining = frames;
- var buffered = mat.zeroSizeMatrix ();
- var position = 0;
-
- factor = hop / (framesize/2);
- w = bf.scaled factor (window framesize);
- channels = mat.height (head frames); // so we don't need to keep a head ptr
-
- syncd = synchronized remaining;
-
- finished' () = syncd \(empty? remaining and mat.empty? buffered);
-
- read' count =
- (framesFor samples acc =
- if samples <= 0 or empty? remaining then
- reverse acc
- else
- this = mat.resizedTo { columns = framesize, rows = channels }
- (mat.newMatrix (RowMajor ())
- (map (bf.multiply w) (mat.asRows (head remaining))));
- remaining := tail remaining;
- framesFor (samples - hop) (this::acc)
- fi;
- source = overlapAdd (framesize - hop)
- (framesFor count [buffered]);
- buffered := mat.columnSlice source count (mat.width source);
- mat.columnSlice source 0 count);
-
- // lose initial padding
- \() (read' (framesize - hop));
-
- {
- get position () = syncd \(position),
- get channels () = channels,
- get sampleRate () = rate,
- get finished? () = finished' (),
- get available () = if finished' () then Known 0 else Unknown () fi,
- read count = syncd
- \(data = read' count;
- position := position + mat.width data;
- data),
- close = \(),
- });
-
-//!!! doc: convert frames back to a stream
-streamed rate { framesize, hop } frames =
- if framesize == hop then
- streamContiguous rate framesize frames
- else
- streamOverlapping rate
- { framesize, hop, window = win.hann } // periodic, not symmetric
- frames
- fi;
-
-monoFrames params stream =
- map ch.mixedDown (frames params stream);
-
-windowedFrames { framesize, hop, window } stream =
- (win = window framesize;
- map (bf.multiply win) (monoFrames { framesize, hop } stream));
-
-frequencyDomainFrames parameters stream =
- (f = fft.realForward parameters.framesize;
- map f (windowedFrames parameters stream));
-
-typedef params = { framesize is number, hop is number };
-typedef winparams = { framesize is number, hop is number, window is number -> vector };
-
-{
- frames is params -> stream -> list,
- monoFrames is params -> stream -> list,
- windowedFrames is winparams -> stream -> list,
- frequencyDomainFrames is winparams -> stream -> list >,
-
- framesOfFile parameters filename =
- frames parameters (af.open filename),
-
- monoFramesOfFile parameters filename =
- monoFrames parameters (af.open filename),
-
- windowedFramesOfFile parameters filename =
- windowedFrames parameters (af.open filename),
-
- frequencyDomainFramesOfFile parameters filename =
- frequencyDomainFrames parameters (af.open filename),
-
- overlapAdd is number -> list -> matrix,
-
- streamed is number -> params -> list -> stream,
- streamOverlapping is number -> winparams -> list -> stream,
-}
-
diff -r 4b104ef8d110 -r 20903ee53719 may/stream/playback.yeti
--- a/may/stream/playback.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-
-module may.stream.playback;
-
-vec = load may.vector;
-mat = load may.matrix;
-af = load may.stream.audiofile;
-ch = load may.stream.channels;
-
-import javax.sound.sampled:
- AudioSystem, AudioFormat, AudioFormat$Encoding, SourceDataLine;
-
-import java.nio: ByteBuffer, ByteOrder;
-
-playBlock line b is ~SourceDataLine -> 'a -> () =
- (len = vec.length b;
- samples = vec.primitive 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;
- \() 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,
- sampleRate = rate,
- channels,
- play = play line,
- close () = (line#drain(); line#close()),
- });
-
-playMatrix rate m =
- (channels = mat.height m;
- line = open { rate, channels };
- line.play [ch.mixedAndInterleavedTo channels m];
- line.close());
-
-playStream stream =
- (line = open { rate = stream.sampleRate, channels = stream.channels };
- blocksize = 10240;
- not stream.finished? loop
- line.play [(ch.mixedAndInterleavedTo line.channels
- (stream.read blocksize))];
- line.close();
- stream.close());
-
-playFile filename =
- playStream (af.open filename);
-
-{
- open, playMatrix, playStream, playFile,
-}
-
diff -r 4b104ef8d110 -r 20903ee53719 may/stream/record.yeti
--- a/may/stream/record.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-
-module may.stream.record;
-
-vec = load may.vector;
-mat = load may.matrix;
-af = load may.stream.audiofile;
-ch = load may.stream.channels;
-
-import javax.sound.sampled:
- AudioSystem, AudioFormat, AudioFormat$Encoding,
- TargetDataLine, DataLine, DataLine$Info;
-
-{ decode } = load may.stream.format;
-
-recordInterleaved seconds line is number -> ~TargetDataLine -> 'a =
- (format = line#getFormat();
- channels = format#getChannels();
- nframes = seconds * format#getSampleRate();
- bytesPerSample = format#getSampleSizeInBits() / 8;
- bytes = new byte[nframes * channels * bytesPerSample];
- bytesRead = line#read(bytes, 0, length bytes);
- if bytesRead <= 0 then vec.zeros 0;
- else
- n = int(bytesRead / bytesPerSample);
- doubles = new double[n];
- decode { format } bytes doubles n;
- vec.vector doubles;
- fi;
- );
-
-recordFor seconds line is number -> ~TargetDataLine -> 'a =
- ch.deinterleaved line#getFormat()#getChannels()
- (recordInterleaved seconds line);
-
-open { rate, channels } =
- (format = new AudioFormat(AudioFormat$Encoding#PCM_SIGNED, rate, 16,
- channels, channels * 2, rate, false);
- info = new DataLine$Info
- (Class#forName("javax.sound.sampled.TargetDataLine"), format);
- if not AudioSystem#isLineSupported(info) then
- failWith "Unable to open 16-bit PCM line at rate \(rate), \(channels) channels";
- fi;
- line = AudioSystem#getLine(info) unsafely_as ~TargetDataLine;
- line#open(format);
- line#start();
- {
- line,
- sampleRate = rate,
- channels,
- norec recordFor t = recordFor t line,
- close () = (line#drain(); line#close()),
- });
-
-recordFor seconds =
- (line = open { rate = 44100, channels = 2 }; //!!! or system defaults?
- data = line.recordFor seconds;
- line.close ();
- {
- sampleRate = line.sampleRate,
- channels = line.channels,
- data
- });
-
-{
- open,
- recordFor
-}
-
-
diff -r 4b104ef8d110 -r 20903ee53719 may/stream/syntheticstream.yeti
--- a/may/stream/syntheticstream.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,97 +0,0 @@
-
-module may.stream.syntheticstream;
-
-ch = load may.stream.channels;
-vec = load may.vector;
-mat = load may.matrix;
-
-load may.vector.type;
-load may.matrix.type;
-load may.stream.type;
-
-generated sampleRate generator =
- (// generator takes sample number as arg, returns number in -1,+1 range
- var position = 0;
- {
- get position () = position,
- get channels () = 1,
- get sampleRate () = sampleRate,
- get available () = Infinite (),
- get finished? () = false,
- read count = ch.deinterleaved 1
- (result = new double[count];
- for [0..count-1] do i:
- result[i] := generator (position + i)
- done;
- position := position + count;
- vec.vector result),
- close = \(),
- });
-
-sinusoid rate freq =
- generated rate (sin . (* (2 * pi * freq / rate)));
-
-whiteNoise rate =
- generated rate \((Math#random() * 2.0) - 1.0);
-
-silent rate =
- generated rate \0;
-
-precalculatedMono rate data =
- (n = vec.length data;
- var position = 0;
- {
- get position () = position,
- get channels () = 1,
- get sampleRate () = rate,
- get available () = Known (n - position),
- get finished? () = not (n > position),
- read count = ch.deinterleaved 1
- (rc = min count (n - position);
- result = vec.slice data position (position + rc);
- position := position + rc;
- result),
- close = \(),
- });
-
-precalculated rate data =
- (n = mat.width data;
- c = mat.height data;
- var position = 0;
- {
- get position () = position,
- get channels () = c,
- get sampleRate () = rate,
- get available () = Known (n - position),
- get finished? () = not (n > position),
- read count =
- (rc = min count (n - position);
- result = mat.columnSlice data position (position + rc);
- position := position + rc;
- result),
- close = \(),
- });
-
-empty rate channels = // degenerate stream with no data in it, occasionally useful
- {
- get position () = 0,
- get channels () = channels,
- get sampleRate () = rate,
- get available () = Known 0,
- get finished? () = true,
- read count = mat.zeroSizeMatrix (),
- close = \(),
- };
-
-{
- generated is number -> (number -> number) -> stream,
- precalculated is number -> matrix -> stream,
- precalculatedMono is number -> vector -> stream,
- sinusoid is number -> number -> stream,
- whiteNoise is number -> stream,
- silent is number -> stream,
- empty is number -> number -> stream,
-}
-
-
-
diff -r 4b104ef8d110 -r 20903ee53719 may/stream/test/audiofile_reference.yeti
--- a/may/stream/test/audiofile_reference.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-
-module may.stream.test.audiofile_reference;
-
-syn = load may.stream.syntheticstream;
-filt = load may.stream.filter;
-
-//!!! docs from turbot
-
-pulseChannel rate =
- (pulseFreq = 2;
- pulseWidth = 0.01 * rate;
- generator i =
- (pulseNo = int ((i * pulseFreq) / rate);
- index = (i * pulseFreq) - (rate * pulseNo);
- if index < pulseWidth then
- s = 1.0 - abs(pulseWidth/2 - index) / (pulseWidth/2);
- if pulseNo % 2 != 0 then (-s) else s fi
- else 0
- fi);
- syn.generated rate generator);
-
-referenceChannels rate =
- (leftovers rate n =
- (syn.generated rate \(n / 20) :. \(leftovers rate (n+1)));
- syn.sinusoid rate 600 :: pulseChannel rate :: leftovers rate 2);
-
-afReference rate channels =
- filt.withDuration (2 * rate)
- (filt.multiplexed (take channels (referenceChannels rate)));
-
-{
- afReference
-}
-
-
diff -r 4b104ef8d110 -r 20903ee53719 may/stream/test/speedtest.yeti
--- a/may/stream/test/speedtest.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-
-program may.stream.test.speedtest;
-
-af = load may.stream.audiofile;
-vec = load may.vector;
-mat = load may.matrix;
-filt = load may.stream.filter;
-
-import java.lang: StackOverflowError;
-
-{ compare, compareUsing, time } = load may.test.test;
-
-before = System#currentTimeMillis();
-
-str = time "open audio file" \(af.openMono "shortish.wav");
-
-conv = time "prepare convolve" \(filt.convolvedWith [ Framesize 256 ] (mat.newRowVector (vec.fromList [ 1, 0.8, 0.5 ])) str);
-
-var len = 0;
-time "read convolve" \((not conv.finished?) loop len := len + mat.width (conv.read 65536));
-
-str.close ();
-
-println "Done";
-
-after = System#currentTimeMillis();
-
-println "Total time: \(after - before)ms for \(len) audio sample frames [\(int (len / ((after - before) / 1000))) fps]";
-
-
-before = System#currentTimeMillis();
-
-str = time "open audio file" \(af.openMono "shortish.wav");
-
-res = time "prepare resample" \(filt.resampledTo 44100 str);
-
-len := 0;
-time "read resampled" \((not res.finished?) loop len := len + mat.width (res.read 65536));
-
-str.close ();
-
-println "Done";
-
-after = System#currentTimeMillis();
-
-println "Total time: \(after - before)ms for \(len) audio sample frames [\(int (len / ((after - before) / 1000))) fps]";
-
diff -r 4b104ef8d110 -r 20903ee53719 may/stream/test/test_audiofile.yeti
--- a/may/stream/test/test_audiofile.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,98 +0,0 @@
-
-module may.stream.test.test_audiofile;
-
-af = load may.stream.audiofile;
-vec = load may.vector;
-mat = load may.matrix;
-bf = load may.vector.blockfuncs;
-
-ref = load may.stream.test.audiofile_reference;
-
-{ compare, compareUsing } = load may.test.test;
-
-testfile name = "may/test/data/\(name).wav";
-
-float n is number -> number =
- // round number to float precision (for comparison with floats)
- (arr = new float[1];
- arr[0] := n;
- arr[0]);
-
-readAll stream =
- case stream.available of
- Known n: stream.read n;
- _: failWith "Known-duration stream required";
- esac;
-
-bitdepthComparator depth =
- (slack = if depth == 8 then 0.015 else 0.001 fi;
- do test ref: abs (test - ref) < slack done);
-
-maxOf m =
- bf.max (vec.fromList (map bf.max (mat.asRows m)));
-
-testReferenceFile rate channels bitdepth =
- (test = readAll (af.open (testfile "\(rate)-\(channels)-\(bitdepth)"));
- ref = readAll (ref.afReference rate channels);
- if mat.equalUnder (bitdepthComparator bitdepth) test ref then
- true
- else
- println "** peak difference: \(maxOf (mat.difference ref test))";
- for [0..mat.height test - 1] do ch:
- if mat.equalUnder (bitdepthComparator bitdepth)
- (mat.newRowVector (mat.getRow ch test))
- (mat.newRowVector (mat.getRow ch ref)) then
- println " channel \(ch): ok";
- else
- println " channel \(ch): not ok";
-// This isn't really simple enough!
-/*!!!
- seriesFor m =
- Series {
- start = 0,
- step = 1,
- values = take 1000 (vec.list (mat.getRow ch m))
- };
- \() (pl.plot (map seriesFor [ test, ref, mat.scaled 10000 (mat.difference test ref) ]));
-*/
- fi;
- done;
- false
- fi);
-
-[
-
-"20samples-open": \(
- f = af.open (testfile "20samples");
- compare f.position 0 and
- compare f.channels 1 and
- compare f.sampleRate 44100 and
- compare f.available (Known 20) and
- compare f.finished? false and
- ( f.close () ; true )
-),
-
-"20samples-read": \(
- all id (map do opener:
- f = opener (testfile "20samples");
- first15 = f.read 15;
- last5 = f.read 10;
- compare (mat.size first15) { rows = 1, columns = 15 } and
- compare (mat.size last5) { rows = 1, columns = 5 } and
- compare (vec.list (mat.getRow 0 first15))
- [ float (32767/32768),0,0,0,0,0,0,0,0,0,0,0,0,0,0 ] and
- compare (vec.list (mat.getRow 0 last5)) [ 0,0,0,0,-1 ] and
- ( f.close () ; true )
- done [ af.open, af.openMono ]);
-),
-
-"8000-1-8": \(
- testReferenceFile 8000 1 8;
-),
-
-"44100-2-16": \(
- testReferenceFile 44100 2 16;
-),
-
-] is hash boolean>
-
diff -r 4b104ef8d110 -r 20903ee53719 may/stream/test/test_channels.yeti
--- a/may/stream/test/test_channels.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-
-module may.stream.test.test_channels;
-
-ch = load may.stream.channels;
-mat = load may.matrix;
-vec = load may.vector;
-
-{ compare, compareUsing } = load may.test.test;
-
-newMatrix data = mat.newMatrix (ColumnMajor ()) (map vec.fromList data);
-
-compareBlocks b1 b2 =
- compare (vec.list b1) (vec.list b2);
-
-[
-
-"interleaved": \(
- compareBlocks (ch.interleaved (newMatrix [[1,4],[2,5],[3,6]]))
- (vec.fromList [1,4,2,5,3,6]) and
- compareBlocks (ch.interleaved (newMatrix [[1],[2],[3]]))
- (vec.fromList [1,2,3])
-),
-
-"deinterleaved": \(
- compareUsing mat.equal (ch.deinterleaved 2 (vec.fromList [1,4,2,5,3,6]))
- (newMatrix [[1,4],[2,5],[3,6]]) and
- compareUsing mat.equal (ch.deinterleaved 1 (vec.fromList [1,2,3]))
- (newMatrix [[1],[2],[3]])
-),
-
-"mixedDown": \(
- compareBlocks (ch.mixedDown (newMatrix [[1,4],[2,5],[3,6]]))
- (vec.fromList [5/2,7/2,9/2]) and
- compareBlocks (ch.mixedDown (newMatrix []))
- (vec.fromList [])
-),
-
-"mixedDownFromInterleaved": \(
- compareBlocks (ch.mixedDownFromInterleaved 2 (vec.fromList [1,4,2,5,3,6]))
- (vec.fromList [5/2,7/2,9/2]) and
- compareBlocks (ch.mixedDownFromInterleaved 1 (vec.fromList [1,2,3]))
- (vec.fromList [1,2,3])
-),
-
-"mixedFromInterleavedTo": \(
- compareBlocks (ch.mixedFromInterleavedTo 1 2 (vec.fromList [1,4,2,5,3,6]))
- (vec.fromList [5/2,7/2,9/2]) and
- compareBlocks (ch.mixedFromInterleavedTo 2 2 (vec.fromList [1,4,2,5,3,6]))
- (vec.fromList [1,4,2,5,3,6]) and
- compareBlocks (ch.mixedFromInterleavedTo 3 2 (vec.fromList [1,4,2,5,3,6]))
- (vec.fromList [1,4,0,2,5,0,3,6,0]) and
- compareBlocks (ch.mixedFromInterleavedTo 1 1 (vec.fromList [1,2,3]))
- (vec.fromList [1,2,3]) and
- compareBlocks (ch.mixedFromInterleavedTo 2 1 (vec.fromList [1,2,3]))
- (vec.fromList [1,1,2,2,3,3]) and
- compareBlocks (ch.mixedFromInterleavedTo 3 1 (vec.fromList [1,2,3]))
- (vec.fromList [1,1,0,2,2,0,3,3,0])
-),
-
-"mixedAndInterleavedTo": \(
- compareBlocks (ch.mixedAndInterleavedTo 1 (newMatrix [[1,4],[2,5],[3,6]]))
- (vec.fromList [5/2,7/2,9/2]) and
- compareBlocks (ch.mixedAndInterleavedTo 2 (newMatrix [[1,4],[2,5],[3,6]]))
- (vec.fromList [1,4,2,5,3,6]) and
- compareBlocks (ch.mixedAndInterleavedTo 3 (newMatrix [[1,4],[2,5],[3,6]]))
- (vec.fromList [1,4,0,2,5,0,3,6,0]) and
- compareBlocks (ch.mixedAndInterleavedTo 1 (newMatrix [[1],[2],[3]]))
- (vec.fromList [1,2,3]) and
- compareBlocks (ch.mixedAndInterleavedTo 2 (newMatrix [[1],[2],[3]]))
- (vec.fromList [1,1,2,2,3,3]) and
- compareBlocks (ch.mixedAndInterleavedTo 3 (newMatrix [[1],[2],[3]]))
- (vec.fromList [1,1,1,2,2,2,3,3,3])
-),
-
-] is hash boolean>;
-
-
diff -r 4b104ef8d110 -r 20903ee53719 may/stream/test/test_filter.yeti
--- a/may/stream/test/test_filter.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,831 +0,0 @@
-
-module may.stream.test.test_filter;
-
-vec = load may.vector;
-bf = load may.vector.blockfuncs;
-mat = load may.matrix;
-cplx = load may.complex;
-fft = load may.transform.fft;
-syn = load may.stream.syntheticstream;
-filt = load may.stream.filter;
-
-//pl = load may.plot;//!!!
-
-pl = { plot things = true; };
-
-{ compare, compareUsing } = load may.test.test;
-
-compareClose = compareUsing
- do m1 m2:
- all id (map2 do v1 v2:
- all id (map2 do a b: abs(a - b) < 1e-10 done v1 v2)
- done m1 m2)
- done;
-
-convolutionOptions = [
- [],
- [ Fast false ],
- [ Fast true ],
- [ Fast true, Framesize 1 ],
- [ Fast true, Framesize 2 ],
- [ Fast true, Framesize 16 ]
-];
-
-makeTests name withUnknown =
- (maybeDuration n str =
- // Truncate a stream, but if withUnknown is true, return it
- // with availability Unknown -- so as to test that other
- // filter functions behave correctly even if availability is
- // not known on their underlying streams
- (ts = filt.withDuration n str;
- if withUnknown then
- ts with
- {
- get available () = if ts.finished? then Known 0 else Unknown () fi;
- }
- else
- ts
- fi);
- maybeKnown n =
- if withUnknown then
- Unknown ()
- else
- Known n
- fi;
-[
-
-"truncatedTo-\(name)": \(
- // not using withDuration wrapper above, because we're actually
- // testing filt.withDuration here rather than just generating a
- // stream for use in another test
- str = filt.withDuration 3 (syn.generated 2 id);
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 2 and
- compare str.available (Known 3) and
- compare str.finished? false and
- compare (vec.list (mat.getRow 0 (str.read 4))) [ 0,1,2 ] and
- compare str.position 3 and
- compare str.available (Known 0) and
- compare str.finished? true and
- ( str.close (); true )
-),
-
-"truncatedTo-b-\(name)": \(
- // as above
- str = filt.withDuration 3 (syn.generated 2 id);
- compare str.position 0 and
- compare (vec.list (mat.getRow 0 (str.read 2))) [ 0,1 ] and
- compare str.position 2 and
- compare str.available (Known 1) and
- compare str.finished? false and
- compare (vec.list (mat.getRow 0 (str.read 2))) [ 2 ] and
- compare str.position 3 and
- compare str.available (Known 0) and
- compare str.finished? true and
- ( str.close (); true )
-),
-
-"extendedTo-\(name)": \(
- // not using withDuration wrapper above for the outer call, because
- // we're actually testing filt.withDuration here rather than just
- // generating a stream for use in another test. The inner call
- // does use the wrapper.
- str = filt.withDuration 5 (maybeDuration 3 (syn.generated 2 id));
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 2 and
- compare str.available (Known 5) and
- compare str.finished? false and
- compare (vec.list (mat.getRow 0 (str.read 4))) [ 0,1,2,0 ] and
- compare str.position 4 and
- compare str.available (Known 1) and
- compare str.finished? false and
- compare (vec.list (mat.getRow 0 (str.read 2))) [ 0 ] and
- compare str.position 5 and
- compare str.available (Known 0) and
- compare str.finished? true and
- ( str.close (); true )
-),
-
-"delayedBy-0-3-\(name)": \(
- str = filt.delayedBy 0 (maybeDuration 3 (syn.generated 2 id));
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 2 and
- compare str.available (maybeKnown 3) and
- compare str.finished? false and
- compare (vec.list (mat.getRow 0 (str.read 4))) [ 0,1,2 ] and
- compare str.position 3 and
- compare str.available (Known 0) and
- compare str.finished? true and
- ( str.close (); true )
-),
-
-"delayedBy-0-inf-\(name)": \(
- str = filt.delayedBy 0 (syn.generated 2 id);
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 2 and
- compare str.available (Infinite ()) and
- compare str.finished? false and
- compare (vec.list (mat.getRow 0 (str.read 4))) [ 0,1,2,3 ] and
- compare str.position 4 and
- compare str.available (Infinite ()) and
- compare str.finished? false and
- ( str.close (); true )
-),
-
-"delayedBy-2-3-\(name)": \(
- str = filt.delayedBy 2 (maybeDuration 3 (syn.generated 2 id));
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 2 and
- compare str.available (maybeKnown 5) and
- compare str.finished? false and
- compare (vec.list (mat.getRow 0 (str.read 4))) [ 0,0,0,1 ] and
- compare str.position 4 and
- compare str.available (maybeKnown 1) and
- compare str.finished? false and
- compare (vec.list (mat.getRow 0 (str.read 4))) [ 2 ] and
- compare str.position 5 and
- compare str.available (Known 0) and
- compare str.finished? true and
- ( str.close (); true )
-),
-
-"delayedBy-m2-3-\(name)": \(
- str = filt.delayedBy (-2) (maybeDuration 3 (syn.generated 2 id));
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 2 and
- compare str.available (maybeKnown 1) and
- compare str.finished? false and
- compare (vec.list (mat.getRow 0 (str.read 4))) [ 2 ] and
- compare str.position 1 and
- compare str.available (Known 0) and
- compare str.finished? true and
- ( str.close (); true )
-),
-
-"delayedBy-m4-3-\(name)": \(
- str = filt.delayedBy (-4) (maybeDuration 3 (syn.generated 2 id));
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 2 and
- compare str.available (Known 0) and
- compare str.finished? true and
- //!!! with this and others, need to check that we read an empty matrix after finished (perhaps have a helper function that checks finished properties such as available count as well)
- ( str.close (); true )
-),
-
-"delayedBy-2-3b-\(name)": \(
- str = filt.delayedBy 2 (maybeDuration 3 (syn.generated 2 id));
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 2 and
- compare str.available (maybeKnown 5) and
- compare str.finished? false and
- compare (vec.list (mat.getRow 0 (str.read 1))) [ 0 ] and
- compare str.position 1 and
- compare str.available (maybeKnown 4) and
- compare str.finished? false and
- compare (vec.list (mat.getRow 0 (str.read 4))) [ 0,0,1,2 ] and
- compare str.position 5 and
- compare str.available (Known 0) and
- compare str.finished? true and
- ( str.close (); true )
-),
-
-"delayedBy-2-3c-\(name)": \(
- str = filt.delayedBy 2 (maybeDuration 3 (syn.generated 2 id));
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 2 and
- compare str.available (maybeKnown 5) and
- compare str.finished? false and
- compare (vec.list (mat.getRow 0 (str.read 7))) [ 0,0,0,1,2 ] and
- compare str.position 5 and
- compare str.available (Known 0) and
- compare str.finished? true and
- ( str.close (); true )
-),
-
-"delayedBy-2-inf-\(name)": \(
- str = filt.delayedBy 2 (syn.generated 2 id);
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 2 and
- compare str.available (Infinite ()) and
- compare str.finished? false and
- compare (vec.list (mat.getRow 0 (str.read 2))) [ 0,0 ] and
- compare str.position 2 and
- compare str.finished? false and
- compare (vec.list (mat.getRow 0 (str.read 2))) [ 0,1 ] and
- compare str.position 4 and
- compare str.finished? false and
- compare (vec.list (mat.getRow 0 (str.read 2))) [ 2,3 ] and
- compare str.position 6 and
- compare str.finished? false and
- ( str.close (); true )
-),
-
-"delayedBy-m2-inf-\(name)": \(
- str = filt.delayedBy (-2) (syn.generated 2 id);
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 2 and
- compare str.available (Infinite ()) and
- compare str.finished? false and
- compare (vec.list (mat.getRow 0 (str.read 2))) [ 2,3 ] and
- compare str.position 2 and
- compare str.finished? false and
- ( str.close (); true )
-),
-
-"mixedTo-1-2-\(name)": \(
- str = filt.mixedTo 2 (maybeDuration 3 (syn.generated 2 id));
- compare str.position 0 and
- compare str.channels 2 and
- compare str.sampleRate 2 and
- compare str.available (maybeKnown 3) and
- compare str.finished? false and
- compare (map vec.list (mat.asRows (str.read 4))) [[0,1,2],[0,1,2]] and
- compare str.position 3 and
- compare str.available (Known 0) and
- compare str.finished? true and
- ( str.close (); true )
-),
-
-"mixedTo-2-1-\(name)": \(
- str = filt.mixedTo 1
- (filt.multiplexed
- [maybeDuration 3 (syn.generated 2 id),
- maybeDuration 3 (syn.generated 2 id)]);
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 2 and
- compare str.available (maybeKnown 3) and
- compare str.finished? false and
- compare (map vec.list (mat.asRows (str.read 4))) [[0,1,2]] and
- compare str.position 3 and
- compare str.available (Known 0) and
- compare str.finished? true and
- ( str.close (); true )
-),
-
-"mixedTo-2-3-\(name)": \(
- str = filt.mixedTo 3
- (filt.multiplexed
- [maybeDuration 3 (syn.generated 2 id),
- maybeDuration 3 (syn.generated 2 (+1))]);
- compare str.position 0 and
- compare str.channels 3 and
- compare str.sampleRate 2 and
- compare str.available (maybeKnown 3) and
- compare str.finished? false and
- compare (map vec.list (mat.asRows (str.read 4))) [[0,1,2],[1,2,3],[0,0,0]] and
- compare str.position 3 and
- compare str.available (Known 0) and
- compare str.finished? true and
- ( str.close (); true )
-),
-
-"mixedTo-1-3-\(name)": \(
- str = filt.mixedTo 3 (maybeDuration 3 (syn.generated 2 id));
- compare str.position 0 and
- compare str.channels 3 and
- compare str.sampleRate 2 and
- compare str.available (maybeKnown 3) and
- compare str.finished? false and
- compare (map vec.list (mat.asRows (str.read 4))) [[0,1,2],[0,1,2],[0,1,2]] and
- compare str.position 3 and
- compare str.available (Known 0) and
- compare str.finished? true and
- ( str.close (); true )
-),
-
-"sum-inf-inf-\(name)": \(
- str = filt.sum [syn.generated 2 (2*), syn.generated 2 (0-)];
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 2 and
- compare str.available (Infinite ()) and
- compare str.finished? false and
- compare (map vec.list (mat.asRows (str.read 4))) [[0,1,2,3]] and
- compare str.available (Infinite ()) and
- compare str.position 4 and
- ( str.close (); true )
-),
-
-"sum-inf-trunc-\(name)": \(
- str = filt.sum [syn.generated 2 (2*), maybeDuration 3 (syn.generated 2 (0-))];
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 2 and
- compare str.available (maybeKnown 3) and
- compare str.finished? false and
- compare (map vec.list (mat.asRows (str.read 4))) [[0,1,2]] and
- compare str.available (Known 0) and
- compare str.finished? true and
- compare str.position 3 and
- ( str.close (); true )
-),
-
-"sum-precalc-trunc-\(name)": \(
- str = filt.sum
- [syn.precalculatedMono 2 (vec.fromList [1,2]),
- maybeDuration 3 (syn.generated 2 (0-))];
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 2 and
- compare str.available (maybeKnown 2) and
- compare str.finished? false and
- compare (map vec.list (mat.asRows (str.read 4))) [[1,1]] and
- compare str.available (Known 0) and
- compare str.finished? true and
- compare str.position 2 and
- ( str.close (); true )
-),
-
-"sum-2-1-\(name)": \(
- str = filt.sum
- [syn.precalculatedMono 2 (vec.fromList [1,2]),
- filt.multiplexed [syn.precalculatedMono 2 (vec.fromList [3,4]),
- maybeDuration 3 (syn.generated 2 (0-))]];
- compare str.position 0 and
- compare str.channels 2 and
- compare str.sampleRate 2 and
- compare str.available (maybeKnown 2) and
- compare str.finished? false and
- compare (map vec.list (mat.asRows (str.read 4))) [[4,6], [0,-1]] and
- compare str.available (Known 0) and
- compare str.finished? true and
- compare str.position 2 and
- ( str.close (); true )
-),
-
-"sum-3-\(name)": \(
- str = filt.sum
- [syn.precalculatedMono 2 (vec.fromList [1,2]),
- syn.precalculatedMono 2 (vec.fromList [3,4]),
- maybeDuration 3 (syn.generated 2 (0-))];
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 2 and
- compare str.available (maybeKnown 2) and
- compare str.finished? false and
- compare (map vec.list (mat.asRows (str.read 4))) [[4,5]] and
- compare str.available (Known 0) and
- compare str.finished? true and
- compare str.position 2 and
- ( str.close (); true )
-),
-
-"multiplexed-inf-inf-\(name)": \(
- str = filt.multiplexed [syn.generated 2 id, syn.generated 2 (0-)];
- compare str.position 0 and
- compare str.channels 2 and
- compare str.sampleRate 2 and
- compare str.available (Infinite ()) and
- compare str.finished? false and
- compare (map vec.list (mat.asRows (str.read 4)))
- [[0,1,2,3], [0,-1,-2,-3]] and
- compare str.available (Infinite ()) and
- compare str.position 4 and
- ( str.close (); true )
-),
-
-"multiplexed-inf-trunc-\(name)": \(
- str = filt.multiplexed [syn.generated 2 id, maybeDuration 3 (syn.generated 2 (0-))];
- compare str.position 0 and
- compare str.channels 2 and
- compare str.sampleRate 2 and
- compare str.available (maybeKnown 3) and
- compare str.finished? false and
- compare (map vec.list (mat.asRows (str.read 4))) [[0,1,2], [0,-1,-2]] and
- compare str.available (Known 0) and
- compare str.finished? true and
- compare str.position 3 and
- ( str.close (); true )
-),
-
-"multiplexed-precalc-trunc-\(name)": \(
- str = filt.multiplexed
- [syn.precalculatedMono 2 (vec.fromList [1,2]),
- maybeDuration 3 (syn.generated 2 (0-))];
- compare str.position 0 and
- compare str.channels 2 and
- compare str.sampleRate 2 and
- compare str.available (maybeKnown 2) and
- compare str.finished? false and
- compare (map vec.list (mat.asRows (str.read 4))) [[1,2], [0,-1]] and
- compare str.available (Known 0) and
- compare str.finished? true and
- compare str.position 2 and
- ( str.close (); true )
-),
-
-"multiplexed-2-1-\(name)": \(
- str = filt.multiplexed
- [syn.precalculatedMono 2 (vec.fromList [1,2]),
- filt.multiplexed [syn.precalculatedMono 2 (vec.fromList [3,4]),
- maybeDuration 3 (syn.generated 2 (0-))]];
- compare str.position 0 and
- compare str.channels 3 and
- compare str.sampleRate 2 and
- compare str.available (maybeKnown 2) and
- compare str.finished? false and
- compare (map vec.list (mat.asRows (str.read 4))) [[1,2], [3,4], [0,-1]] and
- compare str.available (Known 0) and
- compare str.finished? true and
- compare str.position 2 and
- ( str.close (); true )
-),
-
-"multiplexed-2-1b-\(name)": \(
- str = filt.multiplexed
- [syn.precalculatedMono 2 (vec.fromList [1,2]),
- syn.precalculatedMono 2 (vec.fromList [3,4]),
- maybeDuration 3 (syn.generated 2 (0-))];
- compare str.position 0 and
- compare str.channels 3 and
- compare str.sampleRate 2 and
- compare str.available (maybeKnown 2) and
- compare str.finished? false and
- compare (map vec.list (mat.asRows (str.read 4))) [[1,2], [3,4], [0,-1]] and
- compare str.available (Known 0) and
- compare str.finished? true and
- compare str.position 2 and
- ( str.close (); true )
-),
-
-"repeated-2-\(name)": \(
- str = filt.repeated
- (syn.precalculatedMono 2 (vec.fromList [1,2,3]));
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 2 and
- compare str.available (Infinite ()) and
- compare str.finished? false and
- compare (map vec.list (mat.asRows (str.read 1))) [[1]] and
- compare str.position 1 and
- compare (map vec.list (mat.asRows (str.read 2))) [[2,3]] and
- compare str.position 3 and
- compare (map vec.list (mat.asRows (str.read 3))) [[1,2,3]] and
- compare str.position 6 and
- compare (map vec.list (mat.asRows (str.read 5))) [[1,2,3,1,2]] and
- compare (map vec.list (mat.asRows (str.read 9))) [[3,1,2,3,1,2,3,1,2]] and
- compare (map vec.list (mat.asRows (str.read 2))) [[3,1]] and
- compare str.available (Infinite ()) and
- compare str.finished? false and
- compare str.position 22 and
- ( str.close (); true )
-),
-
-"duplicated-1-\(name)": \(
- original = maybeDuration 3 (syn.precalculatedMono 2 (vec.fromList [1,2,3]));
- sn = filt.duplicated 1 original;
- str = (head sn);
- compare (length sn) 1 and
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 2 and
- compare str.available (maybeKnown 3) and
- compare str.finished? false and
- compare (map vec.list (mat.asRows (str.read 1))) [[1]] and
- compare str.position 1 and
- compare str.available (maybeKnown 2) and
- compare (map vec.list (mat.asRows (str.read 3))) [[2,3]] and
- compare str.position 3 and
- compare str.finished? true and
- compare str.available (Known 0) and
- ( str.close (); true )
-),
-
-"duplicated-2-\(name)": \(
- original = maybeDuration 3 (syn.precalculatedMono 2 (vec.fromList [1,2,3]));
- sn = filt.duplicated 2 original;
- s1 = (head sn);
- s2 = (head (tail sn));
-
- compare (length sn) 2 and
-
- compare s1.position 0 and
- compare s1.channels 1 and
- compare s1.sampleRate 2 and
- compare s1.available (maybeKnown 3) and
- compare s1.finished? false and
-
- compare s2.position 0 and
- compare s2.channels 1 and
- compare s2.sampleRate 2 and
- compare s2.available (maybeKnown 3) and
- compare s2.finished? false and
-
- compare (map vec.list (mat.asRows (s1.read 1))) [[1]] and
- compare s1.position 1 and
- compare s1.available (maybeKnown 2) and
- compare s2.position 0 and
- compare s2.available (maybeKnown 3) and
-
- compare (map vec.list (mat.asRows (s2.read 2))) [[1,2]] and
- compare s1.position 1 and
- compare s1.available (maybeKnown 2) and
- compare s2.position 2 and
- compare s2.available (maybeKnown 1) and
-
- compare (map vec.list (mat.asRows (s1.read 3))) [[2,3]] and
- compare s1.position 3 and
- compare s1.finished? true and
- compare s1.available (Known 0) and
- compare s2.position 2 and
- compare s2.finished? false and
- compare s2.available (Known 1) and // when one is known, so is the other
-
- compare (map vec.list (mat.asRows (s1.read 3))) [] and
-
- compare (map vec.list (mat.asRows (s2.read 1))) [[3]] and
- compare s1.position 3 and
- compare s1.finished? true and
- compare s2.position 3 and
- compare s2.finished? true and
-
- ( s1.close (); s2.close() ; true )
-),
-
-"convolvedImpulse-\(name)": \(
- all id
- (map do opts:
- ir = mat.newRowVector (vec.fromList [1,0,-1,0]);
- signal = maybeDuration 4
- (syn.precalculatedMono 2 (vec.fromList [1,0,0,0]));
- c = filt.convolvedWith opts ir signal;
- compareClose (map vec.list (mat.asRows (c.read 4))) [[ 1,0,-1,0 ]] and
- ( c.close (); true )
- done convolutionOptions);
-),
-
-"convolvedImpulse2-\(name)": \(
- all id
- (map do opts:
- ir = mat.newRowVector (vec.fromList [8,6,4,2]);
- signal = maybeDuration 4
- (syn.precalculatedMono 2 (vec.fromList [1,0,0,0]));
- c = filt.convolvedWith opts ir signal;
- compareClose (map vec.list (mat.asRows (c.read 4))) [[ 8,6,4,2 ]] and
- ( c.close (); true )
- done convolutionOptions);
-),
-
-"convolvedImpulse-multichannel-\(name)": \(
- all id
- (map do opts:
- ir = mat.newMatrix (RowMajor ())
- (map vec.fromList [[0,0,0,1],[8,6,4,2],[1,0,-1,0]]);
- signal = maybeDuration 4
- (syn.precalculated 2
- (mat.newMatrix (RowMajor ())
- (map vec.fromList [[1,1,0,0],[0,1,1,0],[0,0,1,1]])));
- c = filt.convolvedWith opts ir signal;
- compareClose (map vec.list (mat.asRows (c.read 4)))
- [[0,0,0,1],[0,8,14,10],[0,0,1,1]] and
- ( c.close (); true )
- done convolutionOptions);
-),
-
-"convolvedWith-\(name)": \(
- all id
- (map do opts:
- fast = not (opts == [ Fast false ]);
- ir = mat.newRowVector (vec.fromList [1,2,3,4,5]);
- signal = maybeDuration 3
- (syn.precalculatedMono 2 (vec.fromList [10,20,30]));
- c = filt.convolvedWith opts ir signal;
- compare c.position 0 and
- compare c.channels 1 and
- compare c.sampleRate 2 and
- (fast or compare c.available (maybeKnown 7)) and
- compareClose (map vec.list (mat.asRows (c.read 3)))
- [[ 10*1,
- 20*1 + 10*2,
- 30*1 + 20*2 + 10*3 ]] and
- (fast or compare c.available (Known 4)) and
- compare c.finished? false and
- compareClose (map vec.list (mat.asRows (c.read 4)))
- [[ 30*2 + 20*3 + 10*4,
- 30*3 + 20*4 + 10*5,
- 30*4 + 20*5,
- 30*5 ]] and
- (fast or (compare c.available (Known 0) and
- compare c.finished? true)) and
- ( c.close (); true )
- done convolutionOptions);
-),
-
-"spaced-\(name)": \(
- original = maybeDuration 3 (syn.precalculatedMono 2 (vec.fromList [1,2,3]));
- str = filt.spaced 3 original;
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 2 and
- compare str.available (maybeKnown 9) and
- compare str.finished? false and
- compare (map vec.list (mat.asRows (str.read 4))) [[1,0,0,2]] and
- compare str.position 4 and
- compare str.available (maybeKnown 5) and
- compare str.finished? false and
- compare (map vec.list (mat.asRows (str.read 1))) [[0]] and
- compare str.position 5 and
- compare str.available (maybeKnown 4) and
- compare str.finished? false and
- compare (map vec.list (mat.asRows (str.read 10))) [[0,3,0,0]] and
- compare str.position 9 and
- compare str.available (Known 0) and
- compare str.finished? true and
- ( str.close (); true )
-),
-
-"picked-\(name)": \(
- original = maybeDuration 8 (syn.precalculatedMono 2 (vec.fromList [1,2,3,4,5,6,7,8]));
- str = filt.picked 3 original;
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 2 and
- compare str.available (maybeKnown 3) and
- compare str.finished? false and
- compare (map vec.list (mat.asRows (str.read 1))) [[1]] and
- compare str.position 1 and
- compare str.available (maybeKnown 2) and
- compare str.finished? false and
- compare (map vec.list (mat.asRows (str.read 3))) [[4,7]] and
- compare str.position 3 and
- compare str.available (Known 0) and
- compare str.finished? true and
- ( str.close (); true )
-),
-
-//!!! still no tests for filters with multi-channel inputs
-
-]);
-
-knowns = makeTests "known" false;
-unknowns = makeTests "unknown" true;
-
-logSpectrumFrom output n =
- (outdata = mat.getRow 0 (output.read n);
- spectrum = cplx.magnitudes (fft.realForward n outdata);
- array (map do v: 20 * Math#log10(v) done (vec.list spectrum)));
-
-makeDiracStream rate n =
- syn.generated rate do x: if x == int(n/2) then 1 else 0 fi done;
-
-filtering = [
-
-"interpolated-sine": \(
- // Interpolating a sinusoid should give us a sinusoid
- //!!! only beyond half the filter length
- sinusoid = syn.sinusoid 8 2; // 2Hz sine sampled at 8Hz: [ 0, 1, 0, -1 ] etc
- input = filt.withDuration 16 sinusoid;
- output = filt.interpolated 2 input;
- result = output.read 32;
- reference = syn.sinusoid 16 2;
- expected = reference.read 32;
- compareOutputs a b = compareClose
- (map vec.list (mat.asRows a)) (map vec.list (mat.asRows b));
- compareOutputs result expected;
-),
-
-"decimated-sine": \(
- // Decimating a sinusoid should give us a sinusoid
- //!!! only beyond half the filter length
- sinusoid = syn.sinusoid 32 2; // 2Hz sine sampled at 16Hz
- input = filt.withDuration 64 sinusoid;
- output = filt.decimated 2 input;
- result = output.read 32;
- reference = syn.sinusoid 16 2;
- expected = reference.read 32;
- compareOutputs a b = compareClose
- (map vec.list (mat.asRows a)) (map vec.list (mat.asRows b));
- compareOutputs result expected;
-),
-
-"interpolated-misc": \(
- // Interpolating any signal by N should give a signal in which
- // every Nth sample is the original signal
- data = vec.fromList [ 0, 0.1, -0.3, -0.4, -0.3, 0, 0.5, 0.2, 0.8, -0.1 ];
- data = vec.concat [ data, bf.scaled (5/4) data, bf.scaled (3/4) data, data ];
- data = vec.concat [ data, data ];
- input = filt.withDuration (vec.length data) (syn.precalculatedMono 4 data);
- factor = 3;
- up = filt.interpolated factor input;
- result = mat.getRow 0 (up.read (factor * vec.length data));
- phase = 0;
- a = vec.list data;
- b = map do i: vec.at result (i*factor + phase) done [0..vec.length data - 1];
- compareClose [a] [b];
-),
-
-"int-dec": \(
- // Interpolating any signal then decimating by the same factor
- // should get us the original back again
- //!!! no, this is phase dependent
-// data = vec.fromList [ 0, 0.1, -0.3, -0.4, -0.3, 0, 0.5, 0.2, 0.8, -0.1 ];
-// data = vec.concat [ data, bf.scaled (5/4) data, bf.scaled (3/4) data, data ];
- data = vec.fromList [ 0, 1, 2, 3 ];
- data = vec.concat [ data, data ];
-
- factor = 3;
-
- updown prepad =
- (input = filt.withDuration (vec.length data) (syn.precalculatedMono 4 data);
- intermediate = filt.interpolated factor input;
- output = filt.decimated factor (filt.delayedBy prepad intermediate);
- output.read (vec.length data));
-
- result = updown 0;
- if not compareClose [vec.list (mat.getRow 0 result)] [vec.list data] then
- \() (pl.plot [Vector data,
- Vector (mat.getRow 0 result),
- Vector (mat.getRow 0 (updown 1)),
- Vector (mat.getRow 0 (updown 2))]);
- up = (filt.interpolated 2 (syn.precalculatedMono 4 data)).read 80;
- \() (pl.plot [Vector (mat.getRow 0 up)]);
- false
- else true fi;
-),
-
-"lowpassed-dirac": \(
- test { rate, cutoff, attenuation, bandwidth, n } =
- (input = makeDiracStream rate n;
- output = filt.lowpassed cutoff attenuation bandwidth input;
- logspec = logSpectrumFrom output n;
- acceptances = map do bin:
- freq = (rate / n) * bin;
- db = logspec[bin];
- //!!! what should these 0.01 actually be?
- if freq < cutoff - bandwidth/2 then (db < 0.01 and db > -0.01)
- elif freq > cutoff + bandwidth/2 then (db < -attenuation)
- else (db < 0.01 and db > -attenuation)
- fi;
- done [0..n/2];
- compare acceptances (map \true [0..n/2]));
- all id
- (map test [
- { rate = 800, cutoff = 200, attenuation = 80, bandwidth = 10, n = 1000 },
- ]);
-),
-
-"highpassed-dirac": \(
- test { rate, cutoff, attenuation, bandwidth, n } =
- (input = makeDiracStream rate n;
- output = filt.highpassed cutoff attenuation bandwidth input;
- logspec = logSpectrumFrom output n;
- acceptances = map do bin:
- freq = (rate / n) * bin;
- db = logspec[bin];
- //!!! what should these 0.01 actually be?
- if freq > cutoff + bandwidth/2 then (db < 0.01 and db > -0.01)
- elif freq < cutoff - bandwidth/2 then (db < -attenuation)
- else (db < 0.01 and db > -attenuation)
- fi;
- done [0..n/2];
- compare acceptances (map \true [0..n/2]));
- all id
- (map test [
- { rate = 800, cutoff = 200, attenuation = 80, bandwidth = 10, n = 1000 },
- ]);
-),
-
-"bandpassed-dirac": \(
- test { rate, f0, f1, attenuation, bandwidth, n } =
- (input = makeDiracStream rate n;
- output = filt.bandpassed f0 f1 attenuation bandwidth input;
- logspec = logSpectrumFrom output n;
- acceptances = map do bin:
- freq = (rate / n) * bin;
- db = logspec[bin];
- //!!! what should these 0.01 actually be?
- if freq < f0 - bandwidth/2 then (db < -attenuation)
- elif freq < f0 + bandwidth/2 then (db < 0.01 and db > -attenuation)
- elif freq < f1 - bandwidth/2 then (db < 0.01 and db > -0.01)
- elif freq < f1 + bandwidth/2 then (db < 0.01 and db > -attenuation)
- else (db < -attenuation)
- fi;
- done [0..n/2];
- compare acceptances (map \true [0..n/2]));
- all id
- (map test [
- { rate = 800, f0 = 200, f1 = 300, attenuation = 80, bandwidth = 10, n = 1000 },
- ]);
-),
-
-];
-
-all = [:];
-for [ knowns, unknowns, filtering ] do h:
- for (keys h) do k: all[k] := h[k] done
-done;
-
-all is hash boolean>;
-
diff -r 4b104ef8d110 -r 20903ee53719 may/stream/test/test_framer.yeti
--- a/may/stream/test/test_framer.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,173 +0,0 @@
-
-module may.stream.test.test_framer;
-
-fr = load may.stream.framer;
-vec = load may.vector;
-mat = load may.matrix;
-syn = load may.stream.syntheticstream;
-
-{ compare, compareUsing } = load may.test.test;
-
-rate = 10;
-
-testStream n is number -> 'a = syn.precalculatedMono rate (vec.fromList [1..n]);
-
-compareFrames frames1 frames2 =
- all id (map2 do f1 f2: compareUsing mat.equal f1 f2 done frames1
- (map (mat.newRowVector . vec.fromList) frames2));
-
-testFramesWith params length expected firstChunkSize =
- (f = fr.frames params (testStream length);
- str = fr.streamed rate params f;
- ts = testStream length; // newly initialised stream
-
- compareFrames f expected and
-
- (firstChunk = str.read firstChunkSize;
- compareUsing mat.equal
- firstChunk (ts.read firstChunkSize)) and
-
- compare str.position firstChunkSize and
- compare str.finished? false and
-
- (restChunk = str.read (length - firstChunkSize);
- compareUsing mat.equal
- restChunk (ts.read (length - firstChunkSize))) and
- compare str.position length and
-
- (trailingZeros = str.read (params.framesize + 1);
- compareUsing mat.equal
- trailingZeros
- (mat.zeroMatrix
- { rows = str.channels, columns = mat.width trailingZeros }) and
- (mat.width trailingZeros < params.framesize)) and
-
- compare str.finished? true and
- compare str.available (Known 0));
-
-testFramesInvertible params length expected =
- all id (map (testFramesWith params length expected) [1..length]);
-
-testFrames params length expected =
- (f = fr.frames params (testStream length);
- compareFrames f expected);
-
-[
-
-"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": \(
- testFramesInvertible { framesize = 2, hop = 2 } 5 [ [1,2], [3,4], [5,0] ];
-),
-
-"frames-4.3x4": \(
- testFrames { framesize = 4, hop = 3 } 4 [ [0,1,2,3], [3,4,0,0] ];
-),
-
-"frames-3.2x4": \(
- testFrames { framesize = 3, hop = 2 } 4 [ [0,1,2], [2,3,4], [4,0,0] ];
-),
-
-"frames-3.1x6": \(
- testFramesInvertible { framesize = 3, hop = 1 } 6
- [ [0,0,1], [0,1,2], [1,2,3], [2,3,4],
- [3,4,5], [4,5,6], [5,6,0], [6,0,0] ];
-),
-
-"frames-4.2x8": \(
- testFramesInvertible { framesize = 4, hop = 2 } 8
- [ [0,0,1,2], [1,2,3,4], [3,4,5,6], [5,6,7,8], [7,8,0,0] ];
-),
-
-"overlapAdd-3.1": \(
- compareUsing (mat.equal)
- (fr.overlapAdd 2 [ mat.newRowVector (vec.fromList [ 1,2,3 ]),
- mat.newRowVector (vec.fromList [ 4,5,6 ]),
- mat.newRowVector (vec.fromList [ 7,8,9 ]) ])
- (mat.newRowVector (vec.fromList [ 1,6,15,14,9 ]))
-),
-
-"overlapAdd-3.2": \(
- compareUsing (mat.equal)
- (fr.overlapAdd 1 [ mat.newRowVector (vec.fromList [ 1,2,3 ]),
- mat.newRowVector (vec.fromList [ 4,5,6 ]),
- mat.newRowVector (vec.fromList [ 7,8,9 ]) ])
- (mat.newRowVector (vec.fromList [ 1,2,7,5,13,8,9 ]))
-),
-
-"overlapAdd-4.2": \(
- compareUsing (mat.equal)
- (fr.overlapAdd 2 [ mat.newRowVector (vec.fromList [ 1,2,3,4 ]),
- mat.newRowVector (vec.fromList [ 5,6,7,8 ]),
- mat.newRowVector (vec.fromList [ 9,0,1,2 ]) ])
- (mat.newRowVector (vec.fromList [ 1,2,8,10,16,8,1,2 ]))
-),
-
-"overlapAdd-6+4.2": \(
- // Must work even if blocks vary in length (what if shorter than
- // overlap though?)
- compareUsing (mat.equal)
- (fr.overlapAdd 2 [ mat.newRowVector (vec.fromList [ 1,2,3,4,5,6 ]),
- mat.newRowVector (vec.fromList [ 7,8,9,0 ]),
- mat.newRowVector (vec.fromList [ 1,2,3 ]),
- mat.newRowVector (vec.fromList [ 4,5,6 ]) ])
- (mat.newRowVector (vec.fromList [ 1,2,3,4,12,14,10,6,8,6 ]))
-),
-
-] is hash boolean>;
-
-
-
diff -r 4b104ef8d110 -r 20903ee53719 may/stream/test/test_syntheticstream.yeti
--- a/may/stream/test/test_syntheticstream.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +0,0 @@
-
-module may.stream.test.test_syntheticstream;
-
-vec = load may.vector;
-mat = load may.matrix;
-syn = load may.stream.syntheticstream;
-
-{ compare, compareUsing } = load may.test.test;
-
-compareApprox eps =
- compareUsing do v1 v2: all id (map2 do f1 f2: abs (f1 - f2) < eps done v1 v2) done;
-
-epsilon = 0.000001;
-
-[
-
-"generated": \(
- str = syn.generated 2 id;
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 2 and
- compare str.available (Infinite ()) and
- compare str.finished? false and
- compare (vec.list (mat.getRow 0 (str.read 4))) [ 0,1,2,3 ] and
- compare str.position 4
-),
-
-"sinusoid": \(
- // 2Hz sine sampled 8 times a second
- str = syn.sinusoid 8 2;
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 8 and
- compare str.available (Infinite ()) and
- compare str.finished? false and
- compareApprox epsilon (vec.list (mat.getRow 0 (str.read 6))) [ 0, 1, 0, -1, 0, 1 ] and
- compare str.position 6
-),
-
-"silent": \(
- str = syn.silent 8;
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 8 and
- compare str.available (Infinite ()) and
- compare str.finished? false and
- compare (vec.list (mat.getRow 0 (str.read 3))) [ 0, 0, 0 ] and
- compare str.position 3
-),
-
-"precalculatedMono-empty": \(
- str = syn.precalculatedMono 2 (vec.fromList []);
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 2 and
- compare str.available (Known 0) and
- compare str.finished? true and
- compare (vec.list (mat.getRow 0 (str.read 3))) [] and
- compare str.position 0
-),
-
-"precalculatedMono": \(
- str = syn.precalculatedMono 2 (vec.fromList [ 1, 2, 3, 4 ]);
- compare str.position 0 and
- compare str.channels 1 and
- compare str.sampleRate 2 and
- compare str.available (Known 4) and
- compare str.finished? false and
- compare (vec.list (mat.getRow 0 (str.read 3))) [ 1, 2, 3 ] and
- compare str.position 3 and
- compare str.available (Known 1) and
- compare str.finished? false and
- compare (vec.list (mat.getRow 0 (str.read 3))) [ 4 ] and
- compare str.position 4 and
- compare str.available (Known 0) and
- compare str.finished? true
-),
-
-] is hash boolean>
-
diff -r 4b104ef8d110 -r 20903ee53719 may/stream/type.yeti
--- a/may/stream/type.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-
-module may.stream.type;
-
-load may.matrix.type;
-
-typedef stream =
- {
- position is number,
- channels is number,
- sampleRate is number,
- available is Known number | Unknown () | Infinite (),
- finished? is boolean,
- read is number -> matrix,
- close is () -> (),
- };
-
-();
-
diff -r 4b104ef8d110 -r 20903ee53719 may/test/all.yeti
--- a/may/test/all.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-
-program may.test.all;
-
-{ runTests } = load may.test.test;
-
-tests = [
-"vector" : load may.vector.test.test_vector,
-"blockfuncs" : load may.vector.test.test_blockfuncs,
-"complex" : load may.complex.test.test_complex,
-"framer" : load may.stream.test.test_framer,
-"channels" : load may.stream.test.test_channels,
-"audiofile" : load may.stream.test.test_audiofile,
-"synstream" : load may.stream.test.test_syntheticstream,
-"filter" : load may.stream.test.test_filter,
-"fft" : load may.transform.test.test_fft,
-"vamppost" : load may.vamp.test.test_vamppost,
-"vamp" : load may.vamp.test.test_vamp,
-"matrix" : load may.matrix.test.test_matrix,
-"plot" : load may.plot.test.test_plot,
-"signal" : load may.signal.test.test_signal,
-"window" : load may.signal.test.test_window,
-];
-
-bad = sum (mapHash do name testHash: runTests name testHash done tests);
-
-if (bad > 0) then
- println "\n** \(bad) test(s) failed!";
-else
- ()
-fi
-
diff -r 4b104ef8d110 -r 20903ee53719 may/test/data/20samples.wav
Binary file may/test/data/20samples.wav has changed
diff -r 4b104ef8d110 -r 20903ee53719 may/test/data/44100-2-16.wav
Binary file may/test/data/44100-2-16.wav has changed
diff -r 4b104ef8d110 -r 20903ee53719 may/test/data/8000-1-8.wav
Binary file may/test/data/8000-1-8.wav has changed
diff -r 4b104ef8d110 -r 20903ee53719 may/test/test.yeti
--- a/may/test/test.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-module may.test.test;
-
-import yeti.lang: FailureException;
-
-var goodCompares = 0;
-
-compareUsing comparator obtained expected =
- if comparator obtained expected then
- goodCompares := goodCompares + 1;
- true;
- else
- println "** expected: \(expected)\n obtained: \(obtained)";
- false;
- fi;
-
-compare obtained expected = compareUsing (==) obtained expected;
-
-time msg f =
- (start = System#currentTimeMillis();
- result = f ();
- end = System#currentTimeMillis();
- println "\(msg): \(end-start)ms";
- result);
-
-select f = fold do r x: if f x then x::r else r fi done [];
-
-failedTests testHash =
- select (!= "")
- (mapHash do name f:
- try
- if f () then "" else
- println "Test \(name) failed";
- name;
- fi
- catch FailureException e:
- println "Test \(name) threw exception: \(e)";
- name;
- yrt;
- 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);
-
-{
- compare, compareUsing,
- time,
- runTests,
-}
-
diff -r 4b104ef8d110 -r 20903ee53719 may/transform/fft.yeti
--- a/may/transform/fft.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-
-module may.transform.fft;
-
-import edu.emory.mathcs.jtransforms.fft: DoubleFFT_1D;
-
-vec = load may.vector;
-complex = load may.complex;
-
-load may.complex.type;
-
-packedToComplex len p is number -> ~double[] -> array =
- (n = len / 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 = new double[n*2-2];
- for [0..(n-1)*2-1] do i:
- ix = int (i/2);
- v[i] :=
- if i == ix*2 then
- complex.real arr[ix]
- else
- complex.imaginary arr[ix]
- fi;
- done;
- v[1] := complex.real arr[n-1];
- v);
-
-//!!! doc: n is supplied separately from the input vector to support partial evaluation
-//!!! doc: output has n/2+1 complex values
-//!!! doc: powers of two only? check with jtransforms
-realForward n =
- (d = new DoubleFFT_1D(n);
- do bl:
- v = vec.primitive bl;
- d#realForward(v);
- packedToComplex (vec.length bl) v;
- done);
-
-//!!! doc: input requires n/2+1 complex values (or should test and throw?)
-//!!! doc: powers of two only? check with jtransforms
-realInverse n =
- (d = new DoubleFFT_1D(n);
- do cplx:
- v = complexToPacked (array cplx);
- d#realInverse(v, true);
- vec.vector v;
- done);
-
-{
-realForward,
-realInverse,
-}
-
diff -r 4b104ef8d110 -r 20903ee53719 may/transform/test/test_fft.yeti
--- a/may/transform/test/test_fft.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-
-module may.transform.test.test_fft;
-
-{ realForward, realInverse } = load may.transform.fft;
-{ list, fromList } = load may.vector;
-{ complex } = load may.complex;
-
-{ compare } = load may.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 (list back) orig);
-
-[
-
-"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 boolean>;
-
-
diff -r 4b104ef8d110 -r 20903ee53719 may/vamp.yeti
--- a/may/vamp.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,321 +0,0 @@
-module may.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;
-
-vec = load may.vector;
-fr = load may.stream.framer;
-af = load may.stream.audiofile;
-mat = load may.matrix;
-vamprdf = load may.vamp.vamprdf;
-vamppost = load may.vamp.vamppost;
-
-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 = vec.fromFloats 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);
-
-stores = [:];
-
-getSingletonStoreFor loader =
- synchronized stores do:
- if loader in stores then
- stores[loader]
- else
- s = store.newRdfStore ();
- loader s;
- stores[loader] := s;
- s;
- fi
- done;
-
-getSystemStore () =
- getSingletonStoreFor vamprdf.loadSystemVampRdf;
-
-getGlobalStore () =
- getSingletonStoreFor vamprdf.loadGlobalVampRdf;
-
-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);
-
-getKnownPluginKeys () =
- (store = getGlobalStore ();
- nodes = vamprdf.allPluginNodes store;
- // ordering is random out of the store; might as well sort
- sort (map do n: (vamprdf.pluginDataByNode store n).pluginKey done nodes));
-
-getDataForKnownPlugin key =
- vamprdf.pluginDataByKey (getGlobalStore ()) key;
-
-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 = getSystemStore ();
- 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;
- _:
- if od#sampleType == OutputDescriptor$SampleType#OneSamplePerStep
- then Series ()
- else Curve ()
- fi;
- 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 (getSystemStore ()) 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 frame time is 'a -> ~RealTime -> 'b =
- featureSet p#process((map vec.floats (mat.asRows frame)) 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 =
- 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 (); failWith text);
-
-processWith key p outputNo stream =
- (blockSize =
- if p.preferredBlockSize == 0 then 2048
- else p.preferredBlockSize fi;
- stepSize =
- if p.preferredStepSize == 0 then
- if p.inputDomain == FrequencyDomain () then blockSize / 2
- else blockSize fi;
- else p.preferredStepSize fi;
- channels = 1;
- params = {
- p, sampleRate = stream.sampleRate, channels = 1,
- framesize = blockSize, blockSize, hop = stepSize
- };
- if p.initialise params then
- {
- 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);
-
-processStream 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: failWith e;
- esac;
-
-processFile key output filename =
- processStream key output (af.open filename);
-
-processStreamStructured key output filename =
- vamppost.postprocess (processStream key output filename);
-
-processFileStructured key output filename =
- vamppost.postprocess (processFile key output filename);
-
-{
-get pluginPath = getPluginPath,
-get pluginKeys = listPlugins,
-loadPlugin,
-categoryOf,
-processStream,
-processFile,
-processStreamStructured,
-processFileStructured,
-getKnownPluginKeys,
-getDataForKnownPlugin,
-}
-
diff -r 4b104ef8d110 -r 20903ee53719 may/vamp/test/test_vamp.yeti
--- a/may/vamp/test/test_vamp.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,150 +0,0 @@
-module may.vamp.test.test_vamp;
-
-v = load may.vamp;
-synthetic = load may.stream.syntheticstream;
-filter = load may.stream.filter;
-mat = load may.matrix;
-vec = load may.vector;
-
-{ compare, compareUsing } = load may.test.test;
-
-testPluginKey = "vamp-test-plugin:vamp-test-plugin";
-
-rate = 44100;
-
-testStream () = filter.withDuration (rate * 20) (synthetic.whiteNoise rate);
-
-processTest output =
- v.processStreamStructured testPluginKey output (testStream ());
-
-float n is number -> number =
- // round number to float precision (for comparison with floats)
- (arr = new float[1];
- arr[0] := n;
- arr[0]);
-
-floats nn = map float nn;
-
-tests =
-[
-
-"version": \(
- case v.loadPlugin rate testPluginKey of
- Error e: (eprintln "version: Error: \(e)"; false);
- OK plugin: compare plugin.version 1;
- esac
-),
-
-"instants": \(
- case processTest "instants" of
- Instants ii:
- compare (map (.time) ii) [ 0, 1.5, 3, 4.5, 6, 7.5, 9, 10.5, 12, 13.5 ];
- other: failWith "wrong structure type: expected Instants tag, got \(other)";
- esac
-),
-
-"curve-oss": \(
- case processTest "curve-oss" of
- Series s:
- compare s.start 0 and
- //!!! want to specify step and block size for processing
- compare s.step (2048/rate) and
- compare s.values (floats (map (* 0.05) [0..19]));
- other: failWith "wrong structure type: expected Series tag, got \(other)";
- esac
-),
-
-"curve-fsr": \(
- case processTest "curve-fsr" of
- Curve c:
- compare (map (.time) c) (map (* 0.4) [0..9]) and
- compare (map (.value) c) (floats (map (* 0.1) [0..9]));
-/*
- compare s.start 0 and
- compare s.step 0.4 and
- compare s.values (floats (map (* 0.1) [0..9]));
-*/
- other: failWith "wrong structure type: expected Series tag, got \(other)";
- esac
-),
-
-"curve-fsr-timed": \(
- case processTest "curve-fsr-timed" of
- Curve c:
- compare (map (.time) c) [ 0, 0, 0, 0.4, 2, 2, 2, 2.4, 4, 4 ] and
- compare (map (.value) c) (floats (map (* 0.1) [0..9]));
- other: failWith "wrong structure type: expected Curve tag, got \(other)";
- esac
-),
-
-"curve-vsr": \(
- case processTest "curve-vsr" of
- Curve c:
- compare (map (.time) c) (map (* 0.75) [0..9]) and
- compare (map (.value) c) (floats (map (* 0.1) [0..9]));
- other: failWith "wrong structure type: expected Curve tag, got \(other)";
- esac
-),
-
-"grid-oss": \(
- case processTest "grid-oss" of //!!! test spacing?
- Grid g:
- compareUsing mat.equal g
- (mat.newMatrix (ColumnMajor ())
- (map do x:
- (vec.fromList . floats) (map do y:
- (x + y + 2) / 30
- done [0..9])
- done [0..19]));
- other: failWith "wrong structure type: expected Grid tag, got \(other)";
- esac
-),
-
-"grid-fsr": \(
- case processTest "grid-fsr" of //!!! test spacing?
- Grid g:
- compareUsing mat.equal g
- (mat.newMatrix (ColumnMajor ())
- (map do x:
- (vec.fromList . floats) (map do y:
- (x + y + 2) / 20
- done [0..9])
- done [0..9]));
- other: failWith "wrong structure type: expected Grid tag, got \(other)";
- esac
-),
-
-"notes-regions": \(
- case processTest "notes-regions" of
- Regions rr:
- compare (map (.time) rr) [0..9] and
- compare (map (.duration) rr) (map do i: Time (if i % 2 == 0 then 1.75 else 0.5 fi) done [0..9]) and
- compare (map (.values) rr) (map do i: array [float i] done (map (* 0.1) [0..9]));
- other: failWith "wrong structure type: expected Curve tag, got \(other)";
- esac
-),
-
-];
-
-// Check we have the test plugin. Without it, all the tests must fail
-
-if contains? testPluginKey v.pluginKeys then
- tests
-else
- eprintln
-"** Vamp test plugin not found!
- Either the Vamp module is not working, or the test plugin is not installed.
- Please ensure vamp-test-plugin.{so,dll,dylib} is in the path,
- or set $VAMP_PATH to point to its location.
-
- Current path: \(v.pluginPath)
- Required plugin key: \"\(testPluginKey)\"
- Plugin keys found: \(v.pluginKeys)
-
- All of the Vamp plugin tests will fail until this is fixed.
-";
- mapIntoHash id \\false (keys tests)
-fi is hash boolean>;
-
-
-
diff -r 4b104ef8d110 -r 20903ee53719 may/vamp/test/test_vamppost.yeti
--- a/may/vamp/test/test_vamppost.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-module may.vamp.test.test_vamppost;
-
-vp = load may.vamp.vamppost;
-
-{ compare } = load may.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.2 1, timed 1.4 2, timed 4.0 3
- ];
-),
-
-"fillFixedSampleRate2": \(
- // As above, but with non-integer output sample rate
- filled = vp.fillTimestamps
- (testdata with { output = { sampleType = FixedSampleRate 2.5 } });
- compare filled [
- timed 0 1 ,
- timed 0.4 1, timed 0.8 2 ,
- timed 1.2 1, timed 1.6 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 boolean>;
-
diff -r 4b104ef8d110 -r 20903ee53719 may/vamp/vamppost.yeti
--- a/may/vamp/vamppost.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,166 +0,0 @@
-module may.vamp.vamppost;
-
-mat = load may.matrix;
-vec = load may.vector;
-
-fillOneSamplePerStep config features =
- (fill' n pending features =
- // For OneSamplePerStep features, the time is incremented
- // between process blocks (a process block is one element in
- // the features list, which is then expanded into the complete
- // pending list)
- case pending of
- feature::rest:
- stamped = feature with
- //!!! how do we ensure feature timestamps are rationals where possible?
- { 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 =
- (fill' n pending features =
- // For FixedSampleRate features without explicit timestamps,
- // the time is incremented from the previous *feature* not the
- // previous process block (i.e. between elements in the
- // pending list)
- case pending of
- feature::rest:
- n = case feature.timestamp of
- Untimed (): n + 1;
- Time t: int (t * rate + 0.5);
- 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 =
-//!!! need to return grid resolution as well -- or will caller read that from output elsewhere if they need it?
- if empty? features then
- mat.zeroMatrix { rows = binCount, columns = 0 };
- else
- mat.newMatrix (ColumnMajor ()) (map (.values) features);
- fi;
-
-timeOf f =
- case f.timestamp of
- Time t: t;
- _: failWith "Internal error: timestamps not filled";
- esac;
-
-structureSeries features =
- if empty? features then { start = 0, step = 0, values = [] }
- else
- t0 = timeOf (head features);
- t1 = if empty? (tail features) then t0
- else timeOf (head (tail features)) fi;
- {
- start = t0,
- step = t1 - t0,
- values = map do f: vec.at f.values 0 done features;
- }
- fi;
-
-structureCurve features =
- map do f: {
- time = timeOf f,
- value = vec.at f.values 0,
- label = f.label
- } done features;
-
-structureInstants features =
- map do f: {
- time = timeOf f,
- label = f.label
- } done features;
-
-structureSegmentation features =
- map do f: {
- time = timeOf f,
- type = vec.at f.values 0,
- label = f.label
- } done features;
-
-structureNotes features =
- map do f: {
- time = timeOf f,
- duration = f.duration,
- pitch = vec.at f.values 0, //!!! no, might be empty
- otherValues = array (tail (vec.list f.values)), //!!! no, might be empty
- label = f.label
- } done features;
-
-structureWithDuration features =
- map do f: {
- time = timeOf f,
- duration = f.duration,
- values = array (vec.list f.values),
- label = f.label
- } done features;
-
-structureWithoutDuration features =
- map do f: {
- time = timeOf f,
- values = array (vec.list f.values),
- label = f.label
- } done features;
-
-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
- Series (): // No duration, one value, not variable rate
- Series (structureSeries features);
- Grid (): // No duration, >1 value, not variable rate
- Grid (structureGrid binCount features);
- Curve (): // No duration, one value, variable rate
- Curve (structureCurve features);
- Instants (): // Zero-valued features
- Instants (structureInstants features);
- Notes (): // Duration, at least one value (pitch or freq)
- Notes (structureNotes features);
- Regions (): // Duration, zero or more values
- Regions (structureWithDuration features);
- Segmentation (): // No duration, one value, segment type in RDF
- Segmentation (structureSegmentation features);
- Unknown (): // Other
- Unknown
- ((if data.output.hasDuration then structureWithDuration
- else structureWithoutDuration
- fi) features);
- esac);
-
-{
-fillTimestamps,
-postprocess = structure
-}
-
-
diff -r 4b104ef8d110 -r 20903ee53719 may/vamp/vamprdf.yeti
--- a/may/vamp/vamprdf.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,290 +0,0 @@
-
-module may.vamp.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;
-
-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,
-}
-
diff -r 4b104ef8d110 -r 20903ee53719 may/vector.yeti
--- a/may/vector.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,189 +0,0 @@
-
-/**
- * Vectors. A May vector is a typesafe, immutable wrapper around a Java
- * primitive array of doubles.
- *
- * Although not as convenient and flexible as a Yeti array or
- * list, a vector can be faster and more compact when dealing
- * with dense data of suitable range and precision such as sampled
- * sequences.
- */
-
-module may.vector;
-
-load may.vector.type;
-
-import java.util: Arrays;
-
-/// Return a vector of n zeros.
-zeros n =
- new double[n];
-
-/// Return a vector of length n, containing all m.
-consts m n =
- (a = zeros n;
- for [0..n-1] do i:
- a[i] := m;
- done;
- a);
-
-/// Return a vector of length n, containing all ones.
-ones n = consts 1.0 n;
-
-/// Return a vector of the values in the given list.
-fromList l is list? -> ~double[] =
- l as ~double[];
-
-/// Return the given vector as a list.
-list' a is ~double[] -> list =
- list a;
-
-/// Return the given vector as a Yeti array.
-array' a is ~double[] -> array =
- array a;
-
-/// Return the length of the given vector.
-length' =
- length . list';
-
-/// Return true if the given vector is empty (has length 0).
-empty?' =
- empty? . list';
-
-/// Return element n in the given vector v. (The function name and
-/// argument order are chosen for symmetry with the similar standard
-/// library array function.)
-at' v n is ~double[] -> number -> number =
- v[n];
-
-/// Return the given vector as a Java primitive float array.
-floats a is ~double[] -> ~float[] =
- (len = length' a;
- f = new float[len];
- for [0..len-1] do i:
- f[i] := a[i];
- done;
- f);
-
-/// Return a vector of the values in the given Java primitive float array.
-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);
-
-/// Return true if the given vectors are equal, using the standard ==
-/// comparator on their elements.
-equal v1 v2 =
- list' v1 == list' v2;
-
-/// Return true if the given vectors are equal, when applying the
-/// given numerical comparator to each element.
-equalUnder comparator v1 v2 =
- length' v1 == length' v2 and
- all id (map2 comparator (list' v1) (list' v2));
-
-/// Return another copy of the given vector.
-copyOf v is ~double[] -> ~double[] =
- Arrays#copyOf(v, list' v |> length);
-
-/// Return a new vector containing a subset of the elements of the
-/// given vector, from index start (inclusive) to index end
-/// (exclusive). (The function name and argument order are chosen for
-/// symmetry with the standard library slice and strSlice functions.)
-slice v start end is ~double[] -> number -> number -> ~double[] =
- if start < 0 then slice v 0 end
- elif start > length' v then slice v (length' v) end
- else
- if end < start then slice v start start
- elif end > length' v then slice v start (length' v)
- else
- Arrays#copyOfRange(v, start, end);
- fi
- fi;
-
-/// Return a new vector of length n, containing the contents of the
-/// given vector v. If v is longer than n, the contents will be
-/// truncated; if shorter, they will be padded with zeros.
-resizedTo n v is number -> ~double[] -> ~double[] =
- Arrays#copyOf(v, n);
-
-/// Return a new vector that is the reverse of the given vector. Name
-/// chosen (in preference to passive "reversed") for symmetry with the
-/// standard library list reverse function.
-reverse v is ~double[] -> ~double[] =
- (len = length (list v);
- a = new double[len];
- for [0..len-1] do i:
- a[len-i-1] := v[i];
- done;
- a);
-
-/// Return a single new vector that contains the contents of all the
-/// given vectors, in order. (Unlike the standard module list concat
-/// function, this one cannot be lazy.)
-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);
-
-/// Return a single new vector that contains the contents of the given
-/// vector, repeated n times. The vector will therefore have length n
-/// times the length of v.
-repeated v n is ~double[] -> number -> ~double[] =
- concat (map \(v) [1..n]);
-
-{
- zeros,
- consts,
- ones,
- vector v = v,
- primitive = copyOf,
- floats,
- fromFloats,
- fromList,
- list = list',
- array = array',
- length = length',
- empty? = empty?',
- at = at',
- equal,
- equalUnder,
- slice,
- resizedTo,
- reverse,
- repeated,
- concat,
-} as {
- zeros is number -> vector,
- consts is number -> number -> vector,
- ones is number -> vector,
- vector is ~double[] -> vector,
- primitive is vector -> ~double[],
- floats is vector -> ~float[],
- fromFloats is ~float[] -> vector,
- fromList is list? -> vector,
- list is vector -> list,
- array is vector -> array,
- length is vector -> number,
- empty? is vector -> boolean,
- at is vector -> number -> number,
- equal is vector -> vector -> boolean,
- equalUnder is (number -> number -> boolean) -> vector -> vector -> boolean,
- slice is vector -> number -> number -> vector,
- resizedTo is number -> vector -> vector,
- reverse is vector -> vector,
- repeated is vector -> number -> vector,
- concat is list? -> vector,
-}
-
-
-
diff -r 4b104ef8d110 -r 20903ee53719 may/vector/blockfuncs.yeti
--- a/may/vector/blockfuncs.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,161 +0,0 @@
-
-module may.vector.blockfuncs;
-
-vec = load may.vector;
-
-load may.vector.type;
-
-//!!! "internal" vector function to retrieve data for read-only
-// purposes without copying
-raw =
- (raw' v is ~double[] -> ~double[] = v;
- raw' as vector -> ~double[]);
-
-sum' v =
- (dat = raw v;
- tot = new double[1];
- for [0..length dat - 1] do i:
- tot[0] := tot[0] + dat[i]
- done;
- tot[0]);
-
-max' v =
- (dat = raw v;
- var mx = 0;
- for [0..length dat - 1] do i:
- if i == 0 or dat[i] > mx then
- mx := dat[i];
- fi
- done;
- mx);
-
-maxindex v =
- (dat = raw v;
- var mx = 0;
- var mi = -1;
- for [0..length dat - 1] do i:
- if i == 0 or dat[i] > mx then
- mx := dat[i];
- mi := i;
- fi
- done;
- mi);
-
-min' v =
- (dat = raw v;
- var mn = 0;
- for [0..length dat - 1] do i:
- if i == 0 or dat[i] < mn then
- mn := dat[i];
- fi
- done;
- mn);
-
-minindex v =
- (dat = raw v;
- var mn = 0;
- var mi = -1;
- for [0..length dat - 1] do i:
- if i == 0 or dat[i] < mn then
- mn := dat[i];
- mi := i;
- fi
- done;
- mi);
-
-mean v =
- case vec.length v of
- 0: 0;
- len: sum' v / len
- esac;
-
-add bb =
- (len = head (sort (map vec.length bb));
- vv = map raw bb;
- out = new double[len];
- for [0..len-1] do i:
- for vv do v:
- out[i] := out[i] + v[i];
- done;
- done;
- vec.vector out);
-
-subtract b1 b2 =
- (v1 = raw b1;
- v2 = raw b2;
- len = if length v1 < length v2 then length v1 else length v2 fi;
- out = new double[len];
- for [0..len-1] do i:
- out[i] := v1[i] - v2[i]
- done;
- vec.vector out);
-
-multiply b1 b2 =
- (v1 = raw b1;
- v2 = raw b2;
- len = if length v1 < length v2 then length v1 else length v2 fi;
- out = new double[len];
- for [0..len-1] do i:
- out[i] := v1[i] * v2[i]
- done;
- vec.vector out);
-
-scaled n v =
- vec.fromList (map (* n) (vec.list v));
-
-divideBy n v = // Not just "scaled (1/n)" -- this way we get exact rationals
- vec.fromList (map (/ n) (vec.list v));
-
-sqr v =
- multiply v v;
-
-rms =
- sqrt . mean . sqr;
-
-abs' =
- vec.fromList . (map abs) . vec.list;
-
-sqrt' =
- vec.fromList . (map sqrt) . vec.list;
-
-unityNormalised v =
- (m = max' (abs' v);
- if m != 0 then
- divideBy m v;
- else
- v;
- fi);
-
-fftshift v =
- (len = vec.length v;
- half = int(len/2 + 0.5); // round up for odd-length sequences
- vec.concat [vec.slice v half len, vec.slice v 0 half]);
-
-ifftshift v =
- (len = vec.length v;
- half = int(len/2); // round down for odd-length sequences
- vec.concat [vec.slice v half len, vec.slice v 0 half]);
-
-{
-sum is vector -> number = sum',
-mean is vector -> number,
-add is list? -> vector,
-subtract is vector -> vector -> vector,
-multiply is vector -> vector -> vector,
-divideBy is number -> vector -> vector,
-scaled is number -> vector -> vector,
-abs is vector -> vector = abs',
-sqr is vector -> vector,
-sqrt is vector -> vector = sqrt',
-rms is vector -> number,
-max is vector -> number = max',
-min is vector -> number = min',
-maxindex is vector -> number,
-minindex is vector -> number,
-unityNormalised is vector -> vector,
-fftshift is vector -> vector,
-ifftshift is vector -> vector,
-}
-
-
-
diff -r 4b104ef8d110 -r 20903ee53719 may/vector/test/test_blockfuncs.yeti
--- a/may/vector/test/test_blockfuncs.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,125 +0,0 @@
-
-module may.vector.test.test_blockfuncs;
-
-stdSqrt = sqrt;
-
-{ zeros, consts, ones, fromList, list } = load may.vector;
-{ sum, max, min, maxindex, minindex, mean, add, subtract, multiply, divideBy, scaled, abs, sqr, sqrt, rms, unityNormalised, fftshift, ifftshift } = load may.vector.blockfuncs;
-{ compare } = load may.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
-),
-
-"max": \(
- compare ((max . fromList) [1,-2,3,0]) 3 and
- compare ((max . fromList) [-1,-2,-3]) (-1) and
- compare ((max . fromList) [4,1]) 4 and
- compare ((max . fromList) []) 0
-),
-
-"min": \(
- compare ((min . fromList) [1,-2,3,0]) (-2) and
- compare ((min . fromList) [-1,-2,-3]) (-3) and
- compare ((min . fromList) [4,1]) 1 and
- compare ((min . fromList) []) 0
-),
-
-"maxindex": \(
- compare ((maxindex . fromList) [1,-2,3,0]) 2 and
- compare ((maxindex . fromList) [-1,-2,-3]) 0 and
- compare ((maxindex . fromList) [4,1]) 0 and
- compare ((maxindex . fromList) []) (-1)
-),
-
-"minindex": \(
- compare ((minindex . fromList) [1,-2,3,0]) 1 and
- compare ((minindex . fromList) [-1,-2,-3]) 2 and
- compare ((minindex . fromList) [4,1]) 1 and
- compare ((minindex . fromList) []) (-1)
-),
-
-"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
-),
-
-"add": \(
- compare (list (add [zeros 0, ones 5])) [] and
- compare (list (add [consts 3 4, fromList [1,2,3] ])) [4,5,6] and
- compare (list (add [consts (-3) 4, fromList [1,2,3] ])) [-2,-1,0]
-),
-
-"subtract": \(
- compare (list (subtract (zeros 0) (ones 5))) [] and
- compare (list (subtract (consts 3 4) (fromList [1,2,3]))) [2,1,0] and
- compare (list (subtract (consts (-3) 4) (fromList [1,2,3]))) [-4,-5,-6]
-),
-
-"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]
-),
-
-"scaled": \(
- compare (list (scaled 5 (ones 0))) [] and
- compare (list (scaled 5 (fromList [1,2,-3]))) [5,10,-15]
-),
-
-"abs": \(
- compare (list (abs (ones 0))) [] and
- compare (list (abs (fromList [1,2,-3]))) [1,2,3]
-),
-
-"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)
-),
-
-"unityNormalised": \(
- compare ((list . unityNormalised . fromList) [1,-2,3,0]) [1/3,-2/3,1,0] and
- compare ((list . unityNormalised . fromList) [-1,-2,-3]) [-1/3,-2/3,-1] and
- compare ((list . unityNormalised . fromList) [4,1]) [1,1/4] and
- compare ((list . unityNormalised . fromList) []) []
-),
-
-"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 boolean>;
-
-
diff -r 4b104ef8d110 -r 20903ee53719 may/vector/test/test_vector.yeti
--- a/may/vector/test/test_vector.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,130 +0,0 @@
-
-module may.vector.test.test_vector;
-
-vec = load may.vector;
-
-{ compare } = load may.test.test;
-
-[
-
-"zeros-empty": \(
- v = vec.zeros 0;
- compare (vec.length v) 0;
-),
-
-"zeros": \(
- v = vec.zeros 3;
- a = vec.array v;
- compare (vec.length v) 3 and
- compare a[0] 0 and
- compare a[1] 0 and
- compare a[2] 0;
-),
-
-"consts-empty": \(
- v = vec.consts 4 0;
- compare (vec.length v) 0;
-),
-
-"consts": \(
- v = vec.consts 4 3;
- a = vec.array v;
- compare (vec.length v) 3 and
- compare a[0] 4 and
- compare a[1] 4 and
- compare a[2] 4;
-),
-
-"ones-empty": \(
- v = vec.ones 0;
- compare (vec.length v) 0;
-),
-
-"ones": \(
- v = vec.ones 3;
- a = vec.array v;
- compare (vec.length v) 3 and
- compare a[0] 1 and
- compare a[1] 1 and
- compare a[2] 1;
-),
-
-"from-list-empty": \(
- v = vec.fromList [];
- compare (vec.length v) 0;
-),
-
-"from-list": \(
- v = vec.fromList [1,2,3,4];
- a = vec.array v;
- compare (vec.length v) 4 and
- compare a[0] 1 and
- compare a[1] 2 and
- compare a[2] 3 and
- compare a[3] 4;
-),
-
-"equal-empty": \(
- vec.equal (vec.fromList []) (vec.fromList [])
-),
-
-"equal": \(
- v = vec.fromList [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'';
-),
-
-"slice": \(
- v = vec.fromList [1,2,3,4];
- vec.equal (vec.slice v 0 4) v and
- vec.equal (vec.slice v 2 4) (vec.fromList [3,4]) and
- vec.equal (vec.slice v (-1) 2) (vec.fromList [1,2]) and
- vec.equal (vec.slice v 3 5) (vec.fromList [4]) and
- vec.equal (vec.slice v 5 7) (vec.fromList []) and
- vec.equal (vec.slice v 3 2) (vec.fromList [])
-),
-
-"resizedTo": \(
- vec.equal (vec.resizedTo 4 (vec.fromList [])) (vec.zeros 4) and
- vec.equal (vec.resizedTo 2 (vec.fromList [1,2])) (vec.fromList [1,2]) and
- vec.equal (vec.resizedTo 3 (vec.fromList [1,2])) (vec.fromList [1,2,0]) and
- vec.equal (vec.resizedTo 2 (vec.fromList [1,2,3])) (vec.fromList [1,2]);
-),
-
-"repeated": \(
- vec.equal (vec.repeated (vec.fromList []) 2) (vec.fromList []) and
- vec.equal (vec.repeated (vec.fromList [1,2,3]) 0) (vec.fromList []) and
- vec.equal (vec.repeated (vec.fromList [1,2,3]) 1) (vec.fromList [1,2,3]) and
- vec.equal (vec.repeated (vec.fromList [1,2,3]) 2) (vec.fromList [1,2,3,1,2,3])
-),
-
-"reverse": \(
- vec.equal (vec.reverse (vec.fromList [])) (vec.fromList []) and
- vec.equal (vec.reverse (vec.fromList [1,2,3])) (vec.fromList [3,2,1]) and
- vec.equal (vec.reverse (vec.fromList [1,2])) (vec.fromList [2,1])
-),
-
-"concat2": \(
- v = vec.fromList [1,2,3];
- w = vec.fromList [4,5,6];
- x = vec.concat [v, w];
- x' = vec.fromList [1,2,3,4,5,6];
- vec.equal x x' and
- vec.equal x' (vec.concat [x', vec.fromList []]) and
- vec.equal x' (vec.concat [vec.fromList [], x'])
-),
-
-"concatn": \(
- v = vec.fromList [1,2,3];
- w = vec.fromList [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.fromList [1,2,3,4,5,6,1,2,3])
-),
-
-] is hash boolean>;
-
-
-
diff -r 4b104ef8d110 -r 20903ee53719 may/vector/type.yeti
--- a/may/vector/type.yeti Mon Sep 16 10:56:19 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-
-module may.vector.type;
-
-typedef opaque vector = ~double[];
-
-();
-
diff -r 4b104ef8d110 -r 20903ee53719 src/may/complex.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/complex.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,112 @@
+
+module may.complex;
+
+load may.vector.type;
+load may.complex.type;
+
+vec = load may.vector;
+
+import java.lang: ClassCastException;
+
+class Cplx(double real, double imag)
+ double getReal()
+ real,
+ double 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;
+
+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();
+
+sum' cc is list?<~Cplx> -> ~Cplx =
+ complex (sum (map real cc)) (sum (map imaginary cc));
+
+add c1 c2 is ~Cplx -> ~Cplx -> ~Cplx =
+ complex (real c1 + real c2) (imaginary c1 + imaginary c2);
+
+multiply c1 c2 is ~Cplx -> ~Cplx -> ~Cplx =
+ (a = real c1;
+ b = imaginary c1;
+ c = real c2;
+ d = imaginary c2;
+ complex (a * c - b * d) (b * c + a * d)); //!!! need units
+
+scale r c is number -> ~Cplx -> ~Cplx =
+ complex (r * real c) (r * imaginary c);
+
+zeros n is number -> array<~Cplx> =
+ array (map \(complex 0 0) [1..n]);
+
+magnitudes cc is list?<~Cplx> -> vector =
+ vec.fromList (map magnitude cc);
+
+angles cc is list?<~Cplx> -> vector =
+ vec.fromList (map angle cc);
+
+{
+ real,
+ imaginary,
+ complex,
+ magnitude,
+ angle,
+ sum = sum',
+ add,
+ multiply,
+ scale,
+ zeros,
+ magnitudes,
+ angles,
+} as {
+ real is cplx -> number,
+ imaginary is cplx -> number,
+ complex is number -> number -> cplx,
+ magnitude is cplx -> number,
+ angle is cplx -> number,
+ sum is list? -> cplx,
+ add is cplx -> cplx -> cplx,
+ multiply is cplx -> cplx -> cplx,
+ scale is number -> cplx -> cplx,
+ zeros is number -> array,
+ magnitudes is list? -> vector,
+ angles is list? -> vector,
+}
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/complex/test/test_complex.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/complex/test/test_complex.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,68 @@
+module may.complex.test.test_complex;
+
+{ real, imaginary, complex, magnitude, angle, sum, scale, zeros, magnitudes, angles }
+ = load may.complex;
+
+{ compare } = load may.test.test;
+
+vec = load may.vector;
+
+[
+
+"complex": \(
+ compare (complex 1 2) (complex 1 2) and
+ complex (-1) 2 != complex 1 2
+),
+
+"real": \(
+ compare (real (complex 3 2)) 3 and
+ compare (real (complex 3.3 2.2)) 3.3
+),
+
+"imaginary": \(
+ compare (imaginary (complex 3 4)) 4 and
+ compare (imaginary (complex 3 (-4.1))) (-4.1)
+),
+
+"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)
+),
+
+"sum": \(
+ compare (sum [complex 2 3, complex (-4) 5]) (complex (-2) 8)
+),
+
+"scale": \(
+ compare (scale 4 (complex 2 3)) (complex 8 12)
+),
+
+"zeros": \(
+ compare (zeros 0) (array []) and
+ compare (zeros 3) (array [complex 0 0, complex 0 0, complex 0 0])
+),
+
+"magnitudes": \(
+ compare (vec.list (magnitudes [ complex (-3) 4, complex 4 3, complex 0 0 ]))
+ [ 5, 5, 0 ] and
+ compare (vec.list (magnitudes (array []))) []
+),
+
+"angles": \(
+ compare (vec.list (angles [ complex 1 0, complex (-1) 0, complex 0 (-1) ]))
+ [ 0, pi, -pi/2 ] and
+ compare (vec.list (angles (array []))) []
+),
+
+
+] is hash boolean>;
+
+
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/complex/type.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/complex/type.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,7 @@
+
+module may.complex.type;
+
+typedef opaque cplx = ~may.Cplx;
+
+();
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/feature/feature.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/feature/feature.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,29 @@
+
+module may.feature.feature;
+
+vec = load may.vector;
+fr = load may.stream.framer;
+
+// Utility functions for feature extractors
+
+magdiff frame1 frame2 =
+ sum (map2 do a b: abs(a - b) done (vec.list frame1) (vec.list frame2));
+
+emptyFrameFor frames =
+ vec.zeros
+ if empty? frames then 0
+ else vec.length (head frames)
+ fi;
+
+features featureFunc frames =
+ (featuresOf prev frames =
+ case frames of
+ frame::rest: featureFunc prev frame :. \(featuresOf frame rest);
+ _: [];
+ esac;
+ featuresOf (emptyFrameFor frames) frames);
+
+{ magdiff, emptyFrameFor, features };
+
+
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/feature/specdiff.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/feature/specdiff.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,20 @@
+
+module may.feature.specdiff;
+
+vec = load may.vector;
+fr = load may.stream.framer;
+cplx = load may.complex;
+
+load may.feature.feature;
+
+specdiff frames =
+ features magdiff (map cplx.magnitudes frames);
+
+specdiffOfFile parameters filename =
+ specdiff (fr.frequencyDomainFramesOfFile parameters filename);
+
+{
+ specdiff,
+ specdiffOfFile,
+}
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/feature/test/test_features.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/feature/test/test_features.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,1 @@
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/matrix.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/matrix.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,842 @@
+
+/**
+ * Matrices. A matrix is a two-dimensional (NxM) container of
+ * double-precision floating point values.
+ *
+ * A matrix may be dense or sparse.
+ *
+ * A dense matrix (the default) is just a series of vectors, making up
+ * the matrix "grid". The values may be stored in either column-major
+ * order, in which case the series consists of one vector for each
+ * column in the matrix, or row-major order, in which case the series
+ * consists of one vector for each row. The default is column-major.
+ *
+ * A sparse matrix has a more complex representation in which only the
+ * non-zero values are stored. This is typically used for matrices
+ * containing sparse data, that is, data in which most of the values
+ * are zero: using a sparse representation is more efficient than a
+ * dense one (in both time and memory) if the matrix is very large but
+ * contains a relatively low proportion of non-zero values. Like dense
+ * matrices, sparse ones may be column-major or row-major.
+ *
+ * The choice of dense or sparse, row- or column-major is a question
+ * of efficiency alone. All functions in this module should return the
+ * same results regardless of how the matrices they operate on are
+ * represented. However, differences in performance can be very large
+ * and it is often worth converting matrices to a different storage
+ * format if you know they can be more efficiently manipulated that
+ * way. For example, multiplying two matrices is fastest if the first
+ * is in column-major and the second in row-major order.
+ *
+ * Use the isRowMajor? and isSparse? functions to query the storage
+ * format of a matrix; use the flipped function to convert between
+ * column-major and row-major storage; and use toSparse and toDense to
+ * convert between sparse and dense storage.
+ *
+ * Note that the matrix representation does not take into account
+ * different forms of zero-width or zero-height matrix. All matrices
+ * of zero width or height are equal to each other, and all are equal
+ * to the zero-sized matrix.
+ */
+
+module may.matrix;
+
+vec = load may.vector;
+bf = load may.vector.blockfuncs;
+
+load may.vector.type;
+load may.matrix.type;
+
+size m =
+ case m of
+ DenseRows r:
+ major = length r;
+ {
+ rows = major,
+ columns = if major > 0 then vec.length r[0] else 0 fi,
+ };
+ DenseCols c:
+ major = length c;
+ {
+ rows = if major > 0 then vec.length c[0] else 0 fi,
+ columns = major,
+ };
+ SparseCSR { values, indices, pointers, extent }:
+ {
+ rows = (length pointers) - 1,
+ columns = extent
+ };
+ SparseCSC { values, indices, pointers, extent }:
+ {
+ rows = extent,
+ columns = (length pointers) - 1
+ };
+ esac;
+
+width m = (size m).columns;
+height m = (size m).rows;
+
+nonZeroValues m =
+ (nz d =
+ sum
+ (map do v:
+ sum (map do n: if n == 0 then 0 else 1 fi done (vec.list v))
+ done d);
+ case m of
+ DenseRows d: nz d;
+ DenseCols d: nz d;
+ SparseCSR d: vec.length d.values;
+ SparseCSC d: vec.length d.values;
+ esac);
+
+density m =
+ ({ rows, columns } = size m;
+ cells = rows * columns;
+ (nonZeroValues m) / cells);
+
+sparseSlice n d =
+ (start = d.pointers[n];
+ end = d.pointers[n+1];
+ {
+ values = vec.slice d.values start end,
+ indices = slice d.indices start end,
+ });
+
+nonEmptySlices d =
+ (ne = array [];
+ for [0..length d.pointers - 2] do i:
+ if d.pointers[i] != d.pointers[i+1] then
+ push ne i
+ fi
+ done;
+ ne);
+
+fromSlice n m d =
+ (slice = sparseSlice n d;
+ var v = 0;
+ for [0..length slice.indices - 1] do i:
+ if slice.indices[i] == m then
+ v := vec.at slice.values i;
+ fi
+ done;
+ v);
+
+filledSlice n d =
+ (slice = sparseSlice n d;
+ dslice = new double[d.extent];
+ for [0..length slice.indices - 1] do i:
+ dslice[slice.indices[i]] := vec.at slice.values i;
+ done;
+ vec.vector dslice);
+
+at' m row col =
+ case m of
+ DenseRows rows: r = rows[row]; vec.at r col;
+ DenseCols cols: c = cols[col]; vec.at c row;
+ SparseCSR data: fromSlice row col data;
+ SparseCSC data: fromSlice col row data;
+ esac;
+
+getColumn j m =
+ case m of
+ DenseCols cols: cols[j];
+ SparseCSC data: filledSlice j data;
+ _: vec.fromList (map do i: at' m i j done [0..height m - 1]);
+ esac;
+
+getRow i m =
+ case m of
+ DenseRows rows: rows[i];
+ SparseCSR data: filledSlice i data;
+ _: vec.fromList (map do j: at' m i j done [0..width m - 1]);
+ esac;
+
+asRows m =
+ map do i: getRow i m done [0 .. (height m) - 1];
+
+asColumns m =
+ map do i: getColumn i m done [0 .. (width m) - 1];
+
+isRowMajor? m =
+ case m of
+ DenseRows _: true;
+ DenseCols _: false;
+ SparseCSR _: true;
+ SparseCSC _: false;
+ esac;
+
+isSparse? m =
+ case m of
+ DenseRows _: false;
+ DenseCols _: false;
+ SparseCSR _: true;
+ SparseCSC _: true;
+ esac;
+
+typeOf m =
+ if isRowMajor? m then RowMajor ()
+ else ColumnMajor ()
+ fi;
+
+flippedTypeOf m =
+ if isRowMajor? m then ColumnMajor ()
+ else RowMajor ()
+ fi;
+
+newColumnMajorStorage { rows, columns } =
+ if rows < 1 then array []
+ else array (map \(vec.zeros rows) [1..columns])
+ fi;
+
+zeroMatrix { rows, columns } =
+ DenseCols (newColumnMajorStorage { rows, columns });
+
+zeroMatrixWithTypeOf m { rows, columns } =
+ if isRowMajor? m then
+ DenseRows (newColumnMajorStorage { rows = columns, columns = rows });
+ else
+ DenseCols (newColumnMajorStorage { rows, columns });
+ fi;
+
+zeroSizeMatrix () = zeroMatrix { rows = 0, columns = 0 };
+
+empty?' m = (width m == 0 or height m == 0);
+
+generate f { rows, columns } =
+ if rows < 1 or columns < 1 then zeroSizeMatrix ()
+ else
+ m = array (map \(new double[rows]) [1..columns]);
+ for [0..columns-1] do col:
+ for [0..rows-1] do row:
+ m[col][row] := f row col;
+ done;
+ done;
+ DenseCols (array (map vec.vector m))
+ fi;
+
+swapij =
+ map do { i, j, v }: { i = j, j = i, v } done;
+
+//!!! should use { row = , column = , value = } instead of i, j, v?
+enumerateSparse m =
+ (enumerate { values, indices, pointers } =
+ concat
+ (map do i:
+ start = pointers[i];
+ end = pointers[i+1];
+ map2 do j v: { i, j, v } done
+ (slice indices start end)
+ (vec.list (vec.slice values start end))
+ done [0..length pointers - 2]);
+ case m of
+ SparseCSC d: swapij (enumerate d);
+ SparseCSR d: enumerate d;
+ _: [];
+ esac);
+
+enumerateDense m =
+ (enumerate d =
+ concat
+ (map do i:
+ vv = d[i];
+ map2 do j v: { i, j, v } done
+ [0..vec.length vv - 1]
+ (vec.list vv);
+ done [0..length d - 1]);
+ case m of
+ DenseCols c: swapij (enumerate c);
+ DenseRows r: enumerate r;
+ _: [];
+ esac);
+
+enumerate m =
+ if isSparse? m then enumerateSparse m else enumerateDense m fi;
+
+// Make a sparse matrix from entries whose i, j values are known to be
+// within range
+makeSparse type size data =
+ (isRow = case type of RowMajor (): true; ColumnMajor (): false esac;
+ ordered =
+ sortBy do a b:
+ if a.maj == b.maj then a.min < b.min else a.maj < b.maj fi
+ done
+ (map
+ if isRow then
+ do { i, j, v }: { maj = i, min = j, v } done;
+ else
+ do { i, j, v }: { maj = j, min = i, v } done;
+ fi
+ (filter do d: d.v != 0 done data));
+ tagger = if isRow then SparseCSR else SparseCSC fi;
+ majorSize = if isRow then size.rows else size.columns fi;
+ minorSize = if isRow then size.columns else size.rows fi;
+ pointers = array [0];
+ setArrayCapacity pointers (size.rows + 1);
+ fillPointers n i data =
+ if n < majorSize then
+ case data of
+ d::rest:
+ (for [n..d-1] \(push pointers i);
+ fillPointers d (i+1) rest);
+ _:
+ for [n..majorSize-1] \(push pointers i);
+ esac;
+ fi;
+ fillPointers 0 0 (map (.maj) ordered);
+ tagger {
+ values = vec.fromList (map (.v) ordered),
+ indices = array (map (.min) ordered),
+ pointers,
+ extent = minorSize,
+ });
+
+// Make a sparse matrix from entries that may contain out-of-range
+// cells which need to be filtered out. This is the public API for
+// makeSparse and is also used to discard out-of-range cells from
+// resizedTo.
+newSparseMatrix type size data =
+ makeSparse type size
+ (filter
+ do { i, j, v }:
+ i == int i and i >= 0 and i < size.rows and
+ j == int j and j >= 0 and j < size.columns
+ done data);
+
+toSparse m =
+ if isSparse? m then m
+ else
+ makeSparse (typeOf m) (size m) (enumerateDense m);
+ fi;
+
+toDense m =
+ if not (isSparse? m) then m
+ elif isRowMajor? m then
+ DenseRows (array (map do row: getRow row m done [0..height m - 1]));
+ else
+ DenseCols (array (map do col: getColumn col m done [0..width m - 1]));
+ fi;
+
+constMatrix n = generate do row col: n done;
+randomMatrix = generate do row col: Math#random() done;
+identityMatrix = constMatrix 1;
+
+transposed m =
+ case m of
+ DenseRows d: DenseCols d;
+ DenseCols d: DenseRows d;
+ SparseCSR d: SparseCSC d;
+ SparseCSC d: SparseCSR d;
+ esac;
+
+flipped m =
+ if isSparse? m then
+ makeSparse (flippedTypeOf m) (size m) (enumerateSparse m)
+ else
+ if isRowMajor? m then
+ generate do row col: at' m row col done (size m);
+ else
+ transposed
+ (generate do row col: at' m col row done
+ { rows = (width m), columns = (height m) });
+ fi
+ fi;
+
+toRowMajor m =
+ if isRowMajor? m then m else flipped m fi;
+
+toColumnMajor m =
+ if not isRowMajor? m then m else flipped m fi;
+
+equal'' comparator vecComparator m1 m2 =
+ // Prerequisite: m1 and m2 have same sparse-p and storage order
+ (compareVecLists vv1 vv2 = all id (map2 vecComparator vv1 vv2);
+ compareSparse d1 d2 =
+ d1.extent == d2.extent and
+ vecComparator d1.values d2.values and
+ d1.indices == d2.indices and
+ d1.pointers == d2.pointers;
+ case m1 of
+ DenseRows d1:
+ case m2 of DenseRows d2: compareVecLists d1 d2; _: false; esac;
+ DenseCols d1:
+ case m2 of DenseCols d2: compareVecLists d1 d2; _: false; esac;
+ SparseCSR d1:
+ case m2 of SparseCSR d2: compareSparse d1 d2; _: false; esac;
+ SparseCSC d1:
+ case m2 of SparseCSC d2: compareSparse d1 d2; _: false; esac;
+ esac);
+
+equal' comparator vecComparator m1 m2 =
+ if empty?' m1 and empty?' m2 then
+ true
+ elif size m1 != size m2 then
+ false
+ elif isRowMajor? m1 != isRowMajor? m2 then
+ equal' comparator vecComparator (flipped m1) m2;
+ elif isSparse? m1 != isSparse? m2 then
+ if isSparse? m1 then
+ equal' comparator vecComparator m1 (toSparse m2)
+ else
+ equal' comparator vecComparator (toSparse m1) m2
+ fi
+ else
+ equal'' comparator vecComparator m1 m2
+ fi;
+
+// Compare matrices using the given comparator for individual cells.
+// Note that matrices with different storage order but the same
+// contents are equal, although comparing them is slow.
+//!!! Document the fact that sparse matrices can only be equal if they
+// have the same set of non-zero cells (regardless of comparator used)
+equalUnder comparator =
+ equal' comparator (vec.equalUnder comparator);
+
+equal =
+ equal' (==) vec.equal;
+
+newMatrix type data = //!!! NB does not copy data
+ (tagger = case type of RowMajor (): DenseRows; ColumnMajor (): DenseCols esac;
+ if empty? data or vec.empty? (head data)
+ then zeroSizeMatrix ()
+ else tagger (array data)
+ fi);
+
+newRowVector data = //!!! NB does not copy data
+ DenseRows (array [data]);
+
+newColumnVector data = //!!! NB does not copy data
+ DenseCols (array [data]);
+
+denseLinearOp op m1 m2 =
+ if isRowMajor? m1 then
+ newMatrix (typeOf m1)
+ (map2 do c1 c2: op c1 c2 done (asRows m1) (asRows m2));
+ else
+ newMatrix (typeOf m1)
+ (map2 do c1 c2: op c1 c2 done (asColumns m1) (asColumns m2));
+ fi;
+
+sparseSumOrDifference op m1 m2 =
+ (h = [:];
+ for (enumerate m1) do { i, j, v }:
+ if not (i in h) then h[i] := [:] fi;
+ h[i][j] := v;
+ done;
+ for (enumerate m2) do { i, j, v }:
+ if not (i in h) then h[i] := [:] fi;
+ if j in h[i] then h[i][j] := op h[i][j] v;
+ else h[i][j] := op 0 v;
+ fi;
+ done;
+ entries = concat
+ (map do i:
+ kk = keys h[i];
+ map2 do j v: { i, j, v } done kk (map (at h[i]) kk)
+ done (keys h));
+ makeSparse (typeOf m1) (size m1) entries);
+
+sum' m1 m2 =
+ if (size m1) != (size m2)
+ then failWith "Matrices are not the same size: \(size m1), \(size m2)";
+ elif isSparse? m1 and isSparse? m2 then
+ sparseSumOrDifference (+) m1 m2;
+ else
+ add2 v1 v2 = bf.add [v1,v2];
+ denseLinearOp add2 m1 m2;
+ fi;
+
+difference m1 m2 =
+ if (size m1) != (size m2)
+ then failWith "Matrices are not the same size: \(size m1), \(size m2)";
+ elif isSparse? m1 and isSparse? m2 then
+ sparseSumOrDifference (-) m1 m2;
+ else
+ denseLinearOp bf.subtract m1 m2;
+ fi;
+
+scaled factor m =
+ if isSparse? m then
+ makeSparse (typeOf m) (size m)
+ (map do { i, j, v }: { i, j, v = factor * v } done (enumerate m))
+ elif isRowMajor? m then
+ newMatrix (typeOf m) (map (bf.scaled factor) (asRows m));
+ else
+ newMatrix (typeOf m) (map (bf.scaled factor) (asColumns m));
+ fi;
+
+abs' m =
+ if isSparse? m then
+ makeSparse (typeOf m) (size m)
+ (map do { i, j, v }: { i, j, v = abs v } done (enumerate m))
+ elif isRowMajor? m then
+ newMatrix (typeOf m) (map bf.abs (asRows m));
+ else
+ newMatrix (typeOf m) (map bf.abs (asColumns m));
+ fi;
+
+filter' f m =
+ if isSparse? m then
+ makeSparse (typeOf m) (size m)
+ (map do { i, j, v }: { i, j, v = if f v then v else 0 fi } done
+ (enumerate m))
+ else
+ vfilter = vec.fromList . (map do i: if f i then i else 0 fi done) . vec.list;
+ if isRowMajor? m then
+ newMatrix (typeOf m) (map vfilter (asRows m));
+ else
+ newMatrix (typeOf m) (map vfilter (asColumns m));
+ fi;
+ fi;
+
+sparseProductLeft size m1 m2 =
+ ({ values, indices, pointers } = case m1 of
+ SparseCSR d: d;
+ SparseCSC d: d;
+ _: failWith "sparseProductLeft called for non-sparse m1";
+ esac;
+ rows = isRowMajor? m1;
+ data = array (map \(new double[size.rows]) [1..size.columns]);
+ for [0..size.columns - 1] do j':
+ c = getColumn j' m2;
+ var p = 0;
+ for [0..length indices - 1] do ix:
+ ix == pointers[p+1] loop (p := p + 1);
+ i = if rows then p else indices[ix] fi;
+ j = if rows then indices[ix] else p fi;
+ data[j'][i] := data[j'][i] + (vec.at values ix) * (vec.at c j);
+ done;
+ done;
+ DenseCols (array (map vec.vector (list data))));
+
+sparseProductRight size m1 m2 =
+ ({ values, indices, pointers } = case m2 of
+ SparseCSR d: d;
+ SparseCSC d: d;
+ _: failWith "sparseProductLeft called for non-sparse m1";
+ esac;
+ rows = isRowMajor? m2;
+ data = array (map \(new double[size.columns]) [1..size.rows]);
+ for [0..size.rows - 1] do i':
+ r = getRow i' m1;
+ var p = 0;
+ for [0..length indices - 1] do ix:
+ ix == pointers[p+1] loop (p := p + 1);
+ i = if rows then p else indices[ix] fi;
+ j = if rows then indices[ix] else p fi;
+ data[i'][j] := data[i'][j] + (vec.at values ix) * (vec.at r i);
+ done;
+ done;
+ DenseRows (array (map vec.vector (list data))));
+
+sparseProduct size m1 m2 =
+ case m2 of
+ SparseCSC d:
+ ({ values, indices, pointers } = case m1 of
+ SparseCSR d1: d1;
+ SparseCSC d1: d1;
+ _: failWith "sparseProduct called for non-sparse matrices";
+ esac;
+ rows = isRowMajor? m1;
+ var p = 0;
+ pindices = new int[length indices];
+ for [0..length indices - 1] do ix:
+ ix == pointers[p+1] loop (p := p + 1);
+ pindices[ix] := p;
+ done;
+ entries =
+ (map do j':
+ cs = sparseSlice j' d;
+ hin = mapIntoHash
+ (at cs.indices) (vec.at cs.values)
+ [0..length cs.indices - 1];
+ hout = [:];
+ for [0..length indices - 1] do ix:
+ i = if rows then pindices[ix] else indices[ix] fi;
+ j = if rows then indices[ix] else pindices[ix] fi;
+ if j in hin then
+ p = (vec.at values ix) * hin[j];
+ hout[i] := p + (if i in hout then hout[i] else 0 fi);
+ fi;
+ done;
+ map do i:
+ { i, j = j', v = hout[i] }
+ done (keys hout);
+ done (nonEmptySlices d));
+ makeSparse (ColumnMajor ()) size (concat entries));
+ SparseCSR _:
+ sparseProduct size m1 (flipped m2);
+ _: failWith "sparseProduct called for non-sparse matrices";
+ esac;
+
+denseProduct size m1 m2 =
+ (data = array (map \(new double[size.rows]) [1..size.columns]);
+ for [0..size.rows - 1] do i:
+ row = getRow i m1;
+ for [0..size.columns - 1] do j:
+ data[j][i] := bf.sum (bf.multiply row (getColumn j m2));
+ done;
+ done;
+ DenseCols (array (map vec.vector (list data))));
+
+product m1 m2 =
+ if (size m1).columns != (size m2).rows
+ then failWith "Matrix dimensions incompatible: \(size m1), \(size m2) (\((size m1).columns) != \((size m2).rows))";
+ else
+ size = { rows = (size m1).rows, columns = (size m2).columns };
+ if isSparse? m1 then
+ if isSparse? m2 then
+ sparseProduct size m1 m2
+ else
+ sparseProductLeft size m1 m2
+ fi
+ elif isSparse? m2 then
+ sparseProductRight size m1 m2
+ else
+ denseProduct size m1 m2
+ fi;
+ fi;
+
+entryWiseProduct m1 m2 = // or element-wise, or Hadamard product
+//!!! todo: faster, sparse version, units
+ if (size m1) != (size m2)
+ then failWith "Matrices are not the same size: \(size m1), \(size m2)";
+ else generate do row col: at' m1 row col * at' m2 row col done (size m1);
+ fi;
+
+concatAgainstGrain tagger getter counter mm =
+ (n = counter (size (head mm));
+ tagger (array
+ (map do i:
+ vec.concat (map (getter i) mm)
+ done [0..n-1])));
+
+concatWithGrain tagger getter counter mm =
+ tagger (array
+ (concat
+ (map do m:
+ n = counter (size m);
+ map do i: getter i m done [0..n-1]
+ done mm)));
+
+sparseConcat direction first mm =
+ (dimension d f = if direction == d then sum (map f mm) else f first fi;
+ rows = dimension (Vertical ()) height;
+ columns = dimension (Horizontal ()) width;
+ entries ioff joff ui uj mm acc =
+ case mm of
+ m::rest:
+ entries
+ (ioff + ui * height m)
+ (joff + uj * width m)
+ ui uj rest
+ ((map do { i, j, v }: { i = i + ioff, j = j + joff, v }
+ done (enumerate m)) ++ acc);
+ _: acc;
+ esac;
+ makeSparse (typeOf first) { rows, columns }
+ if direction == Vertical () then entries 0 0 1 0 mm []
+ else entries 0 0 0 1 mm [] fi);
+
+checkDimensionsFor direction first mm =
+ (counter = if direction == Horizontal () then (.rows) else (.columns) fi;
+ n = counter (size first);
+ if not (all id (map do m: counter (size m) == n done mm)) then
+ failWith "Matrix dimensions incompatible for concat (found \(map do m: counter (size m) done mm) not all of which are \(n))";
+ fi);
+
+concat' direction mm = //!!! doc: storage order is taken from first matrix in sequence; concat is obviously not lazy (unlike std module)
+ case length mm of
+ 0: zeroSizeMatrix ();
+ 1: head mm;
+ _:
+ first = head mm;
+ checkDimensionsFor direction first mm;
+ if all isSparse? mm then
+ sparseConcat direction first mm
+ else
+ row = isRowMajor? first;
+ // horizontal, row-major: against grain with rows
+ // horizontal, col-major: with grain with cols
+ // vertical, row-major: with grain with rows
+ // vertical, col-major: against grain with cols
+ case direction of
+ Horizontal ():
+ if row then concatAgainstGrain DenseRows getRow (.rows) mm;
+ else concatWithGrain DenseCols getColumn (.columns) mm;
+ fi;
+ Vertical ():
+ if row then concatWithGrain DenseRows getRow (.rows) mm;
+ else concatAgainstGrain DenseCols getColumn (.columns) mm;
+ fi;
+ esac;
+ fi;
+ esac;
+
+//!!! doc this filter -- zero-size elts are ignored
+concat direction mm =
+ concat' direction (filter do mat: not (empty?' mat) done mm);
+
+//!!! next two v. clumsy
+
+//!!! doc note: argument order chosen for consistency with std module slice
+//!!! NB always returns dense matrix, should have sparse version
+rowSlice m start end = //!!! doc: storage order same as input
+ if start < 0 then rowSlice m 0 end
+ elif start > height m then rowSlice m (height m) end
+ else
+ if end < start then rowSlice m start start
+ elif end > height m then rowSlice m start (height m)
+ else
+ if isRowMajor? m then
+ DenseRows (array (map ((flip getRow) m) [start .. end - 1]))
+ else
+ DenseCols (array (map do v: vec.slice v start end done (asColumns m)))
+ fi;
+ fi;
+ fi;
+
+//!!! doc note: argument order chosen for consistency with std module slice
+//!!! NB always returns dense matrix, should have sparse version
+columnSlice m start end = //!!! doc: storage order same as input
+ if start < 0 then columnSlice m 0 end
+ elif start > width m then columnSlice m (width m) end
+ else
+ if end < start then columnSlice m start start
+ elif end > width m then columnSlice m start (width m)
+ else
+ if not isRowMajor? m then
+ DenseCols (array (map ((flip getColumn) m) [start .. end - 1]))
+ else
+ DenseRows (array (map do v: vec.slice v start end done (asRows m)))
+ fi;
+ fi;
+ fi;
+
+resizedTo newsize m =
+ (if newsize == (size m) then
+ m
+ elif isSparse? m then
+ // don't call makeSparse directly: want to discard
+ // out-of-range cells
+ newSparseMatrix (typeOf m) newsize (enumerateSparse m)
+ elif (height m) == 0 or (width m) == 0 then
+ zeroMatrixWithTypeOf m newsize;
+ else
+ growrows = newsize.rows - (height m);
+ growcols = newsize.columns - (width m);
+ rowm = isRowMajor? m;
+ resizedTo newsize
+ if rowm and growrows < 0 then
+ rowSlice m 0 newsize.rows
+ elif (not rowm) and growcols < 0 then
+ columnSlice m 0 newsize.columns
+ elif growrows < 0 then
+ rowSlice m 0 newsize.rows
+ elif growcols < 0 then
+ columnSlice m 0 newsize.columns
+ else
+ if growrows > 0 then
+ concat (Vertical ())
+ [m, zeroMatrixWithTypeOf m ((size m) with { rows = growrows })]
+ else
+ concat (Horizontal ())
+ [m, zeroMatrixWithTypeOf m ((size m) with { columns = growcols })]
+ fi
+ fi
+ fi);
+
+{
+ size,
+ width,
+ height,
+ density,
+ nonZeroValues,
+ at = at',
+ getColumn,
+ getRow,
+ isRowMajor?,
+ isSparse?,
+ generate,
+ constMatrix,
+ randomMatrix,
+ zeroMatrix,
+ identityMatrix,
+ zeroSizeMatrix,
+ empty? = empty?',
+ equal,
+ equalUnder,
+ transposed,
+ flipped,
+ toRowMajor,
+ toColumnMajor,
+ toSparse,
+ toDense,
+ scaled,
+ resizedTo,
+ asRows,
+ asColumns,
+ sum = sum',
+ difference,
+ abs = abs',
+ filter = filter',
+ product,
+ entryWiseProduct,
+ concat,
+ rowSlice,
+ columnSlice,
+ newMatrix,
+ newRowVector,
+ newColumnVector,
+ newSparseMatrix,
+ enumerate
+}
+as
+{
+//!!! check whether these are right to be .selector rather than just selector
+
+ size is matrix -> { .rows is number, .columns is number },
+ width is matrix -> number,
+ height is matrix -> number,
+ density is matrix -> number,
+ nonZeroValues is matrix -> number,
+ at is matrix -> number -> number -> number,
+ getColumn is number -> matrix -> vector,
+ getRow is number -> matrix -> vector,
+ isRowMajor? is matrix -> boolean,
+ isSparse? is matrix -> boolean,
+ generate is (number -> number -> number) -> { .rows is number, .columns is number } -> matrix,
+ constMatrix is number -> { .rows is number, .columns is number } -> matrix,
+ randomMatrix is { .rows is number, .columns is number } -> matrix,
+ zeroMatrix is { .rows is number, .columns is number } -> matrix,
+ identityMatrix is { .rows is number, .columns is number } -> matrix,
+ zeroSizeMatrix is () -> matrix,
+ empty? is matrix -> boolean,
+ equal is matrix -> matrix -> boolean,
+ equalUnder is (number -> number -> boolean) -> matrix -> matrix -> boolean,
+ transposed is matrix -> matrix,
+ flipped is matrix -> matrix,
+ toRowMajor is matrix -> matrix,
+ toColumnMajor is matrix -> matrix,
+ toSparse is matrix -> matrix,
+ toDense is matrix -> matrix,
+ scaled is number -> matrix -> matrix,
+ thresholded is number -> matrix -> matrix,
+ resizedTo is { .rows is number, .columns is number } -> matrix -> matrix,
+ asRows is matrix -> list,
+ asColumns is matrix -> list,
+ sum is matrix -> matrix -> matrix,
+ difference is matrix -> matrix -> matrix,
+ abs is matrix -> matrix,
+ filter is (number -> boolean) -> matrix -> matrix,
+ product is matrix -> matrix -> matrix,
+ entryWiseProduct is matrix -> matrix -> matrix,
+ concat is (Horizontal () | Vertical ()) -> list -> matrix,
+ rowSlice is matrix -> number -> number -> matrix,
+ columnSlice is matrix -> number -> number -> matrix,
+ newMatrix is (ColumnMajor () | RowMajor ()) -> list -> matrix,
+ newRowVector is vector -> matrix,
+ newColumnVector is vector -> matrix,
+ newSparseMatrix is (ColumnMajor () | RowMajor ()) -> { .rows is number, .columns is number } -> list<{ .i is number, .j is number, .v is number }> -> matrix,
+ enumerate is matrix -> list<{ .i is number, .j is number, .v is number }>
+}
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/matrix/test/speedtest.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/matrix/test/speedtest.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,218 @@
+
+program may.matrix.test.speedtest;
+
+mat = load may.matrix;
+vec = load may.vector;
+
+{ compare, compareUsing, time } = load may.test.test;
+
+norec time f = time "" f;
+
+compareMatrices = compareUsing mat.equal;
+
+makeMatrices sz sparsity =
+ (print "Making \(sz) * \(sz) random matrix...";
+ m = time \(mat.randomMatrix { rows = sz, columns = sz });
+ makeSparse () =
+ (print "Making \(sparsity * 100)% sparse version (as dense matrix)...";
+ t = time \(mat.filter (> sparsity) m);
+ println "Reported density: \(mat.density t) (non-zero values: \(mat.nonZeroValues t))";
+ print "Converting to sparse matrix...";
+ s = time \(mat.toSparse t);
+ println "Reported density: \(mat.density s) (non-zero values: \(mat.nonZeroValues s))";
+ s);
+ s = makeSparse ();
+ println "Making types:";
+ print "Col-major dense...";
+ cmd = time \(mat.toColumnMajor m);
+ print "Row-major dense...";
+ rmd = time \(mat.toRowMajor m);
+ print "Col-major sparse...";
+ cms = time \(mat.toColumnMajor s);
+ print "Row-major sparse...";
+ rms = time \(mat.toRowMajor s);
+ println "";
+ { cmd, rmd, cms, rms });
+
+println "\nR * M multiplies:\n";
+
+sz = 2000;
+sparsity = 0.95;
+
+{ cmd, rmd, cms, rms } = makeMatrices sz sparsity;
+
+row = mat.newRowVector (vec.fromList (map \(Math#random()) [1..sz]));
+col = mat.newColumnVector (vec.fromList (map \(Math#random()) [1..sz]));
+
+print "R * CMD... ";
+a = (time \(mat.product row cmd));
+
+print "R * RMD... ";
+b = (time \(mat.product row rmd));
+
+print "R * CMS... ";
+c = (time \(mat.product row cms));
+
+print "R * RMS... ";
+d = (time \(mat.product row rms));
+
+println "\nChecking results: \(compareMatrices a b) \(compareMatrices c d)";
+
+println "\nM * C multiplies:\n";
+
+print "CMD * C... ";
+a = (time \(mat.product cmd col));
+
+print "RMD * C... ";
+b = (time \(mat.product rmd col));
+
+print "CMS * C... ";
+c = (time \(mat.product cms col));
+
+print "RMS * C... ";
+d = (time \(mat.product rms col));
+
+println "\nChecking results: \(compareMatrices a b) \(compareMatrices c d)";
+
+reportOn m =
+ (print " ";
+ println "isSparse: \(mat.isSparse? m), density \(mat.density m)");
+
+println "\nM * M multiplies (and a few sums):\n";
+
+sz = 500;
+
+{ cmd, rmd, cms, rms } = makeMatrices sz sparsity;
+
+print "CMS * CMD... ";
+reportOn (time \(mat.product cms cmd));
+
+print "CMS * RMD... ";
+reportOn (time \(mat.product cms rmd));
+
+print "RMS * CMD... ";
+reportOn (time \(mat.product rms cmd));
+
+print "RMS * RMD... ";
+reportOn (time \(mat.product rms rmd));
+
+println "";
+
+print "CMD * CMS... ";
+reportOn (time \(mat.product cmd cms));
+
+print "CMD * RMS... ";
+reportOn (time \(mat.product cmd rms));
+
+print "RMD * CMS... ";
+reportOn (time \(mat.product rmd cms));
+
+print "RMD * RMS... ";
+reportOn (time \(mat.product rmd rms));
+
+println "";
+
+print "CMS * CMS... ";
+reportOn (time \(mat.product cms cms));
+
+print "CMS * RMS... ";
+reportOn (time \(mat.product cms rms));
+
+print "RMS * CMS... ";
+reportOn (time \(mat.product rms cms));
+
+print "RMS * RMS... ";
+reportOn (time \(mat.product rms rms));
+
+println "";
+
+print "CMD + CMD... ";
+reportOn (time \(mat.sum cmd cmd));
+
+print "CMD + RMD... ";
+reportOn (time \(mat.sum cmd rmd));
+
+print "RMD + CMD... ";
+reportOn (time \(mat.sum rmd cmd));
+
+print "RMD + RMD... ";
+reportOn (time \(mat.sum rmd rmd));
+
+println "";
+
+print "CMS + CMS... ";
+reportOn (time \(mat.sum cms cms));
+
+print "CMS + RMS... ";
+reportOn (time \(mat.sum cms rms));
+
+print "RMS + CMS... ";
+reportOn (time \(mat.sum rms cms));
+
+print "RMS + RMS... ";
+reportOn (time \(mat.sum rms rms));
+
+println "";
+
+print "CMD * CMD... ";
+reportOn (time \(mat.product cmd cmd));
+
+print "CMD * RMD... ";
+reportOn (time \(mat.product cmd rmd));
+
+print "RMD * CMD... ";
+reportOn (time \(mat.product rmd cmd));
+
+print "RMD * RMD... ";
+reportOn (time \(mat.product rmd rmd));
+
+println "\nLarge sparse M * M multiplies and adds:\n";
+
+sz = 5000000;
+nnz = 10000;
+
+print "Calculating \(nnz) non-zero entry records...";
+entries = time \(e = map \({ i = int (Math#random() * sz),
+ j = int (Math#random() * sz),
+ v = Math#random() }) [1..nnz];
+ \() (length e); // make sure list non-lazy for timing purposes
+ e);
+
+print "Making \(sz) * \(sz) random matrix with \(nnz) entries...";
+rms = time \(mat.newSparseMatrix (RowMajor ()) { rows = sz, columns = sz }
+ entries);
+println "Reported density: \(mat.density rms) (non-zero values: \(mat.nonZeroValues rms))";
+
+print "Making col-major copy...";
+cms = time \(mat.toColumnMajor rms);
+println "Reported density: \(mat.density cms) (non-zero values: \(mat.nonZeroValues cms))";
+
+println "";
+
+print "CMS * CMS... ";
+reportOn (time \(mat.product cms cms));
+
+print "CMS * RMS... ";
+reportOn (time \(mat.product cms rms));
+
+print "RMS * CMS... ";
+reportOn (time \(mat.product rms cms));
+
+print "RMS * RMS... ";
+reportOn (time \(mat.product rms rms));
+
+println "";
+
+print "CMS + CMS... ";
+reportOn (time \(mat.sum cms cms));
+
+print "CMS + RMS... ";
+reportOn (time \(mat.sum cms rms));
+
+print "RMS + CMS... ";
+reportOn (time \(mat.sum rms cms));
+
+print "RMS + RMS... ";
+reportOn (time \(mat.sum rms rms));
+
+();
diff -r 4b104ef8d110 -r 20903ee53719 src/may/matrix/test/test_matrix.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/matrix/test/test_matrix.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,575 @@
+
+module may.matrix.test.test_matrix;
+
+mat = load may.matrix;
+vec = load may.vector;
+
+load may.vector.type;
+load may.matrix.type;
+
+import yeti.lang: FailureException;
+
+{ compare, compareUsing } = load may.test.test;
+
+compareMatrices = compareUsing mat.equal;
+
+makeTests name flipper =
+ (constMatrix n s = flipper (mat.constMatrix n s);
+ zeroMatrix s = flipper (mat.zeroMatrix s);
+ randomMatrix s = flipper (mat.randomMatrix s);
+ identityMatrix s = flipper (mat.identityMatrix s);
+ generate f s = flipper (mat.generate f s);
+ newMatrix t d = flipper (mat.newMatrix t (map vec.fromList d));
+[
+
+"constMatrixEmpty-\(name)": \(
+ m = constMatrix 2 { rows = 0, columns = 0 };
+ compare (mat.size m) { columns = 0, rows = 0 }
+),
+
+"constMatrixEmpty2-\(name)": \(
+ compare (mat.size (constMatrix 2 { rows = 0, columns = 4 })) { columns = 0, rows = 0 } and
+ compare (mat.size (constMatrix 2 { rows = 4, columns = 0 })) { columns = 0, rows = 0 }
+),
+
+"constMatrix-\(name)": \(
+ m = constMatrix 2 { rows = 3, columns = 4 };
+ compare (mat.size m) { columns = 4, rows = 3 } and
+ all id (map do row: compare (vec.list (mat.getRow row m)) [2,2,2,2] done [0..2]) and
+ all id (map do col: compare (vec.list (mat.getColumn col m)) [2,2,2] done [0..3])
+),
+
+"randomMatrixEmpty-\(name)": \(
+ m = randomMatrix { rows = 0, columns = 0 };
+ compare (mat.size m) { columns = 0, rows = 0 }
+),
+
+"randomMatrix-\(name)": \(
+ m = randomMatrix { rows = 3, columns = 4 };
+ compare (mat.size m) { columns = 4, rows = 3 }
+),
+
+"zeroMatrixEmpty-\(name)": \(
+ m = zeroMatrix { rows = 0, columns = 0 };
+ compare (mat.size m) { columns = 0, rows = 0 }
+),
+
+"zeroMatrix-\(name)": \(
+ m = zeroMatrix { rows = 3, columns = 4 };
+ compare (mat.size m) { columns = 4, rows = 3 } and
+ all id (map do row: compare (vec.list (mat.getRow row m)) [0,0,0,0] done [0..2]) and
+ all id (map do col: compare (vec.list (mat.getColumn col m)) [0,0,0] done [0..3])
+),
+
+"identityMatrixEmpty-\(name)": \(
+ m = identityMatrix { rows = 0, columns = 0 };
+ compare (mat.size m) { columns = 0, rows = 0 }
+),
+
+"identityMatrix-\(name)": \(
+ m = identityMatrix { rows = 3, columns = 4 };
+ compare (mat.size m) { columns = 4, rows = 3 } and
+ all id (map do row: compare (vec.list (mat.getRow row m)) [1,1,1,1] done [0..2]) and
+ all id (map do col: compare (vec.list (mat.getColumn col m)) [1,1,1] done [0..3])
+),
+
+"generateEmpty-\(name)": \(
+ m = generate do row col: 0 done { rows = 0, columns = 0 };
+ compare (mat.size m) { columns = 0, rows = 0 }
+),
+
+"generate-\(name)": \(
+ m = generate do row col: row * 10 + col done { rows = 2, columns = 3 };
+ compare (vec.list (mat.getRow 0 m)) [0,1,2] and
+ compare (vec.list (mat.getRow 1 m)) [10,11,12]
+),
+
+"widthAndHeight-\(name)": \(
+ m = constMatrix 2 { rows = 3, columns = 4 };
+ compare (mat.size m) { columns = mat.width m, rows = mat.height m }
+),
+
+"equal-\(name)": \(
+ m = newMatrix (ColumnMajor ()) [[1,4],[0,5],[3,6]];
+ n = m;
+ p = newMatrix (RowMajor ()) [[1,0,3],[4,5,6]];
+ q = newMatrix (ColumnMajor ()) [[1,0,3],[4,5,6]];
+ r = newMatrix (ColumnMajor ()) [[1,4],[0,5]];
+ compareMatrices m n and
+ compareMatrices m p and
+ compareMatrices n p and
+ not mat.equal m q and
+ not mat.equal m r
+),
+
+"equalUnder-\(name)": \(
+ p = newMatrix (ColumnMajor ()) [[1,2,3],[4,5,6]];
+ q = newMatrix (ColumnMajor ()) [[1,2,3],[4,5,6]];
+ r = newMatrix (ColumnMajor ()) [[4,3,1],[3,1,2]];
+ s = newMatrix (ColumnMajor ()) [[1,4,5],[6,7,8]];
+ t = newMatrix (ColumnMajor ()) [[1,4,5],[6,7,9]];
+ mat.equalUnder (==) p p and
+ mat.equalUnder (==) p q and
+ mat.equalUnder (!=) p r and
+ mat.equalUnder do a b: a % 2 == b % 2 done p s and
+ not mat.equalUnder do a b: a % 2 == b % 2 done p t
+),
+
+"at-\(name)": \(
+ generator row col = row * 10 + col;
+ m = generate generator { rows = 2, columns = 3 };
+ all id
+ (map do row: all id
+ (map do col: mat.at m row col == generator row col done [0..2])
+ done [0..1])
+),
+
+"transposedEmpty-\(name)": \(
+ compare (mat.size (mat.transposed (constMatrix 2 { rows = 0, columns = 0 }))) { columns = 0, rows = 0 } and
+ compare (mat.size (mat.transposed (constMatrix 2 { rows = 0, columns = 4 }))) { columns = 0, rows = 0 } and
+ compare (mat.size (mat.transposed (constMatrix 2 { rows = 4, columns = 0 }))) { columns = 0, rows = 0 }
+),
+
+"transposedSize-\(name)": \(
+ compare (mat.size (mat.transposed (constMatrix 2 { rows = 3, columns = 4 }))) { columns = 3, rows = 4 }
+),
+
+"transposed-\(name)": \(
+ generator row col = row * 10 + col;
+ m = generate generator { rows = 2, columns = 3 };
+ m' = mat.transposed m;
+ all id
+ (map do row: all id
+ // like at test, but with col/row flipped
+ (map do col: mat.at m' col row == generator row col done [0..2])
+ done [0..1])
+),
+
+"transposed-back-\(name)": \(
+ m = newMatrix (ColumnMajor ()) [[1,4],[2,5],[3,6]];
+ compareMatrices m (mat.transposed (mat.transposed m)) and
+ not mat.equal m (mat.transposed m);
+),
+
+"flipped-\(name)": \(
+ m = newMatrix (ColumnMajor ()) [[1,4],[0,5],[3,6]];
+ m' = mat.flipped m;
+ m'' = newMatrix (RowMajor ()) [[1,0,3],[4,5,6]];
+ compareMatrices m m' and compareMatrices m m'' and compareMatrices m' m'';
+),
+
+"flipped-back-\(name)": \(
+ m = newMatrix (ColumnMajor ()) [[1,4],[0,5],[3,6]];
+ compareMatrices m (mat.flipped (mat.flipped m));
+),
+
+"flipped-empty-\(name)": \(
+ m = constMatrix 2 { rows = 0, columns = 4 };
+ compareMatrices (mat.flipped m) (mat.flipped (constMatrix 0 { rows = 0, columns = 0 }));
+),
+
+"toRowMajor-\(name)": \(
+ m = newMatrix (ColumnMajor ()) [[1,4],[0,5],[3,6]];
+ m' = mat.toRowMajor m;
+ m'' = newMatrix (RowMajor ()) [[1,0,3],[4,5,6]];
+ m''' = mat.toRowMajor m'';
+ compareMatrices m m' and compareMatrices m m'' and compareMatrices m' m''
+ and compareMatrices m m''';
+),
+
+"toColumnMajor-\(name)": \(
+ m = newMatrix (RowMajor ()) [[1,4],[0,5],[3,6]];
+ m' = mat.toColumnMajor m;
+ m'' = newMatrix (ColumnMajor ()) [[1,0,3],[4,5,6]];
+ m''' = mat.toColumnMajor m'';
+ compareMatrices m m' and compareMatrices m m'' and compareMatrices m' m''
+ and compareMatrices m m''';
+),
+
+"scaled-\(name)": \(
+ compareMatrices
+ (mat.scaled 0.5 (constMatrix 2 { rows = 3, columns = 4 }))
+ (constMatrix 1 { rows = 3, columns = 4 }) and
+ compareMatrices
+ (mat.scaled 0.5 (constMatrix (-3) { rows = 3, columns = 4 }))
+ (constMatrix (-1.5) { rows = 3, columns = 4 }) and
+ compareMatrices
+ (mat.scaled 0.5 (constMatrix 2 { rows = 0, columns = 2 }))
+ (constMatrix 5 { rows = 0, columns = 0 })
+),
+
+"sum-\(name)": \(
+ compareMatrices
+ (mat.sum (constMatrix 2 { rows = 3, columns = 4 })
+ (constMatrix 1 { rows = 3, columns = 4 }))
+ (constMatrix 3 { rows = 3, columns = 4 })
+),
+
+"sumFail-\(name)": \(
+ try
+ \() (mat.sum (constMatrix 2 { rows = 3, columns = 4 })
+ (constMatrix 1 { rows = 3, columns = 5 }));
+ false;
+ catch FailureException e:
+ true
+ yrt
+),
+
+"sparseSum-\(name)": \(
+ s = mat.newSparseMatrix (ColumnMajor ()) { rows = 2, columns = 3 } [
+ { i = 0, j = 0, v = 1 },
+ { i = 0, j = 2, v = 2 },
+ { i = 1, j = 1, v = 4 },
+ ];
+ t = mat.newSparseMatrix (ColumnMajor ()) { rows = 2, columns = 3 } [
+ { i = 0, j = 1, v = 7 },
+ { i = 1, j = 0, v = 5 },
+ { i = 1, j = 1, v = -4 }, // NB this means [1,1] -> 0, sparse zero
+ ];
+ tot = mat.sum s t;
+ mat.isSparse? tot and
+ compareMatrices tot (mat.sum (mat.toDense s) t) and
+ compareMatrices tot (mat.sum (mat.toDense s) (mat.toDense t)) and
+ compareMatrices tot (mat.sum s (mat.toDense t)) and
+ compareMatrices tot
+ (mat.newSparseMatrix (RowMajor ()) { rows = 2, columns = 3 } [
+ { i = 0, j = 0, v = 1 },
+ { i = 0, j = 1, v = 7 },
+ { i = 0, j = 2, v = 2 },
+ { i = 1, j = 0, v = 5 },
+ ]) and
+ compare (mat.density tot) (4/6)
+),
+
+"difference-\(name)": \(
+ compareMatrices
+ (mat.difference (constMatrix 2 { rows = 3, columns = 4 })
+ (constMatrix 1 { rows = 3, columns = 4 }))
+ (constMatrix 1 { rows = 3, columns = 4 })
+),
+
+"differenceFail-\(name)": \(
+ try
+ \() (mat.difference (constMatrix 2 { rows = 3, columns = 4 })
+ (constMatrix 1 { rows = 3, columns = 5 }));
+ false;
+ catch FailureException e:
+ true
+ yrt
+),
+
+"sparseDifference-\(name)": \(
+ s = mat.newSparseMatrix (ColumnMajor ()) { rows = 2, columns = 3 } [
+ { i = 0, j = 0, v = 1 },
+ { i = 0, j = 2, v = 2 },
+ { i = 1, j = 1, v = 4 },
+ ];
+ t = mat.newSparseMatrix (ColumnMajor ()) { rows = 2, columns = 3 } [
+ { i = 0, j = 1, v = 7 },
+ { i = 1, j = 0, v = 5 },
+ { i = 1, j = 1, v = 6 },
+ ];
+ diff = mat.difference s t;
+ mat.isSparse? diff and
+ compareMatrices diff (mat.difference (mat.toDense s) t) and
+ compareMatrices diff (mat.difference (mat.toDense s) (mat.toDense t)) and
+ compareMatrices diff (mat.difference s (mat.toDense t)) and
+ compareMatrices diff
+ (mat.newSparseMatrix (RowMajor ()) { rows = 2, columns = 3 } [
+ { i = 0, j = 0, v = 1 },
+ { i = 0, j = 1, v = -7 },
+ { i = 0, j = 2, v = 2 },
+ { i = 1, j = 0, v = -5 },
+ { i = 1, j = 1, v = -2 },
+ ])
+),
+
+"abs-\(name)": \(
+ compareMatrices
+ (mat.abs (newMatrix (ColumnMajor ()) [[-1,4],[2,-5],[-3,0]]))
+ (newMatrix (ColumnMajor ()) [[1,4],[2,5],[3,0]])
+),
+
+"product-\(name)": \(
+ compareMatrices
+ (mat.product (constMatrix 2 { rows = 4, columns = 2 })
+ (constMatrix 3 { rows = 2, columns = 3 }))
+ (constMatrix 12 { rows = 4, columns = 3 }) and
+ compareMatrices
+ (mat.product (newMatrix (ColumnMajor ()) [[1,4],[2,5],[3,6]])
+ (newMatrix (ColumnMajor ()) [[7,9,11],[8,10,12]]))
+ (newMatrix (ColumnMajor ()) [[58,139],[64,154]])
+),
+
+"sparseProduct-\(name)": \(
+ s = mat.newSparseMatrix (ColumnMajor ()) { rows = 2, columns = 3 } [
+ { i = 0, j = 0, v = 1 },
+ { i = 0, j = 2, v = 2 },
+ { i = 1, j = 1, v = 4 },
+ ];
+ t = mat.newSparseMatrix (ColumnMajor ()) { rows = 3, columns = 2 } [
+ { i = 0, j = 1, v = 7 },
+ { i = 1, j = 0, v = 5 },
+ { i = 2, j = 0, v = 6 },
+ ];
+ prod = mat.product s t;
+ mat.isSparse? prod and
+ compareMatrices prod (mat.product (mat.toDense s) t) and
+ compareMatrices prod (mat.product (mat.toDense s) (mat.toDense t)) and
+ compareMatrices prod (mat.product s (mat.toDense t)) and
+ compareMatrices prod
+ (mat.newSparseMatrix (RowMajor ()) { rows = 2, columns = 2 } [
+ { i = 0, j = 0, v = 12 },
+ { i = 0, j = 1, v = 7 },
+ { i = 1, j = 0, v = 20 },
+ ])
+),
+
+"productFail-\(name)": \(
+ try
+ \() (mat.product (constMatrix 2 { rows = 4, columns = 2 })
+ (constMatrix 3 { rows = 3, columns = 2 }));
+ false;
+ catch FailureException e:
+ true
+ yrt
+),
+
+"resizedTo-\(name)": \(
+ compareMatrices
+ (mat.resizedTo { rows = 2, columns = 2 }
+ (newMatrix (ColumnMajor ()) [[1,4],[2,5],[3,6]]))
+ (newMatrix (ColumnMajor ()) [[1,4],[2,5]]) and
+ compareMatrices
+ (mat.resizedTo { rows = 3, columns = 4 }
+ (newMatrix (ColumnMajor ()) [[1,4],[2,5],[3,6]]))
+ (newMatrix (ColumnMajor ()) [[1,4,0],[2,5,0],[3,6,0],[0,0,0]]) and
+ compareMatrices
+ (mat.resizedTo { rows = 1, columns = 1 }
+ (newMatrix (ColumnMajor ()) [[1,4],[2,5],[3,6]]))
+ (newMatrix (RowMajor ()) [[1]]) and
+ compareMatrices
+ (mat.resizedTo { rows = 2, columns = 3 }
+ (mat.zeroSizeMatrix ()))
+ (newMatrix (RowMajor ()) [[0,0,0],[0,0,0]]) and
+ mat.isSparse?
+ (mat.resizedTo { rows = 1, columns = 1 }
+ (mat.toSparse (newMatrix (ColumnMajor ()) [[1,4],[2,5],[3,6]])))
+),
+
+"zeroSizeMatrix-\(name)": \(
+ compareMatrices
+ (mat.zeroSizeMatrix ())
+ (newMatrix (ColumnMajor ()) []) and
+ compareMatrices
+ (mat.zeroSizeMatrix ())
+ (newMatrix (ColumnMajor ()) [[]]) and
+ compareMatrices
+ (newMatrix (ColumnMajor ()) [[]])
+ (newMatrix (RowMajor ()) [[]]) and
+ compareMatrices
+ (mat.zeroSizeMatrix ())
+ (mat.newSparseMatrix (ColumnMajor ()) { rows = 0, columns = 1 } []) and
+ compareMatrices
+ (mat.zeroSizeMatrix ())
+ (mat.newSparseMatrix (ColumnMajor ()) { rows = 1, columns = 0 } [])
+),
+
+"asRows-\(name)": \(
+ compare
+ (map vec.list
+ (mat.asRows (newMatrix (ColumnMajor ()) [[1,4],[0,5],[3,6]])))
+ [[1,0,3],[4,5,6]];
+),
+
+"asColumns-\(name)": \(
+ compare
+ (map vec.list
+ (mat.asColumns (newMatrix (ColumnMajor ()) [[1,4],[0,5],[3,6]])))
+ [[1,4],[0,5],[3,6]];
+),
+
+"concat-horiz-\(name)": \(
+ compareMatrices
+ (mat.concat (Horizontal ())
+ [(newMatrix (ColumnMajor ()) [[1,4],[0,5]]),
+ (newMatrix (RowMajor ()) [[3],[6]])])
+ (newMatrix (ColumnMajor ()) [[1,4],[0,5],[3,6]])
+),
+
+"concatEmpty-horiz-\(name)": \(
+ compareMatrices
+ (mat.concat (Horizontal ())
+ [(newMatrix (ColumnMajor ()) [[]]),
+ (newMatrix (RowMajor ()) [[]]),
+ (mat.zeroSizeMatrix ())])
+ (mat.zeroSizeMatrix ());
+),
+
+"sparseConcat-horiz-\(name)": \(
+ s = mat.concat (Horizontal ())
+ [mat.toSparse (newMatrix (ColumnMajor ()) [[1,4],[0,5]]),
+ mat.toSparse (newMatrix (RowMajor ()) [[3],[6]])];
+ compareMatrices s (newMatrix (ColumnMajor ()) [[1,4],[0,5],[3,6]]) and
+ compare (mat.isSparse? s) true and
+ compare (mat.density s) (5/6)
+),
+
+"concatFail-horiz-\(name)": \(
+ try
+ \() (mat.concat (Horizontal ())
+ [(newMatrix (ColumnMajor ()) [[1,4],[0,5]]),
+ (newMatrix (ColumnMajor ()) [[3],[6]])]);
+ false
+ catch FailureException e:
+ true
+ yrt
+),
+
+"concat-vert-\(name)": \(
+ compareMatrices
+ (mat.concat (Vertical ())
+ [(newMatrix (ColumnMajor ()) [[1,4],[0,5]]),
+ (newMatrix (RowMajor ()) [[3,6]])])
+ (newMatrix (ColumnMajor ()) [[1,4,3],[0,5,6]])
+),
+
+"sparseConcat-vert-\(name)": \(
+ s = mat.concat (Vertical ())
+ [mat.toSparse (newMatrix (ColumnMajor ()) [[1,4],[0,5]]),
+ mat.toSparse (newMatrix (RowMajor ()) [[3,6]])];
+ compareMatrices s (newMatrix (ColumnMajor ()) [[1,4,3],[0,5,6]]) and
+ compare (mat.isSparse? s) true and
+ compare (mat.density s) (5/6)
+),
+
+"concatFail-vert-\(name)": \(
+ try
+ \() (mat.concat (Vertical ())
+ [(newMatrix (ColumnMajor ()) [[1,4],[0,5]]),
+ (newMatrix (RowMajor ()) [[3],[6]])]);
+ false
+ catch FailureException e:
+ true
+ yrt
+),
+
+"rowSlice-\(name)": \(
+ compareMatrices
+ (mat.rowSlice (newMatrix (RowMajor ()) [[1,0],[3,4],[0,6],[7,8]]) 1 3)
+ (newMatrix (RowMajor ()) [[3,4],[0,6]]) and
+ compareMatrices
+ (mat.rowSlice (newMatrix (RowMajor ()) [[1,0],[3,4],[0,6],[7,8]]) 3 6)
+ (newMatrix (RowMajor ()) [[7,8]])
+),
+
+"columnSlice-\(name)": \(
+ compareMatrices
+ (mat.columnSlice (newMatrix (RowMajor ()) [[1,0,3,4],[0,6,7,8]]) 1 3)
+ (newMatrix (RowMajor ()) [[0,3],[6,7]]) and
+ compareMatrices
+ (mat.columnSlice (newMatrix (RowMajor ()) [[1,0,3,4],[0,6,7,8]]) 2 5)
+ (newMatrix (RowMajor ()) [[3,4],[7,8]])
+),
+
+"density-\(name)": \(
+ compare (mat.density (newMatrix (ColumnMajor ()) [[1,2,0],[0,5,0]])) (3/6) and
+ compare (mat.density (newMatrix (ColumnMajor ()) [[1,2,3],[4,5,6]])) (6/6) and
+ compare (mat.density (newMatrix (ColumnMajor ()) [[0,0,0],[0,0,0]])) 0
+),
+
+"nonZeroValues-\(name)": \(
+ compare (mat.nonZeroValues (newMatrix (ColumnMajor ()) [[1,2,0],[0,5,0]])) 3 and
+ compare (mat.nonZeroValues (newMatrix (ColumnMajor ()) [[1,2,3],[4,5,6]])) 6 and
+ compare (mat.nonZeroValues (newMatrix (ColumnMajor ()) [[0,0,0],[0,0,0]])) 0
+),
+
+"toSparse-\(name)": \(
+ m = newMatrix (ColumnMajor ()) [[1,2,0],[-1,-4,6],[0,0,3]];
+ compareMatrices (mat.toSparse m) m and
+ compareMatrices (mat.toDense (mat.toSparse m)) m and
+ compare (mat.density (mat.toSparse m)) (6/9)
+),
+
+"toDense-\(name)": \(
+ m = newMatrix (ColumnMajor ()) [[1,2,0],[-1,-4,6],[0,0,3]];
+ compareMatrices (mat.toDense m) m and
+ compareMatrices (mat.toSparse (mat.toDense m)) m
+),
+
+"filter-\(name)": \(
+ m = newMatrix (ColumnMajor ()) [[1,2,0],[-1,-4,6],[0,0,3]];
+ compareMatrices
+ (mat.filter (> 2) m)
+ (newMatrix (ColumnMajor ()) [[0,0,0],[0,0,6],[0,0,3]]) and
+ compare (mat.density (mat.filter (> 2) m)) (2/9)
+),
+
+"newSparseMatrix-\(name)": \(
+ s = mat.newSparseMatrix (ColumnMajor ()) { rows = 2, columns = 3 } [
+ { i = 0, j = 0, v = 1 },
+ { i = 0, j = 2, v = 2 },
+ { i = 1, j = 1, v = 4 },
+ ];
+ // If there are zeros in the entries list, they should not end up
+ // in the sparse data
+ t = mat.newSparseMatrix (ColumnMajor ()) { rows = 2, columns = 3 } [
+ { i = 0, j = 0, v = 1 },
+ { i = 0, j = 2, v = 0 },
+ { i = 1, j = 1, v = 4 },
+ ];
+ // Any out-of-range or non-integer i, j should be ignored too
+ u = mat.newSparseMatrix (ColumnMajor ()) { rows = 2, columns = 3 } [
+ { i = -1, j = 0, v = 1 },
+ { i = 0, j = 4, v = 3 },
+ { i = 1, j = 1.5, v = 4 },
+ ];
+ compare (mat.density s) (3/6) and
+ compare (mat.density t) (2/6) and
+ compareMatrices s (newMatrix (RowMajor ()) [[1,0,2],[0,4,0]]) and
+ compareMatrices t (newMatrix (RowMajor ()) [[1,0,0],[0,4,0]]) and
+ compareMatrices u (newMatrix (RowMajor ()) [[0,0,0],[0,0,0]])
+),
+
+"enumerate-\(name)": \(
+ m = newMatrix (ColumnMajor ()) [[1,2,0],[-1,-4,6],[0,0,3]];
+ all = [
+ { i = 0, j = 0, v = 1 },
+ { i = 1, j = 0, v = 2 },
+ { i = 2, j = 0, v = 0 },
+ { i = 0, j = 1, v = -1 },
+ { i = 1, j = 1, v = -4 },
+ { i = 2, j = 1, v = 6 },
+ { i = 0, j = 2, v = 0 },
+ { i = 1, j = 2, v = 0 },
+ { i = 2, j = 2, v = 3 },
+ ];
+ sortEntries =
+ sortBy do a b:
+ if a.i == b.i then a.j < b.j else a.i < b.i fi
+ done;
+ compare
+ (sortEntries (mat.enumerate m))
+ (sortEntries
+ (if mat.isSparse? m then filter do d: d.v != 0 done all else all fi));
+),
+
+]);
+
+colhash = makeTests "column-dense" id;
+rowhash = makeTests "row-dense" mat.flipped;
+sparsecolhash = makeTests "column-sparse" mat.toSparse;
+
+// there are two possible orders for constructing a sparse row-major
+// matrix from a dense col-major one, so test them both:
+sparserowhash1 = makeTests "row-sparse-a" (mat.toSparse . mat.flipped);
+sparserowhash2 = makeTests "row-sparse-b" (mat.flipped . mat.toSparse);
+
+all = [:];
+for [ colhash, rowhash, sparsecolhash, sparserowhash1, sparserowhash2 ] do h:
+ for (keys h) do k: all[k] := h[k] done;
+done;
+
+all is hash boolean>;
+
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/matrix/type.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/matrix/type.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,23 @@
+
+module may.matrix.type;
+
+load may.vector.type;
+
+typedef opaque matrix =
+ DenseRows. array | // array of rows
+ DenseCols. array | // array of columns
+ SparseCSR. {
+ .values is vector,
+ .indices is array, // column index of each value
+ .pointers is array, // offset of first value in each row
+ .extent is number // max possible index + 1, i.e. number of columns
+ } |
+ SparseCSC. {
+ .values is vector,
+ .indices is array, // row index of each value
+ .pointers is array, // offset of first value in each column
+ .extent is number // max pointers index + 1, i.e. number of rows
+ };
+
+();
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/plot.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/plot.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,132 @@
+module may.plot;
+
+vec = load may.vector;
+mat = load may.matrix;
+
+{ distinctColour } = load may.plot.colour;
+
+import org.jzy3d.plot3d.builder: Mapper;
+import org.jzy3d.maths: Range, Coord3d;
+import org.jzy3d.chart: Chart, ChartLauncher;
+import org.jzy3d.plot3d.builder: Builder;
+import org.jzy3d.plot3d.builder.concrete: OrthonormalGrid;
+import org.jzy3d.colors.colormaps: ColorMapRainbow;
+import org.jzy3d.colors: ColorMapper, Color;
+import org.jzy3d.plot3d.rendering.canvas: Quality;
+import org.jzy3d.plot3d.rendering.view.modes: ViewPositionMode;
+import org.jzy3d.plot3d.primitives: FlatLine2d, Point;
+
+newMatrixMapper matrix =
+ (class MMapper extends Mapper
+ double f(double x, double y)
+ result = mat.at matrix y x;
+ println "f(\(x),\(y)) -> \(result)";
+ result
+ end;
+ new MMapper());
+
+newMatrixLogMapper matrix =
+ (class MMapper extends Mapper
+ double f(double x, double y)
+ ln (mat.at matrix y x)
+ end;
+ new MMapper());
+
+newMapper mapFunction =
+ (class FMapper extends Mapper
+ double f(double x, double y)
+ mapFunction x y
+ end;
+ new FMapper());
+
+plotMatrix chart colour matrix is ~Chart -> ~Color -> 'a -> () =
+ (mapper = newMatrixMapper matrix;
+ size = mat.size matrix;
+ //!!! doesn't work if either rows or columns is 1
+ xrange = new Range(0, size.columns - 1);
+ yrange = new Range(0, size.rows - 1);
+ grid = new OrthonormalGrid(xrange, size.columns, yrange, size.rows);
+ println "Matrix size: \(size)";
+ surface = Builder#buildOrthonormal(grid, mapper); //??? big?
+ println "Z Bounds: \(surface#getBounds()#getZmin()) -> \(surface#getBounds()#getZmax())";
+ surface#setFaceDisplayed(true);
+ surface#setWireframeDisplayed(true);
+ surface#setWireframeColor(colour);
+ chart#getScene()#getGraph()#add(surface);
+ ());
+
+plotCurve chart colour depth curve is ~Chart -> ~Color -> number -> 'a -> () =
+ (scene = chart#getScene();
+ xx = map (.time) curve;
+ yy = map (.value) curve;
+ line = new FlatLine2d(xx as ~float[], yy as ~float[], depth);
+ line#setWireframeDisplayed(true);
+ line#setWireframeColor(colour);
+ line#setWireframeWidth(2);
+ line#setFaceDisplayed(false);
+ scene#add(line);
+ chart#getView()#setViewPoint(new Coord3d(0, 0, 0));
+/*
+ axes = chart#getAxeLayout();
+ axes#setXAxeLabelDisplayed(false);
+ axes#setYAxeLabelDisplayed(false);
+ axes#setZAxeLabelDisplayed(true);
+ axes#setZAxeLabel("unit goes here"); //!!!
+ axes#setYTickLabelDisplayed(false);
+*/
+ ());
+
+plotSeries chart colour depth { start, step, values } is ~Chart -> ~Color -> number -> 'a -> () =
+ (scene = chart#getScene();
+ xx = map do i: start + step * i done [0..length values - 1];
+ yy = list values;
+ line = new FlatLine2d(xx as ~float[], yy as ~float[], depth);
+ line#setWireframeDisplayed(true);
+ line#setWireframeColor(colour);
+ line#setWireframeWidth(2);
+ line#setFaceDisplayed(false);
+ scene#add(line);
+ chart#getView()#setViewPoint(new Coord3d(0, 0, 0));
+/*
+ axes = chart#getAxeLayout();
+ axes#setXAxeLabelDisplayed(false);
+ axes#setYAxeLabelDisplayed(false);
+ axes#setZAxeLabelDisplayed(true);
+ axes#setZAxeLabel("unit goes here"); //!!!
+ axes#setYTickLabelDisplayed(false);
+*/
+ ());
+
+plot structures =
+ (chart = new Chart(Quality#Nicest);
+ var j = 0;
+ for structures do s:
+ colour = distinctColour j;
+ case s of
+ Grid matrix:
+ plotMatrix chart colour matrix;
+ Curve curve:
+ plotCurve chart colour j curve;
+ Series series:
+ plotSeries chart colour j series;
+ Vector vector:
+ plotSeries chart colour j
+ { start = 0, step = 1, values = vec.list vector };
+ other:
+ failWith "Unable to plot \(other)";
+ esac;
+ j := j + 1;
+ done;
+ ChartLauncher#openChart(chart);
+ chart);
+
+{
+ newMatrixMapper,
+ newMatrixLogMapper,
+ newMapper,
+ plotMatrix,
+ plotCurve,
+ plotSeries,
+ plot,
+}
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/plot/chart.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/plot/chart.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,241 @@
+module may.plot.chart;
+
+{ distinctColour } = load may.plot.colour;
+
+import org.jzy3d.plot3d.text.drawable: DrawableTextBillboard, DrawableTextBitmap;
+import org.jzy3d.maths: Range, Coord3d;
+import org.jzy3d.plot3d.primitives: Shape, HistogramBar, FlatLine2d, Polygon, Quad, Point;
+import org.jzy3d.plot3d.primitives.axes.layout.providers: StaticTickProvider, RegularTickProvider;
+import org.jzy3d.plot3d.primitives.axes.layout.renderers: ITickRenderer, TickLabelMap, IntegerTickRenderer;
+import org.jzy3d.chart: Chart, ChartLauncher;
+import org.jzy3d.plot3d.builder: Builder;
+import org.jzy3d.colors: Color;
+import org.jzy3d.plot3d.rendering.canvas: Quality;
+import org.jzy3d.plot3d.rendering.view.modes: ViewPositionMode;
+
+import javax.imageio: ImageIO;
+
+import java.io: File;
+
+newPercentTickRenderer () =
+ (f v = " \(int (v * 100))%";
+ class PercentageTickRenderer extends ITickRenderer
+ String format(double value) f value,
+ String format(float value) f value
+ end;
+ new PercentageTickRenderer());
+
+newPaddedIntTickRenderer () =
+ (f v = " \(int (v + 0.5))";
+ class PaddedIntTickRenderer extends ITickRenderer
+ String format(double value) f value,
+ String format(float value) f value
+ end;
+ new PaddedIntTickRenderer());
+
+parseOptions options defaultKeys defaultXKeys =
+ (parsed = {
+ var keys = array (sort defaultKeys),
+ var labels = [:],
+ var animated = false,
+ var normalised = false,
+ var unit = "",
+ var xkeys = array (sort defaultXKeys),
+ var saveTo = "",
+ var display = true,
+ };
+ for options
+ \case of
+ Keys kk: parsed.keys := array kk;
+ XKeys xk: parsed.xkeys := array xk;
+ Animated a: parsed.animated := a;
+ Normalised n: parsed.normalised := n;
+ Unit u: parsed.unit := u;
+ Labels ll: parsed.labels := ll;
+ SaveTo file: parsed.saveTo := file;
+ Display d: parsed.display := d;
+ esac;
+ if empty? parsed.labels then
+ parsed.labels := mapIntoHash id id parsed.keys
+ fi;
+ parsed);
+
+newChart opts =
+ (quality = Quality#Fastest;
+ quality#setAnimated(opts.animated);
+// if opts.display then
+ new Chart(quality);
+// else
+// new Chart(quality, "offscreen,640,640");
+// fi);
+ );
+
+showChart opts chart is 'a -> ~Chart -> () =
+ (if opts.display then
+ \() ChartLauncher#openChart(chart);
+ else
+ \() ChartLauncher#openStaticChart(chart);
+ fi;
+ if opts.saveTo != "" then
+ \() chart#screenshot(opts.saveTo);
+ fi);
+
+plotBarChart options values =
+ (opts = parseOptions options (keys values) [];
+ chart = newChart opts;
+ var n = length opts.keys;
+ scene = chart#getScene();
+ ticks = new double[n];
+ tickLabels = new TickLabelMap();
+ var i = 0;
+ var x = n - i - 1;
+ total = sum (map do k: if k in values then values[k] else 0 fi done opts.keys);
+ for opts.keys do k:
+ bar = new HistogramBar();
+ v = if k in values then values[k] else 0 fi;
+ v = if opts.normalised and total > 0 then v / total else v fi;
+ bar#setData(new Coord3d(x, 0, 0), v, 0.45, distinctColour i);
+ bar#setWireframeDisplayed(false);
+ scene#add(bar);
+ ticks[i] := i;
+ tickLabels#register(x, opts.labels[k]);
+ i := i + 1;
+ x := x - 1;
+ done;
+ chart#getView()#setViewPoint(new Coord3d(pi/2, 0, 0));
+ axes = chart#getAxeLayout();
+ axes#setXAxeLabelDisplayed(false);
+ axes#setYAxeLabelDisplayed(false);
+ axes#setZAxeLabelDisplayed(true);
+ if opts.normalised then
+ axes#setZAxeLabel("");
+ axes#setZTickRenderer(newPercentTickRenderer ());
+ else
+ axes#setZAxeLabel(opts.unit);
+ fi;
+ axes#setXTickProvider(new StaticTickProvider(ticks));
+ axes#setXTickRenderer(tickLabels);
+ axes#setYTickLabelDisplayed(false);
+ showChart opts chart);
+
+plotLines options values =
+ (opts = parseOptions options (keys values) (keys values[head (keys values)]);
+ chart = newChart opts;
+ scene = chart#getScene();
+ n = length opts.xkeys;
+ var z = 0;
+ for opts.keys do k:
+ v = values[k];
+ x = new float[n];
+ y = new float[n];
+ var i = 0;
+ for opts.xkeys do xk:
+ x[i] := i;
+ y[i] := if xk in v then v[xk] else 0 fi;
+ i := i + 1;
+ done;
+ line = new FlatLine2d(x, y, z);
+ line#setWireframeDisplayed(true);
+ line#setWireframeColor(distinctColour z);
+ line#setWireframeWidth(2);
+ line#setFaceDisplayed(false);
+ scene#add(line);
+ z := z + 1;
+ done;
+ chart#getView()#setViewPoint(new Coord3d(0, 0, 0));
+ axes = chart#getAxeLayout();
+ axes#setXAxeLabelDisplayed(false);
+ axes#setYAxeLabelDisplayed(false);
+ axes#setZAxeLabelDisplayed(true);
+ axes#setZAxeLabel(opts.unit);
+ axes#setYTickLabelDisplayed(false);
+ showChart opts chart);
+
+stack keys xkeys values normalised =
+ (stacked = mapIntoHash id \(mapIntoHash id \{ y0 = 0, y1 = 0 } xkeys) keys;
+ prev = mapIntoHash id \0 xkeys;
+ valueOf k xk =
+ if k in values and xk in values[k]
+ then values[k][xk] else 0
+ fi;
+ for xkeys do xk:
+ total = sum (map do k: valueOf k xk done keys);
+ for keys do k:
+ value =
+ if normalised and total > 0
+ then (valueOf k xk) / total
+ else (valueOf k xk)
+ fi;
+ height = prev[xk] + value;
+ stacked[k][xk] := { y0 = prev[xk], y1 = height };
+ prev[xk] := height;
+ done;
+ done;
+ stacked);
+
+newRect x y0 y1 z colour is number -> number -> number -> number -> ~Color -> 'a =
+ (poly = new Quad();
+ poly#add(new Point(new Coord3d(x + 0.5, z, y0)));
+ poly#add(new Point(new Coord3d(x + 0.5, z, y1)));
+ poly#add(new Point(new Coord3d(x - 0.5, z, y1)));
+ poly#add(new Point(new Coord3d(x - 0.5, z, y0)));
+ poly#setWireframeDisplayed(true);
+ poly#setWireframeColor(colour);
+ poly#setFaceDisplayed(true);
+ poly#setColor(colour);
+ poly);
+
+plotStacked options values =
+ (opts = parseOptions options (keys values) (keys values[head (keys values)]);
+ chart = newChart opts;
+ scene = chart#getScene();
+ stacked = stack opts.keys opts.xkeys values opts.normalised;
+ var z = 0;
+ var ty = 0;
+ nxk = length opts.xkeys;
+ xticks = new double[nxk];
+ xtickLabels = new TickLabelMap();
+ for [0..nxk - 1] do x:
+ xticks[x] := x;
+ k = opts.xkeys[x];
+ xtickLabels#register(x, if k in opts.labels then opts.labels[k] else k fi);
+ done;
+ for opts.keys do k:
+ ranges = stacked[k];
+ c = distinctColour z;
+ for [0..nxk - 1] do x:
+ xk = opts.xkeys[x];
+ rect = newRect x ranges[xk].y0 ranges[xk].y1 z c;
+ scene#add(rect);
+ done;
+ text = new DrawableTextBitmap(opts.labels[k], new Coord3d(-(nxk/5 + 0.5), z, ty), c);
+ scene#add(text);
+ z := z - 1;
+ ty := ty + 0.1;
+ done;
+ chart#getView()#setViewPoint(new Coord3d(-pi/2, 0, 0));
+ axes = chart#getAxeLayout();
+ axes#setXAxeLabelDisplayed(false);
+ if nxk < 10 then
+ axes#setXTickProvider(new StaticTickProvider(xticks));
+ fi;
+ axes#setXTickRenderer(xtickLabels);
+ axes#setYAxeLabelDisplayed(false);
+ axes#setZAxeLabelDisplayed(true);
+ if opts.normalised then
+ axes#setZAxeLabel("");
+ axes#setZTickRenderer(newPercentTickRenderer ());
+ else
+ axes#setZAxeLabel(opts.unit);
+ axes#setZTickRenderer(newPaddedIntTickRenderer ());
+ fi;
+ axes#setYTickLabelDisplayed(false);
+ showChart opts chart);
+
+{
+ plotBarChart,
+ plotLines,
+ stack,
+ plotStacked,
+}
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/plot/colour.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/plot/colour.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,27 @@
+module may.plot.colour;
+
+import org.jzy3d.colors: Color;
+
+distinctColours = array [
+ { r = 82, g = 126, b = 154 }, // dark steel blue
+ { r = 161, g = 54, b = 2 }, // red
+ { r = 207, g = 228, b = 148 }, // grey-green
+ { r = 21, g = 183, b = 197 }, // light blue
+ { r = 251, g = 116, b = 43 }, // light red
+ { r = 200, g = 125, b = 234 }, // light purple
+ { r = 126, g = 33, b = 28 }, // dried blood!
+ { r = 188, g = 13, b = 207 }, // mid purple
+];
+
+distinctColour n =
+ if n < 0
+ then distinctColour (-n)
+ else
+ rgb = distinctColours[n % (length distinctColours)];
+ new Color(rgb.r / 255.0, rgb.g / 255.0, rgb.b / 255.0);
+ fi;
+
+{
+ distinctColour
+};
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/plot/test/test_plot.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/plot/test/test_plot.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,50 @@
+module may.plot.test.test_plot;
+
+ch = load may.plot.chart;
+
+{ compare } = load may.test.test;
+
+[
+
+"stack": \(
+ compare
+ (ch.stack
+ [ "Conrad", "Alice", "Bob" ]
+ [ "Jan", "Feb", "Mar" ]
+ [ "Alice": [ "Jan": 3, "Mar": 2 ],
+ "Bob": [ "Jan": 0, "Feb": 1, "Mar": 4 ],
+ "Conrad": [ "Feb": 2, "Mar": 1 ] ]
+ false)
+ [ "Conrad": [ "Jan": { y0 = 0, y1 = 0 },
+ "Feb": { y0 = 0, y1 = 2 },
+ "Mar": { y0 = 0, y1 = 1 } ],
+ "Alice": [ "Jan": { y0 = 0, y1 = 3 },
+ "Feb": { y0 = 2, y1 = 2 },
+ "Mar": { y0 = 1, y1 = 3 } ],
+ "Bob": [ "Jan": { y0 = 3, y1 = 3 },
+ "Feb": { y0 = 2, y1 = 3 },
+ "Mar": { y0 = 3, y1 = 7 } ] ]
+),
+
+"stack-normalised": \(
+ compare
+ (ch.stack
+ [ "Conrad", "Alice", "Bob" ]
+ [ "Jan", "Feb", "Mar" ]
+ [ "Alice": [ "Jan": 3, "Mar": 2 ],
+ "Bob": [ "Jan": 0, "Feb": 1, "Mar": 4 ],
+ "Conrad": [ "Feb": 2, "Mar": 1 ] ]
+ true)
+ [ "Conrad": [ "Jan": { y0 = 0, y1 = 0 },
+ "Feb": { y0 = 0, y1 = 2/3 },
+ "Mar": { y0 = 0, y1 = 1/7 } ],
+ "Alice": [ "Jan": { y0 = 0, y1 = 1 },
+ "Feb": { y0 = 2/3, y1 = 2/3 },
+ "Mar": { y0 = 1/7, y1 = 3/7 } ],
+ "Bob": [ "Jan": { y0 = 1, y1 = 1 },
+ "Feb": { y0 = 2/3, y1 = 1 },
+ "Mar": { y0 = 3/7, y1 = 1 } ] ]
+),
+
+] is hash boolean>;
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/signal/autocorrelation.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/signal/autocorrelation.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,27 @@
+
+module may.signal.autocorrelation;
+
+acf len series =
+ (a = array series;
+ map do i:
+ sum (map do j:
+ a[j] * a[j-i]
+ done [i..length a - 1])
+ done [0..len-1]);
+
+acfNormalised len series =
+ (n = length series;
+ map2 do v i: if n == i then 0 else v / (n - i) fi done
+ (acf len series) [0..len-1]);
+
+acfUnityNormalised len series =
+ (a = acfNormalised len series;
+ max = head (sortBy (>) a);
+ map (/ max) a);
+
+{
+ acf,
+ acfNormalised,
+ acfUnityNormalised,
+}
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/signal/test/test_signal.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/signal/test/test_signal.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,24 @@
+module may.signal.test.test_signal;
+
+{ acf, acfNormalised, acfUnityNormalised } = load may.signal.autocorrelation;
+
+{ compare } = load may.test.test;
+
+[
+
+"unnormalised": \(
+ compare (acf 12 (array [1,0,0, 1,0,0, 1,0,0, 1,0,0]))
+ [4,0,0, 3,0,0, 2,0,0, 1,0,0 ];
+),
+
+"normalised": \(
+ compare (acfNormalised 9 (array [1,0,0, 1,0,0, 1,0,0, 1,0,0]))
+ [4/12,0,0, 3/9,0,0, 2/6,0,0 ];
+),
+
+"normalisedUnity": \(
+ compare (acfUnityNormalised 9 (array [1,0,0, 1,0,0, 1,0,0, 1,0,0]))
+ [1,0,0, 1,0,0, 1,0,0 ];
+),
+
+] is hash boolean>;
diff -r 4b104ef8d110 -r 20903ee53719 src/may/signal/test/test_window.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/signal/test/test_window.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,192 @@
+
+module may.signal.test.test_window;
+
+win = load may.signal.window;
+vec = load may.vector;
+
+{ compare, compareUsing } = load may.test.test;
+
+functions = [
+ Hann () : win.hann,
+ Hamming () : win.hamming,
+ Blackman () : win.blackman,
+ Nuttall () : win.nuttall,
+ BlackmanNuttall () : win.blackmanNuttall,
+ BlackmanHarris () : win.blackmanHarris,
+ Boxcar () : win.boxcar,
+ Bartlett () : win.bartlett,
+];
+
+close aa bb = all id (map2 do a b: (abs (a - b) < 0.0001) done aa bb);
+
+isSymmetric a =
+ (len = (vec.length a);
+ b = if len % 2 == 0 then
+ half = vec.slice a 0 (len/2);
+ vec.concat [half, vec.reverse half];
+ else
+ half = vec.slice a 0 (int (len/2));
+ mid = vec.slice a (int (len/2)) (int (len/2) + 1);
+ vec.concat [half, mid, vec.reverse half];
+ fi;
+ compareUsing close (vec.list a) (vec.list b));
+
+[
+
+"windowFunction": \(
+ all id (map do type:
+ f = functions[type];
+ a = f 10;
+ b = win.windowFunction type [ Symmetric false ] 10;
+ compareUsing close (vec.list a) (vec.list b)
+ or (eprintln "** failed window type: \(type)"; false)
+ done (keys functions));
+),
+
+"symmetric-even": \(
+ len = 10;
+ all id (map do type:
+ f = win.windowFunction type [ Symmetric true ];
+ v = f len;
+ (compare (vec.length v) len and isSymmetric v) or
+ (eprintln "** failed window type: \(type)"; false);
+ done (keys functions));
+),
+
+"symmetric-odd": \(
+ len = 11;
+ all id (map do type:
+ f = win.windowFunction type [ Symmetric true ];
+ v = f len;
+ (compare (vec.length v) len and isSymmetric v) or
+ (eprintln "** failed window type: \(type)"; false);
+ done (keys functions));
+),
+
+"periodic-even": \(
+ // We can't actually test whether a function is periodic, given
+ // only one cycle of it! But we can make sure that all but the
+ // first sample is symmetric, which is what a symmetric window
+ // becomes when generated in periodic mode
+ len = 10;
+ all id (map do type:
+ f = win.windowFunction type [ Symmetric false ];
+ v = f len;
+ (compare (vec.length v) len and isSymmetric (vec.slice v 1 len)) or
+ (eprintln "** failed window type: \(type)"; false);
+ done (keys functions));
+),
+
+"periodic-odd": \(
+ len = 11;
+ all id (map do type:
+ f = win.windowFunction type [ Symmetric false ];
+ v = f len;
+ (compare (vec.length v) len and isSymmetric (vec.slice v 1 len)) or
+ (eprintln "** failed window type: \(type)"; false);
+ done (keys functions));
+),
+
+"bartlett-periodic": \(
+ compare (vec.list (win.bartlett 1)) [1] and
+ compare (vec.list (win.bartlett 2)) [0,1] and
+ compare (vec.list (win.bartlett 3)) [0,2/3,2/3] and
+ compare (vec.list (win.bartlett 4)) [0,1/2,1,1/2]
+),
+
+"bartlett-symmetric": \(
+ b = win.windowFunction (Bartlett ()) [ Symmetric true ];
+ compare (vec.list (b 1)) [1] and
+ compare (vec.list (b 2)) [0,0] and
+ compare (vec.list (b 3)) [0,1,0] and
+ compare (vec.list (b 4)) [0,2/3,2/3,0] and
+ compare (vec.list (b 5)) [0,1/2,1,1/2,0]
+),
+
+"hann": \(
+ compareUsing close (vec.list (win.hann 10)) [
+ 0, 0.0955, 0.3455, 0.6545, 0.9045,
+ 1.0000, 0.9045, 0.6545, 0.3455, 0.0955,
+ ] and
+ compareUsing close
+ (vec.list (win.windowFunction (Hann ()) [ Symmetric true ] 10)) [
+ 0, 0.1170, 0.4132, 0.7500, 0.9698,
+ 0.9698, 0.7500, 0.4132, 0.1170, 0,
+ ]
+),
+
+"hamming": \(
+ compareUsing close (vec.list (win.hamming 10)) [
+ 0.0800, 0.1679, 0.3979, 0.6821, 0.9121,
+ 1.0000, 0.9121, 0.6821, 0.3979, 0.1679,
+ ] and
+ compareUsing close
+ (vec.list (win.windowFunction (Hamming ()) [ Symmetric true ] 10)) [
+ 0.0800, 0.1876, 0.4601, 0.7700, 0.9723,
+ 0.9723, 0.7700, 0.4601, 0.1876, 0.0800,
+ ]
+),
+
+"blackman": \(
+ compareUsing close (vec.list (win.blackman 10)) [
+ 0, 0.0402, 0.2008, 0.5098, 0.8492,
+ 1.0000, 0.8492, 0.5098, 0.2008, 0.0402,
+ ] and
+ compareUsing close
+ (vec.list (win.windowFunction (Blackman ()) [ Symmetric true ] 10)) [
+ 0, 0.0509, 0.2580, 0.6300, 0.9511,
+ 0.9511, 0.6300, 0.2580, 0.0509, 0,
+ ]
+),
+
+"blackmanHarris": \(
+ compareUsing close (vec.list (win.blackmanHarris 10)) [
+ 0.0001, 0.0110, 0.1030, 0.3859, 0.7938,
+ 1.0000, 0.7938, 0.3859, 0.1030, 0.0110,
+ ] and
+ compareUsing close
+ (vec.list (win.windowFunction (BlackmanHarris ()) [ Symmetric true ] 10)) [
+ 0.0001, 0.0151, 0.1470, 0.5206, 0.9317,
+ 0.9317, 0.5206, 0.1470, 0.0151, 0.0001,
+ ]
+),
+
+"dirac": \(
+ compareUsing close (vec.list (win.dirac 1)) [ 1 ] and
+ compareUsing close (vec.list (win.dirac 5)) [ 0, 0, 1, 0, 0 ] and
+ compareUsing close (vec.list (win.dirac 6)) [ 0, 0, 0, 1, 0, 0 ]
+),
+
+"sinc": \(
+ compareUsing close (vec.list (win.sinc 1 5)) [ 0, 0, 1, 0, 0 ] and
+ compareUsing close (vec.list (win.sinc 2 5)) [ 0, 0, 1, 0, 0 ] and
+ compareUsing close (vec.list (win.sinc 4 7)) [ -0.2122, 0, 0.6366, 1, 0.6366, 0, -0.2122 ] and
+ compareUsing close (vec.list (win.sinc 4 6)) [ -0.2122, 0, 0.6366, 1, 0.6366, 0 ]
+),
+
+"kaiser": \(
+ compareUsing close (vec.list (win.kaiser 4 10)) [
+ 0.0885, 0.2943, 0.5644, 0.8216, 0.9789,
+ 0.9789, 0.8216, 0.5644, 0.2943, 0.0885
+ ] and
+ compareUsing close (vec.list (win.kaiser 2.5 11)) [
+ 0.3040, 0.5005, 0.6929, 0.8546, 0.9622,
+ 1.0000, 0.9622, 0.8546, 0.6929, 0.5005, 0.3040
+ ]
+),
+
+"degenerate": \(
+ all id (map do type:
+ f = functions[type];
+ periodic = f;
+ symmetric = win.windowFunction type [ Symmetric true ];
+ (compare (vec.list (periodic 0)) [] and
+ compare (vec.list (periodic 1)) [1] and
+ compare (vec.list (symmetric 0)) [] and
+ compare (vec.list (symmetric 1)) [1])
+ or (eprintln "** failed window type: \(type)"; false)
+ done (keys functions));
+),
+
+] is hash boolean>;
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/signal/window.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/signal/window.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,191 @@
+module may.signal.window;
+
+vec = load may.vector;
+bf = load may.vector.blockfuncs;
+
+cosineWindowSymmetric a0 a1 a2 a3 n =
+ (n1 = n - 1;
+ vec.fromList
+ (map do i:
+ a0
+ - a1 * cos(2 * pi * i / n1)
+ + a2 * cos(4 * pi * i / n1)
+ - a3 * cos(6 * pi * i / n1)
+ done [0..n1]));
+
+cosineWindowPeriodic a0 a1 a2 a3 n =
+ (vec.fromList
+ (map do i:
+ a0
+ - a1 * cos(2 * pi * i / n)
+ + a2 * cos(4 * pi * i / n)
+ - a3 * cos(6 * pi * i / n)
+ done [0..n-1]));
+
+cosineWindow a0 a1 a2 a3 sampling n =
+ if n < 2 then vec.ones n
+ else
+ case sampling of
+ Symmetric (): cosineWindowSymmetric;
+ Periodic (): cosineWindowPeriodic;
+ esac a0 a1 a2 a3 n;
+ fi;
+
+bartlettSymmetric n =
+ if n < 2 then vec.ones n
+ else
+ vec.fromList
+ (n1 = n - 1;
+ h = int (n1 / 2);
+ concat [
+ map do i:
+ 2 * i / n1
+ done [0..h],
+ map do i:
+ 2 - (2 * i / n1)
+ done [h+1..n1]
+ ]);
+ fi;
+
+bartlettPeriodic n =
+ if n < 2 then vec.ones n
+ else
+ vec.slice (bartlettSymmetric (n+1)) 0 n;
+ fi;
+
+bartlett sampling =
+ case sampling of
+ Symmetric (): bartlettSymmetric;
+ Periodic (): bartlettPeriodic;
+ esac;
+
+hann = cosineWindow 0.5 0.5 0.0 0.0;
+hamming = cosineWindow 0.54 0.46 0.0 0.0;
+blackman = cosineWindow 0.42 0.50 0.08 0.0;
+nuttall = cosineWindow 0.355768 0.487396 0.144232 0.012604;
+blackmanNuttall = cosineWindow 0.3635819 0.4891775 0.1365995 0.0106411;
+blackmanHarris = cosineWindow 0.35875 0.48829 0.14128 0.01168;
+
+boxcar n = vec.ones n;
+
+/**
+ * Vector of size n with the "middle" sample equal to 1 and all others
+ * equal to 0. The middle sample is sample (n-1)/2 for odd n or n/2+1
+ * for even n.
+ */
+dirac n =
+ if n < 2 then vec.ones n
+ else
+ n0 = if n % 2 == 0 then n/2 else (n-1)/2 fi;
+ vec.concat [ vec.zeros n0, vec.ones 1, vec.zeros n0 ]
+ fi;
+
+/**
+ * Make a vector of size n containing the values of sinc(x) with
+ * x=0 in the middle, i.e. at sample (n-1)/2 for odd n or n/2+1 for
+ * even n, such that the distance from -pi to pi (the point at
+ * which the sinc function first crosses zero, for negative and
+ * positive arguments respectively) is p samples. p does not have
+ * to be an integer.
+ */
+sinc p n =
+ if n < 2 then vec.ones n
+ else
+ n0 = if n % 2 == 0 then n/2 else (n-1)/2 fi;
+ n1 = if n % 2 == 0 then n/2 else (n+1)/2 fi;
+ half = 1 :: map do i: x = i * 2*pi / p; sin(x) / x done [1..n/2];
+ vec.fromList ((take n0 (reverse half)) ++ (take n1 half));
+ fi;
+
+kaiser beta n =
+ (bes0 x =
+ (fact x = fold do x y: x*y done 1 [1..x]; // x!
+ ipow a b = fold do x _: x*a done 1 [1..b]; // a^b where b∈ℕ
+ square x = x*x;
+ term x i =
+ case i of
+ 0: 1;
+ _: (ipow (x/2) (i*2)) / (square (fact i));
+ esac;
+ sum (map (term x) [0..20]));
+ denominator = bes0 beta;
+ vec.fromList
+ (map do i:
+ k = 2*i / (n-1) - 1;
+ bes0 (beta * sqrt (1 - k*k)) / denominator;
+ done [0..n-1]));
+
+/**
+ Kaiser window with sidelobe attenuation of 𝛼 dB and window length n
+*/
+kaiserForAttenuation alpha n =
+ (beta =
+ if alpha > 50 then 0.1102 * (alpha - 8.7)
+ elif alpha > 21 then 0.5842 * Math#pow(alpha - 21, 0.4) + 0.07886 * (alpha - 21)
+ else 0
+ fi;
+ kaiser beta n);
+
+/**
+ Kaiser window with sidelobe attenuation of alpha dB and transition
+ bandwidth of (tw * samplerate) / (2 * pi)
+*/
+kaiserForTransitionLength alpha tw =
+ (m = if alpha > 21
+ then Math#ceil((alpha - 7.95) / (2.285 * tw))
+ else Math#ceil(5.79 / tw)
+ fi;
+ kaiserForAttenuation alpha (m+1));
+
+/**
+ Kaiser window with sidelobe attenuation of alpha dB and transition
+ bandwidth of tbw Hz at the given sampleRate
+*/
+kaiserForBandwidth alpha tbw samplerate =
+ kaiserForTransitionLength alpha ((tbw * 2 * pi) / samplerate);
+
+windowFunction type options =
+ (var sampling = Periodic ();
+ var beta = 4;
+ for options \case of
+ Symmetric s: if s then sampling := Symmetric () fi;
+ Beta b: beta := b;
+ esac;
+ case type of
+ Hann (): hann sampling;
+ Hamming (): hamming sampling;
+ Blackman (): blackman sampling;
+ Nuttall (): nuttall sampling;
+ BlackmanNuttall (): blackmanNuttall sampling;
+ BlackmanHarris (): blackmanHarris sampling;
+ Boxcar (): boxcar;
+ Bartlett (): bartlett sampling;
+ Kaiser (): kaiser beta;
+ esac);
+
+//!!! should use vector. but does anyone use this function anyway? would we use it in framer if it used vector?
+windowed windowFunc frames =
+ case frames of
+ []: frames;
+ _: (first = head frames;
+ window = windowFunc (vec.length first);
+ map (bf.multiply window) frames);
+ esac;
+
+{
+cosineWindow,
+hann = hann (Periodic ()),
+hamming = hamming (Periodic ()),
+blackman = blackman (Periodic ()),
+nuttall = nuttall (Periodic ()),
+blackmanNuttall = blackmanNuttall (Periodic ()),
+blackmanHarris = blackmanHarris (Periodic ()),
+boxcar,
+bartlett = bartlett (Periodic ()),
+dirac,
+sinc,
+kaiser, kaiserForAttenuation, kaiserForTransitionLength, kaiserForBandwidth,
+windowFunction,
+windowed
+};
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/stream/audiofile.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/stream/audiofile.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,165 @@
+
+module may.stream.audiofile;
+
+load may.stream.type;
+
+import javax.sound.sampled:
+ AudioSystem, AudioInputStream, AudioFormat, AudioFormat$Encoding,
+ AudioFileFormat, AudioFileFormat$Type,
+ TargetDataLine, LineListener, Line, Line$Info, Control, Control$Type,
+ UnsupportedAudioFileException;
+
+import java.io: File, IOException;
+
+import java.nio: ByteBuffer, ByteOrder;
+
+ch = load may.stream.channels;
+vec = load may.vector;
+mat = load may.matrix;
+
+{ decode } = load may.stream.format;
+
+readInterleaved' { format is ~AudioFormat, aistream is ~AudioInputStream } nframes =
+ (channels = format#getChannels();
+ bytesPerSample = format#getSampleSizeInBits() / 8;
+ bytes = new byte[nframes * channels * bytesPerSample];
+ bytesRead = aistream#read(bytes);
+ if bytesRead <= 0 then vec.zeros 0;
+ else
+ n = int(bytesRead / bytesPerSample);
+ doubles = new double[n];
+ decode { format } bytes doubles n;
+ vec.vector doubles;
+ fi;
+ );
+
+read' { format is ~AudioFormat, aistream is ~AudioInputStream } n =
+ (b = readInterleaved' { format, aistream } n;
+ channels = format#getChannels();
+ ch.deinterleaved channels b;
+ );
+
+readMono' { format is ~AudioFormat, aistream is ~AudioInputStream } n =
+ (b = readInterleaved' { format, aistream } n;
+ channels = format#getChannels();
+ ch.deinterleaved 1 (ch.mixedDownFromInterleaved channels b);
+ );
+
+// Note, all this assumes aistream is non-blocking (i.e. available()
+// is to the end of file). Our stream interface does support
+// indefinite and infinite streams, but audiofile doesn't yet.
+
+available' { format is ~AudioFormat, aistream is ~AudioInputStream } =
+ aistream#available() / ((format#getSampleSizeInBits() / 8) * format#getChannels());
+
+close' { aistream is ~AudioInputStream } =
+ aistream#close();
+
+openWithReader reader ch name is 'a -> number -> string -> stream =
+ (f = new File(name);
+ aistream = AudioSystem#getAudioInputStream(f);
+ format = aistream#getFormat();
+ len = available' { format, aistream }; // at start of stream
+ syncd = synchronized aistream;
+ {
+ get position () = syncd \(len - available' { aistream, format }),
+ get channels () = if ch == 0 then format#getChannels() else ch fi,
+ get sampleRate () = format#getSampleRate(),
+ get available () = syncd \(Known (available' { aistream, format })),
+ get finished? () = syncd \(not (aistream#available() > 0)),
+ read = syncd \(reader { aistream, format }),
+ close () = syncd \(close' { aistream }),
+ });
+
+/**
+ * Open the named audio file and return a stream object for
+ * reading from it. May throw UnsupportedAudioFileException
+ * or IOException.
+ */
+open = openWithReader read' 0;
+
+/**
+ * Open the named audio file and return a stream object that
+ * reads mono samples from it, using
+ * may.stream.channels.mixedDown to mix channels as necessary.
+ * May throw UnsupportedAudioFileException or IOException.
+ */
+openMono = openWithReader readMono' 1;
+
+/**
+ * Open an audio file with the given name and write all available
+ * samples to it from the given stream, returning the number of
+ * sample frames written. The stream must have finite length.
+ *
+ * Example:
+ * : str = audiofile.open "in.wav";
+ * : n = audiofile.write str "out.wav";
+ */
+write str name is stream -> string -> number =
+ (var tot = 0;
+ case str.available of
+ Infinite (): failWith "Cannot write infinite stream to file";
+ _: ()
+ esac;
+ class StreamAdapter extends TargetDataLine
+ int read(byte[] bytes, int off, int len)
+ (bb = ByteBuffer#wrap(bytes, off, len);
+ bb#order(ByteOrder#LITTLE_ENDIAN);
+ m = str.read (len / (str.channels * 2));
+ tot := mat.width m;
+ for [0..mat.width m - 1] do i:
+ for [0..str.channels - 1] do c:
+ v = int (32767 * mat.at m c i);
+ v = if v < -32768 then -32768
+ elif v > 32767 then 32767
+ else v fi;
+ \() bb#putShort((i * str.channels + c) * 2, v);
+ done
+ done;
+ str.channels * 2 * mat.width m),
+ int available()
+ case str.available of
+ Known n: str.channels * n * 2;
+ other: str.channels * 1024; //!!!???
+ esac,
+ AudioFormat getFormat()
+ new AudioFormat(str.sampleRate, 16, str.channels, true, false),
+ void open(),
+ void open(AudioFormat format),
+ void open(AudioFormat format, int bufferSize),
+ void close(),
+ void drain(),
+ void flush(),
+ boolean isOpen() true,
+ boolean isActive() true,
+ boolean isRunning() true,
+ int getFramePosition() str.position,
+ long getLongFramePosition() str.position,
+ long getMicrosecondPosition() 0,
+ int getBufferSize() 0,
+ float getLevel() 1.0,
+ void start(),
+ void stop(),
+ void addLineListener(LineListener ll),
+ void removeLineListener(LineListener ll),
+ Control getControl(Control$Type c),
+ Control[] getControls(),
+ Line$Info getLineInfo(),
+ boolean isControlSupported(Control$Type c) false,
+ end;
+ f = new File(name);
+ n = AudioSystem#write(new AudioInputStream(new StreamAdapter()),
+ AudioFileFormat$Type#WAVE, f);
+ str.close ();
+ n); //!!! this is wrong, should be number of frames (is number of bytes?)
+
+{
+ open,
+ openMono,
+ write,
+} as {
+ open is string -> stream,
+ openMono is string -> stream,
+ write is stream -> string -> number,
+}
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/stream/channels.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/stream/channels.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,116 @@
+
+module may.stream.channels;
+
+vec = load may.vector;
+mat = load may.matrix;
+bf = load may.vector.blockfuncs;
+
+load may.vector.type;
+
+//!!! "internal" vector function to retrieve data for read-only
+// purposes without copying
+//!!! need to have this in some internal-use module
+raw =
+ (raw' v is ~double[] -> ~double[] = v;
+ raw' as vector -> ~double[]);
+
+interleaved m =
+ ({ columns, rows } = (mat.size m);
+ if rows == 1 then
+ mat.getRow 0 m
+ else
+ v = new double[columns * rows];
+ for [0..rows-1] do row:
+ for [0..columns-1] do col:
+ v[col * rows + row] := mat.at m row col;
+ done;
+ done;
+ vec.vector v;
+ fi);
+
+deinterleaved channels b =
+ if channels == 1 then
+ mat.newRowVector b
+ else
+ rows = (vec.length b) / channels;
+ vv = array (map \(new double[rows]) [0..channels-1]);
+ v = raw b;
+ for [0..rows-1] do row:
+ for [0..channels-1] do col:
+ vv[col][row] := v[channels * row + col];
+ done
+ done;
+ mat.newMatrix (RowMajor ()) (map vec.vector vv);
+ fi;
+
+mixedDown m = //!!!doc: average, not sum
+ ({ columns, rows } = (mat.size m);
+ if rows < 1 or columns < 1 then
+ vec.zeros 0
+ elif rows == 1 then
+ mat.getRow 0 m
+ else
+ bf.divideBy rows (bf.add (mat.asRows m));
+ fi);
+
+mixedDownFromInterleaved channels b =
+ if channels == 1 then
+ b;
+ else
+ columns = ((vec.length b) / channels);
+ v = raw b;
+ v' = new double[columns];
+ for [0..channels-1] do row:
+ for [0..columns-1] do col:
+ v'[col] := v'[col] + v[col * channels + row];
+ done;
+ done;
+ bf.divideBy channels (vec.vector v');
+ fi;
+
+mixedFromInterleavedTo targetChannels channels b =
+ if targetChannels == channels then
+ b;
+ elif targetChannels == 1 then
+ mixedDownFromInterleaved channels b;
+ else
+ columns = ((vec.length b) / channels);
+ v = raw b;
+ v' = new double[columns * targetChannels];
+ for [0..targetChannels-1] do target:
+ for [0..columns-1] do col:
+ if target < channels then
+ v'[col * targetChannels + target] := v[col * channels + target];
+ elif channels == 1 and target == 1 then
+ v'[col * targetChannels + target] := v[col * channels];
+ fi
+ done
+ done;
+ vec.vector v';
+ fi;
+
+mixedTo targetChannels m =
+ if targetChannels == mat.height m then // n -> n: pass unmodified
+ m
+ elif targetChannels == 1 then // n -> 1: mix down
+ deinterleaved 1 (mixedDown m)
+ elif mat.height m == 1 then // 1 -> n: copy to all channels
+ mat.newMatrix (RowMajor ()) (map \(mat.getRow 0 m) [1..targetChannels])
+ else // n -> m: truncate or add zeros
+ mat.resizedTo { rows = targetChannels, columns = mat.width m } m
+ fi;
+
+mixedAndInterleavedTo targetChannels m =
+ if targetChannels == 1 then
+ mixedDown m
+ else
+ interleaved (mixedTo targetChannels m);
+ fi;
+
+//!!! some of these names are terrible
+{
+ interleaved, deinterleaved,
+ mixedDown, mixedDownFromInterleaved,
+ mixedFromInterleavedTo, mixedTo, mixedAndInterleavedTo
+}
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/stream/filter.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/stream/filter.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,570 @@
+
+module may.stream.filter;
+
+mat = load may.matrix;
+ch = load may.stream.channels;
+vec = load may.vector;
+bf = load may.vector.blockfuncs;
+fr = load may.stream.framer;
+win = load may.signal.window;
+fft = load may.transform.fft;
+syn = load may.stream.syntheticstream;
+cplx = load may.complex;
+
+load may.stream.type;
+load may.vector.type;
+load may.matrix.type;
+
+//!!! todo: synchronized for everything with state assignment
+
+minDurationOf d1 d2 =
+ case d1 of
+ Known a:
+ case d2 of
+ Known b: Known (min a b);
+ Unknown (): Unknown ();
+ Infinite (): Known a;
+ esac;
+ Unknown ():
+ case d2 of
+ Known b: Known b;
+ Unknown (): Unknown ();
+ Infinite (): Unknown ();
+ esac;
+ Infinite ():
+ d2;
+ esac;
+
+withDuration nsamples s = //!!! should nsamples be a time in seconds? (no)
+ (var pos = 0;
+ s with
+ {
+ get position () = pos,
+ get available () = Known (nsamples - pos),
+ get finished? () = not (nsamples > pos),
+ read count =
+ (n = min count (nsamples - pos);
+ pos := pos + n;
+ if not s.finished? then
+ mat.resizedTo { rows = s.channels, columns = n } (s.read n);
+ else
+ mat.zeroMatrix { columns = n, rows = s.channels }
+ fi),
+ });
+
+delayedBy nsamples s = //!!! doc: nsamples may be -ve
+ (var prepos = 0;
+ zeros n = mat.toRowMajor
+ (prepos := prepos + n;
+ mat.zeroMatrix { rows = s.channels, columns = n });
+ delay =
+ if nsamples < 0 then
+ \0 (s.read (-nsamples));
+ else
+ nsamples
+ fi;
+ {
+ get position () =
+ if prepos < delay then prepos
+ elif s.position + nsamples < 0 then 0
+ else s.position + nsamples
+ fi,
+ get channels () = s.channels,
+ get sampleRate () = s.sampleRate,
+ get available () =
+ case s.available of
+ Known a: Known (a + delay - prepos);
+ other: other
+ esac,
+ get finished? () = (prepos >= delay) and s.finished?,
+ read count =
+ if prepos >= delay then s.read count
+ elif prepos + count < delay then zeros count
+ else
+ nleft = delay - prepos;
+ left = zeros nleft;
+ right = s.read (count - nleft);
+ mat.concat (Horizontal ()) [left, right];
+ fi,
+ close = s.close
+ });
+
+scaledBy factor s =
+ s with
+ {
+ read count = mat.scaled factor (s.read count);
+ };
+
+inverted s = //!!! todo: test
+ s with
+ {
+ read count =
+ (m = s.read count;
+ if mat.empty? m then m
+ else mat.difference (mat.zeroMatrix (mat.size m)) m
+ fi)
+ };
+
+//!!! poor name, confusion with mixed, but consistent with channels.yeti
+mixedTo targetChannels s =
+ s with
+ {
+ get channels () = targetChannels,
+ read count = ch.mixedTo targetChannels (s.read count),
+ };
+
+//!!! what should happen if we mix or multiplex a finite-length and an
+//infinite-length stream? or even two streams with differing finite
+//lengths? write tests for this. At the moment the resulting stream
+//has duration equal to the shortest source stream and finishes as
+//soon as any one of them finishes
+
+sum' streams =
+ (mix m1 m2 =
+ (sz = { rows = max (mat.height m1) (mat.height m2),
+ columns = min (mat.width m1) (mat.width m2) };
+ if sz.columns == 0 then
+ mat.zeroSizeMatrix ()
+ else
+ mat.sum (mat.resizedTo sz m1) (mat.resizedTo sz m2);
+ fi);
+ {
+ get position () = head (sort (map (.position) streams)),
+ get channels () = head (sortBy (>) (map (.channels) streams)),
+ get sampleRate () = (head streams).sampleRate, //!!! document this
+ get available () =
+ fold do dur s: minDurationOf dur s.available done (Infinite ()) streams,
+ get finished? () = any id (map (.finished?) streams),
+ read count =
+ (readTo acc ss count =
+ case ss of
+ first::rest:
+ part = first.read count;
+ case acc of
+ Some m:
+ readTo (Some (mix m part)) rest count;
+ None ():
+ readTo (Some part) rest count;
+ esac;
+ _: acc;
+ esac;
+ case readTo (None ()) streams count of
+ None (): mat.zeroSizeMatrix ();
+ Some m: m;
+ esac),
+ close () = for streams do s: s.close() done,
+ });
+
+mean streams = //!!! todo: test
+ scaledBy (1 / (length streams)) (sum' streams);
+
+difference s1 s2 = //!!! todo: test
+ sum' [ s1, inverted s2 ];
+
+multiplexed streams =
+ {
+ get position () = head (sort (map (.position) streams)), // can differ after EOS
+ get channels () = sum (map (.channels) streams),
+ get sampleRate () = (head streams).sampleRate, //!!! document this
+ get available () =
+ fold do dur s: minDurationOf dur s.available done (Infinite ()) streams,
+ get finished? () = any id (map (.finished?) streams),
+ read count =
+ (outs = map do s: s.read count done streams;
+ minlen = head (sort (map mat.width outs));
+ outs = map do m:
+ mat.resizedTo { rows = mat.height m, columns = minlen } m
+ done outs;
+ mat.concat (Vertical ()) outs
+ ),
+ close () = for streams do s: s.close() done,
+ };
+
+repeated s =
+ // There is no way to reset a stream (as in principle, they might
+ // be "live") so we can't read from the same stream repeatedly --
+ // we have to cache its output and then repeat that. This is a
+ // little tricky to do efficiently without knowing how long the
+ // stream is (in general) or how much is going to be requested at
+ // a time.
+ if s.available == Infinite () then s
+ else
+ var pos = 0;
+ var cache = mat.zeroSizeMatrix ();
+ chunks = array [];
+ cachedPartsFor count =
+ (start = pos % (mat.width cache);
+ avail = (mat.width cache) - start;
+ if avail >= count then
+ pos := pos + count;
+ [mat.columnSlice cache start (start + count)]
+ else
+ pos := pos + avail;
+ mat.columnSlice cache start (start + avail) ::
+ cachedPartsFor (count - avail);
+ fi);
+ readFromCache count =
+ (if (mat.width cache) == 0 then
+ cache := mat.concat (Horizontal ()) (list chunks);
+ clearArray chunks;
+ fi;
+ if (mat.width cache) == 0 then
+ cache
+ else
+ mat.concat (Horizontal ()) (cachedPartsFor count);
+ fi);
+ s with
+ {
+ get position () = pos,
+ get available () = Infinite (),
+ get finished? () = false,
+ read count =
+ if s.finished? then
+ readFromCache count
+ else
+ part = s.read count;
+ len = (mat.width part);
+ push chunks part;
+ pos := pos + len;
+ if len == count then part
+ else
+ mat.concat (Horizontal ())
+ [part, readFromCache (count - len)];
+ fi;
+ fi,
+ }
+ fi;
+
+duplicated copies s =
+//!!! doc fact that original s cannot be used independently of this afterward
+// (so maybe name is misleading?)
+ array if copies < 2 then map \s [1..copies];
+ else
+ pos = [:];
+ lowtide () = head (sort (map (at pos) (keys pos)));
+ var hightide = 0;
+ var cache = mat.zeroSizeMatrix ();
+ syncd = synchronized pos;
+ advance i n =
+ (formerLow = lowtide ();
+ pos[i] := pos[i] + n;
+ encroachment = lowtide () - formerLow;
+ if encroachment > 0 then
+ cache := mat.columnSlice cache encroachment (mat.width cache);
+ fi);
+ map do instance:
+ pos[instance] := 0;
+ {
+ get position () = syncd \(pos[instance]),
+ get channels () = syncd \(s.channels),
+ get sampleRate () = syncd \(s.sampleRate),
+ get available () = syncd
+ \(case s.available of
+ Known av: Known (av + (hightide - pos[instance]));
+ other: other;
+ esac),
+ get finished? () = syncd
+ \(if not s.finished? then false
+ else pos[instance] >= hightide
+ fi),
+ read count = syncd
+ \(ready = hightide - pos[instance];
+ if s.finished? and ready <= 0
+ then mat.zeroSizeMatrix ()
+ else
+ if count > ready then
+ more = s.read (count - ready);
+ cache := mat.concat (Horizontal ()) [cache, more];
+ hightide := hightide + (mat.width more);
+ fi;
+ offset = pos[instance] - (lowtide ());
+ chunk = mat.columnSlice cache offset (offset + count);
+ advance instance (mat.width chunk);
+ chunk;
+ fi),
+ close () = syncd
+ \(delete pos instance;
+ if empty? pos then
+ s.close ()
+ fi),
+ }
+ done [1..copies];
+ fi;
+
+zeroPaddedFreqFrames framesize channels =
+ // take a multi-channel stream, convert to a series of list of
+ // complex frequency-domain frames calculated from input padded to
+ // framesize*2, in which each frame contains all channels one
+ // after another (not interleaved) [because we lack a complex
+ // matrix type]
+ (forwardTransform = fft.realForward (framesize * 2);
+ do stream:
+ padded =
+ (map (mat.resizedTo { rows = channels, columns = framesize * 2 })
+ (fr.frames { framesize, hop = framesize } stream));
+ map do fr: concat (map forwardTransform (mat.asRows fr)) done padded;
+ done);
+
+doFastConvolve irframes sigframes =
+ (var history = [];
+ irlen = length irframes;
+ n = length (head irframes);
+ map do sigf:
+ history := take irlen (sigf::history);
+ fold do cc1 cc2: map2 cplx.add cc1 cc2 done
+ (list (cplx.zeros n))
+ (map2 do irf histf:
+ map2 cplx.multiply irf histf;
+ done irframes history);
+ done sigframes);
+
+splitInto n fr =
+ (parts = splitAt n fr;
+ if empty? parts.snd then [parts.fst]
+ else parts.fst :: splitInto n parts.snd;
+ fi);
+
+fastConvolvedWith ir framesize s =
+ // prerequisite: ir and s have same number of channels
+ (framer = zeroPaddedFreqFrames framesize (mat.height ir);
+ ch = s.channels;
+ irfr = framer (syn.precalculated 1 ir); // rate arg is irrelevant here
+ sigfr = framer s;
+ inverseTransform = fft.realInverse (framesize * 2);
+ extended = sigfr ++
+ map do _: list (cplx.zeros (ch * (framesize + 1))) done
+ [1..length irfr-1];
+ cframes = doFastConvolve irfr extended;
+ rframes = (mat.zeroMatrix { rows = ch, columns = framesize * 2}) ::
+ map do fr:
+ mat.newMatrix (RowMajor ())
+ (map inverseTransform (splitInto (framesize+1) fr))
+ done cframes;
+ fr.streamOverlapping s.sampleRate
+ { framesize = framesize * 2, hop = framesize, window = win.boxcar }
+ rframes;
+);
+
+plainConvolvedWith ir s =
+ // prerequisite: ir and s have same number of channels
+ (var history = mat.toRowMajor
+ (mat.zeroMatrix { rows = s.channels, columns = mat.width ir - 1 });
+ s with
+ {
+ get finished? () =
+ s.finished? and (mat.empty? history),
+ get available () =
+ case s.available of
+ Known n: Known (n + mat.width history);
+ other: other;
+ esac,
+ read count =
+ (// Example: The IR is four samples long; we have three
+ // samples in history; two samples are available to read
+ // before the stream runs out. That means we can return
+ // up to five samples. Caller requests 6.
+ signal = s.read count; // -> two samples
+ siglen = mat.width signal; // -> 2
+ histlen = mat.width history; // -> 3
+ convlen = min count (siglen + histlen); // -> 5
+ input = mat.resizedTo { rows = s.channels, columns = convlen }
+ signal; // example input now 5 samples, of which 2 are signal
+ output = array (map \(new double[convlen]) [1..s.channels]);
+ for [0..s.channels - 1] do ch:
+ for [0..mat.width input - 1] do i:
+ for [0..mat.width ir - 1] do j:
+ v =
+ if i >= j then
+ mat.at input ch (i - j)
+ else
+ ix = mat.width ir + i - j - 1;
+ if ix >= histlen then
+ 0
+ else
+ mat.at history ch ix
+ fi
+ fi;
+ output[ch][i] := output[ch][i] + v * (mat.at ir ch j);
+ done;
+ done;
+ done;
+ // Remove from history a number of samples equal to the
+ // number returned; add to it a number equal to the number
+ // read from source
+ extended = mat.concat (Horizontal ()) [history, signal]; // -> 5
+ newlen = (histlen + siglen) - convlen; // -> 0
+ w = mat.width extended;
+ history := mat.columnSlice extended (w - newlen) w;
+ mat.newMatrix (RowMajor ()) (map vec.vector output)),
+ });
+
+nextPowerOfTwo n =
+ (nextPowerOfTwo' p n =
+ if p >= n then p
+ else nextPowerOfTwo' (p * 2) n
+ fi;
+ nextPowerOfTwo' 1 n);
+
+//!!! todo: partial application so as to preprocess ir (in fast convolution case)
+convolvedWith options ir s = //!!! cheap mono thing here
+ (if mat.height ir != s.channels then
+ failWith "Signal stream and IR must have same number of channels (\(s.channels) != \(mat.height ir))"
+ fi;
+ var type = Fast ();
+ var framesize = nextPowerOfTwo (mat.width ir);
+ for options \case of
+ Fast s: if s then type := Fast () else type := Plain () fi;
+ Framesize n: framesize := nextPowerOfTwo n;
+ esac;
+ case type of
+ Fast ():
+ fastConvolvedWith ir framesize s;
+ Plain ():
+ plainConvolvedWith ir s;
+ esac);
+
+/**
+ Produce a sinc window of width equal to zc zero crossings per half,
+ with n samples from peak to first zero crossing, multiplied by a
+ Kaiser window with attenuation 𝛼 dB
+ */
+kaiserSincWindow zc n 𝛼 =
+ (sw = win.sinc (n*2) (n*zc*2 + 1);
+ kw = win.kaiserForAttenuation 𝛼 (vec.length sw);
+ bf.multiply sw kw);
+
+bandpassed f0 f1 attenuation bandwidth s =
+ (rate = s.sampleRate;
+ kw = win.kaiserForBandwidth attenuation bandwidth rate;
+ filterLength = vec.length kw;
+ // First arg to sinc is the complete cycle length for the cutoff frequency
+ idealFor freq =
+ bf.scaled (2 * freq / rate)
+ (win.sinc (rate / freq) filterLength);
+ idealBandpass =
+ if f1 < rate/2 then
+ if f0 > 0 then bf.subtract (idealFor f1) (idealFor f0)
+ else idealFor f1
+ fi
+ else
+ if f0 > 0 then bf.subtract (win.dirac filterLength) (idealFor f0)
+ else win.dirac filterLength;
+ fi;
+ fi;
+ filter = bf.multiply idealBandpass kw;
+ filtered = convolvedWith [Framesize 1024]
+ (mat.newMatrix (RowMajor ()) (map \filter [1..s.channels]))
+ s;
+ delayedBy (- (int (filterLength / 2))) filtered);
+
+lowpassed f attenuation bandwidth s =
+ bandpassed 0 f attenuation bandwidth s;
+
+highpassed f attenuation bandwidth s =
+ bandpassed f (s.sampleRate/2) attenuation bandwidth s;
+
+spaced mult s = //!!! mult must be an integer [how to enforce this??]
+ (spaceToNext pos = case (pos % mult) of 0: 0; n: (mult - n) esac;
+ readWithoutPadding n =
+ (toRead = int Math#ceil(n / mult);
+ source = s.read toRead;
+ targetWidth = min n (mult * mat.width source);
+ mat.newMatrix (ColumnMajor ())
+ (map do i:
+ if i % mult == 0 then
+ mat.getColumn (int (i / mult)) source
+ else
+ vec.zeros s.channels
+ fi
+ done [0..targetWidth-1]));
+ var pos = 0;
+ s with
+ {
+ get position () = pos,
+ get available () =
+ case s.available of
+ Known n: Known ((spaceToNext pos) + (n * mult));
+ other: other
+ esac,
+ read n =
+ (sp = spaceToNext pos;
+ result = mat.toRowMajor
+ (mat.concat (Horizontal ())
+ [mat.zeroMatrix { rows = s.channels, columns = min sp n },
+ readWithoutPadding (max (n - sp) 0)]);
+ pos := pos + mat.width result;
+ result),
+ });
+
+interpolated factor s = //!!! factor must be an integer [how to enforce this??]
+ if factor == 1 then s
+ else
+ nzc = 13;
+ attenuation = 80;
+ filter = kaiserSincWindow nzc factor attenuation;
+ out = delayedBy (- (nzc * factor))
+ (convolvedWith [Framesize 1024]
+ (mat.newMatrix (RowMajor ()) (map \filter [1..s.channels]))
+ (spaced factor s));
+ out with { get sampleRate () = s.sampleRate * factor };
+ fi;
+
+picked frac s = //!!! frac must be an integer [how to enforce this??]
+ s with
+ {
+ get position () = int Math#ceil(s.position / frac),
+ get available () =
+ case s.available of
+ Known n: Known (int Math#ceil(n / frac));
+ other: other
+ esac,
+ read n =
+ (m = s.read (n*frac);
+ obtained = int Math#ceil((mat.width m) / frac);
+ mat.flipped
+ (mat.newMatrix (ColumnMajor ())
+ (map do c: mat.getColumn (c*frac) m done [0..obtained-1])))
+ };
+
+decimated factor s = //!!! factor must be an integer [how to enforce this??]
+ if factor == 1 then s
+ else
+ nzc = 13;
+ attenuation = 80;
+ filter = kaiserSincWindow nzc factor attenuation;
+ filtered =
+ (convolvedWith [Framesize 1024]
+ (mat.newMatrix (RowMajor ()) (map \filter [1..s.channels])) s);
+ out = scaledBy (1/factor)
+ (delayedBy (- nzc)
+ (picked factor filtered));
+ out with { get sampleRate () = s.sampleRate / factor };
+ fi;
+
+gcd a b = (c = a % b; if c == 0 then b else gcd b c fi);
+
+//!!! slow -- can't in this form be used for e.g. 44100 -> 48000 conversion
+resampledTo rate s =
+ (g = gcd rate s.sampleRate;
+ println "orig = \(s.sampleRate), target = \(rate), g = \(g), up = \(rate/g), down = \(s.sampleRate/g)";
+ decimated (s.sampleRate / g) (interpolated (rate/g) s));
+
+{
+ withDuration is number -> stream -> stream,
+ delayedBy is number -> stream -> stream,
+ scaledBy is number -> stream -> stream,
+ inverted is stream -> stream,
+ mixedTo is number -> stream -> stream,
+ sum is list -> stream = sum',
+ mean is list -> stream,
+ difference is stream -> stream -> stream,
+ multiplexed is list -> stream,
+ repeated is stream -> stream,
+ duplicated is number -> stream -> array,
+ convolvedWith is list -> matrix -> stream -> stream,
+ kaiserSincWindow,
+ lowpassed, bandpassed, highpassed,
+ spaced, interpolated, decimated, picked,
+ resampledTo,
+}
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/stream/format.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/stream/format.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,55 @@
+
+module may.stream.format;
+
+import java.nio: ByteBuffer, ByteOrder;
+
+import javax.sound.sampled:
+ AudioFormat, AudioFormat$Encoding,
+ UnsupportedAudioFileException;
+
+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("Audio 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);
+
+{
+ decode
+}
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/stream/framer.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/stream/framer.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,214 @@
+
+module may.stream.framer;
+
+/**
+ * Framer expresses a stream (or a file) as a lazy list of (possibly
+ * overlapping) frames of data.
+ */
+
+vec = load may.vector;
+bf = load may.vector.blockfuncs;
+af = load may.stream.audiofile;
+win = load may.signal.window;
+fft = load may.transform.fft;
+mat = load may.matrix;
+ch = load may.stream.channels;
+
+load may.stream.type;
+load may.matrix.type;
+load may.vector.type;
+load may.complex.type;
+
+//!!! todo: synchronized for everything with state assignment
+
+blockList framesize stream =
+ if stream.finished? then
+ stream.close ();
+ []
+ else
+ mat.resizedTo { rows = stream.channels, columns = framesize }
+ (stream.read framesize)
+ :. \(blockList framesize stream);
+ fi;
+
+overlappingBlockList size hop stream valid buffer =
+ (
+ m = stream.read hop;
+ obtained = mat.width m;
+
+ // Retain framesize - hop samples from old buffer, add hop samples
+ // (zero-padded if necessary) just read
+ buffer = map2
+ do buf row:
+ vec.concat
+ [vec.slice buf hop size,
+ vec.resizedTo hop (mat.getRow row m)];
+ done buffer [0..stream.channels-1];
+
+ // Number of "valid" elements (not tail-end zero-padding) left in buffer
+ remaining = valid - (hop - obtained);
+
+ if remaining <= 0 then
+ stream.close ();
+ [];
+ else
+ mat.newMatrix (RowMajor ()) 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 (map \(vec.zeros framesize) [0..stream.channels-1]);
+ fi;
+
+streamContiguous rate framesize frames =
+ (var remaining = frames;
+ var buffered = mat.zeroSizeMatrix ();
+ var position = 0;
+ channels = mat.height (head frames); // so we don't need to keep a head ptr
+ {
+ get position () = position,
+ get channels () = channels,
+ get sampleRate () = rate,
+ get finished? () = empty? remaining and mat.empty? buffered,
+ get available () =
+ // Don't take length of frames -- we don't want to lose laziness.
+ // If the caller cares that much, they can measure frames themselves
+ if empty? remaining then
+ Known (mat.width buffered)
+ else
+ Unknown ()
+ fi,
+ read count =
+ (framesFor samples acc =
+ if samples <= 0 or empty? remaining then
+ reverse acc
+ else
+ this = head remaining;
+ remaining := tail remaining;
+ framesFor (samples - mat.width this) (this :: acc)
+ fi;
+ source = mat.concat (Horizontal ()) (framesFor count [buffered]);
+ toReturn = mat.columnSlice source 0 count;
+ position := position + mat.width toReturn;
+ buffered := mat.columnSlice source count (mat.width source);
+ toReturn),
+ close = \(),
+ });
+
+overlapAdd overlap frames =
+ (ola fr pending acc =
+ case fr of
+ first::rest:
+ (w = mat.width pending;
+ pre = mat.columnSlice pending 0 (w - overlap);
+ added = mat.sum first
+ (mat.resizedTo (mat.size first)
+ (mat.columnSlice pending (w - overlap) w));
+ ola rest added (pre::acc));
+ _:
+ reverse (pending::acc);
+ esac;
+ case frames of
+ first::rest:
+ mat.concat (Horizontal ()) (ola rest first []);
+ _:
+ mat.zeroSizeMatrix ();
+ esac);
+
+streamOverlapping rate { framesize, hop, window } frames =
+ (var remaining = frames;
+ var buffered = mat.zeroSizeMatrix ();
+ var position = 0;
+
+ factor = hop / (framesize/2);
+ w = bf.scaled factor (window framesize);
+ channels = mat.height (head frames); // so we don't need to keep a head ptr
+
+ syncd = synchronized remaining;
+
+ finished' () = syncd \(empty? remaining and mat.empty? buffered);
+
+ read' count =
+ (framesFor samples acc =
+ if samples <= 0 or empty? remaining then
+ reverse acc
+ else
+ this = mat.resizedTo { columns = framesize, rows = channels }
+ (mat.newMatrix (RowMajor ())
+ (map (bf.multiply w) (mat.asRows (head remaining))));
+ remaining := tail remaining;
+ framesFor (samples - hop) (this::acc)
+ fi;
+ source = overlapAdd (framesize - hop)
+ (framesFor count [buffered]);
+ buffered := mat.columnSlice source count (mat.width source);
+ mat.columnSlice source 0 count);
+
+ // lose initial padding
+ \() (read' (framesize - hop));
+
+ {
+ get position () = syncd \(position),
+ get channels () = channels,
+ get sampleRate () = rate,
+ get finished? () = finished' (),
+ get available () = if finished' () then Known 0 else Unknown () fi,
+ read count = syncd
+ \(data = read' count;
+ position := position + mat.width data;
+ data),
+ close = \(),
+ });
+
+//!!! doc: convert frames back to a stream
+streamed rate { framesize, hop } frames =
+ if framesize == hop then
+ streamContiguous rate framesize frames
+ else
+ streamOverlapping rate
+ { framesize, hop, window = win.hann } // periodic, not symmetric
+ frames
+ fi;
+
+monoFrames params stream =
+ map ch.mixedDown (frames params stream);
+
+windowedFrames { framesize, hop, window } stream =
+ (win = window framesize;
+ map (bf.multiply win) (monoFrames { framesize, hop } stream));
+
+frequencyDomainFrames parameters stream =
+ (f = fft.realForward parameters.framesize;
+ map f (windowedFrames parameters stream));
+
+typedef params = { framesize is number, hop is number };
+typedef winparams = { framesize is number, hop is number, window is number -> vector };
+
+{
+ frames is params -> stream -> list,
+ monoFrames is params -> stream -> list,
+ windowedFrames is winparams -> stream -> list,
+ frequencyDomainFrames is winparams -> stream -> list >,
+
+ framesOfFile parameters filename =
+ frames parameters (af.open filename),
+
+ monoFramesOfFile parameters filename =
+ monoFrames parameters (af.open filename),
+
+ windowedFramesOfFile parameters filename =
+ windowedFrames parameters (af.open filename),
+
+ frequencyDomainFramesOfFile parameters filename =
+ frequencyDomainFrames parameters (af.open filename),
+
+ overlapAdd is number -> list -> matrix,
+
+ streamed is number -> params -> list -> stream,
+ streamOverlapping is number -> winparams -> list -> stream,
+}
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/stream/playback.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/stream/playback.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,62 @@
+
+module may.stream.playback;
+
+vec = load may.vector;
+mat = load may.matrix;
+af = load may.stream.audiofile;
+ch = load may.stream.channels;
+
+import javax.sound.sampled:
+ AudioSystem, AudioFormat, AudioFormat$Encoding, SourceDataLine;
+
+import java.nio: ByteBuffer, ByteOrder;
+
+playBlock line b is ~SourceDataLine -> 'a -> () =
+ (len = vec.length b;
+ samples = vec.primitive 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;
+ \() 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,
+ sampleRate = rate,
+ channels,
+ play = play line,
+ close () = (line#drain(); line#close()),
+ });
+
+playMatrix rate m =
+ (channels = mat.height m;
+ line = open { rate, channels };
+ line.play [ch.mixedAndInterleavedTo channels m];
+ line.close());
+
+playStream stream =
+ (line = open { rate = stream.sampleRate, channels = stream.channels };
+ blocksize = 10240;
+ not stream.finished? loop
+ line.play [(ch.mixedAndInterleavedTo line.channels
+ (stream.read blocksize))];
+ line.close();
+ stream.close());
+
+playFile filename =
+ playStream (af.open filename);
+
+{
+ open, playMatrix, playStream, playFile,
+}
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/stream/record.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/stream/record.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,69 @@
+
+module may.stream.record;
+
+vec = load may.vector;
+mat = load may.matrix;
+af = load may.stream.audiofile;
+ch = load may.stream.channels;
+
+import javax.sound.sampled:
+ AudioSystem, AudioFormat, AudioFormat$Encoding,
+ TargetDataLine, DataLine, DataLine$Info;
+
+{ decode } = load may.stream.format;
+
+recordInterleaved seconds line is number -> ~TargetDataLine -> 'a =
+ (format = line#getFormat();
+ channels = format#getChannels();
+ nframes = seconds * format#getSampleRate();
+ bytesPerSample = format#getSampleSizeInBits() / 8;
+ bytes = new byte[nframes * channels * bytesPerSample];
+ bytesRead = line#read(bytes, 0, length bytes);
+ if bytesRead <= 0 then vec.zeros 0;
+ else
+ n = int(bytesRead / bytesPerSample);
+ doubles = new double[n];
+ decode { format } bytes doubles n;
+ vec.vector doubles;
+ fi;
+ );
+
+recordFor seconds line is number -> ~TargetDataLine -> 'a =
+ ch.deinterleaved line#getFormat()#getChannels()
+ (recordInterleaved seconds line);
+
+open { rate, channels } =
+ (format = new AudioFormat(AudioFormat$Encoding#PCM_SIGNED, rate, 16,
+ channels, channels * 2, rate, false);
+ info = new DataLine$Info
+ (Class#forName("javax.sound.sampled.TargetDataLine"), format);
+ if not AudioSystem#isLineSupported(info) then
+ failWith "Unable to open 16-bit PCM line at rate \(rate), \(channels) channels";
+ fi;
+ line = AudioSystem#getLine(info) unsafely_as ~TargetDataLine;
+ line#open(format);
+ line#start();
+ {
+ line,
+ sampleRate = rate,
+ channels,
+ norec recordFor t = recordFor t line,
+ close () = (line#drain(); line#close()),
+ });
+
+recordFor seconds =
+ (line = open { rate = 44100, channels = 2 }; //!!! or system defaults?
+ data = line.recordFor seconds;
+ line.close ();
+ {
+ sampleRate = line.sampleRate,
+ channels = line.channels,
+ data
+ });
+
+{
+ open,
+ recordFor
+}
+
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/stream/syntheticstream.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/stream/syntheticstream.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,97 @@
+
+module may.stream.syntheticstream;
+
+ch = load may.stream.channels;
+vec = load may.vector;
+mat = load may.matrix;
+
+load may.vector.type;
+load may.matrix.type;
+load may.stream.type;
+
+generated sampleRate generator =
+ (// generator takes sample number as arg, returns number in -1,+1 range
+ var position = 0;
+ {
+ get position () = position,
+ get channels () = 1,
+ get sampleRate () = sampleRate,
+ get available () = Infinite (),
+ get finished? () = false,
+ read count = ch.deinterleaved 1
+ (result = new double[count];
+ for [0..count-1] do i:
+ result[i] := generator (position + i)
+ done;
+ position := position + count;
+ vec.vector result),
+ close = \(),
+ });
+
+sinusoid rate freq =
+ generated rate (sin . (* (2 * pi * freq / rate)));
+
+whiteNoise rate =
+ generated rate \((Math#random() * 2.0) - 1.0);
+
+silent rate =
+ generated rate \0;
+
+precalculatedMono rate data =
+ (n = vec.length data;
+ var position = 0;
+ {
+ get position () = position,
+ get channels () = 1,
+ get sampleRate () = rate,
+ get available () = Known (n - position),
+ get finished? () = not (n > position),
+ read count = ch.deinterleaved 1
+ (rc = min count (n - position);
+ result = vec.slice data position (position + rc);
+ position := position + rc;
+ result),
+ close = \(),
+ });
+
+precalculated rate data =
+ (n = mat.width data;
+ c = mat.height data;
+ var position = 0;
+ {
+ get position () = position,
+ get channels () = c,
+ get sampleRate () = rate,
+ get available () = Known (n - position),
+ get finished? () = not (n > position),
+ read count =
+ (rc = min count (n - position);
+ result = mat.columnSlice data position (position + rc);
+ position := position + rc;
+ result),
+ close = \(),
+ });
+
+empty rate channels = // degenerate stream with no data in it, occasionally useful
+ {
+ get position () = 0,
+ get channels () = channels,
+ get sampleRate () = rate,
+ get available () = Known 0,
+ get finished? () = true,
+ read count = mat.zeroSizeMatrix (),
+ close = \(),
+ };
+
+{
+ generated is number -> (number -> number) -> stream,
+ precalculated is number -> matrix -> stream,
+ precalculatedMono is number -> vector -> stream,
+ sinusoid is number -> number -> stream,
+ whiteNoise is number -> stream,
+ silent is number -> stream,
+ empty is number -> number -> stream,
+}
+
+
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/stream/test/audiofile_reference.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/stream/test/audiofile_reference.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,35 @@
+
+module may.stream.test.audiofile_reference;
+
+syn = load may.stream.syntheticstream;
+filt = load may.stream.filter;
+
+//!!! docs from turbot
+
+pulseChannel rate =
+ (pulseFreq = 2;
+ pulseWidth = 0.01 * rate;
+ generator i =
+ (pulseNo = int ((i * pulseFreq) / rate);
+ index = (i * pulseFreq) - (rate * pulseNo);
+ if index < pulseWidth then
+ s = 1.0 - abs(pulseWidth/2 - index) / (pulseWidth/2);
+ if pulseNo % 2 != 0 then (-s) else s fi
+ else 0
+ fi);
+ syn.generated rate generator);
+
+referenceChannels rate =
+ (leftovers rate n =
+ (syn.generated rate \(n / 20) :. \(leftovers rate (n+1)));
+ syn.sinusoid rate 600 :: pulseChannel rate :: leftovers rate 2);
+
+afReference rate channels =
+ filt.withDuration (2 * rate)
+ (filt.multiplexed (take channels (referenceChannels rate)));
+
+{
+ afReference
+}
+
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/stream/test/speedtest.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/stream/test/speedtest.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,47 @@
+
+program may.stream.test.speedtest;
+
+af = load may.stream.audiofile;
+vec = load may.vector;
+mat = load may.matrix;
+filt = load may.stream.filter;
+
+import java.lang: StackOverflowError;
+
+{ compare, compareUsing, time } = load may.test.test;
+
+before = System#currentTimeMillis();
+
+str = time "open audio file" \(af.openMono "shortish.wav");
+
+conv = time "prepare convolve" \(filt.convolvedWith [ Framesize 256 ] (mat.newRowVector (vec.fromList [ 1, 0.8, 0.5 ])) str);
+
+var len = 0;
+time "read convolve" \((not conv.finished?) loop len := len + mat.width (conv.read 65536));
+
+str.close ();
+
+println "Done";
+
+after = System#currentTimeMillis();
+
+println "Total time: \(after - before)ms for \(len) audio sample frames [\(int (len / ((after - before) / 1000))) fps]";
+
+
+before = System#currentTimeMillis();
+
+str = time "open audio file" \(af.openMono "shortish.wav");
+
+res = time "prepare resample" \(filt.resampledTo 44100 str);
+
+len := 0;
+time "read resampled" \((not res.finished?) loop len := len + mat.width (res.read 65536));
+
+str.close ();
+
+println "Done";
+
+after = System#currentTimeMillis();
+
+println "Total time: \(after - before)ms for \(len) audio sample frames [\(int (len / ((after - before) / 1000))) fps]";
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/stream/test/test_audiofile.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/stream/test/test_audiofile.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,98 @@
+
+module may.stream.test.test_audiofile;
+
+af = load may.stream.audiofile;
+vec = load may.vector;
+mat = load may.matrix;
+bf = load may.vector.blockfuncs;
+
+ref = load may.stream.test.audiofile_reference;
+
+{ compare, compareUsing } = load may.test.test;
+
+testfile name = "may/test/data/\(name).wav";
+
+float n is number -> number =
+ // round number to float precision (for comparison with floats)
+ (arr = new float[1];
+ arr[0] := n;
+ arr[0]);
+
+readAll stream =
+ case stream.available of
+ Known n: stream.read n;
+ _: failWith "Known-duration stream required";
+ esac;
+
+bitdepthComparator depth =
+ (slack = if depth == 8 then 0.015 else 0.001 fi;
+ do test ref: abs (test - ref) < slack done);
+
+maxOf m =
+ bf.max (vec.fromList (map bf.max (mat.asRows m)));
+
+testReferenceFile rate channels bitdepth =
+ (test = readAll (af.open (testfile "\(rate)-\(channels)-\(bitdepth)"));
+ ref = readAll (ref.afReference rate channels);
+ if mat.equalUnder (bitdepthComparator bitdepth) test ref then
+ true
+ else
+ println "** peak difference: \(maxOf (mat.difference ref test))";
+ for [0..mat.height test - 1] do ch:
+ if mat.equalUnder (bitdepthComparator bitdepth)
+ (mat.newRowVector (mat.getRow ch test))
+ (mat.newRowVector (mat.getRow ch ref)) then
+ println " channel \(ch): ok";
+ else
+ println " channel \(ch): not ok";
+// This isn't really simple enough!
+/*!!!
+ seriesFor m =
+ Series {
+ start = 0,
+ step = 1,
+ values = take 1000 (vec.list (mat.getRow ch m))
+ };
+ \() (pl.plot (map seriesFor [ test, ref, mat.scaled 10000 (mat.difference test ref) ]));
+*/
+ fi;
+ done;
+ false
+ fi);
+
+[
+
+"20samples-open": \(
+ f = af.open (testfile "20samples");
+ compare f.position 0 and
+ compare f.channels 1 and
+ compare f.sampleRate 44100 and
+ compare f.available (Known 20) and
+ compare f.finished? false and
+ ( f.close () ; true )
+),
+
+"20samples-read": \(
+ all id (map do opener:
+ f = opener (testfile "20samples");
+ first15 = f.read 15;
+ last5 = f.read 10;
+ compare (mat.size first15) { rows = 1, columns = 15 } and
+ compare (mat.size last5) { rows = 1, columns = 5 } and
+ compare (vec.list (mat.getRow 0 first15))
+ [ float (32767/32768),0,0,0,0,0,0,0,0,0,0,0,0,0,0 ] and
+ compare (vec.list (mat.getRow 0 last5)) [ 0,0,0,0,-1 ] and
+ ( f.close () ; true )
+ done [ af.open, af.openMono ]);
+),
+
+"8000-1-8": \(
+ testReferenceFile 8000 1 8;
+),
+
+"44100-2-16": \(
+ testReferenceFile 44100 2 16;
+),
+
+] is hash boolean>
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/stream/test/test_channels.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/stream/test/test_channels.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,77 @@
+
+module may.stream.test.test_channels;
+
+ch = load may.stream.channels;
+mat = load may.matrix;
+vec = load may.vector;
+
+{ compare, compareUsing } = load may.test.test;
+
+newMatrix data = mat.newMatrix (ColumnMajor ()) (map vec.fromList data);
+
+compareBlocks b1 b2 =
+ compare (vec.list b1) (vec.list b2);
+
+[
+
+"interleaved": \(
+ compareBlocks (ch.interleaved (newMatrix [[1,4],[2,5],[3,6]]))
+ (vec.fromList [1,4,2,5,3,6]) and
+ compareBlocks (ch.interleaved (newMatrix [[1],[2],[3]]))
+ (vec.fromList [1,2,3])
+),
+
+"deinterleaved": \(
+ compareUsing mat.equal (ch.deinterleaved 2 (vec.fromList [1,4,2,5,3,6]))
+ (newMatrix [[1,4],[2,5],[3,6]]) and
+ compareUsing mat.equal (ch.deinterleaved 1 (vec.fromList [1,2,3]))
+ (newMatrix [[1],[2],[3]])
+),
+
+"mixedDown": \(
+ compareBlocks (ch.mixedDown (newMatrix [[1,4],[2,5],[3,6]]))
+ (vec.fromList [5/2,7/2,9/2]) and
+ compareBlocks (ch.mixedDown (newMatrix []))
+ (vec.fromList [])
+),
+
+"mixedDownFromInterleaved": \(
+ compareBlocks (ch.mixedDownFromInterleaved 2 (vec.fromList [1,4,2,5,3,6]))
+ (vec.fromList [5/2,7/2,9/2]) and
+ compareBlocks (ch.mixedDownFromInterleaved 1 (vec.fromList [1,2,3]))
+ (vec.fromList [1,2,3])
+),
+
+"mixedFromInterleavedTo": \(
+ compareBlocks (ch.mixedFromInterleavedTo 1 2 (vec.fromList [1,4,2,5,3,6]))
+ (vec.fromList [5/2,7/2,9/2]) and
+ compareBlocks (ch.mixedFromInterleavedTo 2 2 (vec.fromList [1,4,2,5,3,6]))
+ (vec.fromList [1,4,2,5,3,6]) and
+ compareBlocks (ch.mixedFromInterleavedTo 3 2 (vec.fromList [1,4,2,5,3,6]))
+ (vec.fromList [1,4,0,2,5,0,3,6,0]) and
+ compareBlocks (ch.mixedFromInterleavedTo 1 1 (vec.fromList [1,2,3]))
+ (vec.fromList [1,2,3]) and
+ compareBlocks (ch.mixedFromInterleavedTo 2 1 (vec.fromList [1,2,3]))
+ (vec.fromList [1,1,2,2,3,3]) and
+ compareBlocks (ch.mixedFromInterleavedTo 3 1 (vec.fromList [1,2,3]))
+ (vec.fromList [1,1,0,2,2,0,3,3,0])
+),
+
+"mixedAndInterleavedTo": \(
+ compareBlocks (ch.mixedAndInterleavedTo 1 (newMatrix [[1,4],[2,5],[3,6]]))
+ (vec.fromList [5/2,7/2,9/2]) and
+ compareBlocks (ch.mixedAndInterleavedTo 2 (newMatrix [[1,4],[2,5],[3,6]]))
+ (vec.fromList [1,4,2,5,3,6]) and
+ compareBlocks (ch.mixedAndInterleavedTo 3 (newMatrix [[1,4],[2,5],[3,6]]))
+ (vec.fromList [1,4,0,2,5,0,3,6,0]) and
+ compareBlocks (ch.mixedAndInterleavedTo 1 (newMatrix [[1],[2],[3]]))
+ (vec.fromList [1,2,3]) and
+ compareBlocks (ch.mixedAndInterleavedTo 2 (newMatrix [[1],[2],[3]]))
+ (vec.fromList [1,1,2,2,3,3]) and
+ compareBlocks (ch.mixedAndInterleavedTo 3 (newMatrix [[1],[2],[3]]))
+ (vec.fromList [1,1,1,2,2,2,3,3,3])
+),
+
+] is hash boolean>;
+
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/stream/test/test_filter.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/stream/test/test_filter.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,831 @@
+
+module may.stream.test.test_filter;
+
+vec = load may.vector;
+bf = load may.vector.blockfuncs;
+mat = load may.matrix;
+cplx = load may.complex;
+fft = load may.transform.fft;
+syn = load may.stream.syntheticstream;
+filt = load may.stream.filter;
+
+//pl = load may.plot;//!!!
+
+pl = { plot things = true; };
+
+{ compare, compareUsing } = load may.test.test;
+
+compareClose = compareUsing
+ do m1 m2:
+ all id (map2 do v1 v2:
+ all id (map2 do a b: abs(a - b) < 1e-10 done v1 v2)
+ done m1 m2)
+ done;
+
+convolutionOptions = [
+ [],
+ [ Fast false ],
+ [ Fast true ],
+ [ Fast true, Framesize 1 ],
+ [ Fast true, Framesize 2 ],
+ [ Fast true, Framesize 16 ]
+];
+
+makeTests name withUnknown =
+ (maybeDuration n str =
+ // Truncate a stream, but if withUnknown is true, return it
+ // with availability Unknown -- so as to test that other
+ // filter functions behave correctly even if availability is
+ // not known on their underlying streams
+ (ts = filt.withDuration n str;
+ if withUnknown then
+ ts with
+ {
+ get available () = if ts.finished? then Known 0 else Unknown () fi;
+ }
+ else
+ ts
+ fi);
+ maybeKnown n =
+ if withUnknown then
+ Unknown ()
+ else
+ Known n
+ fi;
+[
+
+"truncatedTo-\(name)": \(
+ // not using withDuration wrapper above, because we're actually
+ // testing filt.withDuration here rather than just generating a
+ // stream for use in another test
+ str = filt.withDuration 3 (syn.generated 2 id);
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 2 and
+ compare str.available (Known 3) and
+ compare str.finished? false and
+ compare (vec.list (mat.getRow 0 (str.read 4))) [ 0,1,2 ] and
+ compare str.position 3 and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ ( str.close (); true )
+),
+
+"truncatedTo-b-\(name)": \(
+ // as above
+ str = filt.withDuration 3 (syn.generated 2 id);
+ compare str.position 0 and
+ compare (vec.list (mat.getRow 0 (str.read 2))) [ 0,1 ] and
+ compare str.position 2 and
+ compare str.available (Known 1) and
+ compare str.finished? false and
+ compare (vec.list (mat.getRow 0 (str.read 2))) [ 2 ] and
+ compare str.position 3 and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ ( str.close (); true )
+),
+
+"extendedTo-\(name)": \(
+ // not using withDuration wrapper above for the outer call, because
+ // we're actually testing filt.withDuration here rather than just
+ // generating a stream for use in another test. The inner call
+ // does use the wrapper.
+ str = filt.withDuration 5 (maybeDuration 3 (syn.generated 2 id));
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 2 and
+ compare str.available (Known 5) and
+ compare str.finished? false and
+ compare (vec.list (mat.getRow 0 (str.read 4))) [ 0,1,2,0 ] and
+ compare str.position 4 and
+ compare str.available (Known 1) and
+ compare str.finished? false and
+ compare (vec.list (mat.getRow 0 (str.read 2))) [ 0 ] and
+ compare str.position 5 and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ ( str.close (); true )
+),
+
+"delayedBy-0-3-\(name)": \(
+ str = filt.delayedBy 0 (maybeDuration 3 (syn.generated 2 id));
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 2 and
+ compare str.available (maybeKnown 3) and
+ compare str.finished? false and
+ compare (vec.list (mat.getRow 0 (str.read 4))) [ 0,1,2 ] and
+ compare str.position 3 and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ ( str.close (); true )
+),
+
+"delayedBy-0-inf-\(name)": \(
+ str = filt.delayedBy 0 (syn.generated 2 id);
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 2 and
+ compare str.available (Infinite ()) and
+ compare str.finished? false and
+ compare (vec.list (mat.getRow 0 (str.read 4))) [ 0,1,2,3 ] and
+ compare str.position 4 and
+ compare str.available (Infinite ()) and
+ compare str.finished? false and
+ ( str.close (); true )
+),
+
+"delayedBy-2-3-\(name)": \(
+ str = filt.delayedBy 2 (maybeDuration 3 (syn.generated 2 id));
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 2 and
+ compare str.available (maybeKnown 5) and
+ compare str.finished? false and
+ compare (vec.list (mat.getRow 0 (str.read 4))) [ 0,0,0,1 ] and
+ compare str.position 4 and
+ compare str.available (maybeKnown 1) and
+ compare str.finished? false and
+ compare (vec.list (mat.getRow 0 (str.read 4))) [ 2 ] and
+ compare str.position 5 and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ ( str.close (); true )
+),
+
+"delayedBy-m2-3-\(name)": \(
+ str = filt.delayedBy (-2) (maybeDuration 3 (syn.generated 2 id));
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 2 and
+ compare str.available (maybeKnown 1) and
+ compare str.finished? false and
+ compare (vec.list (mat.getRow 0 (str.read 4))) [ 2 ] and
+ compare str.position 1 and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ ( str.close (); true )
+),
+
+"delayedBy-m4-3-\(name)": \(
+ str = filt.delayedBy (-4) (maybeDuration 3 (syn.generated 2 id));
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 2 and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ //!!! with this and others, need to check that we read an empty matrix after finished (perhaps have a helper function that checks finished properties such as available count as well)
+ ( str.close (); true )
+),
+
+"delayedBy-2-3b-\(name)": \(
+ str = filt.delayedBy 2 (maybeDuration 3 (syn.generated 2 id));
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 2 and
+ compare str.available (maybeKnown 5) and
+ compare str.finished? false and
+ compare (vec.list (mat.getRow 0 (str.read 1))) [ 0 ] and
+ compare str.position 1 and
+ compare str.available (maybeKnown 4) and
+ compare str.finished? false and
+ compare (vec.list (mat.getRow 0 (str.read 4))) [ 0,0,1,2 ] and
+ compare str.position 5 and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ ( str.close (); true )
+),
+
+"delayedBy-2-3c-\(name)": \(
+ str = filt.delayedBy 2 (maybeDuration 3 (syn.generated 2 id));
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 2 and
+ compare str.available (maybeKnown 5) and
+ compare str.finished? false and
+ compare (vec.list (mat.getRow 0 (str.read 7))) [ 0,0,0,1,2 ] and
+ compare str.position 5 and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ ( str.close (); true )
+),
+
+"delayedBy-2-inf-\(name)": \(
+ str = filt.delayedBy 2 (syn.generated 2 id);
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 2 and
+ compare str.available (Infinite ()) and
+ compare str.finished? false and
+ compare (vec.list (mat.getRow 0 (str.read 2))) [ 0,0 ] and
+ compare str.position 2 and
+ compare str.finished? false and
+ compare (vec.list (mat.getRow 0 (str.read 2))) [ 0,1 ] and
+ compare str.position 4 and
+ compare str.finished? false and
+ compare (vec.list (mat.getRow 0 (str.read 2))) [ 2,3 ] and
+ compare str.position 6 and
+ compare str.finished? false and
+ ( str.close (); true )
+),
+
+"delayedBy-m2-inf-\(name)": \(
+ str = filt.delayedBy (-2) (syn.generated 2 id);
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 2 and
+ compare str.available (Infinite ()) and
+ compare str.finished? false and
+ compare (vec.list (mat.getRow 0 (str.read 2))) [ 2,3 ] and
+ compare str.position 2 and
+ compare str.finished? false and
+ ( str.close (); true )
+),
+
+"mixedTo-1-2-\(name)": \(
+ str = filt.mixedTo 2 (maybeDuration 3 (syn.generated 2 id));
+ compare str.position 0 and
+ compare str.channels 2 and
+ compare str.sampleRate 2 and
+ compare str.available (maybeKnown 3) and
+ compare str.finished? false and
+ compare (map vec.list (mat.asRows (str.read 4))) [[0,1,2],[0,1,2]] and
+ compare str.position 3 and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ ( str.close (); true )
+),
+
+"mixedTo-2-1-\(name)": \(
+ str = filt.mixedTo 1
+ (filt.multiplexed
+ [maybeDuration 3 (syn.generated 2 id),
+ maybeDuration 3 (syn.generated 2 id)]);
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 2 and
+ compare str.available (maybeKnown 3) and
+ compare str.finished? false and
+ compare (map vec.list (mat.asRows (str.read 4))) [[0,1,2]] and
+ compare str.position 3 and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ ( str.close (); true )
+),
+
+"mixedTo-2-3-\(name)": \(
+ str = filt.mixedTo 3
+ (filt.multiplexed
+ [maybeDuration 3 (syn.generated 2 id),
+ maybeDuration 3 (syn.generated 2 (+1))]);
+ compare str.position 0 and
+ compare str.channels 3 and
+ compare str.sampleRate 2 and
+ compare str.available (maybeKnown 3) and
+ compare str.finished? false and
+ compare (map vec.list (mat.asRows (str.read 4))) [[0,1,2],[1,2,3],[0,0,0]] and
+ compare str.position 3 and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ ( str.close (); true )
+),
+
+"mixedTo-1-3-\(name)": \(
+ str = filt.mixedTo 3 (maybeDuration 3 (syn.generated 2 id));
+ compare str.position 0 and
+ compare str.channels 3 and
+ compare str.sampleRate 2 and
+ compare str.available (maybeKnown 3) and
+ compare str.finished? false and
+ compare (map vec.list (mat.asRows (str.read 4))) [[0,1,2],[0,1,2],[0,1,2]] and
+ compare str.position 3 and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ ( str.close (); true )
+),
+
+"sum-inf-inf-\(name)": \(
+ str = filt.sum [syn.generated 2 (2*), syn.generated 2 (0-)];
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 2 and
+ compare str.available (Infinite ()) and
+ compare str.finished? false and
+ compare (map vec.list (mat.asRows (str.read 4))) [[0,1,2,3]] and
+ compare str.available (Infinite ()) and
+ compare str.position 4 and
+ ( str.close (); true )
+),
+
+"sum-inf-trunc-\(name)": \(
+ str = filt.sum [syn.generated 2 (2*), maybeDuration 3 (syn.generated 2 (0-))];
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 2 and
+ compare str.available (maybeKnown 3) and
+ compare str.finished? false and
+ compare (map vec.list (mat.asRows (str.read 4))) [[0,1,2]] and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ compare str.position 3 and
+ ( str.close (); true )
+),
+
+"sum-precalc-trunc-\(name)": \(
+ str = filt.sum
+ [syn.precalculatedMono 2 (vec.fromList [1,2]),
+ maybeDuration 3 (syn.generated 2 (0-))];
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 2 and
+ compare str.available (maybeKnown 2) and
+ compare str.finished? false and
+ compare (map vec.list (mat.asRows (str.read 4))) [[1,1]] and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ compare str.position 2 and
+ ( str.close (); true )
+),
+
+"sum-2-1-\(name)": \(
+ str = filt.sum
+ [syn.precalculatedMono 2 (vec.fromList [1,2]),
+ filt.multiplexed [syn.precalculatedMono 2 (vec.fromList [3,4]),
+ maybeDuration 3 (syn.generated 2 (0-))]];
+ compare str.position 0 and
+ compare str.channels 2 and
+ compare str.sampleRate 2 and
+ compare str.available (maybeKnown 2) and
+ compare str.finished? false and
+ compare (map vec.list (mat.asRows (str.read 4))) [[4,6], [0,-1]] and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ compare str.position 2 and
+ ( str.close (); true )
+),
+
+"sum-3-\(name)": \(
+ str = filt.sum
+ [syn.precalculatedMono 2 (vec.fromList [1,2]),
+ syn.precalculatedMono 2 (vec.fromList [3,4]),
+ maybeDuration 3 (syn.generated 2 (0-))];
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 2 and
+ compare str.available (maybeKnown 2) and
+ compare str.finished? false and
+ compare (map vec.list (mat.asRows (str.read 4))) [[4,5]] and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ compare str.position 2 and
+ ( str.close (); true )
+),
+
+"multiplexed-inf-inf-\(name)": \(
+ str = filt.multiplexed [syn.generated 2 id, syn.generated 2 (0-)];
+ compare str.position 0 and
+ compare str.channels 2 and
+ compare str.sampleRate 2 and
+ compare str.available (Infinite ()) and
+ compare str.finished? false and
+ compare (map vec.list (mat.asRows (str.read 4)))
+ [[0,1,2,3], [0,-1,-2,-3]] and
+ compare str.available (Infinite ()) and
+ compare str.position 4 and
+ ( str.close (); true )
+),
+
+"multiplexed-inf-trunc-\(name)": \(
+ str = filt.multiplexed [syn.generated 2 id, maybeDuration 3 (syn.generated 2 (0-))];
+ compare str.position 0 and
+ compare str.channels 2 and
+ compare str.sampleRate 2 and
+ compare str.available (maybeKnown 3) and
+ compare str.finished? false and
+ compare (map vec.list (mat.asRows (str.read 4))) [[0,1,2], [0,-1,-2]] and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ compare str.position 3 and
+ ( str.close (); true )
+),
+
+"multiplexed-precalc-trunc-\(name)": \(
+ str = filt.multiplexed
+ [syn.precalculatedMono 2 (vec.fromList [1,2]),
+ maybeDuration 3 (syn.generated 2 (0-))];
+ compare str.position 0 and
+ compare str.channels 2 and
+ compare str.sampleRate 2 and
+ compare str.available (maybeKnown 2) and
+ compare str.finished? false and
+ compare (map vec.list (mat.asRows (str.read 4))) [[1,2], [0,-1]] and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ compare str.position 2 and
+ ( str.close (); true )
+),
+
+"multiplexed-2-1-\(name)": \(
+ str = filt.multiplexed
+ [syn.precalculatedMono 2 (vec.fromList [1,2]),
+ filt.multiplexed [syn.precalculatedMono 2 (vec.fromList [3,4]),
+ maybeDuration 3 (syn.generated 2 (0-))]];
+ compare str.position 0 and
+ compare str.channels 3 and
+ compare str.sampleRate 2 and
+ compare str.available (maybeKnown 2) and
+ compare str.finished? false and
+ compare (map vec.list (mat.asRows (str.read 4))) [[1,2], [3,4], [0,-1]] and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ compare str.position 2 and
+ ( str.close (); true )
+),
+
+"multiplexed-2-1b-\(name)": \(
+ str = filt.multiplexed
+ [syn.precalculatedMono 2 (vec.fromList [1,2]),
+ syn.precalculatedMono 2 (vec.fromList [3,4]),
+ maybeDuration 3 (syn.generated 2 (0-))];
+ compare str.position 0 and
+ compare str.channels 3 and
+ compare str.sampleRate 2 and
+ compare str.available (maybeKnown 2) and
+ compare str.finished? false and
+ compare (map vec.list (mat.asRows (str.read 4))) [[1,2], [3,4], [0,-1]] and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ compare str.position 2 and
+ ( str.close (); true )
+),
+
+"repeated-2-\(name)": \(
+ str = filt.repeated
+ (syn.precalculatedMono 2 (vec.fromList [1,2,3]));
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 2 and
+ compare str.available (Infinite ()) and
+ compare str.finished? false and
+ compare (map vec.list (mat.asRows (str.read 1))) [[1]] and
+ compare str.position 1 and
+ compare (map vec.list (mat.asRows (str.read 2))) [[2,3]] and
+ compare str.position 3 and
+ compare (map vec.list (mat.asRows (str.read 3))) [[1,2,3]] and
+ compare str.position 6 and
+ compare (map vec.list (mat.asRows (str.read 5))) [[1,2,3,1,2]] and
+ compare (map vec.list (mat.asRows (str.read 9))) [[3,1,2,3,1,2,3,1,2]] and
+ compare (map vec.list (mat.asRows (str.read 2))) [[3,1]] and
+ compare str.available (Infinite ()) and
+ compare str.finished? false and
+ compare str.position 22 and
+ ( str.close (); true )
+),
+
+"duplicated-1-\(name)": \(
+ original = maybeDuration 3 (syn.precalculatedMono 2 (vec.fromList [1,2,3]));
+ sn = filt.duplicated 1 original;
+ str = (head sn);
+ compare (length sn) 1 and
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 2 and
+ compare str.available (maybeKnown 3) and
+ compare str.finished? false and
+ compare (map vec.list (mat.asRows (str.read 1))) [[1]] and
+ compare str.position 1 and
+ compare str.available (maybeKnown 2) and
+ compare (map vec.list (mat.asRows (str.read 3))) [[2,3]] and
+ compare str.position 3 and
+ compare str.finished? true and
+ compare str.available (Known 0) and
+ ( str.close (); true )
+),
+
+"duplicated-2-\(name)": \(
+ original = maybeDuration 3 (syn.precalculatedMono 2 (vec.fromList [1,2,3]));
+ sn = filt.duplicated 2 original;
+ s1 = (head sn);
+ s2 = (head (tail sn));
+
+ compare (length sn) 2 and
+
+ compare s1.position 0 and
+ compare s1.channels 1 and
+ compare s1.sampleRate 2 and
+ compare s1.available (maybeKnown 3) and
+ compare s1.finished? false and
+
+ compare s2.position 0 and
+ compare s2.channels 1 and
+ compare s2.sampleRate 2 and
+ compare s2.available (maybeKnown 3) and
+ compare s2.finished? false and
+
+ compare (map vec.list (mat.asRows (s1.read 1))) [[1]] and
+ compare s1.position 1 and
+ compare s1.available (maybeKnown 2) and
+ compare s2.position 0 and
+ compare s2.available (maybeKnown 3) and
+
+ compare (map vec.list (mat.asRows (s2.read 2))) [[1,2]] and
+ compare s1.position 1 and
+ compare s1.available (maybeKnown 2) and
+ compare s2.position 2 and
+ compare s2.available (maybeKnown 1) and
+
+ compare (map vec.list (mat.asRows (s1.read 3))) [[2,3]] and
+ compare s1.position 3 and
+ compare s1.finished? true and
+ compare s1.available (Known 0) and
+ compare s2.position 2 and
+ compare s2.finished? false and
+ compare s2.available (Known 1) and // when one is known, so is the other
+
+ compare (map vec.list (mat.asRows (s1.read 3))) [] and
+
+ compare (map vec.list (mat.asRows (s2.read 1))) [[3]] and
+ compare s1.position 3 and
+ compare s1.finished? true and
+ compare s2.position 3 and
+ compare s2.finished? true and
+
+ ( s1.close (); s2.close() ; true )
+),
+
+"convolvedImpulse-\(name)": \(
+ all id
+ (map do opts:
+ ir = mat.newRowVector (vec.fromList [1,0,-1,0]);
+ signal = maybeDuration 4
+ (syn.precalculatedMono 2 (vec.fromList [1,0,0,0]));
+ c = filt.convolvedWith opts ir signal;
+ compareClose (map vec.list (mat.asRows (c.read 4))) [[ 1,0,-1,0 ]] and
+ ( c.close (); true )
+ done convolutionOptions);
+),
+
+"convolvedImpulse2-\(name)": \(
+ all id
+ (map do opts:
+ ir = mat.newRowVector (vec.fromList [8,6,4,2]);
+ signal = maybeDuration 4
+ (syn.precalculatedMono 2 (vec.fromList [1,0,0,0]));
+ c = filt.convolvedWith opts ir signal;
+ compareClose (map vec.list (mat.asRows (c.read 4))) [[ 8,6,4,2 ]] and
+ ( c.close (); true )
+ done convolutionOptions);
+),
+
+"convolvedImpulse-multichannel-\(name)": \(
+ all id
+ (map do opts:
+ ir = mat.newMatrix (RowMajor ())
+ (map vec.fromList [[0,0,0,1],[8,6,4,2],[1,0,-1,0]]);
+ signal = maybeDuration 4
+ (syn.precalculated 2
+ (mat.newMatrix (RowMajor ())
+ (map vec.fromList [[1,1,0,0],[0,1,1,0],[0,0,1,1]])));
+ c = filt.convolvedWith opts ir signal;
+ compareClose (map vec.list (mat.asRows (c.read 4)))
+ [[0,0,0,1],[0,8,14,10],[0,0,1,1]] and
+ ( c.close (); true )
+ done convolutionOptions);
+),
+
+"convolvedWith-\(name)": \(
+ all id
+ (map do opts:
+ fast = not (opts == [ Fast false ]);
+ ir = mat.newRowVector (vec.fromList [1,2,3,4,5]);
+ signal = maybeDuration 3
+ (syn.precalculatedMono 2 (vec.fromList [10,20,30]));
+ c = filt.convolvedWith opts ir signal;
+ compare c.position 0 and
+ compare c.channels 1 and
+ compare c.sampleRate 2 and
+ (fast or compare c.available (maybeKnown 7)) and
+ compareClose (map vec.list (mat.asRows (c.read 3)))
+ [[ 10*1,
+ 20*1 + 10*2,
+ 30*1 + 20*2 + 10*3 ]] and
+ (fast or compare c.available (Known 4)) and
+ compare c.finished? false and
+ compareClose (map vec.list (mat.asRows (c.read 4)))
+ [[ 30*2 + 20*3 + 10*4,
+ 30*3 + 20*4 + 10*5,
+ 30*4 + 20*5,
+ 30*5 ]] and
+ (fast or (compare c.available (Known 0) and
+ compare c.finished? true)) and
+ ( c.close (); true )
+ done convolutionOptions);
+),
+
+"spaced-\(name)": \(
+ original = maybeDuration 3 (syn.precalculatedMono 2 (vec.fromList [1,2,3]));
+ str = filt.spaced 3 original;
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 2 and
+ compare str.available (maybeKnown 9) and
+ compare str.finished? false and
+ compare (map vec.list (mat.asRows (str.read 4))) [[1,0,0,2]] and
+ compare str.position 4 and
+ compare str.available (maybeKnown 5) and
+ compare str.finished? false and
+ compare (map vec.list (mat.asRows (str.read 1))) [[0]] and
+ compare str.position 5 and
+ compare str.available (maybeKnown 4) and
+ compare str.finished? false and
+ compare (map vec.list (mat.asRows (str.read 10))) [[0,3,0,0]] and
+ compare str.position 9 and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ ( str.close (); true )
+),
+
+"picked-\(name)": \(
+ original = maybeDuration 8 (syn.precalculatedMono 2 (vec.fromList [1,2,3,4,5,6,7,8]));
+ str = filt.picked 3 original;
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 2 and
+ compare str.available (maybeKnown 3) and
+ compare str.finished? false and
+ compare (map vec.list (mat.asRows (str.read 1))) [[1]] and
+ compare str.position 1 and
+ compare str.available (maybeKnown 2) and
+ compare str.finished? false and
+ compare (map vec.list (mat.asRows (str.read 3))) [[4,7]] and
+ compare str.position 3 and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ ( str.close (); true )
+),
+
+//!!! still no tests for filters with multi-channel inputs
+
+]);
+
+knowns = makeTests "known" false;
+unknowns = makeTests "unknown" true;
+
+logSpectrumFrom output n =
+ (outdata = mat.getRow 0 (output.read n);
+ spectrum = cplx.magnitudes (fft.realForward n outdata);
+ array (map do v: 20 * Math#log10(v) done (vec.list spectrum)));
+
+makeDiracStream rate n =
+ syn.generated rate do x: if x == int(n/2) then 1 else 0 fi done;
+
+filtering = [
+
+"interpolated-sine": \(
+ // Interpolating a sinusoid should give us a sinusoid
+ //!!! only beyond half the filter length
+ sinusoid = syn.sinusoid 8 2; // 2Hz sine sampled at 8Hz: [ 0, 1, 0, -1 ] etc
+ input = filt.withDuration 16 sinusoid;
+ output = filt.interpolated 2 input;
+ result = output.read 32;
+ reference = syn.sinusoid 16 2;
+ expected = reference.read 32;
+ compareOutputs a b = compareClose
+ (map vec.list (mat.asRows a)) (map vec.list (mat.asRows b));
+ compareOutputs result expected;
+),
+
+"decimated-sine": \(
+ // Decimating a sinusoid should give us a sinusoid
+ //!!! only beyond half the filter length
+ sinusoid = syn.sinusoid 32 2; // 2Hz sine sampled at 16Hz
+ input = filt.withDuration 64 sinusoid;
+ output = filt.decimated 2 input;
+ result = output.read 32;
+ reference = syn.sinusoid 16 2;
+ expected = reference.read 32;
+ compareOutputs a b = compareClose
+ (map vec.list (mat.asRows a)) (map vec.list (mat.asRows b));
+ compareOutputs result expected;
+),
+
+"interpolated-misc": \(
+ // Interpolating any signal by N should give a signal in which
+ // every Nth sample is the original signal
+ data = vec.fromList [ 0, 0.1, -0.3, -0.4, -0.3, 0, 0.5, 0.2, 0.8, -0.1 ];
+ data = vec.concat [ data, bf.scaled (5/4) data, bf.scaled (3/4) data, data ];
+ data = vec.concat [ data, data ];
+ input = filt.withDuration (vec.length data) (syn.precalculatedMono 4 data);
+ factor = 3;
+ up = filt.interpolated factor input;
+ result = mat.getRow 0 (up.read (factor * vec.length data));
+ phase = 0;
+ a = vec.list data;
+ b = map do i: vec.at result (i*factor + phase) done [0..vec.length data - 1];
+ compareClose [a] [b];
+),
+
+"int-dec": \(
+ // Interpolating any signal then decimating by the same factor
+ // should get us the original back again
+ //!!! no, this is phase dependent
+// data = vec.fromList [ 0, 0.1, -0.3, -0.4, -0.3, 0, 0.5, 0.2, 0.8, -0.1 ];
+// data = vec.concat [ data, bf.scaled (5/4) data, bf.scaled (3/4) data, data ];
+ data = vec.fromList [ 0, 1, 2, 3 ];
+ data = vec.concat [ data, data ];
+
+ factor = 3;
+
+ updown prepad =
+ (input = filt.withDuration (vec.length data) (syn.precalculatedMono 4 data);
+ intermediate = filt.interpolated factor input;
+ output = filt.decimated factor (filt.delayedBy prepad intermediate);
+ output.read (vec.length data));
+
+ result = updown 0;
+ if not compareClose [vec.list (mat.getRow 0 result)] [vec.list data] then
+ \() (pl.plot [Vector data,
+ Vector (mat.getRow 0 result),
+ Vector (mat.getRow 0 (updown 1)),
+ Vector (mat.getRow 0 (updown 2))]);
+ up = (filt.interpolated 2 (syn.precalculatedMono 4 data)).read 80;
+ \() (pl.plot [Vector (mat.getRow 0 up)]);
+ false
+ else true fi;
+),
+
+"lowpassed-dirac": \(
+ test { rate, cutoff, attenuation, bandwidth, n } =
+ (input = makeDiracStream rate n;
+ output = filt.lowpassed cutoff attenuation bandwidth input;
+ logspec = logSpectrumFrom output n;
+ acceptances = map do bin:
+ freq = (rate / n) * bin;
+ db = logspec[bin];
+ //!!! what should these 0.01 actually be?
+ if freq < cutoff - bandwidth/2 then (db < 0.01 and db > -0.01)
+ elif freq > cutoff + bandwidth/2 then (db < -attenuation)
+ else (db < 0.01 and db > -attenuation)
+ fi;
+ done [0..n/2];
+ compare acceptances (map \true [0..n/2]));
+ all id
+ (map test [
+ { rate = 800, cutoff = 200, attenuation = 80, bandwidth = 10, n = 1000 },
+ ]);
+),
+
+"highpassed-dirac": \(
+ test { rate, cutoff, attenuation, bandwidth, n } =
+ (input = makeDiracStream rate n;
+ output = filt.highpassed cutoff attenuation bandwidth input;
+ logspec = logSpectrumFrom output n;
+ acceptances = map do bin:
+ freq = (rate / n) * bin;
+ db = logspec[bin];
+ //!!! what should these 0.01 actually be?
+ if freq > cutoff + bandwidth/2 then (db < 0.01 and db > -0.01)
+ elif freq < cutoff - bandwidth/2 then (db < -attenuation)
+ else (db < 0.01 and db > -attenuation)
+ fi;
+ done [0..n/2];
+ compare acceptances (map \true [0..n/2]));
+ all id
+ (map test [
+ { rate = 800, cutoff = 200, attenuation = 80, bandwidth = 10, n = 1000 },
+ ]);
+),
+
+"bandpassed-dirac": \(
+ test { rate, f0, f1, attenuation, bandwidth, n } =
+ (input = makeDiracStream rate n;
+ output = filt.bandpassed f0 f1 attenuation bandwidth input;
+ logspec = logSpectrumFrom output n;
+ acceptances = map do bin:
+ freq = (rate / n) * bin;
+ db = logspec[bin];
+ //!!! what should these 0.01 actually be?
+ if freq < f0 - bandwidth/2 then (db < -attenuation)
+ elif freq < f0 + bandwidth/2 then (db < 0.01 and db > -attenuation)
+ elif freq < f1 - bandwidth/2 then (db < 0.01 and db > -0.01)
+ elif freq < f1 + bandwidth/2 then (db < 0.01 and db > -attenuation)
+ else (db < -attenuation)
+ fi;
+ done [0..n/2];
+ compare acceptances (map \true [0..n/2]));
+ all id
+ (map test [
+ { rate = 800, f0 = 200, f1 = 300, attenuation = 80, bandwidth = 10, n = 1000 },
+ ]);
+),
+
+];
+
+all = [:];
+for [ knowns, unknowns, filtering ] do h:
+ for (keys h) do k: all[k] := h[k] done
+done;
+
+all is hash boolean>;
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/stream/test/test_framer.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/stream/test/test_framer.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,173 @@
+
+module may.stream.test.test_framer;
+
+fr = load may.stream.framer;
+vec = load may.vector;
+mat = load may.matrix;
+syn = load may.stream.syntheticstream;
+
+{ compare, compareUsing } = load may.test.test;
+
+rate = 10;
+
+testStream n is number -> 'a = syn.precalculatedMono rate (vec.fromList [1..n]);
+
+compareFrames frames1 frames2 =
+ all id (map2 do f1 f2: compareUsing mat.equal f1 f2 done frames1
+ (map (mat.newRowVector . vec.fromList) frames2));
+
+testFramesWith params length expected firstChunkSize =
+ (f = fr.frames params (testStream length);
+ str = fr.streamed rate params f;
+ ts = testStream length; // newly initialised stream
+
+ compareFrames f expected and
+
+ (firstChunk = str.read firstChunkSize;
+ compareUsing mat.equal
+ firstChunk (ts.read firstChunkSize)) and
+
+ compare str.position firstChunkSize and
+ compare str.finished? false and
+
+ (restChunk = str.read (length - firstChunkSize);
+ compareUsing mat.equal
+ restChunk (ts.read (length - firstChunkSize))) and
+ compare str.position length and
+
+ (trailingZeros = str.read (params.framesize + 1);
+ compareUsing mat.equal
+ trailingZeros
+ (mat.zeroMatrix
+ { rows = str.channels, columns = mat.width trailingZeros }) and
+ (mat.width trailingZeros < params.framesize)) and
+
+ compare str.finished? true and
+ compare str.available (Known 0));
+
+testFramesInvertible params length expected =
+ all id (map (testFramesWith params length expected) [1..length]);
+
+testFrames params length expected =
+ (f = fr.frames params (testStream length);
+ compareFrames f expected);
+
+[
+
+"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": \(
+ testFramesInvertible { framesize = 2, hop = 2 } 5 [ [1,2], [3,4], [5,0] ];
+),
+
+"frames-4.3x4": \(
+ testFrames { framesize = 4, hop = 3 } 4 [ [0,1,2,3], [3,4,0,0] ];
+),
+
+"frames-3.2x4": \(
+ testFrames { framesize = 3, hop = 2 } 4 [ [0,1,2], [2,3,4], [4,0,0] ];
+),
+
+"frames-3.1x6": \(
+ testFramesInvertible { framesize = 3, hop = 1 } 6
+ [ [0,0,1], [0,1,2], [1,2,3], [2,3,4],
+ [3,4,5], [4,5,6], [5,6,0], [6,0,0] ];
+),
+
+"frames-4.2x8": \(
+ testFramesInvertible { framesize = 4, hop = 2 } 8
+ [ [0,0,1,2], [1,2,3,4], [3,4,5,6], [5,6,7,8], [7,8,0,0] ];
+),
+
+"overlapAdd-3.1": \(
+ compareUsing (mat.equal)
+ (fr.overlapAdd 2 [ mat.newRowVector (vec.fromList [ 1,2,3 ]),
+ mat.newRowVector (vec.fromList [ 4,5,6 ]),
+ mat.newRowVector (vec.fromList [ 7,8,9 ]) ])
+ (mat.newRowVector (vec.fromList [ 1,6,15,14,9 ]))
+),
+
+"overlapAdd-3.2": \(
+ compareUsing (mat.equal)
+ (fr.overlapAdd 1 [ mat.newRowVector (vec.fromList [ 1,2,3 ]),
+ mat.newRowVector (vec.fromList [ 4,5,6 ]),
+ mat.newRowVector (vec.fromList [ 7,8,9 ]) ])
+ (mat.newRowVector (vec.fromList [ 1,2,7,5,13,8,9 ]))
+),
+
+"overlapAdd-4.2": \(
+ compareUsing (mat.equal)
+ (fr.overlapAdd 2 [ mat.newRowVector (vec.fromList [ 1,2,3,4 ]),
+ mat.newRowVector (vec.fromList [ 5,6,7,8 ]),
+ mat.newRowVector (vec.fromList [ 9,0,1,2 ]) ])
+ (mat.newRowVector (vec.fromList [ 1,2,8,10,16,8,1,2 ]))
+),
+
+"overlapAdd-6+4.2": \(
+ // Must work even if blocks vary in length (what if shorter than
+ // overlap though?)
+ compareUsing (mat.equal)
+ (fr.overlapAdd 2 [ mat.newRowVector (vec.fromList [ 1,2,3,4,5,6 ]),
+ mat.newRowVector (vec.fromList [ 7,8,9,0 ]),
+ mat.newRowVector (vec.fromList [ 1,2,3 ]),
+ mat.newRowVector (vec.fromList [ 4,5,6 ]) ])
+ (mat.newRowVector (vec.fromList [ 1,2,3,4,12,14,10,6,8,6 ]))
+),
+
+] is hash boolean>;
+
+
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/stream/test/test_syntheticstream.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/stream/test/test_syntheticstream.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,80 @@
+
+module may.stream.test.test_syntheticstream;
+
+vec = load may.vector;
+mat = load may.matrix;
+syn = load may.stream.syntheticstream;
+
+{ compare, compareUsing } = load may.test.test;
+
+compareApprox eps =
+ compareUsing do v1 v2: all id (map2 do f1 f2: abs (f1 - f2) < eps done v1 v2) done;
+
+epsilon = 0.000001;
+
+[
+
+"generated": \(
+ str = syn.generated 2 id;
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 2 and
+ compare str.available (Infinite ()) and
+ compare str.finished? false and
+ compare (vec.list (mat.getRow 0 (str.read 4))) [ 0,1,2,3 ] and
+ compare str.position 4
+),
+
+"sinusoid": \(
+ // 2Hz sine sampled 8 times a second
+ str = syn.sinusoid 8 2;
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 8 and
+ compare str.available (Infinite ()) and
+ compare str.finished? false and
+ compareApprox epsilon (vec.list (mat.getRow 0 (str.read 6))) [ 0, 1, 0, -1, 0, 1 ] and
+ compare str.position 6
+),
+
+"silent": \(
+ str = syn.silent 8;
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 8 and
+ compare str.available (Infinite ()) and
+ compare str.finished? false and
+ compare (vec.list (mat.getRow 0 (str.read 3))) [ 0, 0, 0 ] and
+ compare str.position 3
+),
+
+"precalculatedMono-empty": \(
+ str = syn.precalculatedMono 2 (vec.fromList []);
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 2 and
+ compare str.available (Known 0) and
+ compare str.finished? true and
+ compare (vec.list (mat.getRow 0 (str.read 3))) [] and
+ compare str.position 0
+),
+
+"precalculatedMono": \(
+ str = syn.precalculatedMono 2 (vec.fromList [ 1, 2, 3, 4 ]);
+ compare str.position 0 and
+ compare str.channels 1 and
+ compare str.sampleRate 2 and
+ compare str.available (Known 4) and
+ compare str.finished? false and
+ compare (vec.list (mat.getRow 0 (str.read 3))) [ 1, 2, 3 ] and
+ compare str.position 3 and
+ compare str.available (Known 1) and
+ compare str.finished? false and
+ compare (vec.list (mat.getRow 0 (str.read 3))) [ 4 ] and
+ compare str.position 4 and
+ compare str.available (Known 0) and
+ compare str.finished? true
+),
+
+] is hash boolean>
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/stream/type.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/stream/type.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,18 @@
+
+module may.stream.type;
+
+load may.matrix.type;
+
+typedef stream =
+ {
+ position is number,
+ channels is number,
+ sampleRate is number,
+ available is Known number | Unknown () | Infinite (),
+ finished? is boolean,
+ read is number -> matrix,
+ close is () -> (),
+ };
+
+();
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/test/all.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/test/all.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,31 @@
+
+program may.test.all;
+
+{ runTests } = load may.test.test;
+
+tests = [
+"vector" : load may.vector.test.test_vector,
+"blockfuncs" : load may.vector.test.test_blockfuncs,
+"complex" : load may.complex.test.test_complex,
+"framer" : load may.stream.test.test_framer,
+"channels" : load may.stream.test.test_channels,
+"audiofile" : load may.stream.test.test_audiofile,
+"synstream" : load may.stream.test.test_syntheticstream,
+"filter" : load may.stream.test.test_filter,
+"fft" : load may.transform.test.test_fft,
+"vamppost" : load may.vamp.test.test_vamppost,
+"vamp" : load may.vamp.test.test_vamp,
+"matrix" : load may.matrix.test.test_matrix,
+"plot" : load may.plot.test.test_plot,
+"signal" : load may.signal.test.test_signal,
+"window" : load may.signal.test.test_window,
+];
+
+bad = sum (mapHash do name testHash: runTests name testHash done tests);
+
+if (bad > 0) then
+ println "\n** \(bad) test(s) failed!";
+else
+ ()
+fi
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/test/data/20samples.wav
Binary file src/may/test/data/20samples.wav has changed
diff -r 4b104ef8d110 -r 20903ee53719 src/may/test/data/44100-2-16.wav
Binary file src/may/test/data/44100-2-16.wav has changed
diff -r 4b104ef8d110 -r 20903ee53719 src/may/test/data/8000-1-8.wav
Binary file src/may/test/data/8000-1-8.wav has changed
diff -r 4b104ef8d110 -r 20903ee53719 src/may/test/test.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/test/test.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,56 @@
+module may.test.test;
+
+import yeti.lang: FailureException;
+
+var goodCompares = 0;
+
+compareUsing comparator obtained expected =
+ if comparator obtained expected then
+ goodCompares := goodCompares + 1;
+ true;
+ else
+ println "** expected: \(expected)\n obtained: \(obtained)";
+ false;
+ fi;
+
+compare obtained expected = compareUsing (==) obtained expected;
+
+time msg f =
+ (start = System#currentTimeMillis();
+ result = f ();
+ end = System#currentTimeMillis();
+ println "\(msg): \(end-start)ms";
+ result);
+
+select f = fold do r x: if f x then x::r else r fi done [];
+
+failedTests testHash =
+ select (!= "")
+ (mapHash do name f:
+ try
+ if f () then "" else
+ println "Test \(name) failed";
+ name;
+ fi
+ catch FailureException e:
+ println "Test \(name) threw exception: \(e)";
+ name;
+ yrt;
+ 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);
+
+{
+ compare, compareUsing,
+ time,
+ runTests,
+}
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/transform/fft.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/transform/fft.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,60 @@
+
+module may.transform.fft;
+
+import edu.emory.mathcs.jtransforms.fft: DoubleFFT_1D;
+
+vec = load may.vector;
+complex = load may.complex;
+
+load may.complex.type;
+
+packedToComplex len p is number -> ~double[] -> array =
+ (n = len / 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 = new double[n*2-2];
+ for [0..(n-1)*2-1] do i:
+ ix = int (i/2);
+ v[i] :=
+ if i == ix*2 then
+ complex.real arr[ix]
+ else
+ complex.imaginary arr[ix]
+ fi;
+ done;
+ v[1] := complex.real arr[n-1];
+ v);
+
+//!!! doc: n is supplied separately from the input vector to support partial evaluation
+//!!! doc: output has n/2+1 complex values
+//!!! doc: powers of two only? check with jtransforms
+realForward n =
+ (d = new DoubleFFT_1D(n);
+ do bl:
+ v = vec.primitive bl;
+ d#realForward(v);
+ packedToComplex (vec.length bl) v;
+ done);
+
+//!!! doc: input requires n/2+1 complex values (or should test and throw?)
+//!!! doc: powers of two only? check with jtransforms
+realInverse n =
+ (d = new DoubleFFT_1D(n);
+ do cplx:
+ v = complexToPacked (array cplx);
+ d#realInverse(v, true);
+ vec.vector v;
+ done);
+
+{
+realForward,
+realInverse,
+}
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/transform/test/test_fft.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/transform/test/test_fft.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,46 @@
+
+module may.transform.test.test_fft;
+
+{ realForward, realInverse } = load may.transform.fft;
+{ list, fromList } = load may.vector;
+{ complex } = load may.complex;
+
+{ compare } = load may.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 (list back) orig);
+
+[
+
+"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 boolean>;
+
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/vamp.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/vamp.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,321 @@
+module may.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;
+
+vec = load may.vector;
+fr = load may.stream.framer;
+af = load may.stream.audiofile;
+mat = load may.matrix;
+vamprdf = load may.vamp.vamprdf;
+vamppost = load may.vamp.vamppost;
+
+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 = vec.fromFloats 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);
+
+stores = [:];
+
+getSingletonStoreFor loader =
+ synchronized stores do:
+ if loader in stores then
+ stores[loader]
+ else
+ s = store.newRdfStore ();
+ loader s;
+ stores[loader] := s;
+ s;
+ fi
+ done;
+
+getSystemStore () =
+ getSingletonStoreFor vamprdf.loadSystemVampRdf;
+
+getGlobalStore () =
+ getSingletonStoreFor vamprdf.loadGlobalVampRdf;
+
+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);
+
+getKnownPluginKeys () =
+ (store = getGlobalStore ();
+ nodes = vamprdf.allPluginNodes store;
+ // ordering is random out of the store; might as well sort
+ sort (map do n: (vamprdf.pluginDataByNode store n).pluginKey done nodes));
+
+getDataForKnownPlugin key =
+ vamprdf.pluginDataByKey (getGlobalStore ()) key;
+
+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 = getSystemStore ();
+ 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;
+ _:
+ if od#sampleType == OutputDescriptor$SampleType#OneSamplePerStep
+ then Series ()
+ else Curve ()
+ fi;
+ 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 (getSystemStore ()) 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 frame time is 'a -> ~RealTime -> 'b =
+ featureSet p#process((map vec.floats (mat.asRows frame)) 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 =
+ 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 (); failWith text);
+
+processWith key p outputNo stream =
+ (blockSize =
+ if p.preferredBlockSize == 0 then 2048
+ else p.preferredBlockSize fi;
+ stepSize =
+ if p.preferredStepSize == 0 then
+ if p.inputDomain == FrequencyDomain () then blockSize / 2
+ else blockSize fi;
+ else p.preferredStepSize fi;
+ channels = 1;
+ params = {
+ p, sampleRate = stream.sampleRate, channels = 1,
+ framesize = blockSize, blockSize, hop = stepSize
+ };
+ if p.initialise params then
+ {
+ 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);
+
+processStream 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: failWith e;
+ esac;
+
+processFile key output filename =
+ processStream key output (af.open filename);
+
+processStreamStructured key output filename =
+ vamppost.postprocess (processStream key output filename);
+
+processFileStructured key output filename =
+ vamppost.postprocess (processFile key output filename);
+
+{
+get pluginPath = getPluginPath,
+get pluginKeys = listPlugins,
+loadPlugin,
+categoryOf,
+processStream,
+processFile,
+processStreamStructured,
+processFileStructured,
+getKnownPluginKeys,
+getDataForKnownPlugin,
+}
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/vamp/test/test_vamp.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/vamp/test/test_vamp.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,150 @@
+module may.vamp.test.test_vamp;
+
+v = load may.vamp;
+synthetic = load may.stream.syntheticstream;
+filter = load may.stream.filter;
+mat = load may.matrix;
+vec = load may.vector;
+
+{ compare, compareUsing } = load may.test.test;
+
+testPluginKey = "vamp-test-plugin:vamp-test-plugin";
+
+rate = 44100;
+
+testStream () = filter.withDuration (rate * 20) (synthetic.whiteNoise rate);
+
+processTest output =
+ v.processStreamStructured testPluginKey output (testStream ());
+
+float n is number -> number =
+ // round number to float precision (for comparison with floats)
+ (arr = new float[1];
+ arr[0] := n;
+ arr[0]);
+
+floats nn = map float nn;
+
+tests =
+[
+
+"version": \(
+ case v.loadPlugin rate testPluginKey of
+ Error e: (eprintln "version: Error: \(e)"; false);
+ OK plugin: compare plugin.version 1;
+ esac
+),
+
+"instants": \(
+ case processTest "instants" of
+ Instants ii:
+ compare (map (.time) ii) [ 0, 1.5, 3, 4.5, 6, 7.5, 9, 10.5, 12, 13.5 ];
+ other: failWith "wrong structure type: expected Instants tag, got \(other)";
+ esac
+),
+
+"curve-oss": \(
+ case processTest "curve-oss" of
+ Series s:
+ compare s.start 0 and
+ //!!! want to specify step and block size for processing
+ compare s.step (2048/rate) and
+ compare s.values (floats (map (* 0.05) [0..19]));
+ other: failWith "wrong structure type: expected Series tag, got \(other)";
+ esac
+),
+
+"curve-fsr": \(
+ case processTest "curve-fsr" of
+ Curve c:
+ compare (map (.time) c) (map (* 0.4) [0..9]) and
+ compare (map (.value) c) (floats (map (* 0.1) [0..9]));
+/*
+ compare s.start 0 and
+ compare s.step 0.4 and
+ compare s.values (floats (map (* 0.1) [0..9]));
+*/
+ other: failWith "wrong structure type: expected Series tag, got \(other)";
+ esac
+),
+
+"curve-fsr-timed": \(
+ case processTest "curve-fsr-timed" of
+ Curve c:
+ compare (map (.time) c) [ 0, 0, 0, 0.4, 2, 2, 2, 2.4, 4, 4 ] and
+ compare (map (.value) c) (floats (map (* 0.1) [0..9]));
+ other: failWith "wrong structure type: expected Curve tag, got \(other)";
+ esac
+),
+
+"curve-vsr": \(
+ case processTest "curve-vsr" of
+ Curve c:
+ compare (map (.time) c) (map (* 0.75) [0..9]) and
+ compare (map (.value) c) (floats (map (* 0.1) [0..9]));
+ other: failWith "wrong structure type: expected Curve tag, got \(other)";
+ esac
+),
+
+"grid-oss": \(
+ case processTest "grid-oss" of //!!! test spacing?
+ Grid g:
+ compareUsing mat.equal g
+ (mat.newMatrix (ColumnMajor ())
+ (map do x:
+ (vec.fromList . floats) (map do y:
+ (x + y + 2) / 30
+ done [0..9])
+ done [0..19]));
+ other: failWith "wrong structure type: expected Grid tag, got \(other)";
+ esac
+),
+
+"grid-fsr": \(
+ case processTest "grid-fsr" of //!!! test spacing?
+ Grid g:
+ compareUsing mat.equal g
+ (mat.newMatrix (ColumnMajor ())
+ (map do x:
+ (vec.fromList . floats) (map do y:
+ (x + y + 2) / 20
+ done [0..9])
+ done [0..9]));
+ other: failWith "wrong structure type: expected Grid tag, got \(other)";
+ esac
+),
+
+"notes-regions": \(
+ case processTest "notes-regions" of
+ Regions rr:
+ compare (map (.time) rr) [0..9] and
+ compare (map (.duration) rr) (map do i: Time (if i % 2 == 0 then 1.75 else 0.5 fi) done [0..9]) and
+ compare (map (.values) rr) (map do i: array [float i] done (map (* 0.1) [0..9]));
+ other: failWith "wrong structure type: expected Curve tag, got \(other)";
+ esac
+),
+
+];
+
+// Check we have the test plugin. Without it, all the tests must fail
+
+if contains? testPluginKey v.pluginKeys then
+ tests
+else
+ eprintln
+"** Vamp test plugin not found!
+ Either the Vamp module is not working, or the test plugin is not installed.
+ Please ensure vamp-test-plugin.{so,dll,dylib} is in the path,
+ or set $VAMP_PATH to point to its location.
+
+ Current path: \(v.pluginPath)
+ Required plugin key: \"\(testPluginKey)\"
+ Plugin keys found: \(v.pluginKeys)
+
+ All of the Vamp plugin tests will fail until this is fixed.
+";
+ mapIntoHash id \\false (keys tests)
+fi is hash boolean>;
+
+
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/vamp/test/test_vamppost.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/vamp/test/test_vamppost.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,83 @@
+module may.vamp.test.test_vamppost;
+
+vp = load may.vamp.vamppost;
+
+{ compare } = load may.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.2 1, timed 1.4 2, timed 4.0 3
+ ];
+),
+
+"fillFixedSampleRate2": \(
+ // As above, but with non-integer output sample rate
+ filled = vp.fillTimestamps
+ (testdata with { output = { sampleType = FixedSampleRate 2.5 } });
+ compare filled [
+ timed 0 1 ,
+ timed 0.4 1, timed 0.8 2 ,
+ timed 1.2 1, timed 1.6 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 boolean>;
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/vamp/vamppost.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/vamp/vamppost.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,166 @@
+module may.vamp.vamppost;
+
+mat = load may.matrix;
+vec = load may.vector;
+
+fillOneSamplePerStep config features =
+ (fill' n pending features =
+ // For OneSamplePerStep features, the time is incremented
+ // between process blocks (a process block is one element in
+ // the features list, which is then expanded into the complete
+ // pending list)
+ case pending of
+ feature::rest:
+ stamped = feature with
+ //!!! how do we ensure feature timestamps are rationals where possible?
+ { 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 =
+ (fill' n pending features =
+ // For FixedSampleRate features without explicit timestamps,
+ // the time is incremented from the previous *feature* not the
+ // previous process block (i.e. between elements in the
+ // pending list)
+ case pending of
+ feature::rest:
+ n = case feature.timestamp of
+ Untimed (): n + 1;
+ Time t: int (t * rate + 0.5);
+ 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 =
+//!!! need to return grid resolution as well -- or will caller read that from output elsewhere if they need it?
+ if empty? features then
+ mat.zeroMatrix { rows = binCount, columns = 0 };
+ else
+ mat.newMatrix (ColumnMajor ()) (map (.values) features);
+ fi;
+
+timeOf f =
+ case f.timestamp of
+ Time t: t;
+ _: failWith "Internal error: timestamps not filled";
+ esac;
+
+structureSeries features =
+ if empty? features then { start = 0, step = 0, values = [] }
+ else
+ t0 = timeOf (head features);
+ t1 = if empty? (tail features) then t0
+ else timeOf (head (tail features)) fi;
+ {
+ start = t0,
+ step = t1 - t0,
+ values = map do f: vec.at f.values 0 done features;
+ }
+ fi;
+
+structureCurve features =
+ map do f: {
+ time = timeOf f,
+ value = vec.at f.values 0,
+ label = f.label
+ } done features;
+
+structureInstants features =
+ map do f: {
+ time = timeOf f,
+ label = f.label
+ } done features;
+
+structureSegmentation features =
+ map do f: {
+ time = timeOf f,
+ type = vec.at f.values 0,
+ label = f.label
+ } done features;
+
+structureNotes features =
+ map do f: {
+ time = timeOf f,
+ duration = f.duration,
+ pitch = vec.at f.values 0, //!!! no, might be empty
+ otherValues = array (tail (vec.list f.values)), //!!! no, might be empty
+ label = f.label
+ } done features;
+
+structureWithDuration features =
+ map do f: {
+ time = timeOf f,
+ duration = f.duration,
+ values = array (vec.list f.values),
+ label = f.label
+ } done features;
+
+structureWithoutDuration features =
+ map do f: {
+ time = timeOf f,
+ values = array (vec.list f.values),
+ label = f.label
+ } done features;
+
+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
+ Series (): // No duration, one value, not variable rate
+ Series (structureSeries features);
+ Grid (): // No duration, >1 value, not variable rate
+ Grid (structureGrid binCount features);
+ Curve (): // No duration, one value, variable rate
+ Curve (structureCurve features);
+ Instants (): // Zero-valued features
+ Instants (structureInstants features);
+ Notes (): // Duration, at least one value (pitch or freq)
+ Notes (structureNotes features);
+ Regions (): // Duration, zero or more values
+ Regions (structureWithDuration features);
+ Segmentation (): // No duration, one value, segment type in RDF
+ Segmentation (structureSegmentation features);
+ Unknown (): // Other
+ Unknown
+ ((if data.output.hasDuration then structureWithDuration
+ else structureWithoutDuration
+ fi) features);
+ esac);
+
+{
+fillTimestamps,
+postprocess = structure
+}
+
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/vamp/vamprdf.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/vamp/vamprdf.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,290 @@
+
+module may.vamp.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;
+
+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,
+}
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/vector.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/vector.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,189 @@
+
+/**
+ * Vectors. A May vector is a typesafe, immutable wrapper around a Java
+ * primitive array of doubles.
+ *
+ * Although not as convenient and flexible as a Yeti array or
+ * list, a vector can be faster and more compact when dealing
+ * with dense data of suitable range and precision such as sampled
+ * sequences.
+ */
+
+module may.vector;
+
+load may.vector.type;
+
+import java.util: Arrays;
+
+/// Return a vector of n zeros.
+zeros n =
+ new double[n];
+
+/// Return a vector of length n, containing all m.
+consts m n =
+ (a = zeros n;
+ for [0..n-1] do i:
+ a[i] := m;
+ done;
+ a);
+
+/// Return a vector of length n, containing all ones.
+ones n = consts 1.0 n;
+
+/// Return a vector of the values in the given list.
+fromList l is list? -> ~double[] =
+ l as ~double[];
+
+/// Return the given vector as a list.
+list' a is ~double[] -> list =
+ list a;
+
+/// Return the given vector as a Yeti array.
+array' a is ~double[] -> array =
+ array a;
+
+/// Return the length of the given vector.
+length' =
+ length . list';
+
+/// Return true if the given vector is empty (has length 0).
+empty?' =
+ empty? . list';
+
+/// Return element n in the given vector v. (The function name and
+/// argument order are chosen for symmetry with the similar standard
+/// library array function.)
+at' v n is ~double[] -> number -> number =
+ v[n];
+
+/// Return the given vector as a Java primitive float array.
+floats a is ~double[] -> ~float[] =
+ (len = length' a;
+ f = new float[len];
+ for [0..len-1] do i:
+ f[i] := a[i];
+ done;
+ f);
+
+/// Return a vector of the values in the given Java primitive float array.
+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);
+
+/// Return true if the given vectors are equal, using the standard ==
+/// comparator on their elements.
+equal v1 v2 =
+ list' v1 == list' v2;
+
+/// Return true if the given vectors are equal, when applying the
+/// given numerical comparator to each element.
+equalUnder comparator v1 v2 =
+ length' v1 == length' v2 and
+ all id (map2 comparator (list' v1) (list' v2));
+
+/// Return another copy of the given vector.
+copyOf v is ~double[] -> ~double[] =
+ Arrays#copyOf(v, list' v |> length);
+
+/// Return a new vector containing a subset of the elements of the
+/// given vector, from index start (inclusive) to index end
+/// (exclusive). (The function name and argument order are chosen for
+/// symmetry with the standard library slice and strSlice functions.)
+slice v start end is ~double[] -> number -> number -> ~double[] =
+ if start < 0 then slice v 0 end
+ elif start > length' v then slice v (length' v) end
+ else
+ if end < start then slice v start start
+ elif end > length' v then slice v start (length' v)
+ else
+ Arrays#copyOfRange(v, start, end);
+ fi
+ fi;
+
+/// Return a new vector of length n, containing the contents of the
+/// given vector v. If v is longer than n, the contents will be
+/// truncated; if shorter, they will be padded with zeros.
+resizedTo n v is number -> ~double[] -> ~double[] =
+ Arrays#copyOf(v, n);
+
+/// Return a new vector that is the reverse of the given vector. Name
+/// chosen (in preference to passive "reversed") for symmetry with the
+/// standard library list reverse function.
+reverse v is ~double[] -> ~double[] =
+ (len = length (list v);
+ a = new double[len];
+ for [0..len-1] do i:
+ a[len-i-1] := v[i];
+ done;
+ a);
+
+/// Return a single new vector that contains the contents of all the
+/// given vectors, in order. (Unlike the standard module list concat
+/// function, this one cannot be lazy.)
+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);
+
+/// Return a single new vector that contains the contents of the given
+/// vector, repeated n times. The vector will therefore have length n
+/// times the length of v.
+repeated v n is ~double[] -> number -> ~double[] =
+ concat (map \(v) [1..n]);
+
+{
+ zeros,
+ consts,
+ ones,
+ vector v = v,
+ primitive = copyOf,
+ floats,
+ fromFloats,
+ fromList,
+ list = list',
+ array = array',
+ length = length',
+ empty? = empty?',
+ at = at',
+ equal,
+ equalUnder,
+ slice,
+ resizedTo,
+ reverse,
+ repeated,
+ concat,
+} as {
+ zeros is number -> vector,
+ consts is number -> number -> vector,
+ ones is number -> vector,
+ vector is ~double[] -> vector,
+ primitive is vector -> ~double[],
+ floats is vector -> ~float[],
+ fromFloats is ~float[] -> vector,
+ fromList is list? -> vector,
+ list is vector -> list,
+ array is vector -> array,
+ length is vector -> number,
+ empty? is vector -> boolean,
+ at is vector -> number -> number,
+ equal is vector -> vector -> boolean,
+ equalUnder is (number -> number -> boolean) -> vector -> vector -> boolean,
+ slice is vector -> number -> number -> vector,
+ resizedTo is number -> vector -> vector,
+ reverse is vector -> vector,
+ repeated is vector -> number -> vector,
+ concat is list? -> vector,
+}
+
+
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/vector/blockfuncs.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/vector/blockfuncs.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,161 @@
+
+module may.vector.blockfuncs;
+
+vec = load may.vector;
+
+load may.vector.type;
+
+//!!! "internal" vector function to retrieve data for read-only
+// purposes without copying
+raw =
+ (raw' v is ~double[] -> ~double[] = v;
+ raw' as vector -> ~double[]);
+
+sum' v =
+ (dat = raw v;
+ tot = new double[1];
+ for [0..length dat - 1] do i:
+ tot[0] := tot[0] + dat[i]
+ done;
+ tot[0]);
+
+max' v =
+ (dat = raw v;
+ var mx = 0;
+ for [0..length dat - 1] do i:
+ if i == 0 or dat[i] > mx then
+ mx := dat[i];
+ fi
+ done;
+ mx);
+
+maxindex v =
+ (dat = raw v;
+ var mx = 0;
+ var mi = -1;
+ for [0..length dat - 1] do i:
+ if i == 0 or dat[i] > mx then
+ mx := dat[i];
+ mi := i;
+ fi
+ done;
+ mi);
+
+min' v =
+ (dat = raw v;
+ var mn = 0;
+ for [0..length dat - 1] do i:
+ if i == 0 or dat[i] < mn then
+ mn := dat[i];
+ fi
+ done;
+ mn);
+
+minindex v =
+ (dat = raw v;
+ var mn = 0;
+ var mi = -1;
+ for [0..length dat - 1] do i:
+ if i == 0 or dat[i] < mn then
+ mn := dat[i];
+ mi := i;
+ fi
+ done;
+ mi);
+
+mean v =
+ case vec.length v of
+ 0: 0;
+ len: sum' v / len
+ esac;
+
+add bb =
+ (len = head (sort (map vec.length bb));
+ vv = map raw bb;
+ out = new double[len];
+ for [0..len-1] do i:
+ for vv do v:
+ out[i] := out[i] + v[i];
+ done;
+ done;
+ vec.vector out);
+
+subtract b1 b2 =
+ (v1 = raw b1;
+ v2 = raw b2;
+ len = if length v1 < length v2 then length v1 else length v2 fi;
+ out = new double[len];
+ for [0..len-1] do i:
+ out[i] := v1[i] - v2[i]
+ done;
+ vec.vector out);
+
+multiply b1 b2 =
+ (v1 = raw b1;
+ v2 = raw b2;
+ len = if length v1 < length v2 then length v1 else length v2 fi;
+ out = new double[len];
+ for [0..len-1] do i:
+ out[i] := v1[i] * v2[i]
+ done;
+ vec.vector out);
+
+scaled n v =
+ vec.fromList (map (* n) (vec.list v));
+
+divideBy n v = // Not just "scaled (1/n)" -- this way we get exact rationals
+ vec.fromList (map (/ n) (vec.list v));
+
+sqr v =
+ multiply v v;
+
+rms =
+ sqrt . mean . sqr;
+
+abs' =
+ vec.fromList . (map abs) . vec.list;
+
+sqrt' =
+ vec.fromList . (map sqrt) . vec.list;
+
+unityNormalised v =
+ (m = max' (abs' v);
+ if m != 0 then
+ divideBy m v;
+ else
+ v;
+ fi);
+
+fftshift v =
+ (len = vec.length v;
+ half = int(len/2 + 0.5); // round up for odd-length sequences
+ vec.concat [vec.slice v half len, vec.slice v 0 half]);
+
+ifftshift v =
+ (len = vec.length v;
+ half = int(len/2); // round down for odd-length sequences
+ vec.concat [vec.slice v half len, vec.slice v 0 half]);
+
+{
+sum is vector -> number = sum',
+mean is vector -> number,
+add is list? -> vector,
+subtract is vector -> vector -> vector,
+multiply is vector -> vector -> vector,
+divideBy is number -> vector -> vector,
+scaled is number -> vector -> vector,
+abs is vector -> vector = abs',
+sqr is vector -> vector,
+sqrt is vector -> vector = sqrt',
+rms is vector -> number,
+max is vector -> number = max',
+min is vector -> number = min',
+maxindex is vector -> number,
+minindex is vector -> number,
+unityNormalised is vector -> vector,
+fftshift is vector -> vector,
+ifftshift is vector -> vector,
+}
+
+
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/vector/test/test_blockfuncs.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/vector/test/test_blockfuncs.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,125 @@
+
+module may.vector.test.test_blockfuncs;
+
+stdSqrt = sqrt;
+
+{ zeros, consts, ones, fromList, list } = load may.vector;
+{ sum, max, min, maxindex, minindex, mean, add, subtract, multiply, divideBy, scaled, abs, sqr, sqrt, rms, unityNormalised, fftshift, ifftshift } = load may.vector.blockfuncs;
+{ compare } = load may.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
+),
+
+"max": \(
+ compare ((max . fromList) [1,-2,3,0]) 3 and
+ compare ((max . fromList) [-1,-2,-3]) (-1) and
+ compare ((max . fromList) [4,1]) 4 and
+ compare ((max . fromList) []) 0
+),
+
+"min": \(
+ compare ((min . fromList) [1,-2,3,0]) (-2) and
+ compare ((min . fromList) [-1,-2,-3]) (-3) and
+ compare ((min . fromList) [4,1]) 1 and
+ compare ((min . fromList) []) 0
+),
+
+"maxindex": \(
+ compare ((maxindex . fromList) [1,-2,3,0]) 2 and
+ compare ((maxindex . fromList) [-1,-2,-3]) 0 and
+ compare ((maxindex . fromList) [4,1]) 0 and
+ compare ((maxindex . fromList) []) (-1)
+),
+
+"minindex": \(
+ compare ((minindex . fromList) [1,-2,3,0]) 1 and
+ compare ((minindex . fromList) [-1,-2,-3]) 2 and
+ compare ((minindex . fromList) [4,1]) 1 and
+ compare ((minindex . fromList) []) (-1)
+),
+
+"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
+),
+
+"add": \(
+ compare (list (add [zeros 0, ones 5])) [] and
+ compare (list (add [consts 3 4, fromList [1,2,3] ])) [4,5,6] and
+ compare (list (add [consts (-3) 4, fromList [1,2,3] ])) [-2,-1,0]
+),
+
+"subtract": \(
+ compare (list (subtract (zeros 0) (ones 5))) [] and
+ compare (list (subtract (consts 3 4) (fromList [1,2,3]))) [2,1,0] and
+ compare (list (subtract (consts (-3) 4) (fromList [1,2,3]))) [-4,-5,-6]
+),
+
+"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]
+),
+
+"scaled": \(
+ compare (list (scaled 5 (ones 0))) [] and
+ compare (list (scaled 5 (fromList [1,2,-3]))) [5,10,-15]
+),
+
+"abs": \(
+ compare (list (abs (ones 0))) [] and
+ compare (list (abs (fromList [1,2,-3]))) [1,2,3]
+),
+
+"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)
+),
+
+"unityNormalised": \(
+ compare ((list . unityNormalised . fromList) [1,-2,3,0]) [1/3,-2/3,1,0] and
+ compare ((list . unityNormalised . fromList) [-1,-2,-3]) [-1/3,-2/3,-1] and
+ compare ((list . unityNormalised . fromList) [4,1]) [1,1/4] and
+ compare ((list . unityNormalised . fromList) []) []
+),
+
+"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 boolean>;
+
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/vector/test/test_vector.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/vector/test/test_vector.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,130 @@
+
+module may.vector.test.test_vector;
+
+vec = load may.vector;
+
+{ compare } = load may.test.test;
+
+[
+
+"zeros-empty": \(
+ v = vec.zeros 0;
+ compare (vec.length v) 0;
+),
+
+"zeros": \(
+ v = vec.zeros 3;
+ a = vec.array v;
+ compare (vec.length v) 3 and
+ compare a[0] 0 and
+ compare a[1] 0 and
+ compare a[2] 0;
+),
+
+"consts-empty": \(
+ v = vec.consts 4 0;
+ compare (vec.length v) 0;
+),
+
+"consts": \(
+ v = vec.consts 4 3;
+ a = vec.array v;
+ compare (vec.length v) 3 and
+ compare a[0] 4 and
+ compare a[1] 4 and
+ compare a[2] 4;
+),
+
+"ones-empty": \(
+ v = vec.ones 0;
+ compare (vec.length v) 0;
+),
+
+"ones": \(
+ v = vec.ones 3;
+ a = vec.array v;
+ compare (vec.length v) 3 and
+ compare a[0] 1 and
+ compare a[1] 1 and
+ compare a[2] 1;
+),
+
+"from-list-empty": \(
+ v = vec.fromList [];
+ compare (vec.length v) 0;
+),
+
+"from-list": \(
+ v = vec.fromList [1,2,3,4];
+ a = vec.array v;
+ compare (vec.length v) 4 and
+ compare a[0] 1 and
+ compare a[1] 2 and
+ compare a[2] 3 and
+ compare a[3] 4;
+),
+
+"equal-empty": \(
+ vec.equal (vec.fromList []) (vec.fromList [])
+),
+
+"equal": \(
+ v = vec.fromList [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'';
+),
+
+"slice": \(
+ v = vec.fromList [1,2,3,4];
+ vec.equal (vec.slice v 0 4) v and
+ vec.equal (vec.slice v 2 4) (vec.fromList [3,4]) and
+ vec.equal (vec.slice v (-1) 2) (vec.fromList [1,2]) and
+ vec.equal (vec.slice v 3 5) (vec.fromList [4]) and
+ vec.equal (vec.slice v 5 7) (vec.fromList []) and
+ vec.equal (vec.slice v 3 2) (vec.fromList [])
+),
+
+"resizedTo": \(
+ vec.equal (vec.resizedTo 4 (vec.fromList [])) (vec.zeros 4) and
+ vec.equal (vec.resizedTo 2 (vec.fromList [1,2])) (vec.fromList [1,2]) and
+ vec.equal (vec.resizedTo 3 (vec.fromList [1,2])) (vec.fromList [1,2,0]) and
+ vec.equal (vec.resizedTo 2 (vec.fromList [1,2,3])) (vec.fromList [1,2]);
+),
+
+"repeated": \(
+ vec.equal (vec.repeated (vec.fromList []) 2) (vec.fromList []) and
+ vec.equal (vec.repeated (vec.fromList [1,2,3]) 0) (vec.fromList []) and
+ vec.equal (vec.repeated (vec.fromList [1,2,3]) 1) (vec.fromList [1,2,3]) and
+ vec.equal (vec.repeated (vec.fromList [1,2,3]) 2) (vec.fromList [1,2,3,1,2,3])
+),
+
+"reverse": \(
+ vec.equal (vec.reverse (vec.fromList [])) (vec.fromList []) and
+ vec.equal (vec.reverse (vec.fromList [1,2,3])) (vec.fromList [3,2,1]) and
+ vec.equal (vec.reverse (vec.fromList [1,2])) (vec.fromList [2,1])
+),
+
+"concat2": \(
+ v = vec.fromList [1,2,3];
+ w = vec.fromList [4,5,6];
+ x = vec.concat [v, w];
+ x' = vec.fromList [1,2,3,4,5,6];
+ vec.equal x x' and
+ vec.equal x' (vec.concat [x', vec.fromList []]) and
+ vec.equal x' (vec.concat [vec.fromList [], x'])
+),
+
+"concatn": \(
+ v = vec.fromList [1,2,3];
+ w = vec.fromList [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.fromList [1,2,3,4,5,6,1,2,3])
+),
+
+] is hash boolean>;
+
+
+
diff -r 4b104ef8d110 -r 20903ee53719 src/may/vector/type.yeti
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/may/vector/type.yeti Mon Sep 23 12:35:35 2013 +0100
@@ -0,0 +1,7 @@
+
+module may.vector.type;
+
+typedef opaque vector = ~double[];
+
+();
+