% framedata - return abstract data object for buffering signal frames
%
% framedata :: 
% 	( s:seq [C,T] 	~'C-channel signal of length T',
%	  N:natural 	~'frame size',
%	  M:natural 	~'hop size',
%	  L:natural 	~'frames per buffer' | ([] => whole file) 
%	  options... )
%	-> seq [N,L]. 
%
% Options:
% 	wrap/0     if 1, then buffer samples as if one continuous circular signal
% 	            otherwise, don't return buffers that wrap round from end to start
% 	random/0:   if [], return buffers sequentially, if number or random state
% 	            vector, return buffers in random order, using value as seed. 
%  filter/[]  optional filter function to apply to long windows before buffering
%
% Note: if the signal is a multichannel signal, ie of size CxT with C>1,
% the samples for each channel are interleaved.

function a=framedata(signal,frame,hop,width,varargin)
	if nargin<4, width = []; end
	opts=options('wrap',0,'random',[],'dim',2,'filter',@id,varargin{:});
	if isempty(width), opts.truncate=0; end 
	
	[span,jump]=windowparams(size(signal),frame,hop,width,opts);
	if isempty(opts.random),
		wd=window(signal,span,jump,'wrap',opts.wrap,'dim',opts.dim);
	else
		wd=rndwindow(signal,span,opts.dim);
	end

	wd=opts.filter(wd);
	if size(wd,1)>1, wd=map(@flatten,wd); end
	olap=frame-hop;
	if hop<frame, buffn=@(x)buffer(x,frame,olap,'nodelay');
	else buffn=@(x)buffer(x,frame,olap); 
	end

	% NB: makes sense to cache large windows to save repeated calls
	% extract current large window from data source while processing
	% the buffered window.
	a=map(buffn,cache(wd));
end
