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