]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/audio/mixfs/mixfs.c
mixfs: add reading (audio loopback)
[plan9front.git] / sys / src / cmd / audio / mixfs / mixfs.c
1 #include <u.h>
2 #include <libc.h>
3 #include <fcall.h>
4 #include <thread.h>
5 #include <9p.h>
6
7 enum {
8         NBUF = 8*1024,
9         NDELAY = 2048,
10         NCHAN = 2,
11         FREQ = 44100,
12 };
13
14 typedef struct Stream Stream;
15 struct Stream
16 {
17         int     used;
18         int     run;
19         int     rd;
20         ulong   wp;
21         QLock;
22         Rendez;
23 };
24
25 ulong   mixrp;
26 int     mixbuf[NBUF][NCHAN];
27 int     lbbuf[NBUF][NCHAN];
28 Lock    mixlock;
29 Stream  streams[16];
30
31 int
32 s16(uchar *p)
33 {
34         int v;
35
36         v = p[0]<<(sizeof(int)-2)*8 | p[1]<<(sizeof(int)-1)*8;
37         v >>= (sizeof(int)-2)*8;
38         return v;
39 }
40
41 int
42 clip16(int v)
43 {
44         if(v > 0x7fff)
45                 return 0x7fff;
46         if(v < -0x8000)
47                 return -0x8000;
48         return v;
49 }
50
51 void
52 fsopen(Req *r)
53 {
54         Stream *s;
55
56         if(strcmp(r->fid->file->name, "audio") != 0){
57                 respond(r, nil);
58                 return;
59         }
60         for(s = streams; s < streams+nelem(streams); s++){
61                 qlock(s);
62                 if(s->used == 0 && s->run == 0){
63                         s->used = 1;
64                         qunlock(s);
65
66                         s->rd = (r->ifcall.mode&OWRITE) == 0;
67                         r->fid->aux = s;
68                         respond(r, nil);
69                         return;
70                 }
71                 qunlock(s);
72         }
73         respond(r, "all streams in use");
74 }
75
76 void
77 fsclunk(Fid *f)
78 {
79         Stream *s;
80
81         if(f->file != nil && strcmp(f->file->name, "audio") == 0 && (s = f->aux) != nil){
82                 f->aux = nil;
83                 s->used = 0;
84         }
85 }
86
87 void
88 audioproc(void *)
89 {
90         static uchar buf[NBUF*NCHAN*2];
91         int sweep, fd, i, j, n, m, v;
92         Stream *s;
93         uchar *p;
94
95         threadsetname("audioproc");
96
97         fd = -1;
98         sweep = 0;
99         for(;;){
100                 m = NBUF;
101                 for(s = streams; s < streams+nelem(streams); s++){
102                         qlock(s);
103                         if(s->run){
104                                 n = (long)(s->wp - mixrp);
105                                 if(n <= 0 && (s->used == 0 || sweep))
106                                         s->run = 0;
107                                 else if(n < m)
108                                         m = n;
109                                 if(n < NDELAY)
110                                         rwakeup(s);
111                         }
112                         qunlock(s);
113                 }
114                 m %= NBUF;
115
116                 if(m == 0){
117                         int ms;
118
119                         ms = 100;
120                         if(fd >= 0){
121                                 if(sweep){
122                                         close(fd);
123                                         fd = -1;
124                                 } else {
125                                         /* attempt to sleep just shortly before buffer underrun */
126                                         ms = seek(fd, 0, 2);
127                                         if(ms > 0){
128                                                 ms *= 800;
129                                                 ms /= FREQ*NCHAN*2;
130                                         } else
131                                                 ms = 4;
132                                 }
133                                 sweep = 1;
134                         }
135                         sleep(ms);
136                         continue;
137                 }
138                 sweep = 0;
139                 if(fd < 0)
140                 if((fd = open("/dev/audio", OWRITE)) < 0){
141                         fprint(2, "%s: open /dev/audio: %r\n", argv0);
142                         sleep(1000);
143                         continue;
144                 }
145
146                 p = buf;
147                 for(i=0; i<m; i++){
148                         for(j=0; j<NCHAN; j++){
149                                 v = clip16(mixbuf[mixrp % NBUF][j]);
150                                 lbbuf[mixrp % NBUF][j] = v;
151                                 mixbuf[mixrp % NBUF][j] = 0;
152                                 *p++ = v & 0xFF;
153                                 *p++ = v >> 8;
154                         }
155                         mixrp++;
156                 }
157                 write(fd, buf, p - buf);
158         }
159 }
160
161 void
162 fsread(Req *r)
163 {
164         Srv *srv;
165         int i, j, n, m, v;
166         Stream *s;
167         uchar *p;
168
169         p = (uchar*)r->ofcall.data;
170         n = r->ifcall.count;
171         n &= ~(NCHAN*2 - 1);
172         r->ofcall.count = n;
173         n /= (NCHAN*2);
174
175         srv = r->srv;
176         srvrelease(srv);
177         s = r->fid->aux;
178         qlock(s);
179         while(n > 0){
180                 if(s->run == 0){
181                         s->wp = mixrp;
182                         s->run = 1;
183                 }
184                 m = NBUF-1 - (long)(s->wp - mixrp);
185                 if(m <= 0){
186                         s->run = 1;
187                         rsleep(s);
188                         continue;
189                 }
190                 if(m > n)
191                         m = n;
192
193                 lock(&mixlock);
194                 for(i=0; i<m; i++){
195                         for(j=0; j<NCHAN; j++){
196                                 v = lbbuf[s->wp % NBUF][j];
197                                 *p++ = v & 0xFF;
198                                 *p++ = v >> 8;
199                         }
200                         s->wp++;
201                 }
202                 unlock(&mixlock);
203
204                 n -= m;
205         }
206         qunlock(s);
207         respond(r, nil);
208         srvacquire(srv);
209 }
210
211 void
212 fswrite(Req *r)
213 {
214         Srv *srv;
215         int i, j, n, m;
216         Stream *s;
217         uchar *p;
218
219         p = (uchar*)r->ifcall.data;
220         n = r->ifcall.count;
221         r->ofcall.count = n;
222         n /= (NCHAN*2);
223
224         srv = r->srv;
225         srvrelease(srv);
226         s = r->fid->aux;
227         qlock(s);
228         while(n > 0){
229                 if(s->run == 0){
230                         s->wp = mixrp;
231                         s->run = 1;
232                 }
233                 m = NBUF-1 - (long)(s->wp - mixrp);
234                 if(m <= 0){
235                         s->run = 1;
236                         rsleep(s);
237                         continue;
238                 }
239                 if(m > n)
240                         m = n;
241
242                 lock(&mixlock);
243                 for(i=0; i<m; i++){
244                         for(j=0; j<NCHAN; j++){
245                                 mixbuf[s->wp % NBUF][j] += s16(p);
246                                 p += 2;
247                         }
248                         s->wp++;
249                 }
250                 unlock(&mixlock);
251
252                 n -= m;
253         }
254         if((long)(s->wp - mixrp) >= NDELAY){
255                 s->run = 1;
256                 rsleep(s);
257         }
258         qunlock(s);
259         respond(r, nil);
260         srvacquire(srv);
261 }
262
263 void
264 fsstat(Req *r)
265 {
266         Stream *s;
267
268         if(r->fid->file == nil){
269                 respond(r, "bug");
270                 return;
271         }
272         if(strcmp(r->fid->file->name, "audio") == 0 && (s = r->fid->aux) != nil){
273                 qlock(s);
274                 if(s->run){
275                         r->d.length = (long)(s->wp - mixrp);
276                         r->d.length *= NCHAN*2;
277                 } else {
278                         r->d.length = 0;
279                 }
280                 qunlock(s);
281         }
282         respond(r, nil);
283 }
284
285 void
286 fsstart(Srv *)
287 {
288         Stream *s;
289
290         for(s=streams; s < streams+nelem(streams); s++){
291                 s->used = s->run = s->rd = 0;
292                 s->Rendez.l = &s->QLock;
293         }
294         proccreate(audioproc, nil, 16*1024);
295 }
296
297 void
298 fsend(Srv *)
299 {
300         threadexitsall(nil);
301 }
302
303 Srv fs = {
304         .open=          fsopen,
305         .read=          fsread,
306         .write=         fswrite,
307         .stat=          fsstat,
308         .destroyfid=    fsclunk,
309         .start=         fsstart,
310         .end=           fsend,
311 };
312
313 void
314 usage(void)
315 {
316         fprint(2, "usage: %s [-D] [-s srvname] [-m mtpt]\n", argv0);
317         exits("usage");
318 }
319
320 void
321 threadmain(int argc, char **argv)
322 {
323         char *srv = nil;
324         char *mtpt = "/mnt/mix";
325
326         ARGBEGIN{
327         case 'D':
328                 chatty9p++;
329                 break;
330         case 's':
331                 srv = EARGF(usage());
332                 break;
333         case 'm':
334                 mtpt = EARGF(usage());
335                 break;
336         default:
337                 usage();
338         }ARGEND;
339
340         if(argc)
341                 usage();
342
343         fs.tree = alloctree(nil, nil, DMDIR|0777, nil);
344         createfile(fs.tree->root, "audio", nil, 0666, nil);
345         threadpostmountsrv(&fs, srv, mtpt, MREPL);
346
347         mtpt = smprint("%s/audio", mtpt);
348         if(bind(mtpt, "/dev/audio", MREPL) < 0)
349                 sysfatal("bind: %r");
350         free(mtpt);
351
352         threadexits(0);
353 }