changeset 42:ae596261e75f

Various fixes and development to audio handling
author samer
date Tue, 02 Dec 2014 14:51:13 +0000
parents 79632e7bcb52
children 62e31e7980e6
files TODO arrows/@asampler/asampler.m arrows/@asink/construct.m arrows/@unfolder/unfolder.m arrows/alatch.m arrows/alinein.m arrows/anull.m arrows/audio_bench.m arrows/azeromean.m arrows/dsp/adct.m arrows/stats/accumstats.m arrows/stats/stats_pca.m arrows/ugather.m audio/@linein/linein.m audio/@lineout/lineout.m audio/mp3file.m audio/private/pipein.m audio/sndfile.m dsp/specgrm.m dsp/synth/@sine/block.m dsp/synth/@sine/block_sr.m general/disposables.m general/funutils/@function_handle/and.m general/funutils/@function_handle/private/acount.m general/funutils/@function_handle/table.m general/funutils/README.txt general/tostring.m sequences/+seq/unfold_finite.m sequences/framedata.m sinks/@sinkarray/sinkarray.m
diffstat 30 files changed, 215 insertions(+), 94 deletions(-) [+]
line wrap: on
line diff
--- a/TODO	Tue Jan 29 17:22:52 2013 +0000
+++ b/TODO	Tue Dec 02 14:51:13 2014 +0000
@@ -13,3 +13,10 @@
 	actions and functions
 		analogues of arrow combinators
 
+
+Rationalise buffering
+
+small to large by stacking along some dimension
+large to small: slices of along some dimension
+arbitrary rebuffering along some dimension
+
--- a/arrows/@asampler/asampler.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/arrows/@asampler/asampler.m	Tue Dec 02 14:51:13 2014 +0000
@@ -1,4 +1,4 @@
-% asampler - sampling arrow
+% asampler - random sampling arrow
 %
 % asampler ::
 %    (E -> [E->A])  ~'function to generate sample array of given size',
--- a/arrows/@asink/construct.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/arrows/@asink/construct.m	Tue Dec 02 14:51:13 2014 +0000
@@ -1,9 +1,13 @@
 function u=construct(s,sizes_in)
 	u=mkunit(s);
-	if sizes_in{1}(1)~=channels(s.sink)
-		error('Number of rows in input does not match channels in sink');
+	if ~isa(s.sink,'sink')
+		snk=construct(s.sink(sizes_in{1}(1)));
+	else
+		if sizes_in{1}(1)~=channels(s.sink)
+			error('Number of rows in input does not match channels in sink');
+		end
+		snk=construct(s.sink);
 	end
-	snk=construct(s.sink);
 	
 	if isempty(s.window), 
 		write=snk.writer(sizes_in{1}(2));
--- a/arrows/@unfolder/unfolder.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/arrows/@unfolder/unfolder.m	Tue Dec 02 14:51:13 2014 +0000
@@ -1,4 +1,4 @@
-% unfolder - unfolding arrow
+% unfolder - arrow to generate sequence by unfolding state
 %
 % unfolder ::
 %    (S -> B, S)  ~'function to state to output and next state',
--- a/arrows/alatch.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/arrows/alatch.m	Tue Dec 02 14:51:13 2014 +0000
@@ -1,4 +1,4 @@
-% alatch - Arrow which produced the value in the last event received.
+% alatch - Arrow which produces the value in the last event received.
 %
 % alatch :: A -> arrow( {box(A)}, {A}, A).
 function o=alatch(x0)
--- a/arrows/alinein.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/arrows/alinein.m	Tue Dec 02 14:51:13 2014 +0000
@@ -1,6 +1,6 @@
-% linein - Audio input arrow
+% alinein - arrow to input audio as overlapped frames
 %
-% linein ::
+% alinein ::
 %    nonneg     ~'sampling rate',
 %    N:natural  ~'block size',
 %    M:natural  ~'hop size',
