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 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 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 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 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 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 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 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 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 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 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