/* * Copyright (c) 2002, 2016, 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("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("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("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