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