|
@@ -0,0 +1,660 @@
|
|
|
+#include <u.h>
|
|
|
+#include <libc.h>
|
|
|
+#include <thread.h>
|
|
|
+#include "usb.h"
|
|
|
+#include "usbaudio.h"
|
|
|
+#include "usbaudioctl.h"
|
|
|
+
|
|
|
+int endpt[2] = {-1, -1};
|
|
|
+int interface[2] = {-1, -1};
|
|
|
+int featureid[2] = {-1, -1};
|
|
|
+int selectorid[2] = {-1, -1};
|
|
|
+int curalt[2] = {-1, -1};
|
|
|
+int buttonendpt = -1;
|
|
|
+
|
|
|
+int id;
|
|
|
+Device *ad;
|
|
|
+
|
|
|
+Audiocontrol controls[2][Ncontrol] = {
|
|
|
+ {
|
|
|
+ [Speed_control] = { "speed", 0, {0}, 0, 44100, Undef},
|
|
|
+ [Mute_control] = { "mute", 0, {0}, 0, 0, Undef},
|
|
|
+ [Volume_control] = { "volume", 0, {0}, 0, 0, Undef},
|
|
|
+ [Bass_control] = { "bass", 0, {0}, 0, 0, Undef},
|
|
|
+ [Mid_control] = { "mid", 0, {0}, 0, 0, Undef},
|
|
|
+ [Treble_control] = { "treble", 0, {0}, 0, 0, Undef},
|
|
|
+ [Equalizer_control] = { "equalizer", 0, {0}, 0, 0, Undef},
|
|
|
+ [Agc_control] = { "agc", 0, {0}, 0, 0, Undef},
|
|
|
+ [Delay_control] = { "delay", 0, {0}, 0, 0, Undef},
|
|
|
+ [Bassboost_control] = { "bassboost", 0, {0}, 0, 0, Undef},
|
|
|
+ [Loudness_control] = { "loudness", 0, {0}, 0, 0, Undef},
|
|
|
+ [Channel_control] = { "channels", 0, {0}, 0, 2, Undef},
|
|
|
+ [Resolution_control] = { "resolution", 0, {0}, 0, 16, Undef},
|
|
|
+ }, {
|
|
|
+ [Speed_control] = { "speed", 0, {0}, 0, 44100, Undef},
|
|
|
+ [Mute_control] = { "mute", 0, {0}, 0, 0, Undef},
|
|
|
+ [Volume_control] = { "volume", 0, {0}, 0, 0, Undef},
|
|
|
+ [Bass_control] = { "bass", 0, {0}, 0, 0, Undef},
|
|
|
+ [Mid_control] = { "mid", 0, {0}, 0, 0, Undef},
|
|
|
+ [Treble_control] = { "treble", 0, {0}, 0, 0, Undef},
|
|
|
+ [Equalizer_control] = { "equalizer", 0, {0}, 0, 0, Undef},
|
|
|
+ [Agc_control] = { "agc", 0, {0}, 0, 0, Undef},
|
|
|
+ [Delay_control] = { "delay", 0, {0}, 0, 0, Undef},
|
|
|
+ [Bassboost_control] = { "bassboost", 0, {0}, 0, 0, Undef},
|
|
|
+ [Loudness_control] = { "loudness", 0, {0}, 0, 0, Undef},
|
|
|
+ [Channel_control] = { "channels", 0, {0}, 0, 2, Undef},
|
|
|
+ [Resolution_control] = { "resolution", 0, {0}, 0, 16, Undef},
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+int
|
|
|
+setaudioalt(int rec, Audiocontrol *c, int control)
|
|
|
+{
|
|
|
+ if (debug & Dbgcontrol)
|
|
|
+ fprint(2, "setcontrol %s: Set alt %d\n", c->name, control);
|
|
|
+ curalt[rec] = control;
|
|
|
+ if (setupcmd(ad->ep[0], RH2D|Rstandard|Rinterface, SET_INTERFACE, control, interface[rec], nil, 0) < 0){
|
|
|
+ if (debug & Dbgcontrol) fprint(2, "setcontrol: setupcmd %s failed\n", c->name);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ return control;
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+findalt(int rec, int nchan, int res, int speed)
|
|
|
+{
|
|
|
+ Endpt *ep;
|
|
|
+ Audioalt *a;
|
|
|
+ Dalt *da;
|
|
|
+ int i, j, k, retval;
|
|
|
+
|
|
|
+ retval = -1;
|
|
|
+ controls[rec][Channel_control].min = 1000000;
|
|
|
+ controls[rec][Channel_control].max = 0;
|
|
|
+ controls[rec][Channel_control].step = Undef;
|
|
|
+ controls[rec][Resolution_control].min = 1000000;
|
|
|
+ controls[rec][Resolution_control].max = 0;
|
|
|
+ controls[rec][Resolution_control].step = Undef;
|
|
|
+ for (i = 0; i < Nendpt; i++) {
|
|
|
+ if ((ep = ad->ep[i]) == nil)
|
|
|
+ continue;
|
|
|
+ if(ep->csp != CSP(CL_AUDIO, 2, 0))
|
|
|
+ continue;
|
|
|
+ if (ep->iface == nil) {
|
|
|
+ fprint(2, "\tno interface\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ if ((rec == Play && (ep->iface->addr & 0x80))
|
|
|
+ || (rec == Record && (ep->iface->addr & 0x80) == 0))
|
|
|
+ continue;
|
|
|
+ for (j = 0; j < 16; j++) {
|
|
|
+ if ((da = ep->iface->dalt[j]) == nil || (a = da->devspec) == nil)
|
|
|
+ continue;
|
|
|
+ if (a->nchan < controls[rec][Channel_control].min)
|
|
|
+ controls[rec][Channel_control].min = a->nchan;
|
|
|
+ if (a->nchan > controls[rec][Channel_control].max)
|
|
|
+ controls[rec][Channel_control].max = a->nchan;
|
|
|
+ if (a->res < controls[rec][Resolution_control].min)
|
|
|
+ controls[rec][Resolution_control].min = a->res;
|
|
|
+ if (a->res > controls[rec][Resolution_control].max)
|
|
|
+ controls[rec][Resolution_control].max = a->res;
|
|
|
+ controls[rec][Channel_control].settable = 1;
|
|
|
+ controls[rec][Channel_control].readable = 1;
|
|
|
+ controls[rec][Resolution_control].settable = 1;
|
|
|
+ controls[rec][Resolution_control].readable = 1;
|
|
|
+ controls[rec][Speed_control].settable = 1;
|
|
|
+ controls[rec][Speed_control].readable = 1;
|
|
|
+ if (a->nchan == nchan && a->res == res){
|
|
|
+ if(speed == Undef)
|
|
|
+ retval = j;
|
|
|
+ else if(a->caps & (has_discfreq|onefreq)){
|
|
|
+ for(k = 0; k < nelem(a->freqs); k++){
|
|
|
+ if(a->freqs[k] == speed){
|
|
|
+ retval = j;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if(speed >= a->minfreq && speed <= a->maxfreq)
|
|
|
+ retval = j;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if ((debug & Dbgcontrol) && retval < 0){
|
|
|
+ fprint(2, "findalt(%d, %d, %d, %d) failed\n", rec, nchan, res, speed);
|
|
|
+ }
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+setspeed(int rec, int speed)
|
|
|
+{
|
|
|
+ int ps, n, no;
|
|
|
+ Audioalt *a;
|
|
|
+ Dalt *da;
|
|
|
+ Endpt *ep;
|
|
|
+ char cmdbuf[32];
|
|
|
+ uchar buf[3];
|
|
|
+
|
|
|
+ if (curalt[rec] < 0){
|
|
|
+ fprint(2, "Must set channels and resolution before speed\n");
|
|
|
+ return Undef;
|
|
|
+ }
|
|
|
+ if (endpt[rec] < 0)
|
|
|
+ sysfatal("endpt[%s] not set\n", rec?"Record":"Playback");
|
|
|
+ ep = ad->ep[endpt[rec]];
|
|
|
+ if (ep->iface == nil)
|
|
|
+ sysfatal("no interface");
|
|
|
+ if (curalt[rec] < 0)
|
|
|
+ sysfatal("curalt[%s] not set\n", rec?"Record":"Playback");
|
|
|
+ da = ep->iface->dalt[curalt[rec]];
|
|
|
+ a = da->devspec;
|
|
|
+ no = -1;
|
|
|
+ if (a->caps & onefreq){
|
|
|
+ speed = a->freqs[0]; /* speed not settable, but packet size must still be set */
|
|
|
+ }else if (a->caps & has_contfreq){
|
|
|
+ if (speed < a->minfreq)
|
|
|
+ speed = a->minfreq;
|
|
|
+ else if (speed > a->maxfreq)
|
|
|
+ speed = a->maxfreq;
|
|
|
+ if (debug & Dbgcontrol)
|
|
|
+ fprint(2, "Setting continuously variable %s speed to %d\n",
|
|
|
+ rec?"record":"playback", speed);
|
|
|
+ }else if (a->caps & has_discfreq){
|
|
|
+ int dist, i;
|
|
|
+ dist = 1000000;
|
|
|
+ no = -1;
|
|
|
+ for (i = 0; a->freqs[i] > 0; i++)
|
|
|
+ if (abs(a->freqs[i] - speed) < dist){
|
|
|
+ dist = abs(a->freqs[i] - speed);
|
|
|
+ no = i;
|
|
|
+ }
|
|
|
+ if (no == -1){
|
|
|
+ if (debug & Dbgcontrol)
|
|
|
+ fprint(2, "no = -1\n");
|
|
|
+ return Undef;
|
|
|
+ }
|
|
|
+ speed = a->freqs[no];
|
|
|
+ if (debug & Dbgcontrol)
|
|
|
+ fprint(2, "Setting discreetly variable %s speed to %d\n",
|
|
|
+ rec?"record":"playback", speed);
|
|
|
+ }else{
|
|
|
+ if (debug & Dbgcontrol)
|
|
|
+ fprint(2, "can't happen\n?");
|
|
|
+ return Undef;
|
|
|
+ }
|
|
|
+ if (a->caps & has_setspeed){
|
|
|
+ if (debug & Dbgcontrol)
|
|
|
+ fprint(2, "Setting %s speed to %d Hz;",
|
|
|
+ rec?"record":"playback", speed);
|
|
|
+ if (a->caps & has_discfreq){
|
|
|
+ buf[0] = no;
|
|
|
+ buf[1] = 0;
|
|
|
+ buf[2] = 0;
|
|
|
+ }else{
|
|
|
+ buf[0] = speed;
|
|
|
+ buf[1] = speed >> 8;
|
|
|
+ buf[2] = speed >> 16;
|
|
|
+ }
|
|
|
+ if (setupcmd(ad->ep[0], RH2D|Rclass|Rendpt, SET_CUR, sampling_freq_control<<8, endpt[rec], buf, 3) < 0)
|
|
|
+ return Undef;
|
|
|
+ if (setupreq(ad->ep[0], RD2H|Rclass|Rendpt, GET_CUR, sampling_freq_control<<8, endpt[rec], 3) < 0)
|
|
|
+ return Undef;
|
|
|
+ n = setupreply(ad->ep[0], buf, 3);
|
|
|
+ if (n != 3)
|
|
|
+ return Undef;
|
|
|
+ if (buf[2]){
|
|
|
+ if (debug & Dbgcontrol)
|
|
|
+ fprint(2, "Speed out of bounds %d (0x%x)\n",
|
|
|
+ buf[0] | buf[1] << 8 | buf[2] << 16,
|
|
|
+ buf[0] | buf[1] << 8 | buf[2] << 16);
|
|
|
+ }else
|
|
|
+ speed = buf[0] | buf[1] << 8 | buf[2] << 16;
|
|
|
+ if (debug & Dbgcontrol)
|
|
|
+ fprint(2, " speed now %d Hz;", speed);
|
|
|
+ }
|
|
|
+ ps = ((speed * da->interval + 999) / 1000)
|
|
|
+ * controls[rec][Channel_control].value[0]
|
|
|
+ * controls[rec][Resolution_control].value[0]/8;
|
|
|
+ if(ps > da->maxpkt){
|
|
|
+ fprint(2, "packet size %d > maximum packet size %d",
|
|
|
+ ps, da->maxpkt);
|
|
|
+ return Undef;
|
|
|
+ }
|
|
|
+ if (debug & Dbgcontrol)
|
|
|
+ fprint(2, "Configuring %s endpoint for %d Hz\n",
|
|
|
+ rec?"record":"playback", speed);
|
|
|
+ sprint(cmdbuf, "ep %d %d %c %ld %d", endpt[rec], da->interval, rec?'r':'w',
|
|
|
+ controls[rec][Channel_control].value[0]*controls[rec][Resolution_control].value[0]/8,
|
|
|
+ speed);
|
|
|
+ if (write(ad->ctl, cmdbuf, strlen(cmdbuf)) != strlen(cmdbuf)){
|
|
|
+ fprint(2, "writing %s to #U/usb/%d/ctl: %r\n", cmdbuf, id);
|
|
|
+ return Undef;
|
|
|
+ }
|
|
|
+ if (debug & Dbgcontrol) fprint(2, "sent `%s' to /dev/usb/%d/ctl\n", cmdbuf, id);
|
|
|
+ return speed;
|
|
|
+}
|
|
|
+
|
|
|
+long
|
|
|
+getspeed(int rec, int which)
|
|
|
+{
|
|
|
+ int i, n;
|
|
|
+ Audioalt *a;
|
|
|
+ Dalt *da;
|
|
|
+ Endpt *ep;
|
|
|
+ uchar buf[3];
|
|
|
+
|
|
|
+ if (curalt[rec] < 0){
|
|
|
+ fprint(2, "Must set channels and resolution before getspeed\n");
|
|
|
+ return Undef;
|
|
|
+ }
|
|
|
+ if (endpt[rec] < 0)
|
|
|
+ sysfatal("endpt[%s] not set\n", rec?"Record":"Playback");
|
|
|
+ ep = ad->ep[endpt[rec]];
|
|
|
+ if (ep->iface == nil)
|
|
|
+ sysfatal("no interface");
|
|
|
+ if (curalt[rec] < 0)
|
|
|
+ sysfatal("curalt[%s] not set\n", rec?"Record":"Playback");
|
|
|
+ da = ep->iface->dalt[curalt[rec]];
|
|
|
+ a = da->devspec;
|
|
|
+ if (a->caps & onefreq){
|
|
|
+ if (which == GET_RES)
|
|
|
+ return Undef;
|
|
|
+ return a->freqs[0]; /* speed not settable */
|
|
|
+ }else if (a->caps & has_setspeed){
|
|
|
+ if (setupreq(ad->ep[0], RD2H|Rclass|Rendpt, which, sampling_freq_control<<8, endpt[rec], 3) < 0)
|
|
|
+ return Undef;
|
|
|
+ n = setupreply(ad->ep[0], buf, 3);
|
|
|
+ if (n != 3)
|
|
|
+ return Undef;
|
|
|
+ if (buf[2]){
|
|
|
+ if (debug & Dbgcontrol)
|
|
|
+ fprint(2, "Speed out of bounds\n");
|
|
|
+ if ((a->caps & has_discfreq) && (buf[0] | buf[1] << 8) < 8)
|
|
|
+ return a->freqs[buf[0] | buf[1] << 8];
|
|
|
+ }
|
|
|
+ return buf[0] | buf[1] << 8 | buf[2] << 16;
|
|
|
+ }else if (a->caps & has_contfreq){
|
|
|
+ if (which == GET_CUR)
|
|
|
+ return controls[rec][Speed_control].value[0];
|
|
|
+ if (which == GET_MIN)
|
|
|
+ return a->minfreq;
|
|
|
+ if (which == GET_MAX)
|
|
|
+ return a->maxfreq;
|
|
|
+ if (which == GET_RES)
|
|
|
+ return 1;
|
|
|
+ }else if (a->caps & has_discfreq){
|
|
|
+ if (which == GET_CUR)
|
|
|
+ return controls[rec][Speed_control].value[0];
|
|
|
+ if (which == GET_MIN)
|
|
|
+ return a->freqs[0];
|
|
|
+ for (i = 0; i < 8 && a->freqs[i] > 0; i++)
|
|
|
+ ;
|
|
|
+ if (which == GET_MAX)
|
|
|
+ return a->freqs[i-1];
|
|
|
+ if (which == GET_RES)
|
|
|
+ return Undef;
|
|
|
+ }
|
|
|
+ if (debug & Dbgcontrol)
|
|
|
+ fprint(2, "can't happen\n?");
|
|
|
+ return Undef;
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+setcontrol(int rec, char *name, long *value)
|
|
|
+{
|
|
|
+ int i, ctl, m;
|
|
|
+ byte buf[3];
|
|
|
+ int type, req, control, index, count;
|
|
|
+ Audiocontrol *c;
|
|
|
+
|
|
|
+ c = nil;
|
|
|
+ for (ctl = 0; ctl < Ncontrol; ctl++){
|
|
|
+ c = &controls[rec][ctl];
|
|
|
+ if (strcmp(name, c->name) == 0)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (ctl == Ncontrol){
|
|
|
+ if (debug & Dbgcontrol) fprint(2, "setcontrol: control not found\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ if (c->settable == 0) {
|
|
|
+ if (debug & Dbgcontrol) fprint(2, "setcontrol: control %d.%d not settable\n", rec, ctl);
|
|
|
+ if (c->chans){
|
|
|
+ for (i = 0; i < 8; i++)
|
|
|
+ if ((c->chans & 1 << i) && c->value[i] != value[i])
|
|
|
+ return -1;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ if (c->value[0] != value[0])
|
|
|
+ return -1;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ if (c->chans){
|
|
|
+ value[0] = 0; // set to average
|
|
|
+ m = 0;
|
|
|
+ for (i = 1; i < 8; i++)
|
|
|
+ if (c->chans & 1 << i){
|
|
|
+ if (c->min != Undef && value[i] < c->min)
|
|
|
+ value[i] = c->min;
|
|
|
+ if (c->max != Undef && value[i] > c->max)
|
|
|
+ value[i] = c->max;
|
|
|
+ value[0] += value[i];
|
|
|
+ m++;
|
|
|
+ } else
|
|
|
+ value[i] = Undef;
|
|
|
+ if (m) value[0] /= m;
|
|
|
+ }else{
|
|
|
+ if (c->min != Undef && value[0] < c->min)
|
|
|
+ value[0] = c->min;
|
|
|
+ if (c->max != Undef && value[0] > c->max)
|
|
|
+ value[0] = c->max;
|
|
|
+ }
|
|
|
+ req = SET_CUR;
|
|
|
+ count = 1;
|
|
|
+ switch(ctl){
|
|
|
+ default:
|
|
|
+ if (debug & Dbgcontrol) fprint(2, "setcontrol: can't happen\n");
|
|
|
+ return -1;
|
|
|
+ case Speed_control:
|
|
|
+ if ((value[0] = setspeed(rec, value[0])) < 0)
|
|
|
+ return -1;
|
|
|
+ c->value[0] = value[0];
|
|
|
+ return 0;
|
|
|
+ case Equalizer_control:
|
|
|
+ /* not implemented */
|
|
|
+ return -1;
|
|
|
+ case Resolution_control:
|
|
|
+ control = findalt(rec, controls[rec][Channel_control].value[0], value[0], defaultspeed[rec]);
|
|
|
+ if(control < 0 || setaudioalt(rec, c, control) < 0){
|
|
|
+ if (debug & Dbgcontrol) fprint(2, "setcontrol: can't find setting for %s\n",
|
|
|
+ c->name);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ c->value[0] = value[0];
|
|
|
+ controls[rec][Speed_control].value[0] = defaultspeed[rec];
|
|
|
+ return 0;
|
|
|
+ case Volume_control:
|
|
|
+ case Delay_control:
|
|
|
+ count = 2;
|
|
|
+ /* fall through */
|
|
|
+ case Mute_control:
|
|
|
+ case Bass_control:
|
|
|
+ case Mid_control:
|
|
|
+ case Treble_control:
|
|
|
+ case Agc_control:
|
|
|
+ case Bassboost_control:
|
|
|
+ case Loudness_control:
|
|
|
+ type = RH2D|Rclass|Rinterface;
|
|
|
+ control = ctl<<8;
|
|
|
+ index = featureid[rec]<<8;
|
|
|
+ break;
|
|
|
+ case Channel_control:
|
|
|
+ control = findalt(rec, value[0], controls[rec][Resolution_control].value[0], defaultspeed[rec]);
|
|
|
+ if(control < 0 || setaudioalt(rec, c, control) < 0){
|
|
|
+ if (debug & Dbgcontrol) fprint(2, "setcontrol: can't find setting for %s\n",
|
|
|
+ c->name);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ c->value[0] = value[0];
|
|
|
+ controls[rec][Speed_control].value[0] = defaultspeed[rec];
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ if (c->chans){
|
|
|
+ for (i = 1; i < 8; i++)
|
|
|
+ if (c->chans & 1 << i){
|
|
|
+ switch(count){
|
|
|
+ case 2:
|
|
|
+ buf[1] = value[i] >> 8;
|
|
|
+ case 1:
|
|
|
+ buf[0] = value[i];
|
|
|
+ }
|
|
|
+ if (setupcmd(ad->ep[0], type, req, control | i, index, buf, count) < 0){
|
|
|
+ if (debug & Dbgcontrol) fprint(2, "setcontrol: setupcmd %s failed\n",
|
|
|
+ controls[rec][ctl].name);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ c->value[i] = value[i];
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ switch(count){
|
|
|
+ case 2:
|
|
|
+ buf[1] = value[0] >> 8;
|
|
|
+ case 1:
|
|
|
+ buf[0] = value[0];
|
|
|
+ }
|
|
|
+ if (setupcmd(ad->ep[0], type, req, control, index, buf, count) < 0){
|
|
|
+ if (debug & Dbgcontrol) fprint(2, "setcontrol: setupcmd %s failed\n",
|
|
|
+ c->name);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ c->value[0] = value[0];
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+getspecialcontrol(int rec, int ctl, int req, long *value)
|
|
|
+{
|
|
|
+ byte buf[3];
|
|
|
+ int m, n, i;
|
|
|
+ int type, control, index, count;
|
|
|
+ short svalue;
|
|
|
+
|
|
|
+ count = 1;
|
|
|
+ switch(ctl){
|
|
|
+ default:
|
|
|
+ return Undef;
|
|
|
+ case Speed_control:
|
|
|
+ value[0] = getspeed(rec, req);
|
|
|
+ return 0;
|
|
|
+ case Channel_control:
|
|
|
+ case Resolution_control:
|
|
|
+ if (req == GET_MIN)
|
|
|
+ value[0] = controls[rec][ctl].min;
|
|
|
+ if (req == GET_MAX)
|
|
|
+ value[0] = controls[rec][ctl].max;
|
|
|
+ if (req == GET_RES)
|
|
|
+ value[0] = controls[rec][ctl].step;
|
|
|
+ if (req == GET_CUR)
|
|
|
+ value[0] = controls[rec][ctl].value[0];
|
|
|
+ return 0;
|
|
|
+ case Volume_control:
|
|
|
+ case Delay_control:
|
|
|
+ count = 2;
|
|
|
+ /* fall through */
|
|
|
+ case Mute_control:
|
|
|
+ case Bass_control:
|
|
|
+ case Mid_control:
|
|
|
+ case Treble_control:
|
|
|
+ case Equalizer_control:
|
|
|
+ case Agc_control:
|
|
|
+ case Bassboost_control:
|
|
|
+ case Loudness_control:
|
|
|
+ type = RD2H|Rclass|Rinterface;
|
|
|
+ control = ctl<<8;
|
|
|
+ index = featureid[rec]<<8;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (controls[rec][ctl].chans){
|
|
|
+ m = 0;
|
|
|
+ value[0] = 0; // set to average
|
|
|
+ for (i = 1; i < 8; i++){
|
|
|
+ value[i] = Undef;
|
|
|
+ if (controls[rec][ctl].chans & 1 << i){
|
|
|
+ if (setupreq(ad->ep[0], type, req, control | i, index, count) < 0)
|
|
|
+ return Undef;
|
|
|
+ n = setupreply(ad->ep[0], buf, count);
|
|
|
+ if (n != count)
|
|
|
+ return -1;
|
|
|
+ switch (count) {
|
|
|
+ case 2:
|
|
|
+ svalue = buf[1] << 8 | buf[0];
|
|
|
+ if (req == GET_CUR){
|
|
|
+ value[i] = svalue;
|
|
|
+ value[0] += svalue;
|
|
|
+ m++;
|
|
|
+ }else
|
|
|
+ value[0] = svalue;
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ if (req == GET_CUR){
|
|
|
+ value[i] = buf[0];
|
|
|
+ value[0] += buf[0];
|
|
|
+ m++;
|
|
|
+ }else
|
|
|
+ value[0] = buf[0];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (m) value[0] /= m;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ value[0] = Undef;
|
|
|
+ if (setupreq(ad->ep[0], type, req, control, index, count) < 0)
|
|
|
+ return -1;
|
|
|
+ n = setupreply(ad->ep[0], buf, count);
|
|
|
+ if (n != count)
|
|
|
+ return -1;
|
|
|
+ switch (count) {
|
|
|
+ case 2:
|
|
|
+ svalue = buf[1] << 8 | buf[0];
|
|
|
+ value[0] = svalue;
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ value[0] = buf[0];
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+getcontrol(int rec, char *name, long *value)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < Ncontrol; i++){
|
|
|
+ if (strcmp(name, controls[rec][i].name) == 0)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (i == Ncontrol)
|
|
|
+ return -1;
|
|
|
+ if (controls[rec][i].readable == 0)
|
|
|
+ return -1;
|
|
|
+ if(getspecialcontrol(rec, i, GET_CUR, value) < 0)
|
|
|
+ return -1;
|
|
|
+ memmove(controls[rec][i].value, value, sizeof controls[rec][i].value);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+getcontrols(void)
|
|
|
+{
|
|
|
+ int rec, ctl, i;
|
|
|
+ Audiocontrol *c;
|
|
|
+ long v[8];
|
|
|
+
|
|
|
+ for (rec = 0; rec < 2; rec++)
|
|
|
+ for (ctl = 0; ctl < Ncontrol; ctl++){
|
|
|
+ c = &controls[rec][ctl];
|
|
|
+ if (c->readable){
|
|
|
+ if (verbose)
|
|
|
+ fprint(2, "%s %s control",
|
|
|
+ rec?"Record":"Playback", controls[rec][ctl].name);
|
|
|
+ c->min = (getspecialcontrol(rec, ctl, GET_MIN, v) < 0) ? Undef : v[0];
|
|
|
+ if (verbose && c->min != Undef)
|
|
|
+ fprint(2, ", min %ld", c->min);
|
|
|
+ c->max = (getspecialcontrol(rec, ctl, GET_MAX, v) < 0) ? Undef : v[0];
|
|
|
+ if (verbose && c->max != Undef)
|
|
|
+ fprint(2, ", max %ld", c->max);
|
|
|
+ c->step = (getspecialcontrol(rec, ctl, GET_RES, v) < 0) ? Undef : v[0];
|
|
|
+ if (verbose && c->step != Undef)
|
|
|
+ fprint(2, ", step %ld", c->step);
|
|
|
+ if (getspecialcontrol(rec, ctl, GET_CUR, c->value) == 0){
|
|
|
+ if (verbose) {
|
|
|
+ if (c->chans){
|
|
|
+ fprint(2, ", values");
|
|
|
+ for (i = 1; i < 8; i++)
|
|
|
+ if (c->chans & 1 << i)
|
|
|
+ fprint(2, "[%d] %ld ", i, c->value[i]);
|
|
|
+ }else
|
|
|
+ fprint(2, ", value %ld", c->value[0]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (verbose)
|
|
|
+ fprint(2, "\n");
|
|
|
+ } else {
|
|
|
+ c->min = Undef;
|
|
|
+ c->max = Undef;
|
|
|
+ c->step = Undef;
|
|
|
+ c->value[0] = Undef;
|
|
|
+ if (debug & Dbgcontrol)
|
|
|
+ fprint(2, "%s %s control not settable\n",
|
|
|
+ rec?"Playback":"Record", controls[rec][ctl].name);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+ctlparse(char *s, Audiocontrol *c, long *v)
|
|
|
+{
|
|
|
+ int i, j, nf, m;
|
|
|
+ char *vals[9];
|
|
|
+ char *p;
|
|
|
+ long val;
|
|
|
+
|
|
|
+ nf = tokenize(s, vals, nelem(vals));
|
|
|
+ if (nf <= 0)
|
|
|
+ return -1;
|
|
|
+ if (c->chans){
|
|
|
+ j = 0;
|
|
|
+ m = 0;
|
|
|
+ SET(val);
|
|
|
+ v[0] = 0; // will compute average of v[i]
|
|
|
+ for (i = 1; i < 8; i++)
|
|
|
+ if (c->chans & 1 << i) {
|
|
|
+ if (j < nf){
|
|
|
+ val = strtol(vals[j], &p, 0);
|
|
|
+ if (val == 0 && *p != '\0' && *p != '%')
|
|
|
+ return -1;
|
|
|
+ if (*p == '%' && c->min != Undef)
|
|
|
+ val = (val*c->max + (100-val)*c->min)/100;
|
|
|
+ j++;
|
|
|
+ }
|
|
|
+ v[i] = val;
|
|
|
+ v[0] += val;
|
|
|
+ m++;
|
|
|
+ } else
|
|
|
+ v[i] = Undef;
|
|
|
+ if (m) v[0] /= m;
|
|
|
+ } else {
|
|
|
+ val = strtol(vals[0], &p, 0);
|
|
|
+ if (*p == '%' && c->min != Undef)
|
|
|
+ val = (val*c->max + (100-val)*c->min)/100;
|
|
|
+ v[0] = val;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+Aconv(Fmt *fp)
|
|
|
+{
|
|
|
+ char str[256];
|
|
|
+ Audiocontrol *c;
|
|
|
+ int fst, i;
|
|
|
+ char *p;
|
|
|
+
|
|
|
+ c = va_arg(fp->args, Audiocontrol*);
|
|
|
+ p = str;
|
|
|
+ if (c->chans) {
|
|
|
+ fst = 1;
|
|
|
+ for (i = 1; i < 8; i++)
|
|
|
+ if (c->chans & 1 << i){
|
|
|
+ p = seprint(p, str+sizeof str, "%s%ld", fst?"'":" ", c->value[i]);
|
|
|
+ fst = 0;
|
|
|
+ }
|
|
|
+ seprint(p, str+sizeof str, "'");
|
|
|
+ } else
|
|
|
+ seprint(p, str+sizeof str, "%ld", c->value[0]);
|
|
|
+ return fmtstrcpy(fp, str);
|
|
|
+}
|