% lineout - Live audio output 
%
% lineout ::
%    C:natural ~'desired no. of channels',
%    R:nonneg  ~'desired sample rate',
%    options {
%       bufsize :: natural/[] ~'desired buffer size'
%    }
% -> sink(C,R).
classdef lineout < sink
	properties (GetAccess=private, SetAccess=immutable)
		fs
		chans
		opts
	end
	methods
		function s=lineout(channels,rate,varargin)
			s.opts = options('bufsize',0,'bits',16,'pad',0,'quiet',0,varargin{:});
			s.chans=channels;
			s.fs=rate;
		end

		function c=tostring(sig), c=sprintf('lineout(%d,%d)',sig.chans,sig.fs); end
		function c=channels(s), c=s.chans; end
		function c=rate(s), c=s.fs; end
		function s=construct(sig)
			import ishara.audio.*;

			snk=LineSink(audio_format(sig.chans,sig.fs,sig.opts.bits),sig.opts.bufsize);
			snk.setScale(0.999);
			line=snk.getLine();
			ref=disposables('reg',snk);
			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);
				pad=zeros(sig.chans*sig.opts.pad,1);
				padwr=snk.writer(length(pad));
				maybe_pad=@()padwr.write(pad);
			else
				maybe_pad=@nop;
			end

			s.stop    = @stop;
			s.start   = @start;
			s.dispose = @dispose;
			s.writer = @writer;

			s.getLine = @()line;
			s.setGain = @(s)snk.setScale(s);

			function start, line.flush(); line.start(); maybe_pad(); snk.check(); end
			function stop, snk.check(); maybe_pad(); line.drain(); line.stop(); end

			function r=writer(n)
				ch=sig.chans;
				wr=snk.writer(ch*n);
				r=@next;
				function rem=next(x)
					rem=wr.write(x(:))/ch;
				end
			end

			function dispose
				disposables('dereg',ref);
				snk.dispose();
			end
		end
	end
end
