1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include "usb.h"
5 #include "audio.h"
6 #include "audioctl.h"
7
8 int endpt[2] = {-1, -1};
9 int interface[2] = {-1, -1};
10 int featureid[2] = {-1, -1};
11 int selectorid[2] = {-1, -1};
12 int mixerid[2] = {-1, -1};
13 int curalt[2] = {-1, -1};
14 int buttonendpt = -1;
15
16 int id;
17 Dev *ad;
18
19 Audiocontrol controls[2][Ncontrol] = {
20 {
21 [Speed_control] = { "speed", 0, {0}, 0, 44100, Undef},
22 [Mute_control] = { "mute", 0, {0}, 0, 0, Undef},
23 [Volume_control] = { "volume", 0, {0}, 0, 0, Undef},
24 [Bass_control] = { "bass", 0, {0}, 0, 0, Undef},
25 [Mid_control] = { "mid", 0, {0}, 0, 0, Undef},
26 [Treble_control] = { "treble", 0, {0}, 0, 0, Undef},
27 [Equalizer_control] = { "equalizer", 0, {0}, 0, 0, Undef},
28 [Agc_control] = { "agc", 0, {0}, 0, 0, Undef},
29 [Delay_control] = { "delay", 0, {0}, 0, 0, Undef},
30 [Bassboost_control] = { "bassboost", 0, {0}, 0, 0, Undef},
31 [Loudness_control] = { "loudness", 0, {0}, 0, 0, Undef},
32 [Channel_control] = { "channels", 0, {0}, 0, 2, Undef},
33 [Resolution_control] = { "resolution", 0, {0}, 0, 16, Undef},
34 // [Selector_control] = { "selector", 0, {0}, 0, 0, Undef},
35 }, {
36 [Speed_control] = { "speed", 0, {0}, 0, 44100, Undef},
37 [Mute_control] = { "mute", 0, {0}, 0, 0, Undef},
38 [Volume_control] = { "volume", 0, {0}, 0, 0, Undef},
39 [Bass_control] = { "bass", 0, {0}, 0, 0, Undef},
40 [Mid_control] = { "mid", 0, {0}, 0, 0, Undef},
41 [Treble_control] = { "treble", 0, {0}, 0, 0, Undef},
42 [Equalizer_control] = { "equalizer", 0, {0}, 0, 0, Undef},
43 [Agc_control] = { "agc", 0, {0}, 0, 0, Undef},
44 [Delay_control] = { "delay", 0, {0}, 0, 0, Undef},
45 [Bassboost_control] = { "bassboost", 0, {0}, 0, 0, Undef},
46 [Loudness_control] = { "loudness", 0, {0}, 0, 0, Undef},
47 [Channel_control] = { "channels", 0, {0}, 0, 2, Undef},
48 [Resolution_control] = { "resolution", 0, {0}, 0, 16, Undef},
49 // [Selector_control] = { "selector", 0, {0}, 0, 0, Undef},
50 }
51 };
52
53 int
setaudioalt(int rec,Audiocontrol * c,int control)54 setaudioalt(int rec, Audiocontrol *c, int control)
55 {
56 dprint(2, "setcontrol %s: Set alt %d\n", c->name, control);
57 curalt[rec] = control;
58 if(usbcmd(ad, Rh2d|Rstd|Riface, Rsetiface, control, interface[rec], nil, 0) < 0){
59 dprint(2, "setcontrol: setupcmd %s failed\n", c->name);
60 return -1;
61 }
62 return control;
63 }
64
65 int
findalt(int rec,int nchan,int res,int speed)66 findalt(int rec, int nchan, int res, int speed)
67 {
68 Ep *ep;
69 Audioalt *a;
70 Altc *da;
71 int i, j, k, retval;
72
73 retval = -1;
74 controls[rec][Channel_control].min = 1000000;
75 controls[rec][Channel_control].max = 0;
76 controls[rec][Channel_control].step = Undef;
77 controls[rec][Resolution_control].min = 1000000;
78 controls[rec][Resolution_control].max = 0;
79 controls[rec][Resolution_control].step = Undef;
80 for(i = 0; i < nelem(ad->usb->ep); i++){
81 if((ep = ad->usb->ep[i]) == nil)
82 continue;
83 if(ep->iface == nil){
84 fprint(2, "\tno interface\n");
85 return 0;
86 }
87 if(ep->iface->csp != CSP(Claudio, 2, 0))
88 continue;
89 if((rec == Play && (ep->addr & 0x80))
90 || (rec == Record && (ep->addr & 0x80) == 0))
91 continue;
92 for(j = 0; j < 16; j++){
93 if((da = ep->iface->altc[j]) == nil || (a = da->aux) == nil)
94 continue;
95 if(a->nchan < controls[rec][Channel_control].min)
96 controls[rec][Channel_control].min = a->nchan;
97 if(a->nchan > controls[rec][Channel_control].max)
98 controls[rec][Channel_control].max = a->nchan;
99 if(a->res < controls[rec][Resolution_control].min)
100 controls[rec][Resolution_control].min = a->res;
101 if(a->res > controls[rec][Resolution_control].max)
102 controls[rec][Resolution_control].max = a->res;
103 controls[rec][Channel_control].settable = 1;
104 controls[rec][Channel_control].readable = 1;
105 controls[rec][Resolution_control].settable = 1;
106 controls[rec][Resolution_control].readable = 1;
107 controls[rec][Speed_control].settable = 1;
108 controls[rec][Speed_control].readable = 1;
109 if(a->nchan == nchan && a->res == res){
110 if(speed == Undef)
111 retval = j;
112 else if(a->caps & (has_discfreq|onefreq)){
113 for(k = 0; k < nelem(a->freqs); k++){
114 if(a->freqs[k] == speed){
115 retval = j;
116 break;
117 }
118 }
119 }else{
120 if(speed >= a->minfreq && speed <= a->maxfreq)
121 retval = j;
122 }
123 }
124 }
125 }
126 if(usbdebug && retval < 0)
127 fprint(2, "findalt(%d, %d, %d, %d) failed\n", rec, nchan, res, speed);
128 return retval;
129 }
130
131 int
setspeed(int rec,int speed)132 setspeed(int rec, int speed)
133 {
134 int ps, n, no, dist, i;
135 Audioalt *a;
136 Altc *da;
137 Ep *ep;
138 uchar buf[3];
139
140 if(rec == Record && !setrec)
141 return Undef;
142 if(curalt[rec] < 0){
143 fprint(2, "Must set channels and resolution before speed\n");
144 return Undef;
145 }
146 if(endpt[rec] < 0)
147 sysfatal("endpt[%s] not set", rec?"Record":"Playback");
148 ep = ad->usb->ep[endpt[rec]];
149 if(ep->iface == nil)
150 sysfatal("no interface");
151 if(curalt[rec] < 0)
152 sysfatal("curalt[%s] not set", rec?"Record":"Playback");
153 da = ep->iface->altc[curalt[rec]];
154 a = da->aux;
155 if(a->caps & onefreq){
156 dprint(2, "setspeed %d: onefreq\n", speed);
157 /* speed not settable, but packet size must still be set */
158 speed = a->freqs[0];
159 }else if(a->caps & has_contfreq){
160 dprint(2, "setspeed %d: contfreq\n", speed);
161 if(speed < a->minfreq)
162 speed = a->minfreq;
163 else if(speed > a->maxfreq)
164 speed = a->maxfreq;
165 dprint(2, "Setting continuously variable %s speed to %d\n",
166 rec?"record":"playback", speed);
167 }else if(a->caps & has_discfreq){
168 dprint(2, "setspeed %d: discfreq\n", speed);
169 dist = 1000000;
170 no = -1;
171 for(i = 0; a->freqs[i] > 0; i++)
172 if(abs(a->freqs[i] - speed) < dist){
173 dist = abs(a->freqs[i] - speed);
174 no = i;
175 }
176 if(no == -1){
177 dprint(2, "no = -1\n");
178 return Undef;
179 }
180 speed = a->freqs[no];
181 dprint(2, "Setting discreetly variable %s speed to %d\n",
182 rec?"record":"playback", speed);
183 }else{
184 dprint(2, "can't happen\n?");
185 return Undef;
186 }
187 if(a->caps & has_setspeed){
188 dprint(2, "Setting %s speed to %d Hz;", rec?"record":"playback", speed);
189 buf[0] = speed;
190 buf[1] = speed >> 8;
191 buf[2] = speed >> 16;
192 n = endpt[rec];
193 if(rec)
194 n |= 0x80;
195 if(usbcmd(ad, Rh2d|Rclass|Rep, Rsetcur, sampling_freq_control<<8, n, buf, 3) < 0){
196 fprint(2, "Error in setupcmd\n");
197 return Undef;
198 }
199 if((n=usbcmd(ad, Rd2h|Rclass|Rep, Rgetcur, sampling_freq_control<<8, n, buf, 3)) < 0){
200 fprint(2, "Error in setupreq\n");
201 return Undef;
202 }
203 if(n != 3)
204 fprint(2, "Error in setupreply: %d\n", n);
205 else{
206 n = buf[0] | buf[1] << 8 | buf[2] << 16;
207 if(buf[2] || n == 0){
208 dprint(2, "Speed out of bounds %d (0x%x)\n", n, n);
209 }else if(n != speed && ad->usb->vid == 0x077d &&
210 (ad->usb->did == 0x0223 || ad->usb->did == 0x07af)){
211 /* Griffin iMic responds incorrectly to sample rate inquiry */
212 dprint(2, " reported as %d (iMic bug?);", n);
213 }else
214 speed = n;
215 }
216 dprint(2, " speed now %d Hz;", speed);
217 }
218 ps = ((speed * da->interval + 999) / 1000)
219 * controls[rec][Channel_control].value[0]
220 * controls[rec][Resolution_control].value[0]/8;
221 if(ps > ep->maxpkt){
222 fprint(2, "%s: setspeed(rec %d, speed %d): packet size %d > "
223 "maximum packet size %d\n",
224 argv0, rec, speed, ps, ep->maxpkt);
225 return Undef;
226 }
227 dprint(2, "Configuring %s endpoint for %d Hz\n",
228 rec?"record":"playback", speed);
229 epdev[rec] = openep(ad, endpt[rec]);
230 if(epdev[rec] == nil)
231 sysfatal("openep rec %d: %r", rec);
232
233 devctl(epdev[rec], "pollival %d", da->interval);
234 devctl(epdev[rec], "samplesz %ld", controls[rec][Channel_control].value[0] *
235 controls[rec][Resolution_control].value[0]/8);
236 devctl(epdev[rec], "hz %d", speed);
237
238 /* NO: the client uses the endpoint file directly
239 if(opendevdata(epdev[rec], rec ? OREAD : OWRITE) < 0)
240 sysfatal("openep rec %d: %r", rec);
241 */
242 return speed;
243 }
244
245 long
getspeed(int rec,int which)246 getspeed(int rec, int which)
247 {
248 int i, n;
249 Audioalt *a;
250 Altc *da;
251 Ep *ep;
252 uchar buf[3];
253 int r;
254
255 if(curalt[rec] < 0){
256 fprint(2, "Must set channels and resolution before getspeed\n");
257 return Undef;
258 }
259 if(endpt[rec] < 0)
260 sysfatal("endpt[%s] not set", rec?"Record":"Playback");
261 dprint(2, "getspeed: endpt[%d] == %d\n", rec, endpt[rec]);
262 ep = ad->usb->ep[endpt[rec]];
263 if(ep->iface == nil)
264 sysfatal("no interface");
265 if(curalt[rec] < 0)
266 sysfatal("curalt[%s] not set", rec?"Record":"Playback");
267 da = ep->iface->altc[curalt[rec]];
268 a = da->aux;
269 if(a->caps & onefreq){
270 dprint(2, "getspeed: onefreq\n");
271 if(which == Rgetres)
272 return Undef;
273 return a->freqs[0]; /* speed not settable */
274 }
275 if(a->caps & has_setspeed){
276 dprint(2, "getspeed: has_setspeed, ask\n");
277 n = endpt[rec];
278 if(rec)
279 n |= 0x80;
280 r = Rd2h|Rclass|Rep;
281 if(usbcmd(ad,r,which,sampling_freq_control<<8, n, buf, 3) < 0)
282 return Undef;
283 if(n == 3){
284 if(buf[2]){
285 dprint(2, "Speed out of bounds\n");
286 if((a->caps & has_discfreq) && (buf[0] | buf[1] << 8) < 8)
287 return a->freqs[buf[0] | buf[1] << 8];
288 }
289 return buf[0] | buf[1] << 8 | buf[2] << 16;
290 }
291 dprint(2, "getspeed: n = %d\n", n);
292 }
293 if(a->caps & has_contfreq){
294 dprint(2, "getspeed: has_contfreq\n");
295 if(which == Rgetcur)
296 return controls[rec][Speed_control].value[0];
297 if(which == Rgetmin)
298 return a->minfreq;
299 if(which == Rgetmax)
300 return a->maxfreq;
301 if(which == Rgetres)
302 return 1;
303 }
304 if(a->caps & has_discfreq){
305 dprint(2, "getspeed: has_discfreq\n");
306 if(which == Rgetcur)
307 return controls[rec][Speed_control].value[0];
308 if(which == Rgetmin)
309 return a->freqs[0];
310 for(i = 0; i < 8 && a->freqs[i] > 0; i++)
311 ;
312 if(which == Rgetmax)
313 return a->freqs[i-1];
314 if(which == Rgetres)
315 return Undef;
316 }
317 dprint(2, "can't happen\n?");
318 return Undef;
319 }
320
321 int
setcontrol(int rec,char * name,long * value)322 setcontrol(int rec, char *name, long *value)
323 {
324 int i, ctl, m;
325 byte buf[3];
326 int type, req, control, index, count;
327 Audiocontrol *c;
328
329 c = nil;
330 for(ctl = 0; ctl < Ncontrol; ctl++){
331 c = &controls[rec][ctl];
332 if(strcmp(name, c->name) == 0)
333 break;
334 }
335 if(ctl == Ncontrol){
336 dprint(2, "setcontrol: control not found\n");
337 return -1;
338 }
339 if(c->settable == 0){
340 dprint(2, "setcontrol: control %d.%d not settable\n", rec, ctl);
341 if(c->chans){
342 for(i = 0; i < 8; i++)
343 if((c->chans & 1 << i) && c->value[i] != value[i])
344 return -1;
345 return 0;
346 }
347 if(c->value[0] != value[0])
348 return -1;
349 return 0;
350 }
351 if(c->chans){
352 value[0] = 0; // set to average
353 m = 0;
354 for(i = 1; i < 8; i++)
355 if(c->chans & 1 << i){
356 if(c->min != Undef && value[i] < c->min)
357 value[i] = c->min;
358 if(c->max != Undef && value[i] > c->max)
359 value[i] = c->max;
360 value[0] += value[i];
361 m++;
362 }else
363 value[i] = Undef;
364 if(m) value[0] /= m;
365 }else{
366 if(c->min != Undef && value[0] < c->min)
367 value[0] = c->min;
368 if(c->max != Undef && value[0] > c->max)
369 value[0] = c->max;
370 }
371 req = Rsetcur;
372 count = 1;
373 switch(ctl){
374 default:
375 dprint(2, "setcontrol: can't happen\n");
376 return -1;
377 case Speed_control:
378 if((rec != Record || setrec) && (value[0] = setspeed(rec, value[0])) < 0)
379 return -1;
380 c->value[0] = value[0];
381 return 0;
382 case Equalizer_control:
383 /* not implemented */
384 return -1;
385 case Resolution_control:
386 control = findalt(rec, controls[rec][Channel_control].value[0], value[0], defaultspeed[rec]);
387 if(control < 0 || setaudioalt(rec, c, control) < 0){
388 dprint(2, "setcontrol: can't find setting for %s\n", c->name);
389 return -1;
390 }
391 c->value[0] = value[0];
392 controls[rec][Speed_control].value[0] = defaultspeed[rec];
393 return 0;
394 case Volume_control:
395 case Delay_control:
396 count = 2;
397 /* fall through */
398 case Mute_control:
399 case Bass_control:
400 case Mid_control:
401 case Treble_control:
402 case Agc_control:
403 case Bassboost_control:
404 case Loudness_control:
405 type = Rh2d|Rclass|Riface;
406 control = ctl<<8;
407 index = featureid[rec]<<8;
408 break;
409 case Selector_control:
410 type = Rh2d|Rclass|Riface;
411 control = 0;
412 index = selectorid[rec]<<8;
413 break;
414 case Channel_control:
415 control = findalt(rec, value[0], controls[rec][Resolution_control].value[0], defaultspeed[rec]);
416 if(control < 0 || setaudioalt(rec, c, control) < 0){
417 dprint(2, "setcontrol: can't find setting for %s\n", c->name);
418 return -1;
419 }
420 c->value[0] = value[0];
421 controls[rec][Speed_control].value[0] = defaultspeed[rec];
422 return 0;
423 }
424 if(c->chans){
425 for(i = 1; i < 8; i++)
426 if(c->chans & 1 << i){
427 switch(count){
428 case 2:
429 buf[1] = value[i] >> 8;
430 case 1:
431 buf[0] = value[i];
432 }
433 if(usbcmd(ad, type, req, control | i, index, buf, count) < 0){
434 dprint(2, "setcontrol: setupcmd %s failed\n",
435 controls[rec][ctl].name);
436 return -1;
437 }
438 c->value[i] = value[i];
439 }
440 }else{
441 switch(count){
442 case 2:
443 buf[1] = value[0] >> 8;
444 case 1:
445 buf[0] = value[0];
446 }
447 if(usbcmd(ad, type, req, control, index, buf, count) < 0){
448 dprint(2, "setcontrol: setupcmd %s failed\n", c->name);
449 return -1;
450 }
451 }
452 c->value[0] = value[0];
453 return 0;
454 }
455
456 int
getspecialcontrol(int rec,int ctl,int req,long * value)457 getspecialcontrol(int rec, int ctl, int req, long *value)
458 {
459 byte buf[3];
460 int m, n, i;
461 int type, control, index, count, signedbyte;
462 short svalue;
463
464 count = 1;
465 signedbyte = 0;
466 switch(ctl){
467 default:
468 return Undef;
469 case Speed_control:
470 value[0] = getspeed(rec, req);
471 return 0;
472 case Channel_control:
473 case Resolution_control:
474 if(req == Rgetmin)
475 value[0] = controls[rec][ctl].min;
476 if(req == Rgetmax)
477 value[0] = controls[rec][ctl].max;
478 if(req == Rgetres)
479 value[0] = controls[rec][ctl].step;
480 if(req == Rgetcur)
481 value[0] = controls[rec][ctl].value[0];
482 return 0;
483 case Volume_control:
484 case Delay_control:
485 count = 2;
486 /* fall through */
487 case Bass_control:
488 case Mid_control:
489 case Treble_control:
490 case Equalizer_control:
491 signedbyte = 1;
492 type = Rd2h|Rclass|Riface;
493 control = ctl<<8;
494 index = featureid[rec]<<8;
495 break;
496 case Selector_control:
497 type = Rd2h|Rclass|Riface;
498 control = 0;
499 index = selectorid[rec]<<8;
500 break;
501 case Mute_control:
502 case Agc_control:
503 case Bassboost_control:
504 case Loudness_control:
505 if(req != Rgetcur)
506 return Undef;
507 type = Rd2h|Rclass|Riface;
508 control = ctl<<8;
509 index = featureid[rec]<<8;
510 break;
511 }
512 if(controls[rec][ctl].chans){
513 m = 0;
514 value[0] = 0; // set to average
515 for(i = 1; i < 8; i++){
516 value[i] = Undef;
517 if(controls[rec][ctl].chans & 1 << i){
518 n=usbcmd(ad, type,req, control|i,index,buf,count);
519 if(n < 0)
520 return Undef;
521 if(n != count)
522 return -1;
523 switch (count){
524 case 2:
525 svalue = buf[1] << 8 | buf[0];
526 if(req == Rgetcur){
527 value[i] = svalue;
528 value[0] += svalue;
529 m++;
530 }else
531 value[0] = svalue;
532 break;
533 case 1:
534 svalue = buf[0];
535 if(signedbyte && (svalue&0x80))
536 svalue |= 0xFF00;
537 if(req == Rgetcur){
538 value[i] = svalue;
539 value[0] += svalue;
540 m++;
541 }else
542 value[0] = svalue;
543 }
544 }
545 }
546 if(m) value[0] /= m;
547 return 0;
548 }
549 value[0] = Undef;
550 if(usbcmd(ad, type, req, control, index, buf, count) != count)
551 return -1;
552 switch (count){
553 case 2:
554 svalue = buf[1] << 8 | buf[0];
555 value[0] = svalue;
556 break;
557 case 1:
558 svalue = buf[0];
559 if(signedbyte && (svalue&0x80))
560 svalue |= 0xFF00;
561 value[0] = svalue;
562 }
563 return 0;
564 }
565
566 int
getcontrol(int rec,char * name,long * value)567 getcontrol(int rec, char *name, long *value)
568 {
569 int i;
570
571 for(i = 0; i < Ncontrol; i++){
572 if(strcmp(name, controls[rec][i].name) == 0)
573 break;
574 }
575 if(i == Ncontrol)
576 return -1;
577 if(controls[rec][i].readable == 0)
578 return -1;
579 if(getspecialcontrol(rec, i, Rgetcur, value) < 0)
580 return -1;
581 memmove(controls[rec][i].value, value, sizeof controls[rec][i].value);
582 return 0;
583 }
584
585 void
getcontrols(void)586 getcontrols(void)
587 {
588 int rec, ctl, i;
589 Audiocontrol *c;
590 long v[8];
591
592 for(rec = 0; rec < 2; rec++){
593 if(rec == Record && !setrec)
594 continue;
595 for(ctl = 0; ctl < Ncontrol; ctl++){
596 c = &controls[rec][ctl];
597 if(c->readable){
598 if(verbose)
599 fprint(2, "%s %s control",
600 rec?"Record":"Playback", controls[rec][ctl].name);
601 c->min = (getspecialcontrol(rec, ctl, Rgetmin, v) < 0) ? Undef : v[0];
602 if(verbose && c->min != Undef)
603 fprint(2, ", min %ld", c->min);
604 c->max = (getspecialcontrol(rec, ctl, Rgetmax, v) < 0) ? Undef : v[0];
605 if(verbose && c->max != Undef)
606 fprint(2, ", max %ld", c->max);
607 c->step = (getspecialcontrol(rec, ctl, Rgetres, v) < 0) ? Undef : v[0];
608 if(verbose && c->step != Undef)
609 fprint(2, ", step %ld", c->step);
610 if(getspecialcontrol(rec, ctl, Rgetcur, c->value) == 0){
611 if(verbose){
612 if(c->chans){
613 fprint(2, ", values");
614 for(i = 1; i < 8; i++)
615 if(c->chans & 1 << i)
616 fprint(2, "[%d] %ld ", i, c->value[i]);
617 }else
618 fprint(2, ", value %ld", c->value[0]);
619 }
620 }
621 if(verbose)
622 fprint(2, "\n");
623 }else{
624 c->min = Undef;
625 c->max = Undef;
626 c->step = Undef;
627 c->value[0] = Undef;
628 dprint(2, "%s %s control not settable\n",
629 rec?"Playback":"Record", controls[rec][ctl].name);
630 }
631 }
632 }
633 }
634
635 int
ctlparse(char * s,Audiocontrol * c,long * v)636 ctlparse(char *s, Audiocontrol *c, long *v)
637 {
638 int i, j, nf, m;
639 char *vals[9];
640 char *p;
641 long val;
642
643 nf = tokenize(s, vals, nelem(vals));
644 if(nf <= 0)
645 return -1;
646 if(c->chans){
647 j = 0;
648 m = 0;
649 SET(val);
650 v[0] = 0; // will compute average of v[i]
651 for(i = 1; i < 8; i++)
652 if(c->chans & 1 << i){
653 if(j < nf){
654 val = strtol(vals[j], &p, 0);
655 if(val == 0 && *p != '\0' && *p != '%')
656 return -1;
657 if(*p == '%' && c->min != Undef)
658 val = (val*c->max + (100-val)*c->min)/100;
659 j++;
660 }
661 v[i] = val;
662 v[0] += val;
663 m++;
664 }else
665 v[i] = Undef;
666 if(m) v[0] /= m;
667 }else{
668 val = strtol(vals[0], &p, 0);
669 if(*p == '%' && c->min != Undef)
670 val = (val*c->max + (100-val)*c->min)/100;
671 v[0] = val;
672 }
673 return 0;
674 }
675
676 int
Aconv(Fmt * fp)677 Aconv(Fmt *fp)
678 {
679 char str[256];
680 Audiocontrol *c;
681 int fst, i;
682 char *p;
683
684 c = va_arg(fp->args, Audiocontrol*);
685 p = str;
686 if(c->chans){
687 fst = 1;
688 for(i = 1; i < 8; i++)
689 if(c->chans & 1 << i){
690 p = seprint(p, str+sizeof str, "%s%ld", fst?"'":" ", c->value[i]);
691 fst = 0;
692 }
693 seprint(p, str+sizeof str, "'");
694 }else
695 seprint(p, str+sizeof str, "%ld", c->value[0]);
696 return fmtstrcpy(fp, str);
697 }
698