/* * 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 #ifndef WIN32_EXTRA_LEAN #define WIN32_EXTRA_LEAN #endif #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #include "Ports.h" #if USE_PORTS == TRUE typedef struct tag_PortControlID PortControlID; typedef struct tag_PortInfo { // Windows API stuff HMIXER handle; INT32 mixerIndex; int dstLineCount; // how many MIXERLINE structs in dstMixerLine MIXERLINE* dstLines; int srcLineCount; // how many MIXERLINE structs in srcMixerLine MIXERLINE* srcLines; // contains all the Source Lines of dstLines // Java Sound mapping int targetPortCount; // one port per dstLine (playback) int sourcePortCount; // only WAVEIN; one port maps to one srcLine LPMIXERLINE* ports; // points into dstLines and dstLines. Starts with Target Ports (Playback) int maxControlCount; // upper bound of number of controls int usedControlIDs; // number of items already filled in controlIDs PortControlID* controlIDs; // the control IDs themselves int usedMuxData; MIXERCONTROLDETAILS_BOOLEAN* muxData; } PortInfo; #define PORT_CONTROL_TYPE_BOOLEAN 1 #define PORT_CONTROL_TYPE_SIGNED 2 #define PORT_CONTROL_TYPE_UNSIGNED 3 //#define PORT_CONTROL_TYPE_UNSIGNED_DB 4 #define PORT_CONTROL_TYPE_FAKE_VOLUME 5 #define PORT_CONTROL_TYPE_FAKE_BALANCE 6 #define PORT_CONTROL_TYPE_MUX 5 #define PORT_CONTROL_TYPE_MIXER 6 typedef struct tag_PortControlID { PortInfo* portInfo; INT32 controlType; // one of PORT_CONTROL_TYPE_XX INT32 min; INT32 max; MIXERCONTROLDETAILS details; union { MIXERCONTROLDETAILS_BOOLEAN boolValue; MIXERCONTROLDETAILS_SIGNED signedValue; MIXERCONTROLDETAILS_UNSIGNED unsignedValue[2]; INT32 muxIndex; }; } PortControlID; int getControlInfo(HMIXER handle, MIXERLINE* line, MIXERLINECONTROLS* controls); INT32 PORT_GetPortMixerCount() { return (INT32) mixerGetNumDevs(); } #ifdef USE_TRACE char* getLineFlags(DWORD flags) { static char ret[100]; ret[0]=0; if (flags & MIXERLINE_LINEF_ACTIVE) { strcat(ret, "ACTIVE "); flags ^= MIXERLINE_LINEF_ACTIVE; } if (flags & MIXERLINE_LINEF_DISCONNECTED) { strcat(ret, "DISCONNECTED "); flags ^= MIXERLINE_LINEF_DISCONNECTED; } if (flags & MIXERLINE_LINEF_SOURCE) { strcat(ret, "SOURCE "); flags ^= MIXERLINE_LINEF_SOURCE; } if (flags!=0) { UINT_PTR r = (UINT_PTR) ret; r += strlen(ret); sprintf((char*) r, "%d", flags); } return ret; } char* getComponentType(int componentType) { switch (componentType) { case MIXERLINE_COMPONENTTYPE_DST_HEADPHONES: return "DST_HEADPHONES"; case MIXERLINE_COMPONENTTYPE_DST_LINE: return "DST_LINE"; case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS: return "DST_SPEAKERS"; case MIXERLINE_COMPONENTTYPE_DST_DIGITAL: return "DST_DIGITAL"; case MIXERLINE_COMPONENTTYPE_DST_MONITOR: return "DST_MONITOR"; case MIXERLINE_COMPONENTTYPE_DST_TELEPHONE: return "DST_TELEPHONE"; case MIXERLINE_COMPONENTTYPE_DST_UNDEFINED: return "DST_UNDEFINED"; case MIXERLINE_COMPONENTTYPE_DST_VOICEIN: return "DST_VOICEIN"; case MIXERLINE_COMPONENTTYPE_DST_WAVEIN: return "DST_WAVEIN"; case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC: return "SRC_COMPACTDISC"; case MIXERLINE_COMPONENTTYPE_SRC_LINE: return "SRC_LINE"; case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE: return "SRC_MICROPHONE"; case MIXERLINE_COMPONENTTYPE_SRC_ANALOG: return "SRC_ANALOG"; case MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY: return "SRC_AUXILIARY"; case MIXERLINE_COMPONENTTYPE_SRC_DIGITAL: return "SRC_DIGITAL"; case MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER: return "SRC_PCSPEAKER"; case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER: return "SRC_SYNTHESIZER"; case MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE: return "SRC_TELEPHONE"; case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED: return "SRC_UNDEFINED"; case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT: return "SRC_WAVEOUT"; } return ""; } void printMixerLine(MIXERLINE* mixerLine) { TRACE2("MIXERLINE destination=%d, source=%d, ", mixerLine->dwDestination, mixerLine->dwSource); TRACE3("channels=%d, connections=%d, controls=%d, ", mixerLine->cChannels, mixerLine->cConnections, mixerLine->cControls); TRACE3("\"%s\", fdwLine=%s, componentType=%s\n", mixerLine->szName, getLineFlags(mixerLine->fdwLine), getComponentType(mixerLine->dwComponentType)); } char* getControlClass(int controlType) { switch (controlType & MIXERCONTROL_CT_CLASS_MASK) { case MIXERCONTROL_CT_CLASS_CUSTOM : return "CLASS_CUSTOM"; case MIXERCONTROL_CT_CLASS_FADER : return "CLASS_FADER "; case MIXERCONTROL_CT_CLASS_LIST : return "CLASS_LIST "; case MIXERCONTROL_CT_CLASS_METER : return "CLASS_METER "; case MIXERCONTROL_CT_CLASS_NUMBER : return "CLASS_NUMBER"; case MIXERCONTROL_CT_CLASS_SLIDER : return "CLASS_SLIDER"; case MIXERCONTROL_CT_CLASS_SWITCH : return "CLASS_SWITCH"; case MIXERCONTROL_CT_CLASS_TIME : return "CLASS_TIME "; } return "unknown class"; } char* getControlType(int controlType) { switch (controlType) { case MIXERCONTROL_CONTROLTYPE_CUSTOM : return "CUSTOM "; case MIXERCONTROL_CONTROLTYPE_BASS : return "BASS "; case MIXERCONTROL_CONTROLTYPE_EQUALIZER : return "EQUALIZER "; case MIXERCONTROL_CONTROLTYPE_FADER : return "FADER "; case MIXERCONTROL_CONTROLTYPE_TREBLE : return "TREBLE "; case MIXERCONTROL_CONTROLTYPE_VOLUME : return "VOLUME "; case MIXERCONTROL_CONTROLTYPE_MIXER : return "MIXER "; case MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT : return "MULTIPLESELECT "; case MIXERCONTROL_CONTROLTYPE_MUX : return "MUX "; case MIXERCONTROL_CONTROLTYPE_SINGLESELECT : return "SINGLESELECT "; case MIXERCONTROL_CONTROLTYPE_BOOLEANMETER : return "BOOLEANMETER "; case MIXERCONTROL_CONTROLTYPE_PEAKMETER : return "PEAKMETER "; case MIXERCONTROL_CONTROLTYPE_SIGNEDMETER : return "SIGNEDMETER "; case MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER : return "UNSIGNEDMETER "; case MIXERCONTROL_CONTROLTYPE_DECIBELS : return "DECIBELS "; case MIXERCONTROL_CONTROLTYPE_PERCENT : return "PERCENT "; case MIXERCONTROL_CONTROLTYPE_SIGNED : return "SIGNED "; case MIXERCONTROL_CONTROLTYPE_UNSIGNED : return "UNSIGNED "; case MIXERCONTROL_CONTROLTYPE_PAN : return "PAN "; case MIXERCONTROL_CONTROLTYPE_QSOUNDPAN : return "QSOUNDPAN "; case MIXERCONTROL_CONTROLTYPE_SLIDER : return "SLIDER "; case MIXERCONTROL_CONTROLTYPE_BOOLEAN : return "BOOLEAN "; case MIXERCONTROL_CONTROLTYPE_BUTTON : return "BUTTON "; case MIXERCONTROL_CONTROLTYPE_LOUDNESS : return "LOUDNESS "; case MIXERCONTROL_CONTROLTYPE_MONO : return "MONO "; case MIXERCONTROL_CONTROLTYPE_MUTE : return "MUTE "; case MIXERCONTROL_CONTROLTYPE_ONOFF : return "ONOFF "; case MIXERCONTROL_CONTROLTYPE_STEREOENH : return "STEREOENH "; case MIXERCONTROL_CONTROLTYPE_MICROTIME : return "MICROTIME "; case MIXERCONTROL_CONTROLTYPE_MILLITIME : return "MILLITIME "; } return "unknown"; } char* getControlState(DWORD controlState) { static char ret[100]; ret[0]=0; if (controlState & MIXERCONTROL_CONTROLF_DISABLED) { strcat(ret, "DISABLED "); controlState ^= MIXERCONTROL_CONTROLF_DISABLED; } if (controlState & MIXERCONTROL_CONTROLF_MULTIPLE) { strcat(ret, "MULTIPLE "); controlState ^= MIXERCONTROL_CONTROLF_MULTIPLE; } if (controlState & MIXERCONTROL_CONTROLF_UNIFORM) { strcat(ret, "UNIFORM "); controlState ^= MIXERCONTROL_CONTROLF_UNIFORM; } if (controlState!=0) { UINT_PTR r = (UINT_PTR) ret; r += strlen(ret); sprintf((char*) r, "%d", controlState); } return ret; } void printControl(MIXERCONTROL* control) { TRACE3(" %s: dwControlType=%s/%s, ", control->szName, getControlClass(control->dwControlType), getControlType(control->dwControlType)); TRACE3("multpleItems=%d, state=%d, %s\n", control->cMultipleItems, control->fdwControl, getControlState(control->fdwControl)); } void printMixerLineControls(HMIXER handle, MIXERLINE* mixerLine) { MIXERLINECONTROLS controls; DWORD i; TRACE1(" Controls for %s:\n", mixerLine->szName); if (getControlInfo(handle, mixerLine, &controls)) { for (i = 0; i < controls.cControls; i++) { printControl(&controls.pamxctrl[i]); } if (controls.pamxctrl) { free(controls.pamxctrl); controls.pamxctrl = NULL; } } } void printInfo(PortInfo* info) { TRACE5(" PortInfo %p: handle=%p, mixerIndex=%d, dstLineCount=%d, dstLines=%p, ", info, (void*) info->handle, info->mixerIndex, info->dstLineCount, info->dstLines); TRACE5("srcLineCount=%d, srcLines=%p, targetPortCount=%d, sourcePortCount=%d, ports=%p, ", info->srcLineCount, info->srcLines, info->targetPortCount, info->sourcePortCount, info->ports); TRACE3("maxControlCount=%d, usedControlIDs=%d, controlIDs=%p \n", info->maxControlCount, info->usedControlIDs, info->controlIDs); TRACE2("usedMuxData=%d, muxData=%p, controlIDs=%p \n", info->usedMuxData, info->muxData); } #endif // USE_TRACE // internal utility functions int getMixerLineByDestination(HMIXER handle, DWORD dstIndex, MIXERLINE* mixerLine) { mixerLine->cbStruct = sizeof(MIXERLINE); mixerLine->dwDestination = dstIndex; if (mixerGetLineInfo((HMIXEROBJ) handle, mixerLine, MIXER_GETLINEINFOF_DESTINATION | MIXER_OBJECTF_HMIXER ) == MMSYSERR_NOERROR) { return TRUE; } mixerLine->cControls = 0; mixerLine->cConnections = 0; return FALSE; } int getMixerLineByType(HMIXER handle, DWORD linetype, MIXERLINE* mixerLine) { mixerLine->cbStruct = sizeof(MIXERLINE); mixerLine->dwComponentType = linetype; if (mixerGetLineInfo((HMIXEROBJ) handle, mixerLine, MIXER_GETLINEINFOF_COMPONENTTYPE | MIXER_OBJECTF_HMIXER ) == MMSYSERR_NOERROR) { return TRUE; } mixerLine->cControls = 0; mixerLine->cConnections = 0; return FALSE; } int getMixerLineBySource(HMIXER handle, DWORD dstIndex, DWORD srcIndex, MIXERLINE* mixerLine) { mixerLine->cbStruct = sizeof(MIXERLINE); mixerLine->dwDestination = dstIndex; mixerLine->dwSource = srcIndex; if (mixerGetLineInfo((HMIXEROBJ) handle, mixerLine, MIXER_GETLINEINFOF_SOURCE | MIXER_OBJECTF_HMIXER ) == MMSYSERR_NOERROR) { return TRUE; } mixerLine->cControls = 0; mixerLine->cConnections = 0; return FALSE; } int getControlInfo(HMIXER handle, MIXERLINE* line, MIXERLINECONTROLS* controls) { int ret = FALSE; //TRACE2(">getControlInfo for line %s with %d controls\n", line->szName, line->cControls); controls->pamxctrl = NULL; if (line->cControls > 0) { // line points to the requested line. // Reserve memory for the control infos controls->cbStruct = sizeof(MIXERLINECONTROLS); controls->dwLineID = line->dwLineID; controls->cControls = line->cControls; controls->cbmxctrl = sizeof(MIXERCONTROL); controls->pamxctrl = (MIXERCONTROL*) malloc(sizeof(MIXERCONTROL) * line->cControls); if (controls->pamxctrl) { //TRACE0(" calling mixerGetLineControls\n"); ret = mixerGetLineControls((HMIXEROBJ) handle, controls, MIXER_GETLINECONTROLSF_ALL | MIXER_OBJECTF_HMIXER) == MMSYSERR_NOERROR; } } if (!ret) { if (controls->pamxctrl) { free(controls->pamxctrl); controls->pamxctrl = NULL; } } //TRACE0("cControls); i++) { switch (controls->pamxctrl[i].dwControlType & MIXERCONTROL_CT_CLASS_MASK) { case MIXERCONTROL_CT_CLASS_FADER : // fall through case MIXERCONTROL_CT_CLASS_SLIDER : // fall through case MIXERCONTROL_CT_CLASS_SWITCH : ret = TRUE; } } } if (localControls.pamxctrl) { free(localControls.pamxctrl); localControls.pamxctrl = NULL; } return ret; } ///// implemented functions of Ports.h INT32 PORT_GetPortMixerDescription(INT32 mixerIndex, PortMixerDescription* description) { MIXERCAPS mixerCaps; if (mixerGetDevCaps(mixerIndex, &mixerCaps, sizeof(MIXERCAPS)) == MMSYSERR_NOERROR) { strncpy(description->name, mixerCaps.szPname, PORT_STRING_LENGTH-1); description->name[PORT_STRING_LENGTH-1] = 0; sprintf(description->version, "%d.%d", (mixerCaps.vDriverVersion & 0xFF00) >> 8, mixerCaps.vDriverVersion & 0xFF); strncpy(description->description, "Port Mixer", PORT_STRING_LENGTH-1); return TRUE; } return FALSE; } int getDestinationCount(HMIXER handle) { int ret = 0; MIXERCAPS mixerCaps; if (mixerGetDevCaps((UINT_PTR) handle, &mixerCaps, sizeof(MIXERCAPS)) == MMSYSERR_NOERROR) { ret = mixerCaps.cDestinations; } return ret; } void* PORT_Open(INT32 mixerIndex) { PortInfo* info = NULL; MMRESULT mmres; HMIXER handle; MIXERLINE* waveInLine; int success = FALSE; int src, dst, srcIndex, waveInHasControls; int dstCount; TRACE0("PORT_Open\n"); mmres = mixerOpen((LPHMIXER) &handle, mixerIndex, 0, 0, MIXER_OBJECTF_MIXER); if (mmres != MMSYSERR_NOERROR) { return NULL; } info = (PortInfo*) malloc(sizeof(PortInfo)); if (info != NULL) { success = TRUE; memset(info, 0, sizeof(PortInfo)); info->handle = handle; info->mixerIndex = mixerIndex; waveInLine = NULL; waveInHasControls = FALSE; // number of destinations dstCount = getDestinationCount(handle); if (dstCount) { info->dstLines = (MIXERLINE*) malloc(dstCount * sizeof(MIXERLINE)); success = (info->dstLines != NULL); } if (success && info->dstLines) { // go through all destinations and fill the structures in PortInfo for (dst = 0; dst < dstCount; dst++) { if (getMixerLineByDestination(handle, dst, &info->dstLines[info->dstLineCount])) { info->srcLineCount += info->dstLines[info->dstLineCount].cConnections; if (info->dstLines[info->dstLineCount].dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN && !waveInLine) { waveInLine = &info->dstLines[info->dstLineCount]; info->sourcePortCount = waveInLine->cConnections; if (lineHasControls(handle, waveInLine, NULL)) { // add a single port for all the controls that do not show in the MUX/MIXER controls info->sourcePortCount++; waveInHasControls = TRUE; } } else { info->targetPortCount++; } info->dstLineCount++; } } } if (info->srcLineCount) { info->srcLines = (MIXERLINE*) malloc(info->srcLineCount * sizeof(MIXERLINE)); success = (info->srcLines != NULL); } if (success && info->srcLines) { // go through all destinations and fill the source line structures in PortInfo srcIndex = 0; for (dst = 0; dst < info->dstLineCount; dst++) { // remember the srcIndex for mapping the srcLines to this destination line info->dstLines[dst].dwUser = srcIndex; for (src = 0; src < (int) info->dstLines[dst].cConnections; src++) { getMixerLineBySource(handle, dst, src, &info->srcLines[srcIndex++]); } } } // now create the mapping to Java Sound if ((info->targetPortCount + info->sourcePortCount) > 0) { info->ports = (LPMIXERLINE*) malloc((info->targetPortCount + info->sourcePortCount) * sizeof(LPMIXERLINE)); success = (info->ports != NULL); } if (success && info->ports) { // first add the target MIXERLINEs to the array srcIndex = 0; for (dst = 0; dst < info->dstLineCount; dst++) { if (waveInLine != &info->dstLines[dst]) { info->ports[srcIndex++] = &info->dstLines[dst]; } } if (srcIndex != info->targetPortCount) { ERROR2("srcIndex=%d is NOT targetPortCount=%d !\n", srcIndex, info->targetPortCount); } //srcIndex = info->targetPortCount; // should be automatic! if (waveInLine) { // if the recording destination line has controls, add the line if (waveInHasControls) { info->ports[srcIndex++] = waveInLine; } for (src = 0; src < (int) waveInLine->cConnections; src++) { info->ports[srcIndex++] = &info->srcLines[src + waveInLine->dwUser]; } } if (srcIndex != (info->targetPortCount + info->sourcePortCount)) { ERROR2("srcIndex=%d is NOT PortCount=%d !\n", srcIndex, (info->targetPortCount + info->sourcePortCount)); } } } if (!success) { if (handle != NULL) { mixerClose(handle); } 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->handle) { mixerClose(info->handle); info->handle = NULL; } if (info->dstLines) { free(info->dstLines); info->dstLines = NULL; } if (info->srcLines) { free(info->srcLines); info->srcLines=NULL; } if (info->ports) { free(info->ports); info->ports = NULL; } if (info->controlIDs) { free(info->controlIDs); info->controlIDs = NULL; } if (info->muxData) { free(info->muxData); info->muxData = NULL; } free(info); } } INT32 PORT_GetPortCount(void* id) { int ret = 0; PortInfo* info = (PortInfo*) id; if (info != NULL) { ret = info->targetPortCount + info->sourcePortCount; } return ret; } int componentType2type(DWORD componentType) { int ret = 0; if (componentType >= MIXERLINE_COMPONENTTYPE_DST_FIRST && componentType <= MIXERLINE_COMPONENTTYPE_DST_LAST) { ret = PORT_DST_UNKNOWN; } else if (componentType >= MIXERLINE_COMPONENTTYPE_SRC_FIRST && componentType <= MIXERLINE_COMPONENTTYPE_SRC_LAST) { ret = PORT_SRC_UNKNOWN; } // handle special cases switch (componentType) { case MIXERLINE_COMPONENTTYPE_DST_HEADPHONES: ret = PORT_DST_HEADPHONE; break; case MIXERLINE_COMPONENTTYPE_DST_LINE: ret = PORT_DST_LINE_OUT; break; case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS: ret = PORT_DST_SPEAKER; break; case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC: ret = PORT_SRC_COMPACT_DISC; break; case MIXERLINE_COMPONENTTYPE_SRC_LINE: ret = PORT_SRC_LINE_IN; break; case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE: ret = PORT_SRC_MICROPHONE; break; } return ret; } INT32 PORT_GetPortType(void* id, INT32 portIndex) { MIXERLINE* line; PortInfo* info = (PortInfo*) id; if ((portIndex >= 0) && (portIndex < PORT_GetPortCount(id))) { line = info->ports[portIndex]; if (line) { return componentType2type(line->dwComponentType); } } return 0; } INT32 PORT_GetPortName(void* id, INT32 portIndex, char* name, INT32 len) { MIXERLINE* line; PortInfo* info = (PortInfo*) id; if ((portIndex >= 0) && (portIndex < PORT_GetPortCount(id))) { line = info->ports[portIndex]; if (line) { strncpy(name, line->szName, len-1); name[len-1] = 0; return TRUE; } } return FALSE; } int getControlCount(HMIXER handle, MIXERLINE* line, INT32* muxCount) { MIXERLINECONTROLS controls; int ret = 0; UINT i; controls.pamxctrl = NULL; if (getControlInfo(handle, line, &controls)) { for (i = 0; i < controls.cControls; i++) { switch (controls.pamxctrl[i].dwControlType & MIXERCONTROL_CT_CLASS_MASK) { case MIXERCONTROL_CT_CLASS_FADER : // fall through case MIXERCONTROL_CT_CLASS_SLIDER : // fall through case MIXERCONTROL_CT_CLASS_SWITCH : // fall through case MIXERCONTROL_CT_CLASS_LIST : ret++; break; } if ((controls.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER) || (controls.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_MUX)) { ret += controls.pamxctrl[i].cMultipleItems; if (muxCount) { (*muxCount) += controls.pamxctrl[i].cMultipleItems; } } else if ((controls.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME) && (line->cChannels == 2)) { ret++; // for FAKE volume/balance pairs } } } if (controls.pamxctrl) { free(controls.pamxctrl); controls.pamxctrl = NULL; } return ret; } MIXERLINE* findDestLine(PortInfo* info, DWORD dwDestination) { int i; TRACE0(">findDestLine\n"); for (i = 0; i < info->dstLineCount; i++) { if (info->dstLines[i].dwDestination == dwDestination) { TRACE0("dstLines[i]); } } TRACE0("createMuxControl\n"); // go through all controls of dstline controlInfos.pamxctrl = NULL; if (getControlInfo(info->handle, dstLine, &controlInfos)) { for (i = 0; i < controlInfos.cControls; i++) { if (((controlInfos.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER) || (controlInfos.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_MUX)) && (controlInfos.pamxctrl[i].cMultipleItems > 0)) { if (info->usedControlIDs >= info->maxControlCount) { ERROR1("not enough free controlIDs !! maxControlIDs = %d\n", info->maxControlCount); break; } // get the details for this mux control controlID = &(info->controlIDs[info->usedControlIDs]); controlID->portInfo = info; if (controlInfos.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER) { controlID->controlType = PORT_CONTROL_TYPE_MIXER; } else { controlID->controlType = PORT_CONTROL_TYPE_MUX; } details = &(controlID->details); details->cbStruct = sizeof(MIXERCONTROLDETAILS); details->dwControlID = controlInfos.pamxctrl[i].dwControlID; details->cChannels = 1; details->cMultipleItems = controlInfos.pamxctrl[i].cMultipleItems; details->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXT); if (!listTextDetails || (listTextDetailCount < (details->cMultipleItems * details->cChannels))) { // need to allocate new listTextDetails if (listTextDetails) { free(listTextDetails); listTextDetails = NULL; } listTextDetailCount = details->cMultipleItems * details->cChannels; listTextDetails = (MIXERCONTROLDETAILS_LISTTEXT*) malloc(listTextDetailCount * sizeof(MIXERCONTROLDETAILS_LISTTEXT)); if (!listTextDetails) { ERROR0("createMuxControl: unable to allocate listTextDetails!\n"); if (controlInfos.pamxctrl) { free(controlInfos.pamxctrl); controlInfos.pamxctrl = NULL; } TRACE0("paDetails = listTextDetails; if (mixerGetControlDetails((HMIXEROBJ) info->handle, details, MIXER_GETCONTROLDETAILSF_LISTTEXT | MIXER_OBJECTF_HMIXER) != MMSYSERR_NOERROR) { ERROR0("createMuxControl: unable to get control details!\n"); continue; } // prevent freeing this data details->paDetails = NULL; // go through all mux items. If the line matches, then add a BOOLEAN select control for (c = 0; c < details->cMultipleItems; c++) { if (listTextDetails[c].dwParam1 == srcLineID) { // we have found the line in the MUX lines. controlID->muxIndex = c; details->cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); // now look if any other controlID was already part of this MUX line for (m = 0; m < info->usedControlIDs; m++) { if (info->controlIDs[m].details.dwControlID == details->dwControlID) { // reuse the MUX Data TRACE2("Reusing paDetails=%p of controlID[%d]\n", info->controlIDs[m].details.paDetails, m); details->paDetails = info->controlIDs[m].details.paDetails; break; } } if (!details->paDetails) { // first time this MUX control is used, allocate some of the muxData details->paDetails = &(info->muxData[info->usedMuxData]); TRACE2("Setting paDetails=%p to muxData[%d] \n", details->paDetails, info->usedMuxData); info->usedMuxData += details->cMultipleItems; } // finally this line can be added controlObjects[*controlCount] = (creator->newBooleanControl)(creator, controlID, CONTROL_TYPE_SELECT); (*controlCount)++; info->usedControlIDs++; break; } } } } } if (listTextDetails) { free(listTextDetails); listTextDetails = NULL; } if (controlInfos.pamxctrl) { free(controlInfos.pamxctrl); controlInfos.pamxctrl = NULL; } TRACE0("szName; float min; 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; controlID->details.cbStruct = sizeof(MIXERCONTROLDETAILS); controlID->details.dwControlID = mixerControl->dwControlID; controlID->details.cChannels = 1; // uniform controlID->details.cMultipleItems = 0; switch (type) { case PORT_CONTROL_TYPE_BOOLEAN: TRACE0(" PORT_CONTROL_TYPE_BOOLEAN\n"); controlID->details.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); controlID->details.paDetails = &(controlID->boolValue); if (mixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE) { typeName = CONTROL_TYPE_MUTE; } newControl = (creator->newBooleanControl)(creator, controlID, typeName); break; case PORT_CONTROL_TYPE_SIGNED: TRACE0(" PORT_CONTROL_TYPE_SIGNED\n"); controlID->details.cbDetails = sizeof(MIXERCONTROLDETAILS_SIGNED); controlID->details.paDetails = &(controlID->signedValue); controlID->min = (INT32) mixerControl->Bounds.lMinimum; controlID->max = (INT32) mixerControl->Bounds.lMaximum; if (mixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_PAN) { typeName = CONTROL_TYPE_PAN; } newControl = (creator->newFloatControl)(creator, controlID, typeName, -1.0f, 1.0f, 2.0f / (controlID->max - controlID->min + 1), ""); break; case PORT_CONTROL_TYPE_FAKE_VOLUME: // fall through case PORT_CONTROL_TYPE_FAKE_BALANCE: // fall through case PORT_CONTROL_TYPE_UNSIGNED: TRACE0(" PORT_CONTROL_TYPE_UNSIGNED\n"); controlID->details.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); controlID->details.paDetails = &(controlID->unsignedValue[0]); controlID->min = (INT32) mixerControl->Bounds.dwMinimum; controlID->max = (INT32) mixerControl->Bounds.dwMaximum; min = 0.0f; if ((type == PORT_CONTROL_TYPE_FAKE_VOLUME) || (mixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)) { typeName = CONTROL_TYPE_VOLUME; } if (type == PORT_CONTROL_TYPE_FAKE_BALANCE) { typeName = CONTROL_TYPE_BALANCE; min = -1.0f; } if ((type == PORT_CONTROL_TYPE_FAKE_VOLUME) || (type == PORT_CONTROL_TYPE_FAKE_BALANCE)) { controlID->details.cChannels = 2; } TRACE0(" ....PORT_CONTROL_TYPE_UNSIGNED\n"); newControl = (creator->newFloatControl)(creator, controlID, typeName, min, 1.0f, 1.0f / (controlID->max - controlID->min + 1), ""); break; default: ERROR1("createPortControl: unknown type %d !", type); break; } if (newControl) { controlObjects[*controlCount] = newControl; (*controlCount)++; info->usedControlIDs++; } TRACE0("createLineControls for line %s\n", line->szName); // go through all controls of line controlInfos.pamxctrl = NULL; if (getControlInfo(info->handle, line, &controlInfos)) { for (i = 0; i < controlInfos.cControls; i++) { TRACE1(" %d\n", i); mixerControl = &(controlInfos.pamxctrl[i]); type = 0; switch (mixerControl->dwControlType) { case MIXERCONTROL_CONTROLTYPE_BOOLEAN : // fall through case MIXERCONTROL_CONTROLTYPE_BUTTON : // fall through case MIXERCONTROL_CONTROLTYPE_LOUDNESS : // fall through case MIXERCONTROL_CONTROLTYPE_MONO : // fall through case MIXERCONTROL_CONTROLTYPE_MUTE : // fall through case MIXERCONTROL_CONTROLTYPE_ONOFF : // fall through case MIXERCONTROL_CONTROLTYPE_STEREOENH: type = PORT_CONTROL_TYPE_BOOLEAN; break; case MIXERCONTROL_CONTROLTYPE_PAN : // fall through case MIXERCONTROL_CONTROLTYPE_QSOUNDPAN: // fall through case MIXERCONTROL_CONTROLTYPE_SLIDER : type = PORT_CONTROL_TYPE_SIGNED; break; case MIXERCONTROL_CONTROLTYPE_BASS : // fall through //case MIXERCONTROL_CONTROLTYPE_EQUALIZER: // fall through case MIXERCONTROL_CONTROLTYPE_FADER : // fall through case MIXERCONTROL_CONTROLTYPE_TREBLE : type = PORT_CONTROL_TYPE_UNSIGNED; break; case MIXERCONTROL_CONTROLTYPE_VOLUME : type = PORT_CONTROL_TYPE_UNSIGNED; if (line->cChannels == 2 && ((mixerControl->fdwControl & MIXERCONTROL_CONTROLF_UNIFORM) == 0)) { type = PORT_CONTROL_TYPE_FAKE_VOLUME; } break; } if (type != 0) { createPortControl(info, creator, mixerControl, type, controlObjects, controlCount); // create fake balance for fake volume if (type == PORT_CONTROL_TYPE_FAKE_VOLUME) { createPortControl(info, creator, mixerControl, PORT_CONTROL_TYPE_FAKE_BALANCE, controlObjects, controlCount); } } } } if (controlInfos.pamxctrl) { free(controlInfos.pamxctrl); controlInfos.pamxctrl = NULL; } 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)) { line = info->ports[portIndex]; if (line) { // if the memory isn't reserved for the control structures, allocate it if (!info->controlIDs) { int i, maxCount = 0, muxCount = 0; TRACE0("getControl: allocate mem\n"); // get a maximum number of controls // first for all destination lines for (i = 0; i < info->dstLineCount; i++) { MIXERLINE* thisLine = &(info->dstLines[i]); maxCount += getControlCount(info->handle, thisLine, &muxCount); } // then all source lines for (i = 0; i < info->srcLineCount; i++) { MIXERLINE* thisLine = &(info->srcLines[i]); maxCount += getControlCount(info->handle, thisLine, &muxCount); } info->maxControlCount = maxCount; if (maxCount > 0) { info->controlIDs = (PortControlID*) malloc(sizeof(PortControlID) * maxCount); } else { // no ports: nothing to do ! return; } TRACE2("Creating muxData for %d elements and %d controlIDs.\n", muxCount, maxCount); if (muxCount > 0) { info->muxData = (MIXERCONTROLDETAILS_BOOLEAN*) malloc(sizeof(MIXERCONTROLDETAILS_BOOLEAN) * muxCount); } if (!info->controlIDs || (muxCount && !info->muxData)) { ERROR3("PORT_GetControls: info->controlIDs=%p, muxCount=%d, info->muxData=%p !!\n", info->controlIDs, muxCount, info->muxData); return; } } if (info->maxControlCount == 0) { return; } controls = (void*) malloc(info->maxControlCount * sizeof(void*)); if (!controls) { ERROR0("PORT_GetControls: couldn't allocate controls!\n"); return; } // add controls of this line controlCount = 0; // if this line is part of MUX, add the respective BOOLEANCONTROL as a control if ((line->fdwLine & MIXERLINE_LINEF_SOURCE) == MIXERLINE_LINEF_SOURCE) { MIXERLINE* dstLine = findDestLine(info, line->dwDestination); TRACE0("Port_getControls: this is a source line\n"); if (dstLine) { // selection controls (implemented as Mute control) createMuxControl(info, creator, dstLine, line->dwLineID, controls, &controlCount); } // then add all controls in one compound control createLineControls(info, creator, line, controls, &controlCount); addCompoundControl(info, creator, line->szName, controls, &controlCount); } else { TRACE0("getControl: this is a dest line\n"); // if this is a destination line, add its controls createLineControls(info, creator, line, controls, &controlCount); addAllControls(info, creator, controls, &controlCount); // then add all controls of its source lines as one compound control for (i = 0; i < line->cConnections; i++) { // then add all controls MIXERLINE* srcLine = &(info->srcLines[line->dwUser + i]); TRACE1("PORT_getControls: add source line %d\n", i); createLineControls(info, creator, srcLine, controls, &controlCount); addCompoundControl(info, creator, srcLine->szName, controls, &controlCount); } } } } if (controls) { free(controls); } TRACE0("< PORT_getControls\n"); } int getControlValue(PortControlID* controlID) { if (mixerGetControlDetails((HMIXEROBJ) controlID->portInfo->handle, &(controlID->details), MIXER_GETCONTROLDETAILSF_VALUE | MIXER_OBJECTF_HMIXER) != MMSYSERR_NOERROR) { ERROR0("getControlValue: unable to get control details!\n"); //ERROR3(" cbStruct=%d, dwControlID=%d, cChannels=%d, ", controlID->details.cbStruct, controlID->details.dwControlID, controlID->details.cChannels); //ERROR2(" cMultipleItems=%d, cbDetails=%d\n", controlID->details.cMultipleItems, controlID->details.cbDetails); return FALSE; } return TRUE; } int setControlValue(PortControlID* controlID) { if (mixerSetControlDetails((HMIXEROBJ) controlID->portInfo->handle, &(controlID->details), MIXER_SETCONTROLDETAILSF_VALUE | MIXER_OBJECTF_HMIXER) != MMSYSERR_NOERROR) { ERROR0("setControlValue: unable to set control details!\n"); //ERROR3(" cbStruct=%d, dwControlID=%d, cChannels=%d, ", controlID->details.cbStruct, controlID->details.dwControlID, controlID->details.cChannels); //ERROR2(" cMultipleItems=%d, cbDetails=%d\n", controlID->details.cMultipleItems, controlID->details.cbDetails); return FALSE; } return TRUE; } INT32 PORT_GetIntValue(void* controlIDV) { PortControlID* controlID = (PortControlID*) controlIDV; MIXERCONTROLDETAILS_BOOLEAN* bools; int ret = 0; if (getControlValue(controlID)) { switch (controlID->controlType) { case PORT_CONTROL_TYPE_MUX: // fall through case PORT_CONTROL_TYPE_MIXER: bools = (MIXERCONTROLDETAILS_BOOLEAN*) controlID->details.paDetails; ret = (bools[controlID->muxIndex].fValue)?TRUE:FALSE; break; case PORT_CONTROL_TYPE_BOOLEAN: ret = (controlID->boolValue.fValue)?TRUE:FALSE; break; default: ERROR1("PORT_GetIntValue: wrong controlType=%d !\n", controlID->controlType); } } return ret; } void PORT_SetIntValue(void* controlIDV, INT32 value) { PortControlID* controlID = (PortControlID*) controlIDV; MIXERCONTROLDETAILS_BOOLEAN* bools; UINT i; switch (controlID->controlType) { case PORT_CONTROL_TYPE_MUX: if (!value) { // cannot unselect a MUX line return; } if (!getControlValue(controlID)) { return; } bools = (MIXERCONTROLDETAILS_BOOLEAN*) controlID->details.paDetails; for (i = 0; i < controlID->details.cMultipleItems; i++) { bools[i].fValue = (i == (UINT) controlID->muxIndex)?TRUE:FALSE; } break; case PORT_CONTROL_TYPE_MIXER: if (!getControlValue(controlID)) { return; } bools = (MIXERCONTROLDETAILS_BOOLEAN*) controlID->details.paDetails; bools[controlID->muxIndex].fValue = (value?TRUE:FALSE); break; case PORT_CONTROL_TYPE_BOOLEAN: controlID->boolValue.fValue = (value?TRUE:FALSE); break; default: ERROR1("PORT_SetIntValue: wrong controlType=%d !\n", controlID->controlType); return; } setControlValue(controlID); } float getFakeBalance(PortControlID* controlID) { float volL, volR; float range = (float) (controlID->max - controlID->min); // pan is the ratio of left and right volL = (((float) (controlID->unsignedValue[0].dwValue - controlID->min)) / range); volR = (((float) (controlID->unsignedValue[1].dwValue - controlID->min)) / range); if (volL > volR) { return -1.0f + (volR / volL); } else if (volR > volL) { return 1.0f - (volL / volR); } return 0.0f; } float getFakeVolume(PortControlID* controlID) { // volume is the greater value of both UINT vol = controlID->unsignedValue[0].dwValue; if (controlID->unsignedValue[1].dwValue > vol) { vol = controlID->unsignedValue[1].dwValue; } return (((float) (vol - controlID->min)) / (controlID->max - controlID->min)); } /* * sets the unsigned values for left and right volume according to * the given volume (0...1) and balance (-1..0..+1) */ void setFakeVolume(PortControlID* controlID, float vol, float bal) { vol = vol * (controlID->max - controlID->min); if (bal < 0.0f) { controlID->unsignedValue[0].dwValue = (UINT) (vol + 0.5f) + controlID->min; controlID->unsignedValue[1].dwValue = (UINT) ((vol * (bal + 1.0f)) + 0.5f) + controlID->min; } else { controlID->unsignedValue[1].dwValue = (UINT) (vol + 0.5f) + controlID->min; controlID->unsignedValue[0].dwValue = (UINT) ((vol * (1.0f - bal)) + 0.5f) + controlID->min; } } float PORT_GetFloatValue(void* controlIDV) { PortControlID* controlID = (PortControlID*) controlIDV; float ret = 0.0f; float range = (float) (controlID->max - controlID->min); if (getControlValue(controlID)) { switch (controlID->controlType) { case PORT_CONTROL_TYPE_SIGNED: ret = ((float) controlID->signedValue.lValue) / controlID->max; break; case PORT_CONTROL_TYPE_UNSIGNED: ret = (((float) (controlID->unsignedValue[0].dwValue - controlID->min)) / range); break; case PORT_CONTROL_TYPE_FAKE_VOLUME: ret = getFakeVolume(controlID); break; case PORT_CONTROL_TYPE_FAKE_BALANCE: ret = getFakeBalance(controlID); break; default: ERROR1("PORT_GetFloatValue: wrong controlType=%d !\n", controlID->controlType); } } return ret; } void PORT_SetFloatValue(void* controlIDV, float value) { PortControlID* controlID = (PortControlID*) controlIDV; float range = (float) (controlID->max - controlID->min); switch (controlID->controlType) { case PORT_CONTROL_TYPE_SIGNED: controlID->signedValue.lValue = (INT32) ((value * controlID->max) + 0.5f); break; case PORT_CONTROL_TYPE_UNSIGNED: controlID->unsignedValue[0].dwValue = (INT32) ((value * range) + 0.5f) + controlID->min; break; case PORT_CONTROL_TYPE_FAKE_VOLUME: if (!getControlValue(controlID)) { return; } setFakeVolume(controlID, value, getFakeBalance(controlID)); break; case PORT_CONTROL_TYPE_FAKE_BALANCE: if (!getControlValue(controlID)) { return; } setFakeVolume(controlID, getFakeVolume(controlID), value); break; default: ERROR1("PORT_SetFloatValue: wrong controlType=%d !\n", controlID->controlType); return; } setControlValue(controlID); } #endif // USE_PORTS