text: /*
   * Copyright (c) 2002, 2007, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this
   * particular file as subject to the "Classpath" exception as provided
   * by Oracle in the LICENSE file that accompanied this code.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  
  #define USE_ERROR
  //#define USE_TRACE
  
  #include "Ports.h"
  #include "PLATFORM_API_SolarisOS_Utils.h"
  
  #if USE_PORTS == TRUE
  
  #define MONITOR_GAIN_STRING "Monitor Gain"
  
  #define ALL_TARGET_PORT_COUNT 6
  
  // define the following to not use audio_prinfo_t.mod_ports
  #define SOLARIS7_COMPATIBLE
  
  // Solaris audio defines
  static int targetPorts[ALL_TARGET_PORT_COUNT] = {
      AUDIO_SPEAKER,
      AUDIO_HEADPHONE,
      AUDIO_LINE_OUT,
      AUDIO_AUX1_OUT,
      AUDIO_AUX2_OUT,
      AUDIO_SPDIF_OUT
  };
  
  static char* targetPortNames[ALL_TARGET_PORT_COUNT] = {
      "Speaker",
      "Headphone",
      "Line Out",
      "AUX1 Out",
      "AUX2 Out",
      "SPDIF Out"
  };
  
  // defined in Ports.h
  static int targetPortJavaSoundMapping[ALL_TARGET_PORT_COUNT] = {
      PORT_DST_SPEAKER,
      PORT_DST_HEADPHONE,
      PORT_DST_LINE_OUT,
      PORT_DST_UNKNOWN,
      PORT_DST_UNKNOWN,
      PORT_DST_UNKNOWN,
  };
  
  #define ALL_SOURCE_PORT_COUNT 7
  
  // Solaris audio defines
  static int sourcePorts[ALL_SOURCE_PORT_COUNT] = {
      AUDIO_MICROPHONE,
      AUDIO_LINE_IN,
      AUDIO_CD,
      AUDIO_AUX1_IN,
      AUDIO_AUX2_IN,
      AUDIO_SPDIF_IN,
      AUDIO_CODEC_LOOPB_IN
  };
  
  static char* sourcePortNames[ALL_SOURCE_PORT_COUNT] = {
      "Microphone In",
      "Line In",
      "Compact Disc In",
      "AUX1 In",
      "AUX2 In",
      "SPDIF In",
      "Internal Loopback"
  };
  
  // Ports.h defines
  static int sourcePortJavaSoundMapping[ALL_SOURCE_PORT_COUNT] = {
      PORT_SRC_MICROPHONE,
      PORT_SRC_LINE_IN,
      PORT_SRC_COMPACT_DISC,
      PORT_SRC_UNKNOWN,
      PORT_SRC_UNKNOWN,
      PORT_SRC_UNKNOWN,
      PORT_SRC_UNKNOWN
  };
  
  struct tag_PortControlID;
  
  typedef struct tag_PortInfo {
      int fd;                    // file descriptor of the pseudo device
      audio_info_t audioInfo;
      // ports
      int targetPortCount;
      int sourcePortCount;
      // indexes to sourcePorts/targetPorts
      // contains first target ports, then source ports
      int ports[ALL_TARGET_PORT_COUNT + ALL_SOURCE_PORT_COUNT];
      // controls
      int maxControlCount;       // upper bound of number of controls
      int usedControlIDs;        // number of items already filled in controlIDs
      struct tag_PortControlID* controlIDs; // the control IDs themselves
  } PortInfo;
  
  #define PORT_CONTROL_TYPE_PLAY          0x4000000
  #define PORT_CONTROL_TYPE_RECORD        0x8000000
  #define PORT_CONTROL_TYPE_SELECT_PORT   1
  #define PORT_CONTROL_TYPE_GAIN          2
  #define PORT_CONTROL_TYPE_BALANCE       3
  #define PORT_CONTROL_TYPE_MONITOR_GAIN  10
  #define PORT_CONTROL_TYPE_OUTPUT_MUTED  11
  #define PORT_CONTROL_TYPE_PLAYRECORD_MASK PORT_CONTROL_TYPE_PLAY | PORT_CONTROL_TYPE_RECORD
  #define PORT_CONTROL_TYPE_MASK 0xFFFFFF
  
  
  typedef struct tag_PortControlID {
      PortInfo*  portInfo;
      INT32                 controlType;  // PORT_CONTROL_TYPE_XX
      uint_t                port;
  } PortControlID;
  
  
  ///// implemented functions of Ports.h
  
  INT32 PORT_GetPortMixerCount() {
      return (INT32) getAudioDeviceCount();
  }
  
  
  INT32 PORT_GetPortMixerDescription(INT32 mixerIndex, PortMixerDescription* description) {
      AudioDeviceDescription desc;
  
      if (getAudioDeviceDescriptionByIndex(mixerIndex, &desc, TRUE)) {
          strncpy(description->name, desc.name, PORT_STRING_LENGTH-1);
          description->name[PORT_STRING_LENGTH-1] = 0;
          strncpy(description->vendor, desc.vendor, PORT_STRING_LENGTH-1);
          description->vendor[PORT_STRING_LENGTH-1] = 0;
          strncpy(description->version, desc.version, PORT_STRING_LENGTH-1);
          description->version[PORT_STRING_LENGTH-1] = 0;
          /*strncpy(description->description, desc.description, PORT_STRING_LENGTH-1);*/
          strncpy(description->description, "Solaris Ports", PORT_STRING_LENGTH-1);
          description->description[PORT_STRING_LENGTH-1] = 0;
          return TRUE;
      }
      return FALSE;
  }
  
  
  void* PORT_Open(INT32 mixerIndex) {
      PortInfo* info = NULL;
      int fd = -1;
      AudioDeviceDescription desc;
      int success = FALSE;
  
      TRACE0("PORT_Open\n");
      if (getAudioDeviceDescriptionByIndex(mixerIndex, &desc, FALSE)) {
          fd = open(desc.pathctl, O_RDWR);
      }
      if (fd < 0) {
          ERROR1("Couldn't open audio device ctl for device %d!\n", mixerIndex);
          return NULL;
      }
  
      info = (PortInfo*) malloc(sizeof(PortInfo));
      if (info != NULL) {
          memset(info, 0, sizeof(PortInfo));
          info->fd = fd;
          success = TRUE;
      }
      if (!success) {
          if (fd >= 0) {
              close(fd);
          }
          PORT_Close((void*) info);
          info = NULL;
      }
      return info;
  }
  
  void PORT_Close(void* id) {
      TRACE0("PORT_Close\n");
      if (id != NULL) {
          PortInfo* info = (PortInfo*) id;
          if (info->fd >= 0) {
              close(info->fd);
              info->fd = -1;
          }
          if (info->controlIDs) {
              free(info->controlIDs);
              info->controlIDs = NULL;
          }
          free(info);
      }
  }
  
  
  
  INT32 PORT_GetPortCount(void* id) {
      int ret = 0;
      PortInfo* info = (PortInfo*) id;
      if (info != NULL) {
          if (!info->targetPortCount && !info->sourcePortCount) {
              int i;
              AUDIO_INITINFO(&info->audioInfo);
              if (ioctl(info->fd, AUDIO_GETINFO, &info->audioInfo) >= 0) {
                  for (i = 0; i < ALL_TARGET_PORT_COUNT; i++) {
                      if (info->audioInfo.play.avail_ports & targetPorts[i]) {
                          info->ports[info->targetPortCount] = i;
                          info->targetPortCount++;
                      }
  #ifdef SOLARIS7_COMPATIBLE
                      TRACE3("Target %d %s: avail=%d\n", i, targetPortNames[i],
                             info->audioInfo.play.avail_ports & targetPorts[i]);
  #else
                      TRACE4("Target %d %s: avail=%d  mod=%d\n", i, targetPortNames[i],
                             info->audioInfo.play.avail_ports & targetPorts[i],
                             info->audioInfo.play.mod_ports & targetPorts[i]);
  #endif
                  }
                  for (i = 0; i < ALL_SOURCE_PORT_COUNT; i++) {
                      if (info->audioInfo.record.avail_ports & sourcePorts[i]) {
                          info->ports[info->targetPortCount + info->sourcePortCount] = i;
                          info->sourcePortCount++;
                      }
  #ifdef SOLARIS7_COMPATIBLE
                      TRACE3("Source %d %s: avail=%d\n", i, sourcePortNames[i],
                             info->audioInfo.record.avail_ports & sourcePorts[i]);
  #else
                      TRACE4("Source %d %s: avail=%d  mod=%d\n", i, sourcePortNames[i],
                             info->audioInfo.record.avail_ports & sourcePorts[i],
                             info->audioInfo.record.mod_ports & sourcePorts[i]);
  #endif
                  }
              }
          }
          ret = info->targetPortCount + info->sourcePortCount;
      }
      return ret;
  }
  
  int isSourcePort(PortInfo* info, INT32 portIndex) {
      return (portIndex >= info->targetPortCount);
  }
  
  INT32 PORT_GetPortType(void* id, INT32 portIndex) {
      PortInfo* info = (PortInfo*) id;
      if ((portIndex >= 0) && (portIndex < PORT_GetPortCount(id))) {
          if (isSourcePort(info, portIndex)) {
              return sourcePortJavaSoundMapping[info->ports[portIndex]];
          } else {
              return targetPortJavaSoundMapping[info->ports[portIndex]];
          }
      }
      return 0;
  }
  
  // pre-condition: portIndex must have been verified!
  char* getPortName(PortInfo* info, INT32 portIndex) {
      char* ret = NULL;
  
      if (isSourcePort(info, portIndex)) {
          ret = sourcePortNames[info->ports[portIndex]];
      } else {
          ret = targetPortNames[info->ports[portIndex]];
      }
      return ret;
  }
  
  INT32 PORT_GetPortName(void* id, INT32 portIndex, char* name, INT32 len) {
      PortInfo* info = (PortInfo*) id;
      char* n;
  
      if ((portIndex >= 0) && (portIndex < PORT_GetPortCount(id))) {
          n = getPortName(info, portIndex);
          if (n) {
              strncpy(name, n, len-1);
              name[len-1] = 0;
              return TRUE;
          }
      }
      return FALSE;
  }
  
  void createPortControl(PortInfo* info, PortControlCreator* creator, INT32 portIndex,
                         INT32 type, void** controlObjects, int* controlCount) {
      PortControlID* controlID;
      void* newControl = NULL;
      int controlIndex;
      char* jsType = NULL;
      int isBoolean = FALSE;
  
      TRACE0(">createPortControl\n");
  
      // fill the ControlID structure and add this control
      if (info->usedControlIDs >= info->maxControlCount) {
          ERROR1("not enough free controlIDs !! maxControlIDs = %d\n", info->maxControlCount);
          return;
      }
      controlID = &(info->controlIDs[info->usedControlIDs]);
      controlID->portInfo = info;
      controlID->controlType = type;
      controlIndex = info->ports[portIndex];
      if (isSourcePort(info, portIndex)) {
          controlID->port = sourcePorts[controlIndex];
      } else {
          controlID->port = targetPorts[controlIndex];
      }
      switch (type & PORT_CONTROL_TYPE_MASK) {
      case PORT_CONTROL_TYPE_SELECT_PORT:
          jsType = CONTROL_TYPE_SELECT; isBoolean = TRUE; break;
      case PORT_CONTROL_TYPE_GAIN:
          jsType = CONTROL_TYPE_VOLUME;  break;
      case PORT_CONTROL_TYPE_BALANCE:
          jsType = CONTROL_TYPE_BALANCE; break;
      case PORT_CONTROL_TYPE_MONITOR_GAIN:
          jsType = CONTROL_TYPE_VOLUME; break;
      case PORT_CONTROL_TYPE_OUTPUT_MUTED:
          jsType = CONTROL_TYPE_MUTE; isBoolean = TRUE; break;
      }
      if (isBoolean) {
          TRACE0(" PORT_CONTROL_TYPE_BOOLEAN\n");
          newControl = (creator->newBooleanControl)(creator, controlID, jsType);
      }
      else if (jsType == CONTROL_TYPE_BALANCE) {
          TRACE0(" PORT_CONTROL_TYPE_BALANCE\n");
          newControl = (creator->newFloatControl)(creator, controlID, jsType,
                                                  -1.0f, 1.0f, 2.0f / 65.0f, "");
      } else {
          TRACE0(" PORT_CONTROL_TYPE_FLOAT\n");
          newControl = (creator->newFloatControl)(creator, controlID, jsType,
                                                  0.0f, 1.0f, 1.0f / 256.0f, "");
      }
      if (newControl) {
          controlObjects[*controlCount] = newControl;
          (*controlCount)++;
          info->usedControlIDs++;
      }
      TRACE0("<createPortControl\n");
  }
  
  
  void addCompoundControl(PortInfo* info, PortControlCreator* creator, char* name, void** controlObjects, int* controlCount) {
      void* compControl;
  
      TRACE1(">addCompoundControl %d controls\n", *controlCount);
      if (*controlCount) {
          // create compound control and add it to the vector
          compControl = (creator->newCompoundControl)(creator, name, controlObjects, *controlCount);
          if (compControl) {
              TRACE1(" addCompoundControl: calling addControl %p\n", compControl);
              (creator->addControl)(creator, compControl);
          }
          *controlCount = 0;
      }
      TRACE0("<addCompoundControl\n");
  }
  
  void addAllControls(PortInfo* info, PortControlCreator* creator, void** controlObjects, int* controlCount) {
      int i = 0;
  
      TRACE0(">addAllControl\n");
      // go through all controls and add them to the vector
      for (i = 0; i < *controlCount; i++) {
          (creator->addControl)(creator, controlObjects[i]);
      }
      *controlCount = 0;
      TRACE0("<addAllControl\n");
  }
  
  void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) {
      PortInfo* info = (PortInfo*) id;
      int portCount = PORT_GetPortCount(id);
      void* controls[4];
      int controlCount = 0;
      INT32 type;
      int selectable = 1;
  
      TRACE4(">PORT_GetControls(id=%p, portIndex=%d). controlIDs=%p, maxControlCount=%d\n",
             id, portIndex, info->controlIDs, info->maxControlCount);
      if ((portIndex >= 0) && (portIndex < portCount)) {
          // if the memory isn't reserved for the control structures, allocate it
          if (!info->controlIDs) {
              int maxCount = 0;
              TRACE0("getControl: allocate mem\n");
              // get a maximum number of controls:
              // each port has a select, balance, and volume control.
              maxCount = 3 * portCount;
              // then there is monitorGain and outputMuted
              maxCount += (2 * info->targetPortCount);
              info->maxControlCount = maxCount;
              info->controlIDs = (PortControlID*) malloc(sizeof(PortControlID) * maxCount);
          }
          if (!isSourcePort(info, portIndex)) {
              type = PORT_CONTROL_TYPE_PLAY;
              // add master mute control
              createPortControl(info, creator, portIndex,
                                type | PORT_CONTROL_TYPE_OUTPUT_MUTED,
                                controls, &controlCount);
              addAllControls(info, creator, controls, &controlCount);
  #ifdef SOLARIS7_COMPATIBLE
              selectable = info->audioInfo.play.avail_ports & targetPorts[info->ports[portIndex]];
  #else
              selectable = info->audioInfo.play.mod_ports & targetPorts[info->ports[portIndex]];
  #endif
          } else {
              type = PORT_CONTROL_TYPE_RECORD;
  #ifdef SOLARIS7_COMPATIBLE
              selectable = info->audioInfo.record.avail_ports & sourcePorts[info->ports[portIndex]];
  #else
              selectable = info->audioInfo.record.mod_ports & sourcePorts[info->ports[portIndex]];
  #endif
          }
          // add a mixer strip with volume, ...
          createPortControl(info, creator, portIndex,
                            type | PORT_CONTROL_TYPE_GAIN,
                            controls, &controlCount);
          // ... balance, ...
          createPortControl(info, creator, portIndex,
                            type | PORT_CONTROL_TYPE_BALANCE,
                            controls, &controlCount);
          // ... and select control (if not always on)...
          if (selectable) {
              createPortControl(info, creator, portIndex,
                                type | PORT_CONTROL_TYPE_SELECT_PORT,
                                controls, &controlCount);
          }
          // ... packaged in a compound control.
          addCompoundControl(info, creator, getPortName(info, portIndex), controls, &controlCount);
  
          if (type == PORT_CONTROL_TYPE_PLAY) {
              // add a single strip for source ports with monitor gain
              createPortControl(info, creator, portIndex,
                                type | PORT_CONTROL_TYPE_MONITOR_GAIN,
                                controls, &controlCount);
              // also in a compound control
              addCompoundControl(info, creator, MONITOR_GAIN_STRING, controls, &controlCount);
          }
      }
      TRACE0("< PORT_getControls\n");
  }
  
  INT32 PORT_GetIntValue(void* controlIDV) {
      PortControlID* controlID = (PortControlID*) controlIDV;
      audio_info_t audioInfo;
      audio_prinfo_t* prinfo;
  
      AUDIO_INITINFO(&audioInfo);
      if (ioctl(controlID->portInfo->fd, AUDIO_GETINFO, &audioInfo) >= 0) {
          if (controlID->controlType & PORT_CONTROL_TYPE_PLAY) {
              prinfo = &(audioInfo.play);
          } else {
              prinfo = &(audioInfo.record);
          }
          switch (controlID->controlType & PORT_CONTROL_TYPE_MASK) {
          case PORT_CONTROL_TYPE_SELECT_PORT:
              return (prinfo->port & controlID->port)?TRUE:FALSE;
          case PORT_CONTROL_TYPE_OUTPUT_MUTED:
              return (audioInfo.output_muted)?TRUE:FALSE;
          default:
              ERROR1("PORT_GetIntValue: Wrong type %d !\n", controlID->controlType & PORT_CONTROL_TYPE_MASK);
          }
      }
      ERROR0("PORT_GetIntValue: Could not ioctl!\n");
      return 0;
  }
  
  void PORT_SetIntValue(void* controlIDV, INT32 value) {
      PortControlID* controlID = (PortControlID*) controlIDV;
      audio_info_t audioInfo;
      audio_prinfo_t* prinfo;
      int setPort;
  
      if (controlID->controlType & PORT_CONTROL_TYPE_PLAY) {
          prinfo = &(audioInfo.play);
      } else {
          prinfo = &(audioInfo.record);
      }
      switch (controlID->controlType & PORT_CONTROL_TYPE_MASK) {
      case PORT_CONTROL_TYPE_SELECT_PORT:
          // first try to just add this port. if that fails, set ONLY to this port.
          AUDIO_INITINFO(&audioInfo);
          if (ioctl(controlID->portInfo->fd, AUDIO_GETINFO, &audioInfo) >= 0) {
              if (value) {
                  setPort = (prinfo->port | controlID->port);
              } else {
                  setPort = (prinfo->port - controlID->port);
              }
              AUDIO_INITINFO(&audioInfo);
              prinfo->port = setPort;
              if (ioctl(controlID->portInfo->fd, AUDIO_SETINFO, &audioInfo) < 0) {
                  // didn't work. Either this line doesn't support to select several
                  // ports at once (e.g. record), or a real error
                  if (value) {
                      // set to ONLY this port (and disable any other currently selected ports)
                      AUDIO_INITINFO(&audioInfo);
                      prinfo->port = controlID->port;
                      if (ioctl(controlID->portInfo->fd, AUDIO_SETINFO, &audioInfo) < 0) {
                          ERROR2("Error setting output select port %d to port %d!\n", controlID->port, controlID->port);
                      }
                  } else {
                      // assume it's an error
                      ERROR2("Error setting output select port %d to port %d!\n", controlID->port, setPort);
                  }
              }
              break;
          case PORT_CONTROL_TYPE_OUTPUT_MUTED:
              AUDIO_INITINFO(&audioInfo);
              audioInfo.output_muted = (value?TRUE:FALSE);
              if (ioctl(controlID->portInfo->fd, AUDIO_SETINFO, &audioInfo) < 0) {
                  ERROR2("Error setting output muted on port %d to %d!\n", controlID->port, value);
              }
              break;
          default:
              ERROR1("PORT_SetIntValue: Wrong type %d !\n", controlID->controlType & PORT_CONTROL_TYPE_MASK);
          }
      }
  }
  
  float PORT_GetFloatValue(void* controlIDV) {
      PortControlID* controlID = (PortControlID*) controlIDV;
      audio_info_t audioInfo;
      audio_prinfo_t* prinfo;
  
      AUDIO_INITINFO(&audioInfo);
      if (ioctl(controlID->portInfo->fd, AUDIO_GETINFO, &audioInfo) >= 0) {
          if (controlID->controlType & PORT_CONTROL_TYPE_PLAY) {
              prinfo = &(audioInfo.play);
          } else {
              prinfo = &(audioInfo.record);
          }
          switch (controlID->controlType & PORT_CONTROL_TYPE_MASK) {
          case PORT_CONTROL_TYPE_GAIN:
              return ((float) (prinfo->gain - AUDIO_MIN_GAIN))
                  / ((float) (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN));
          case PORT_CONTROL_TYPE_BALANCE:
              return ((float) ((prinfo->balance - AUDIO_LEFT_BALANCE - AUDIO_MID_BALANCE) << 1))
                  / ((float) (AUDIO_RIGHT_BALANCE - AUDIO_LEFT_BALANCE));
          case PORT_CONTROL_TYPE_MONITOR_GAIN:
              return ((float) (audioInfo.monitor_gain - AUDIO_MIN_GAIN))
                  / ((float) (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN));
          default:
              ERROR1("PORT_GetFloatValue: Wrong type %d !\n", controlID->controlType & PORT_CONTROL_TYPE_MASK);
          }
      }
      ERROR0("PORT_GetFloatValue: Could not ioctl!\n");
      return 0.0f;
  }
  
  void PORT_SetFloatValue(void* controlIDV, float value) {
      PortControlID* controlID = (PortControlID*) controlIDV;
      audio_info_t audioInfo;
      audio_prinfo_t* prinfo;
  
      AUDIO_INITINFO(&audioInfo);
  
      if (controlID->controlType & PORT_CONTROL_TYPE_PLAY) {
          prinfo = &(audioInfo.play);
      } else {
          prinfo = &(audioInfo.record);
      }
      switch (controlID->controlType & PORT_CONTROL_TYPE_MASK) {
      case PORT_CONTROL_TYPE_GAIN:
          prinfo->gain = AUDIO_MIN_GAIN
              + (int) ((value * ((float) (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN))) + 0.5f);
          break;
      case PORT_CONTROL_TYPE_BALANCE:
          prinfo->balance =  AUDIO_LEFT_BALANCE + AUDIO_MID_BALANCE
              + ((int) (value * ((float) ((AUDIO_RIGHT_BALANCE - AUDIO_LEFT_BALANCE) >> 1))) + 0.5f);
          break;
      case PORT_CONTROL_TYPE_MONITOR_GAIN:
          audioInfo.monitor_gain = AUDIO_MIN_GAIN
              + (int) ((value * ((float) (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN))) + 0.5f);
          break;
      default:
          ERROR1("PORT_SetFloatValue: Wrong type %d !\n", controlID->controlType & PORT_CONTROL_TYPE_MASK);
          return;
      }
      if (ioctl(controlID->portInfo->fd, AUDIO_SETINFO, &audioInfo) < 0) {
          ERROR0("PORT_SetFloatValue: Could not ioctl!\n");
      }
  }
  
  #endif // USE_PORTS
  