--- a/arrows/anull.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/arrows/anull.m	Tue Dec 02 14:51:13 2014 +0000
@@ -1,4 +1,4 @@
-% asink - Do nothing absorbing arrow
+% anull - arrow to absorb given number of inputs
 %
 % asink :: N:natural -> arrow(_@typelist(N),{},empty).
 function a=asink(nin)
--- a/arrows/audio_bench.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/arrows/audio_bench.m	Tue Dec 02 14:51:13 2014 +0000
@@ -73,12 +73,19 @@
 
 	path=[1,1,2,1]; % path to state of a in state of o 
 	if ~isempty(opts.state), o=o^opts.state; disp('Initialising...'); end
-	if opts.run, 
+	if opts.run==1, 
 		r=arrow_sched(o,opts.period,'start',opts.start); 
+	elseif opts.run==2
+		r=with_arrow(o,@run,{});
 	else
 		r=[];
 	end
 
+	function r=run(unit)
+		uiterate(unit,inf,'draw',1,'chunk',100);
+		r=0;
+	end
+
 	function [x1,x2,x3]=scope_join(x1,x2)
 		x3=[x1(1:M,1),x2(1:M,1)];
 	end
--- a/arrows/azeromean.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/arrows/azeromean.m	Tue Dec 02 14:51:13 2014 +0000
@@ -24,7 +24,9 @@
 		nans=isnan(offset);
 		offset(nans)=x(nans);
 		y = x-repmat(offset,1,size(x,2));
-		offset = offset + rate*sum(score(y),2);
+		delta = rate*sum(score(y),2);
+		delta(~isfinite(delta))=0;
+		offset = offset + delta;
 	end
 
 	function [y,offset]=update_t(x,offset)
@@ -34,7 +36,9 @@
 		y = x;
 		for i=1:w
 			y(:,i)=y(:,i)-offset;
-			offset = offset + rate*(score(y(:,i))+reg(offset));
+			delta = rate*(score(y(:,i))+reg(offset));
+			delta(~isfinite(delta))=0;
+			offset = offset + delta;
 		end
 	end
 
--- a/arrows/dsp/adct.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/arrows/dsp/adct.m	Tue Dec 02 14:51:13 2014 +0000
@@ -1,13 +1,16 @@
 % adct - arrow for DCT
 %
 % adct :: M:natural -> arrow({[[N]]}, {[[M]]}, empty).
+%
+% Produces only the first M DCT coefficients
 function o=adct(M)
+	I=1:M;
 	o=arrf(@mkdct,1,1);
 
 	function f=mkdct(sizes_in)
 		% take DCT size from size(1) of first input
-		W=row(dct(eye(sizes_in{1}(1))),M);
-		f=@(x)W*x;
+		%W=row(dct(eye(sizes_in{1}(1))),M);
+		f=@(x)row(dct(x),I);
 	end
 end
 
--- a/arrows/stats/accumstats.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/arrows/stats/accumstats.m	Tue Dec 02 14:51:13 2014 +0000
@@ -1,4 +1,4 @@
-% accumstats - arrow that collects 1st and 2nd order statistics (struct version)
+% accumstats - Arrow that collects 1st and 2nd order statistics (struct version)
 %
 % accumstats :: arrow({[[N]]},{stats_struct(N)},stats_struct(N)).
 % accumstats :: nonneg -> arrow({[[N]]},{stats_struct(N)},stats_struct(N)).
