samer@0
|
1 function [Sched,GetData]=playaudio_unfold(buflen,unfold_fn,S,Snk,varargin)
|
samer@0
|
2 % playaudio_async - Play stream of audio data asynchronously
|
samer@0
|
3 %
|
samer@0
|
4 % playaudio ::
|
samer@0
|
5 % N:natural ~'buflen',
|
samer@0
|
6 % (S->[[C,N]],S) ~'unfolding function',
|
samer@36
|
7 % sink(C,R) ~'sink for C channels',
|
samer@0
|
8 % options {
|
samer@0
|
9 % maxbuf :: natural/2*max(size(X)) ~'maximum buffer size';
|
samer@0
|
10 % hook :: (iterator(S)->iterator(T)) ~'fn to build iterator';
|
samer@0
|
11 % onstart :: A -> action ~'called BEFORE timer starts';
|
samer@0
|
12 % onstop :: A -> action ~'called AFTER timer stops';
|
samer@0
|
13 % onfinish:: A -> action ~'called when end of signal reached';
|
samer@0
|
14 % defer :: bool / 0 ~'if 1, don't start the timer'
|
samer@0
|
15 % }
|
samer@0
|
16 % -> sched(S) ~'scheduler api functions',
|
samer@0
|
17 % (S -> [[N]]) ~'function to recover audio from iterator state'.
|
samer@0
|
18 %
|
samer@0
|
19 % iterator(S) ::= cell {(S->action S)~'state transformer', S~'initial state'}.
|
samer@0
|
20 %
|
samer@0
|
21 % sched(S) ::= struct {
|
samer@0
|
22 % dispose :: unit -> action unit;
|
samer@0
|
23 % isrunning :: unit -> action bool;
|
samer@0
|
24 % startat :: real -> action unit;
|
samer@0
|
25 % start :: unit -> action unit;
|
samer@0
|
26 % stop :: unit -> action unit;
|
samer@0
|
27 % rewind :: unit -> action unit;
|
samer@0
|
28 % getstate :: unit -> action S;
|
samer@0
|
29 % setstate :: S -> action unit
|
samer@0
|
30 % }.
|
samer@0
|
31 %
|
samer@0
|
32 % The 'hook' option gives the caller an opportunity to elaborate on the
|
samer@0
|
33 % 'iterator' used to drive the audio playback. The 'iterator' is a cell
|
samer@0
|
34 % array contain a state transformer function and initial state. The caller
|
samer@0
|
35 % can use this to build a more complex iterator the does other things
|
samer@0
|
36 % for each buffer of samples.
|
samer@0
|
37 %
|
samer@0
|
38 % The third return value is a function which can be used in a state
|
samer@0
|
39 % transformer function to recover a buffer of audio samples from the
|
samer@0
|
40 % current state.
|
samer@0
|
41 %
|
samer@0
|
42 % NB: all the audio buffers must be the same size for this to work.
|
samer@0
|
43 % NB: the rewind function should only be called when the timer is stopped.
|
samer@0
|
44
|
samer@0
|
45 N=buflen;
|
samer@37
|
46 opts=options('onstop',@nop,'onstart',@nop,'onfinish',@nop, ...
|
samer@0
|
47 'period_adjust',0.9,'preload',2,'sched',@iterate_timed,varargin{:});
|
samer@0
|
48
|
samer@0
|
49 it=feval(getparam(opts,'hook',@id),{@playbuf,S});
|
samer@0
|
50
|
samer@0
|
51 maxbuf=getparam(opts,'maxbuf',2*N);
|
samer@0
|
52 sync_delta=getparam(opts,'sync_delta',0.02);
|
samer@0
|
53 sync_offset=getparam(opts,'sync_offset',0);
|
samer@0
|
54
|
samer@0
|
55 L=construct(Snk);
|
samer@0
|
56 write=L.writer(maxbuf);
|
samer@0
|
57 period=(N/rate(Snk))*opts.period_adjust;
|
samer@0
|
58
|
samer@0
|
59 Sched=opts.sched(it{1},it{2},period, ...
|
samer@0
|
60 'exec_mode','fixedRate', 'busy_mode','drop', opts, ...
|
samer@0
|
61 'onstart',@onstart,'onstop',@onstop);
|
samer@0
|
62 getstate=Sched.getstate;
|
samer@0
|
63 odispose=Sched.dispose;
|
samer@0
|
64 Sched.atend=@()isempty(getstate());
|
samer@0
|
65 Sched.dispose=@dispose;
|
samer@0
|
66 Sched.setGain=L.setGain;
|
samer@0
|
67
|
samer@0
|
68 GetData=@head;
|
samer@0
|
69
|
samer@0
|
70 function dispose
|
samer@0
|
71 odispose();
|
samer@0
|
72 L.dispose();
|
samer@0
|
73 end
|
samer@0
|
74
|
samer@0
|
75 function onstart(S),
|
samer@0
|
76 opts.onstart(S);
|
samer@0
|
77 L.start();
|
samer@0
|
78 for i=1:opts.preload
|
samer@0
|
79 write(zeros(maxbuf,1));
|
samer@0
|
80 end
|
samer@0
|
81 end
|
samer@0
|
82
|
samer@0
|
83 function onstop(S),
|
samer@0
|
84 write(zeros(maxbuf,1));
|
samer@0
|
85 L.stop();
|
samer@0
|
86 opts.onstop(S);
|
samer@0
|
87 end
|
samer@0
|
88
|
samer@0
|
89 function S=playbuf(S)
|
samer@0
|
90 [y,S]=unfold_fn(S);
|
samer@0
|
91 n=size(y,2);
|
samer@0
|
92 if n>0, write(y); end
|
samer@0
|
93 end
|
samer@0
|
94 end
|
samer@0
|
95
|