1 /*
   2  * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #define USE_ERROR
  27 //#define USE_TRACE
  28 
  29 #include "Ports.h"
  30 #include "PLATFORM_API_SolarisOS_Utils.h"
  31 
  32 #if USE_PORTS == TRUE
  33 
  34 #define MONITOR_GAIN_STRING "Monitor Gain"
  35 
  36 #define ALL_TARGET_PORT_COUNT 6
  37 
  38 // define the following to not use audio_prinfo_t.mod_ports
  39 #define SOLARIS7_COMPATIBLE
  40 
  41 // Solaris audio defines
  42 static int targetPorts[ALL_TARGET_PORT_COUNT] = {
  43     AUDIO_SPEAKER,
  44     AUDIO_HEADPHONE,
  45     AUDIO_LINE_OUT,
  46     AUDIO_AUX1_OUT,
  47     AUDIO_AUX2_OUT,
  48     AUDIO_SPDIF_OUT
  49 };
  50 
  51 static char* targetPortNames[ALL_TARGET_PORT_COUNT] = {
  52     "Speaker",
  53     "Headphone",
  54     "Line Out",
  55     "AUX1 Out",
  56     "AUX2 Out",
  57     "SPDIF Out"
  58 };
  59 
  60 // defined in Ports.h
  61 static int targetPortJavaSoundMapping[ALL_TARGET_PORT_COUNT] = {
  62     PORT_DST_SPEAKER,
  63     PORT_DST_HEADPHONE,
  64     PORT_DST_LINE_OUT,
  65     PORT_DST_UNKNOWN,
  66     PORT_DST_UNKNOWN,
  67     PORT_DST_UNKNOWN,
  68 };
  69 
  70 #define ALL_SOURCE_PORT_COUNT 7
  71 
  72 // Solaris audio defines
  73 static int sourcePorts[ALL_SOURCE_PORT_COUNT] = {
  74     AUDIO_MICROPHONE,
  75     AUDIO_LINE_IN,
  76     AUDIO_CD,
  77     AUDIO_AUX1_IN,
  78     AUDIO_AUX2_IN,
  79     AUDIO_SPDIF_IN,
  80     AUDIO_CODEC_LOOPB_IN
  81 };
  82 
  83 static char* sourcePortNames[ALL_SOURCE_PORT_COUNT] = {
  84     "Microphone In",
  85     "Line In",
  86     "Compact Disc In",
  87     "AUX1 In",
  88     "AUX2 In",
  89     "SPDIF In",
  90     "Internal Loopback"
  91 };
  92 
  93 // Ports.h defines
  94 static int sourcePortJavaSoundMapping[ALL_SOURCE_PORT_COUNT] = {
  95     PORT_SRC_MICROPHONE,
  96     PORT_SRC_LINE_IN,
  97     PORT_SRC_COMPACT_DISC,
  98     PORT_SRC_UNKNOWN,
  99     PORT_SRC_UNKNOWN,
 100     PORT_SRC_UNKNOWN,
 101     PORT_SRC_UNKNOWN
 102 };
 103 
 104 struct tag_PortControlID;
 105 
 106 typedef struct tag_PortInfo {
 107     int fd;                    // file descriptor of the pseudo device
 108     audio_info_t audioInfo;
 109     // ports
 110     int targetPortCount;
 111     int sourcePortCount;
 112     // indexes to sourcePorts/targetPorts
 113     // contains first target ports, then source ports
 114     int ports[ALL_TARGET_PORT_COUNT + ALL_SOURCE_PORT_COUNT];
 115     // controls
 116     int maxControlCount;       // upper bound of number of controls
 117     int usedControlIDs;        // number of items already filled in controlIDs
 118     struct tag_PortControlID* controlIDs; // the control IDs themselves
 119 } PortInfo;
 120 
 121 #define PORT_CONTROL_TYPE_PLAY          0x4000000
 122 #define PORT_CONTROL_TYPE_RECORD        0x8000000
 123 #define PORT_CONTROL_TYPE_SELECT_PORT   1
 124 #define PORT_CONTROL_TYPE_GAIN          2
 125 #define PORT_CONTROL_TYPE_BALANCE       3
 126 #define PORT_CONTROL_TYPE_MONITOR_GAIN  10
 127 #define PORT_CONTROL_TYPE_OUTPUT_MUTED  11
 128 #define PORT_CONTROL_TYPE_PLAYRECORD_MASK PORT_CONTROL_TYPE_PLAY | PORT_CONTROL_TYPE_RECORD
 129 #define PORT_CONTROL_TYPE_MASK 0xFFFFFF
 130 
 131 
 132 typedef struct tag_PortControlID {
 133     PortInfo*  portInfo;
 134     INT32                 controlType;  // PORT_CONTROL_TYPE_XX
 135     uint_t                port;
 136 } PortControlID;
 137 
 138 
 139 ///// implemented functions of Ports.h
 140 
 141 INT32 PORT_GetPortMixerCount() {
 142     return (INT32) getAudioDeviceCount();
 143 }
 144 
 145 
 146 INT32 PORT_GetPortMixerDescription(INT32 mixerIndex, PortMixerDescription* description) {
 147     AudioDeviceDescription desc;
 148 
 149     if (getAudioDeviceDescriptionByIndex(mixerIndex, &desc, TRUE)) {
 150         strncpy(description->name, desc.name, PORT_STRING_LENGTH-1);
 151         description->name[PORT_STRING_LENGTH-1] = 0;
 152         strncpy(description->vendor, desc.vendor, PORT_STRING_LENGTH-1);
 153         description->vendor[PORT_STRING_LENGTH-1] = 0;
 154         strncpy(description->version, desc.version, PORT_STRING_LENGTH-1);
 155         description->version[PORT_STRING_LENGTH-1] = 0;
 156         /*strncpy(description->description, desc.description, PORT_STRING_LENGTH-1);*/
 157         strncpy(description->description, "Solaris Ports", PORT_STRING_LENGTH-1);
 158         description->description[PORT_STRING_LENGTH-1] = 0;
 159         return TRUE;
 160     }
 161     return FALSE;
 162 }
 163 
 164 
 165 void* PORT_Open(INT32 mixerIndex) {
 166     PortInfo* info = NULL;
 167     int fd = -1;
 168     AudioDeviceDescription desc;
 169     int success = FALSE;
 170 
 171     TRACE0("PORT_Open\n");
 172     if (getAudioDeviceDescriptionByIndex(mixerIndex, &desc, FALSE)) {
 173         fd = open(desc.pathctl, O_RDWR);
 174     }
 175     if (fd < 0) {
 176         ERROR1("Couldn't open audio device ctl for device %d!\n", mixerIndex);
 177         return NULL;
 178     }
 179 
 180     info = (PortInfo*) malloc(sizeof(PortInfo));
 181     if (info != NULL) {
 182         memset(info, 0, sizeof(PortInfo));
 183         info->fd = fd;
 184         success = TRUE;
 185     }
 186     if (!success) {
 187         if (fd >= 0) {
 188             close(fd);
 189         }
 190         PORT_Close((void*) info);
 191         info = NULL;
 192     }
 193     return info;
 194 }
 195 
 196 void PORT_Close(void* id) {
 197     TRACE0("PORT_Close\n");
 198     if (id != NULL) {
 199         PortInfo* info = (PortInfo*) id;
 200         if (info->fd >= 0) {
 201             close(info->fd);
 202             info->fd = -1;
 203         }
 204         if (info->controlIDs) {
 205             free(info->controlIDs);
 206             info->controlIDs = NULL;
 207         }
 208         free(info);
 209     }
 210 }
 211 
 212 
 213 
 214 INT32 PORT_GetPortCount(void* id) {
 215     int ret = 0;
 216     PortInfo* info = (PortInfo*) id;
 217     if (info != NULL) {
 218         if (!info->targetPortCount && !info->sourcePortCount) {
 219             int i;
 220             AUDIO_INITINFO(&info->audioInfo);
 221             if (ioctl(info->fd, AUDIO_GETINFO, &info->audioInfo) >= 0) {
 222                 for (i = 0; i < ALL_TARGET_PORT_COUNT; i++) {
 223                     if (info->audioInfo.play.avail_ports & targetPorts[i]) {
 224                         info->ports[info->targetPortCount] = i;
 225                         info->targetPortCount++;
 226                     }
 227 #ifdef SOLARIS7_COMPATIBLE
 228                     TRACE3("Target %d %s: avail=%d\n", i, targetPortNames[i],
 229                            info->audioInfo.play.avail_ports & targetPorts[i]);
 230 #else
 231                     TRACE4("Target %d %s: avail=%d  mod=%d\n", i, targetPortNames[i],
 232                            info->audioInfo.play.avail_ports & targetPorts[i],
 233                            info->audioInfo.play.mod_ports & targetPorts[i]);
 234 #endif
 235                 }
 236                 for (i = 0; i < ALL_SOURCE_PORT_COUNT; i++) {
 237                     if (info->audioInfo.record.avail_ports & sourcePorts[i]) {
 238                         info->ports[info->targetPortCount + info->sourcePortCount] = i;
 239                         info->sourcePortCount++;
 240                     }
 241 #ifdef SOLARIS7_COMPATIBLE
 242                     TRACE3("Source %d %s: avail=%d\n", i, sourcePortNames[i],
 243                            info->audioInfo.record.avail_ports & sourcePorts[i]);
 244 #else
 245                     TRACE4("Source %d %s: avail=%d  mod=%d\n", i, sourcePortNames[i],
 246                            info->audioInfo.record.avail_ports & sourcePorts[i],
 247                            info->audioInfo.record.mod_ports & sourcePorts[i]);
 248 #endif
 249                 }
 250             }
 251         }
 252         ret = info->targetPortCount + info->sourcePortCount;
 253     }
 254     return ret;
 255 }
 256 
 257 int isSourcePort(PortInfo* info, INT32 portIndex) {
 258     return (portIndex >= info->targetPortCount);
 259 }
 260 
 261 INT32 PORT_GetPortType(void* id, INT32 portIndex) {
 262     PortInfo* info = (PortInfo*) id;
 263     if ((portIndex >= 0) && (portIndex < PORT_GetPortCount(id))) {
 264         if (isSourcePort(info, portIndex)) {
 265             return sourcePortJavaSoundMapping[info->ports[portIndex]];
 266         } else {
 267             return targetPortJavaSoundMapping[info->ports[portIndex]];
 268         }
 269     }
 270     return 0;
 271 }
 272 
 273 // pre-condition: portIndex must have been verified!
 274 char* getPortName(PortInfo* info, INT32 portIndex) {
 275     char* ret = NULL;
 276 
 277     if (isSourcePort(info, portIndex)) {
 278         ret = sourcePortNames[info->ports[portIndex]];
 279     } else {
 280         ret = targetPortNames[info->ports[portIndex]];
 281     }
 282     return ret;
 283 }
 284 
 285 INT32 PORT_GetPortName(void* id, INT32 portIndex, char* name, INT32 len) {
 286     PortInfo* info = (PortInfo*) id;
 287     char* n;
 288 
 289     if ((portIndex >= 0) && (portIndex < PORT_GetPortCount(id))) {
 290         n = getPortName(info, portIndex);
 291         if (n) {
 292             strncpy(name, n, len-1);
 293             name[len-1] = 0;
 294             return TRUE;
 295         }
 296     }
 297     return FALSE;
 298 }
 299 
 300 void createPortControl(PortInfo* info, PortControlCreator* creator, INT32 portIndex,
 301                        INT32 type, void** controlObjects, int* controlCount) {
 302     PortControlID* controlID;
 303     void* newControl = NULL;
 304     int controlIndex;
 305     char* jsType = NULL;
 306     int isBoolean = FALSE;
 307 
 308     TRACE0(">createPortControl\n");
 309 
 310     // fill the ControlID structure and add this control
 311     if (info->usedControlIDs >= info->maxControlCount) {
 312         ERROR1("not enough free controlIDs !! maxControlIDs = %d\n", info->maxControlCount);
 313         return;
 314     }
 315     controlID = &(info->controlIDs[info->usedControlIDs]);
 316     controlID->portInfo = info;
 317     controlID->controlType = type;
 318     controlIndex = info->ports[portIndex];
 319     if (isSourcePort(info, portIndex)) {
 320         controlID->port = sourcePorts[controlIndex];
 321     } else {
 322         controlID->port = targetPorts[controlIndex];
 323     }
 324     switch (type & PORT_CONTROL_TYPE_MASK) {
 325     case PORT_CONTROL_TYPE_SELECT_PORT:
 326         jsType = CONTROL_TYPE_SELECT; isBoolean = TRUE; break;
 327     case PORT_CONTROL_TYPE_GAIN:
 328         jsType = CONTROL_TYPE_VOLUME;  break;
 329     case PORT_CONTROL_TYPE_BALANCE:
 330         jsType = CONTROL_TYPE_BALANCE; break;
 331     case PORT_CONTROL_TYPE_MONITOR_GAIN:
 332         jsType = CONTROL_TYPE_VOLUME; break;
 333     case PORT_CONTROL_TYPE_OUTPUT_MUTED:
 334         jsType = CONTROL_TYPE_MUTE; isBoolean = TRUE; break;
 335     }
 336     if (isBoolean) {
 337         TRACE0(" PORT_CONTROL_TYPE_BOOLEAN\n");
 338         newControl = (creator->newBooleanControl)(creator, controlID, jsType);
 339     }
 340     else if (jsType == CONTROL_TYPE_BALANCE) {
 341         TRACE0(" PORT_CONTROL_TYPE_BALANCE\n");
 342         newControl = (creator->newFloatControl)(creator, controlID, jsType,
 343                                                 -1.0f, 1.0f, 2.0f / 65.0f, "");
 344     } else {
 345         TRACE0(" PORT_CONTROL_TYPE_FLOAT\n");
 346         newControl = (creator->newFloatControl)(creator, controlID, jsType,
 347                                                 0.0f, 1.0f, 1.0f / 256.0f, "");
 348     }
 349     if (newControl) {
 350         controlObjects[*controlCount] = newControl;
 351         (*controlCount)++;
 352         info->usedControlIDs++;
 353     }
 354     TRACE0("<createPortControl\n");
 355 }
 356 
 357 
 358 void addCompoundControl(PortInfo* info, PortControlCreator* creator, char* name, void** controlObjects, int* controlCount) {
 359     void* compControl;
 360 
 361     TRACE1(">addCompoundControl %d controls\n", *controlCount);
 362     if (*controlCount) {
 363         // create compound control and add it to the vector
 364         compControl = (creator->newCompoundControl)(creator, name, controlObjects, *controlCount);
 365         if (compControl) {
 366             TRACE1(" addCompoundControl: calling addControl %p\n", compControl);
 367             (creator->addControl)(creator, compControl);
 368         }
 369         *controlCount = 0;
 370     }
 371     TRACE0("<addCompoundControl\n");
 372 }
 373 
 374 void addAllControls(PortInfo* info, PortControlCreator* creator, void** controlObjects, int* controlCount) {
 375     int i = 0;
 376 
 377     TRACE0(">addAllControl\n");
 378     // go through all controls and add them to the vector
 379     for (i = 0; i < *controlCount; i++) {
 380         (creator->addControl)(creator, controlObjects[i]);
 381     }
 382     *controlCount = 0;
 383     TRACE0("<addAllControl\n");
 384 }
 385 
 386 void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) {
 387     PortInfo* info = (PortInfo*) id;
 388     int portCount = PORT_GetPortCount(id);
 389     void* controls[4];
 390     int controlCount = 0;
 391     INT32 type;
 392     int selectable = 1;
 393     memset(controls, 0, sizeof(controls));
 394 
 395     TRACE4(">PORT_GetControls(id=%p, portIndex=%d). controlIDs=%p, maxControlCount=%d\n",
 396            id, portIndex, info->controlIDs, info->maxControlCount);
 397     if ((portIndex >= 0) && (portIndex < portCount)) {
 398         // if the memory isn't reserved for the control structures, allocate it
 399         if (!info->controlIDs) {
 400             int maxCount = 0;
 401             TRACE0("getControl: allocate mem\n");
 402             // get a maximum number of controls:
 403             // each port has a select, balance, and volume control.
 404             maxCount = 3 * portCount;
 405             // then there is monitorGain and outputMuted
 406             maxCount += (2 * info->targetPortCount);
 407             info->maxControlCount = maxCount;
 408             info->controlIDs = (PortControlID*) malloc(sizeof(PortControlID) * maxCount);
 409         }
 410         if (!isSourcePort(info, portIndex)) {
 411             type = PORT_CONTROL_TYPE_PLAY;
 412             // add master mute control
 413             createPortControl(info, creator, portIndex,
 414                               type | PORT_CONTROL_TYPE_OUTPUT_MUTED,
 415                               controls, &controlCount);
 416             addAllControls(info, creator, controls, &controlCount);
 417 #ifdef SOLARIS7_COMPATIBLE
 418             selectable = info->audioInfo.play.avail_ports & targetPorts[info->ports[portIndex]];
 419 #else
 420             selectable = info->audioInfo.play.mod_ports & targetPorts[info->ports[portIndex]];
 421 #endif
 422         } else {
 423             type = PORT_CONTROL_TYPE_RECORD;
 424 #ifdef SOLARIS7_COMPATIBLE
 425             selectable = info->audioInfo.record.avail_ports & sourcePorts[info->ports[portIndex]];
 426 #else
 427             selectable = info->audioInfo.record.mod_ports & sourcePorts[info->ports[portIndex]];
 428 #endif
 429         }
 430         // add a mixer strip with volume, ...
 431         createPortControl(info, creator, portIndex,
 432                           type | PORT_CONTROL_TYPE_GAIN,
 433                           controls, &controlCount);
 434         // ... balance, ...
 435         createPortControl(info, creator, portIndex,
 436                           type | PORT_CONTROL_TYPE_BALANCE,
 437                           controls, &controlCount);
 438         // ... and select control (if not always on)...
 439         if (selectable) {
 440             createPortControl(info, creator, portIndex,
 441                               type | PORT_CONTROL_TYPE_SELECT_PORT,
 442                               controls, &controlCount);
 443         }
 444         // ... packaged in a compound control.
 445         addCompoundControl(info, creator, getPortName(info, portIndex), controls, &controlCount);
 446 
 447         if (type == PORT_CONTROL_TYPE_PLAY) {
 448             // add a single strip for source ports with monitor gain
 449             createPortControl(info, creator, portIndex,
 450                               type | PORT_CONTROL_TYPE_MONITOR_GAIN,
 451                               controls, &controlCount);
 452             // also in a compound control
 453             addCompoundControl(info, creator, MONITOR_GAIN_STRING, controls, &controlCount);
 454         }
 455     }
 456     TRACE0("< PORT_getControls\n");
 457 }
 458 
 459 INT32 PORT_GetIntValue(void* controlIDV) {
 460     PortControlID* controlID = (PortControlID*) controlIDV;
 461     audio_info_t audioInfo;
 462     audio_prinfo_t* prinfo;
 463 
 464     AUDIO_INITINFO(&audioInfo);
 465     if (ioctl(controlID->portInfo->fd, AUDIO_GETINFO, &audioInfo) >= 0) {
 466         if (controlID->controlType & PORT_CONTROL_TYPE_PLAY) {
 467             prinfo = &(audioInfo.play);
 468         } else {
 469             prinfo = &(audioInfo.record);
 470         }
 471         switch (controlID->controlType & PORT_CONTROL_TYPE_MASK) {
 472         case PORT_CONTROL_TYPE_SELECT_PORT:
 473             return (prinfo->port & controlID->port)?TRUE:FALSE;
 474         case PORT_CONTROL_TYPE_OUTPUT_MUTED:
 475             return (audioInfo.output_muted)?TRUE:FALSE;
 476         default:
 477             ERROR1("PORT_GetIntValue: Wrong type %d !\n", controlID->controlType & PORT_CONTROL_TYPE_MASK);
 478         }
 479     }
 480     ERROR0("PORT_GetIntValue: Could not ioctl!\n");
 481     return 0;
 482 }
 483 
 484 void PORT_SetIntValue(void* controlIDV, INT32 value) {
 485     PortControlID* controlID = (PortControlID*) controlIDV;
 486     audio_info_t audioInfo;
 487     audio_prinfo_t* prinfo;
 488     int setPort;
 489 
 490     if (controlID->controlType & PORT_CONTROL_TYPE_PLAY) {
 491         prinfo = &(audioInfo.play);
 492     } else {
 493         prinfo = &(audioInfo.record);
 494     }
 495     switch (controlID->controlType & PORT_CONTROL_TYPE_MASK) {
 496     case PORT_CONTROL_TYPE_SELECT_PORT:
 497         // first try to just add this port. if that fails, set ONLY to this port.
 498         AUDIO_INITINFO(&audioInfo);
 499         if (ioctl(controlID->portInfo->fd, AUDIO_GETINFO, &audioInfo) >= 0) {
 500             if (value) {
 501                 setPort = (prinfo->port | controlID->port);
 502             } else {
 503                 setPort = (prinfo->port - controlID->port);
 504             }
 505             AUDIO_INITINFO(&audioInfo);
 506             prinfo->port = setPort;
 507             if (ioctl(controlID->portInfo->fd, AUDIO_SETINFO, &audioInfo) < 0) {
 508                 // didn't work. Either this line doesn't support to select several
 509                 // ports at once (e.g. record), or a real error
 510                 if (value) {
 511                     // set to ONLY this port (and disable any other currently selected ports)
 512                     AUDIO_INITINFO(&audioInfo);
 513                     prinfo->port = controlID->port;
 514                     if (ioctl(controlID->portInfo->fd, AUDIO_SETINFO, &audioInfo) < 0) {
 515                         ERROR2("Error setting output select port %d to port %d!\n", controlID->port, controlID->port);
 516                     }
 517                 } else {
 518                     // assume it's an error
 519                     ERROR2("Error setting output select port %d to port %d!\n", controlID->port, setPort);
 520                 }
 521             }
 522             break;
 523         case PORT_CONTROL_TYPE_OUTPUT_MUTED:
 524             AUDIO_INITINFO(&audioInfo);
 525             audioInfo.output_muted = (value?TRUE:FALSE);
 526             if (ioctl(controlID->portInfo->fd, AUDIO_SETINFO, &audioInfo) < 0) {
 527                 ERROR2("Error setting output muted on port %d to %d!\n", controlID->port, value);
 528             }
 529             break;
 530         default:
 531             ERROR1("PORT_SetIntValue: Wrong type %d !\n", controlID->controlType & PORT_CONTROL_TYPE_MASK);
 532         }
 533     }
 534 }
 535 
 536 float PORT_GetFloatValue(void* controlIDV) {
 537     PortControlID* controlID = (PortControlID*) controlIDV;
 538     audio_info_t audioInfo;
 539     audio_prinfo_t* prinfo;
 540 
 541     AUDIO_INITINFO(&audioInfo);
 542     if (ioctl(controlID->portInfo->fd, AUDIO_GETINFO, &audioInfo) >= 0) {
 543         if (controlID->controlType & PORT_CONTROL_TYPE_PLAY) {
 544             prinfo = &(audioInfo.play);
 545         } else {
 546             prinfo = &(audioInfo.record);
 547         }
 548         switch (controlID->controlType & PORT_CONTROL_TYPE_MASK) {
 549         case PORT_CONTROL_TYPE_GAIN:
 550             return ((float) (prinfo->gain - AUDIO_MIN_GAIN))
 551                 / ((float) (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN));
 552         case PORT_CONTROL_TYPE_BALANCE:
 553             return ((float) ((prinfo->balance - AUDIO_LEFT_BALANCE - AUDIO_MID_BALANCE) << 1))
 554                 / ((float) (AUDIO_RIGHT_BALANCE - AUDIO_LEFT_BALANCE));
 555         case PORT_CONTROL_TYPE_MONITOR_GAIN:
 556             return ((float) (audioInfo.monitor_gain - AUDIO_MIN_GAIN))
 557                 / ((float) (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN));
 558         default:
 559             ERROR1("PORT_GetFloatValue: Wrong type %d !\n", controlID->controlType & PORT_CONTROL_TYPE_MASK);
 560         }
 561     }
 562     ERROR0("PORT_GetFloatValue: Could not ioctl!\n");
 563     return 0.0f;
 564 }
 565 
 566 void PORT_SetFloatValue(void* controlIDV, float value) {
 567     PortControlID* controlID = (PortControlID*) controlIDV;
 568     audio_info_t audioInfo;
 569     audio_prinfo_t* prinfo;
 570 
 571     AUDIO_INITINFO(&audioInfo);
 572 
 573     if (controlID->controlType & PORT_CONTROL_TYPE_PLAY) {
 574         prinfo = &(audioInfo.play);
 575     } else {
 576         prinfo = &(audioInfo.record);
 577     }
 578     switch (controlID->controlType & PORT_CONTROL_TYPE_MASK) {
 579     case PORT_CONTROL_TYPE_GAIN:
 580         prinfo->gain = AUDIO_MIN_GAIN
 581             + (int) ((value * ((float) (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN))) + 0.5f);
 582         break;
 583     case PORT_CONTROL_TYPE_BALANCE:
 584         prinfo->balance =  AUDIO_LEFT_BALANCE + AUDIO_MID_BALANCE
 585             + ((int) (value * ((float) ((AUDIO_RIGHT_BALANCE - AUDIO_LEFT_BALANCE) >> 1))) + 0.5f);
 586         break;
 587     case PORT_CONTROL_TYPE_MONITOR_GAIN:
 588         audioInfo.monitor_gain = AUDIO_MIN_GAIN
 589             + (int) ((value * ((float) (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN))) + 0.5f);
 590         break;
 591     default:
 592         ERROR1("PORT_SetFloatValue: Wrong type %d !\n", controlID->controlType & PORT_CONTROL_TYPE_MASK);
 593         return;
 594     }
 595     if (ioctl(controlID->portInfo->fd, AUDIO_SETINFO, &audioInfo) < 0) {
 596         ERROR0("PORT_SetFloatValue: Could not ioctl!\n");
 597     }
 598 }
 599 
 600 #endif // USE_PORTS