alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (PLATFORM_API_WinOS_Ports.c)

This example Java source code file (PLATFORM_API_WinOS_Ports.c) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

false, int32, mixer_objectf_hmixer, mixercontroldetails_boolean, mixerline, mixerlinecontrols, mmsyserr_noerror, null, port_control_type_fake_volume, portcontrolid, portinfo, trace0, true, uint

The PLATFORM_API_WinOS_Ports.c Java example source code

/*
 * 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 <windows.h>
#include <mmsystem.h>
#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("<getControlInfo \n");
    return ret;
}

// returns TRUE if there are more than MIXER/MUX controls in this line
// if controls is non-NULL, it will be filled with the info
int lineHasControls(HMIXER handle, MIXERLINE* line, MIXERLINECONTROLS* controls) {
    MIXERLINECONTROLS localControls;
    int ret = FALSE;
    UINT i;

    localControls.pamxctrl = NULL;
    if (controls == NULL) {
        controls = &localControls;
    }
    if (getControlInfo(handle, line, controls)) {
        for (i = 0; !ret && (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 : 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("<findDestLine\n");
            return &(info->dstLines[i]);
        }
    }
    TRACE0("<findDestLine NULL\n");
    return NULL;
}

void createMuxControl(PortInfo* info, PortControlCreator* creator, MIXERLINE* dstLine, DWORD srcLineID, void** controlObjects, int* controlCount) {
    MIXERLINECONTROLS controlInfos;
    MIXERCONTROLDETAILS* details;
    MIXERCONTROLDETAILS_LISTTEXT* listTextDetails = NULL;
    UINT listTextDetailCount = 0;
    PortControlID* controlID;
    UINT i, c;
    int m;

    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("<createMuxControl ERROR\n");
                        return;
                    }
                }
                details->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("<createMuxControl\n");
}

void createPortControl(PortInfo* info, PortControlCreator* creator, MIXERCONTROL* mixerControl,
                       INT32 type, void** controlObjects, int* controlCount) {
    PortControlID* controlID;
    void* newControl = NULL;
    char* typeName = mixerControl->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("<createPortControl\n");
}

void createLineControls(PortInfo* info, PortControlCreator* creator, MIXERLINE* line, void** controlObjects, int* controlCount) {
    MIXERLINECONTROLS controlInfos;
    MIXERCONTROL* mixerControl;
    UINT i;
    INT32 type;

    TRACE1(">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("<createLineControls\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) {
    MIXERLINE* line;
    PortInfo* info = (PortInfo*) id;
    int portCount = PORT_GetPortCount(id);
    void** controls = NULL;
    int controlCount;
    UINT i;

    TRACE4(">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

Other Java examples (source code examples)

Here is a short list of links related to this Java PLATFORM_API_WinOS_Ports.c source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.