--- a/arrows/stats/stats_pca.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/arrows/stats/stats_pca.m	Tue Dec 02 14:51:13 2014 +0000
@@ -1,29 +1,20 @@
-function o=astats_pca(I)
-	%GMM=gmm('full');
-	%m0=GMM.blank(1,N);
-	o=arr(@stats_pca);
-	function pca=stats_pca(stats)
-		if isstruct(stats)
-			m1=mstep(stats);
-			[V,D]=eig(m1.cov);
-			J=(1+size(D,1))-I;
-			pca.eigvecs=V(:,J);
-			pca.eigvals=diag(D);
-			pca.eigvals=max(0,pca.eigvals(J));
-			pca.mean=m1.mu;
-		else
-			pca.dummy=1;
-		end
-	end
-	function M=mstep(S)
-		M.mu=S.amu/S.ag;
-		M.cov=msym(S.aco/S.ag - M.mu*M.mu'); 
-	end
+function pca=stats_pca(stats,I)
+	m1=mstep(stats);
+	[V,D]=eig(m1.cov);
+	J=(1+size(D,1))-I;
+	pca.eigvecs=V(:,J);
+	pca.eigvals=diag(D);
+	pca.eigvals=max(0,pca.eigvals(J));
+	pca.mean=m1.mu;
+end
+function M=mstep(S)
+	M.mu=S.amu/S.ag;
+	M.cov=msym(S.aco/S.ag - M.mu*M.mu'); 
+end
 
-	function M=mstep_z(S)
-		M.mu=zeros(size(S.aco,1),1);
-		M.cov=S.aco/S.ag; 
-	end
+%function M=mstep_z(S)
+%	M.mu=zeros(size(S.aco,1),1);
+%	M.cov=S.aco/S.ag; 
+%end
 
-	function A=msym(X), A=(X+X')/2; end
-end
+function A=msym(X), A=(X+X')/2; end
--- a/arrows/ugather.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/arrows/ugather.m	Tue Dec 02 14:51:13 2014 +0000
@@ -19,15 +19,30 @@
 
 function X=ugather(u,its,varargin)
 	opts=options('expand',1,varargin{:});
-	if opts.expand && isinf(its)
-		X=zeros(u.sizes_out{1}(1),32); % initial size is arbitrarily 32
-		done=uiter(u,its,@gatherxx,[],'label','ugather',opts);
-		if done<size(X,2), X=X(:,1:done); end
+	szout = u.sizes_out{1};
+
+	if szout(2)==1
+		if opts.expand && isinf(its)
+			X=zeros(szout(1),32); % initial size is arbitrarily 32
+			done=uiter(u,its,@gatherxx,[],'label','ugather',opts);
+			if done<size(X,2), X=X(:,1:done); end
+		else
+			if ~isinf(its), X=zeros(szout(1),its); end
+
+			done=uiter(u,its,@gatherx,[],'label','ugather',opts);
+			if done<its, X=X(:,1:done); end
+		end
 	else
-		if ~isinf(its), X=zeros(u.sizes_out{1}(1),its); end
+		if opts.expand && isinf(its)
+			X=zeros(szout(1),32); % initial size is arbitrarily 32
+			done=szout(2)*uiter(u,its,@gatherxxw,[],'label','ugather',opts);
+			if done<size(X,2), X=X(:,1:done); end
+		else
+			if ~isinf(its), X=zeros(szout(1),its*szout(2)); end
 
-		done=uiter(u,its,@gatherx,[],'label','ugather',opts);
-		if done<its, X=X(:,1:done); end
+			done=szout(2)*uiter(u,its,@gatherxw,[],'label','ugather',opts);
+			if done<its, X=X(:,1:done); end
+		end
 	end
 
 	function s=gatherx(i,s), X(:,i)=u.process(); end
@@ -38,6 +53,21 @@
 		end
 		X(:,i)=u.process(); 
 	end
+
+	function s=gatherxw(i,s), 
+		i1=1+(i-1)*szout(2);
+		i2=i*szout(2);
+		X(:,i1:i2)=u.process(); 
+	end
+	function s=gatherxxw(i,s), 
+		i1=1+(i-1)*szout(2);
+		i2=i*szout(2);
+		if i2>size(X,2),
+			%fprintf('ugather: expanding at %d.\n',size(X,2));
+			X=[X,zeros(size(X))]; % double size
+		end
+		X(:,i1:i2)=u.process(); 
+	end
 end
 
 
--- a/audio/@linein/linein.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/audio/@linein/linein.m	Tue Dec 02 14:51:13 2014 +0000
@@ -14,7 +14,7 @@
 	methods
 		function s=linein(channels,rate,varargin)
 			s=s@sigbase(channels,rate);
-			s.opts = options('bufsize',0,'bits',16,varargin{:});
+			s.opts = options('bufsize',0,'bits',16,'quiet',0,varargin{:});
 		end
 		function c=tostring(sig), c='linein'; end
 
@@ -24,8 +24,10 @@
 
 			src=LineSource(audio_format(sig.channels,sig.rate,sig.opts.bits),sig.opts.bufsize);
 			ref=disposables('reg',src);
-			fprintf('\nOpened audio input device:\n  %s\n',char(src.toString));
-			fprintf('  Actual buffer size is %d.\n',src.getLine.getBufferSize);
+			if ~sig.opts.quiet
+				fprintf('\nOpened audio input device:\n  %s\n',char(src.toString));
+				fprintf('  Actual buffer size is %d.\n',src.getLine.getBufferSize);
+			end
 
 			s.stop    = @stop;
 			s.start   = @start;
--- a/audio/@lineout/lineout.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/audio/@lineout/lineout.m	Tue Dec 02 14:51:13 2014 +0000
@@ -15,7 +15,7 @@
 	end
 	methods
 		function s=lineout(channels,rate,varargin)
-			s.opts = options('bufsize',0,'bits',16,'pad',0,varargin{:});
+			s.opts = options('bufsize',0,'bits',16,'pad',0,'quiet',0,varargin{:});
 			s.chans=channels;
 			s.fs=rate;
 		end
@@ -30,8 +30,10 @@
 			snk.setScale(0.999);
 			line=snk.getLine();
 			ref=disposables('reg',snk);
-			fprintf('\nOpened audio output device:\n  %s\n',char(snk.toString));
-			fprintf('  Actual buffer size is %d.\n',snk.getLine.getBufferSize);
+			if ~sig.opts.quiet
+				fprintf('\nOpened audio output device:\n  %s\n',char(snk.toString));
+				fprintf('  Actual buffer size is %d.\n',snk.getLine.getBufferSize);
+			end
 
 			if sig.opts.pad>0, 
 				fprintf('  Padding start and stop with %d samples.\n',sig.opts.pad);
--- a/audio/mp3file.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/audio/mp3file.m	Tue Dec 02 14:51:13 2014 +0000
@@ -1,4 +1,9 @@
 function s=mp3dec(file,varargin)
-	s=sndpipe(sprintf('mpg123 --au - "%s"',file),'stringfn',@()sprintf('mp3file(''%s'')',file),varargin{:});
+	% NB: mpg123 was sometimes putting the wrong sampling rate in the header (expecting to seek
+	% back and fix it after writing) but mpg321 seems to get it right first time.
+
+	% mpg321 broken in Macports, 17th Nov 2014
+	% s=sndpipe(sprintf('mpg321 --au - "%s"',file),'stringfn',@()sprintf('mp3file(''%s'')',file),varargin{:});
+	s=sndpipe(sprintf('sox -t mp3 "%s" -t au -',file),'stringfn',@()sprintf('mp3file(''%s'')',file),varargin{:});
 end
 
--- a/audio/private/pipein.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/audio/private/pipein.m	Tue Dec 02 14:51:13 2014 +0000
@@ -1,7 +1,32 @@
 function [str,cleanup]=pipein(cmd,q)
+	import java.io.*;
+
 	if nargin<2, q=false; end
 	if ~q, fprintf('Starting sub-process: %s\n',cmd); end
-	process=java.lang.Runtime.getRuntime().exec({'bash','-c',cmd});
+
+	% OK, here's the deal: the line below doesn't work if Matlab and
+	% Java do not agree about their default character encoding, which
+	% can happen if you change Matlab's character encoding with, eg
+	% >> feature('DefaultCharacterSet','UTF-8')
+	% As far as a I can tell, you cannot change the character encoding
+	% using by Runtime.exec() for command line arguments once the JVM
+	% has been started, and, somewhat perversely, Matlab ingores the 
+	% value of the LANG environment variable on startup, preferring to
+	% us the encoding specified in $MATLAB_ROOT/bin/lcdata.xml
+
+	% NOT GOOD:
+	% process=java.lang.Runtime.getRuntime().exec({'bash','-c',cmd});
+
+	% this is what we're going to do instead: start bash with no
+	% arguments to make it read from standard input, then write cmd 
+	% with proper encoding to that stream.
+
+	% BETTER:
+	cs=feature('DefaultCharacterSet'),
+	process=java.lang.Runtime.getRuntime().exec('bash');
+	writer=OutputStreamWriter(process.getOutputStream(),cs);
+	writer.write(cmd); writer.close();
+
 	str=process.getInputStream();
 	cleanup=@dispose;
 
--- a/audio/sndfile.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/audio/sndfile.m	Tue Dec 02 14:51:13 2014 +0000
@@ -16,9 +16,15 @@
 % Javazoom MP3 decoder does not get the length of the signal right.
 % Would be better to use mpg123 on a pipe in this case.
 function s=sndfile(file,varargin)
+	if iscell(file)
+		s=taket(file{2}-file{1},dropt(file{1},sndfile(file{3})))
+		return
+	end
 	string=sprintf('sndfile(''%s'')',file);
 	if endswith(file,'mp3') || endswith(file,'MP3')
 		s=mp3file(file,'stringfn',@()string,varargin{:}); % Java version doesn't remove padding correctly
+	elseif endswith(file,'m4a') 
+		s=aacfile(file,'stringfn',@()string,varargin{:});
 	else
 		s=sndstream(@filestream,'stringfn',@()string,varargin{:});
 	end
--- a/dsp/specgrm.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/dsp/specgrm.m	Tue Dec 02 14:51:13 2014 +0000
@@ -5,7 +5,8 @@
 %     N:natural | H:[[N]] ~'window length or window', 
 %     M:natural ~'hop size',
 %     options {
-%		   fs :: nonneg/1	~'sampling rate'
+%		   fs :: nonneg/1	~'sampling rate';
+%      range:: [[2]] ~'range for tfdimage'
 %     }
 %  -> [[1+N/2,floor(T/M)]] ~'sequence of short term spectra'.  
 %
@@ -26,7 +27,9 @@
 
 	% Make Spectrogram Display if no arg out
 	if nargout==0
-		tfdimage(y,n,m,getparam(options(varargin{:}),'fs',1));
+		opts=options('fs',1,'range',13.5,varargin{:});
+
+		tfdimage(y,n,m,opts.fs,opts.range);
 		clear y;
 	end
 
--- a/dsp/synth/@sine/block.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/dsp/synth/@sine/block.m	Tue Dec 02 14:51:13 2014 +0000
@@ -1,5 +1,13 @@
 function [y,phi]=block(o,phi,n,f)
-% block - generate sine wave
+% block - generate block of sine wave samples
+%
+% sine/block :: 
+%    sine      ~'sine generator object',
+%    real      ~'phase at start of block',
+%    N:natural ~'number of samples to generate',
+%    real      ~'frequence in cycles per sample'
+% -> [[1,N]]   ~'block of output samples',
+%    real      ~'phase at end of block'.
 
 w=2*pi*f;
 y=sin(2*pi*phi + w*(0:n-1));
--- a/dsp/synth/@sine/block_sr.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/dsp/synth/@sine/block_sr.m	Tue Dec 02 14:51:13 2014 +0000
@@ -1,5 +1,13 @@
 function [y,phi]=block(o,phi,f)
-% block_sr - generate sine wave
+% block_sr - generate block of sine wave samples with signal rate control
+%
+% sine/block :: 
+%    sine      ~'sine generator object',
+%    real      ~'phase at start of block',
+%    [[1,N]]   ~'frequency at signal rate',
+% -> [[1,N]]   ~'block of output samples',
+%    real      ~'phase at end of block'.
+
 
 u=cumsum([phi,f],2);
 y=sin(2*pi*u(1:end-1));
--- a/general/disposables.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/general/disposables.m	Tue Dec 02 14:51:13 2014 +0000
@@ -23,8 +23,10 @@
 			varargout{1}=obj;
 		case 'dereg', 
 			obj=varargin{1};
-			idx=find(registry==obj);
-			registry=registry([1:idx-1,idx+1:length(registry)]);
+			if ~isempty(registry)
+				idx=find(registry==obj);
+				registry=registry([1:idx-1,idx+1:length(registry)]);
+			end
 		case 'get'
 			varargout{1}=registry;
 		case 'dispose'
--- a/general/funutils/@function_handle/and.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/general/funutils/@function_handle/and.m	Tue Dec 02 14:51:13 2014 +0000
@@ -18,7 +18,7 @@
 	if nargin<4, nout=nargout(g); end
 	if nargin<3, nin=nargin(g); end
 	fns = { @q11, @q1n; @qn1, @qnn };
-	h=fns{acount(nin,1),acount(nout,1)};
+	h=fns{acount(nin,1,1),acount(nout,1,1)};
 	function y=q11(x), f(x); y=g(x); end
 	function y=qn1(varargin), f(varargin{:}); y=g(varargin{:}); end
 	function varargout=q1n(x), f(x); [varargout{1:nargout}]=g(x); end
--- a/general/funutils/@function_handle/private/acount.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/general/funutils/@function_handle/private/acount.m	Tue Dec 02 14:51:13 2014 +0000
@@ -1,1 +1,3 @@
-function n=acount(m,k), if m<0 || m>k, n=k+1; else n=m; end; end
+function n=acount(m,k,j), 
+	if nargin<3, j=0; end
+	if m<j || m>k, n=k+1; else n=m; end; end
--- a/general/funutils/@function_handle/table.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/general/funutils/@function_handle/table.m	Tue Dec 02 14:51:13 2014 +0000
@@ -25,15 +25,14 @@
 	end
 
 	function varargout=lookup(varargin)
-		matches=select(@(r)isequal(varargin,r(1:nin)),slices(Table,1));
-		if isempty(matches)
-			[varargout{1:nout}]=f(varargin{:});
-			Table=vertcat(Table,[varargin,varargout]);
-		else
-			varargout=col(head(matches),nin+(1:nout));
+		for i=1:size(Table,1)
+			if isequal(varargin,Table(i,1:nin)),
+				varargout=Table(i,nin+(1:nout));
+				return;
+			end
 		end
+		[varargout{1:nout}]=f(varargin{:});
+		Table=vertcat(Table,[varargin,varargout]);
 	end
 end
 
-
-
--- a/general/funutils/README.txt	Tue Jan 29 17:22:52 2013 +0000
+++ b/general/funutils/README.txt	Tue Dec 02 14:51:13 2014 +0000
@@ -23,20 +23,18 @@
 	<=   le         - select input args from cell array
 	'    ctranspose - flip 1st and 2nd args
 
-	[f1,f2,...,fn] == f1|f2|...|fn
-	[f1;f2;...;fn] == f1&f2&...&fn
-			f/x/y^z == f.^{x,y,z}
 
+ Unused operators:  .'      .*     ~    unary +   unary -
 
- Unused operators: .* 
 
 
-Old functions (some not functional algebraic)
 
-  bind1  - partial application, arbitrary arity
-  flip
-  apply
-  compose
+Old functions
+
+  bind1   - same as rdivide or ./
+  flip    - same as ctranspose or '
+  apply   - samer as mpower  or   .^
+  compose - same as ctranspose(@mtimes)
 
   doreturn(f,x,y,...) ==  feval([f<[];@deal],x,y,...)
   nthret(I,f,x,y,...) ==  feval(f>I,x,y,...)
--- a/general/tostring.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/general/tostring.m	Tue Dec 02 14:51:13 2014 +0000
@@ -13,7 +13,7 @@
 	if ischar(x), s=x;
 	elseif isnumeric(x),
 		sz=size(x);
-		if prod(sz)<16, s=mat2str(x);
+		if prod(sz)<16, s=mat2str(x,5);
 		else s=sprintf('%s[%s]',class(x),mat2str(sz)); end
 	elseif isa(x,'function_handle'), s=func2str(x);
 		if s(1)~='@', s=['@' s]; end
--- a/sequences/+seq/unfold_finite.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/sequences/+seq/unfold_finite.m	Tue Dec 02 14:51:13 2014 +0000
@@ -1,7 +1,7 @@
 % unfold_finite - sequence obtained by iterating fn over state
 %
 % unfold_finite :: 
-%    (S->A,S)	~'unfolding function',
+%    (S->pair(A,S))	~'unfolding function',
 %    S         ~ initial state,
 % -> unfold_finite(S,A) < seq(A).
 %
@@ -19,7 +19,7 @@
 	methods (Static)
 		function d=make(f,s0)
 			xs=f(s0);
-			if isempty(xs), d=nil; else d=unfold_finite(f,xs{2},xs{1}); end
+			if isempty(xs), d=nil; else d=seq.unfold_finite(f,xs{2},xs{1}); end
 		end
 	end
 	methods
--- a/sequences/framedata.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/sequences/framedata.m	Tue Dec 02 14:51:13 2014 +0000
@@ -25,7 +25,7 @@
 	
 	[span,jump]=windowparams(size(signal),frame,hop,width,opts);
 	if isempty(opts.random),
-		wd=windowdata(signal,span,jump,'wrap',opts.wrap,'dim',opts.dim);
+		wd=window(signal,span,jump,'wrap',opts.wrap,'dim',opts.dim);
 	else
 		wd=rndwindow(signal,span,opts.dim);
 	end
--- a/sinks/@sinkarray/sinkarray.m	Tue Jan 29 17:22:52 2013 +0000
+++ b/sinks/@sinkarray/sinkarray.m	Tue Dec 02 14:51:13 2014 +0000
@@ -15,9 +15,9 @@
 	methods
 		function s=sinkarray(cont,ch,len,rate)
 			if nargin<4, rate=nan; end
+			s=s@sinkbase(ch,rate);
 			s.length=len;
 			s.cont=cont;
-			s=s@sinkbase(ch,rate);
 		end
 
 		function s=tostring(sig),
@@ -25,15 +25,20 @@
 		end
 
 		function s=construct(sig)
-			length=sig.length;
-			ch=channels(sig);
-			array=zeros(ch,length);
-			pos=0;
-
 			s.start   = @nop;
 			s.stop    = @nop;
-			s.dispose = @dispose;
-			s.writer  = @writer;
+			if isfinite(sig.length)
+				length=sig.length;
+				array=zeros(channels(sig),length);
+				pos=0;
+
+				s.dispose = @dispose;
+				s.writer  = @writer;
+			else
+				gg=gatherer([channels(sig),1]);
+				s.dispose = @dispose_gatherer;
+				s.writer  = @writer_gatherer;
+			end
 
 			function dispose, sig.cont(array(:,1:pos)); end
 
@@ -52,6 +57,16 @@
 					end
 				end
 			end
+
+			function dispose_gatherer, sig.cont(gg.collect()); end
+			function r=writer_gatherer(n)
+				r = @next;
+				function rem=next(x) 
+					n=size(x,2);
+					for i=1:n, gg.append(x(:,i)); end
+					rem=0; 
+				end
+			end
 		end
 	end
 end