1 /*
2  * Copyright (c) 2002, 2007, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 
27 #define USE_ERROR
28 //#define USE_TRACE
29 
30 #ifndef WIN32_EXTRA_LEAN
31 #define WIN32_EXTRA_LEAN
32 #endif
33 #ifndef WIN32_LEAN_AND_MEAN
34 #define WIN32_LEAN_AND_MEAN
35 #endif
36 
37 #include <windows.h>
38 #include <mmsystem.h>
39 #include "Ports.h"
40 
41 /* include to prevent charset problem */
42 #include "PLATFORM_API_WinOS_Charset_Util.h"
43 
44 #if USE_PORTS == TRUE
45 
46 typedef struct tag_PortControlID PortControlID;
47 
48 typedef struct tag_PortInfo {
49     // Windows API stuff
50     HMIXER handle;
51     INT32 mixerIndex;
52     int dstLineCount;        // how many MIXERLINE structs in dstMixerLine
53     MIXERLINE* dstLines;
54     int srcLineCount;        // how many MIXERLINE structs in srcMixerLine
55     MIXERLINE* srcLines;     // contains all the Source Lines of dstLines
56     // Java Sound mapping
57     int targetPortCount;     // one port per dstLine (playback)
58     int sourcePortCount;     // only WAVEIN; one port maps to one srcLine
59     LPMIXERLINE* ports;      // points into dstLines and dstLines. Starts with Target Ports (Playback)
60     int maxControlCount;       // upper bound of number of controls
61     int usedControlIDs;        // number of items already filled in controlIDs
62     PortControlID* controlIDs; // the control IDs themselves
63     int usedMuxData;
64     MIXERCONTROLDETAILS_BOOLEAN* muxData;
65 } PortInfo;
66 
67 #define PORT_CONTROL_TYPE_BOOLEAN     1
68 #define PORT_CONTROL_TYPE_SIGNED      2
69 #define PORT_CONTROL_TYPE_UNSIGNED    3
70 //#define PORT_CONTROL_TYPE_UNSIGNED_DB 4
71 #define PORT_CONTROL_TYPE_FAKE_VOLUME 5
72 #define PORT_CONTROL_TYPE_FAKE_BALANCE 6
73 #define PORT_CONTROL_TYPE_MUX         5
74 #define PORT_CONTROL_TYPE_MIXER       6
75 
76 typedef struct tag_PortControlID {
77     PortInfo*           portInfo;
78     INT32               controlType;  // one of PORT_CONTROL_TYPE_XX
79     INT32               min;
80     INT32               max;
81     MIXERCONTROLDETAILS details;
82     union {
83         MIXERCONTROLDETAILS_BOOLEAN  boolValue;
84         MIXERCONTROLDETAILS_SIGNED   signedValue;
85         MIXERCONTROLDETAILS_UNSIGNED unsignedValue[2];
86         INT32                        muxIndex;
87     };
88 } PortControlID;
89 
90 
91 int getControlInfo(HMIXER handle, MIXERLINE* line, MIXERLINECONTROLS* controls);
92 
PORT_GetPortMixerCount()93 INT32 PORT_GetPortMixerCount() {
94     return (INT32) mixerGetNumDevs();
95 }
96 
97 #ifdef USE_TRACE
98 
getLineFlags(DWORD flags)99 char* getLineFlags(DWORD flags) {
100     static char ret[100];
101     ret[0]=0;
102     if (flags & MIXERLINE_LINEF_ACTIVE) {
103         strcat(ret, "ACTIVE ");
104         flags ^= MIXERLINE_LINEF_ACTIVE;
105     }
106     if (flags & MIXERLINE_LINEF_DISCONNECTED) {
107         strcat(ret, "DISCONNECTED ");
108         flags ^= MIXERLINE_LINEF_DISCONNECTED;
109     }
110     if (flags & MIXERLINE_LINEF_SOURCE) {
111         strcat(ret, "SOURCE ");
112         flags ^= MIXERLINE_LINEF_SOURCE;
113     }
114     if (flags!=0) {
115         UINT_PTR r = (UINT_PTR) ret;
116         r += strlen(ret);
117         sprintf((char*) r, "%d", flags);
118     }
119     return ret;
120 }
121 
getComponentType(int componentType)122 char* getComponentType(int componentType) {
123     switch (componentType) {
124         case MIXERLINE_COMPONENTTYPE_DST_HEADPHONES:   return "DST_HEADPHONES";
125         case MIXERLINE_COMPONENTTYPE_DST_LINE:         return "DST_LINE";
126         case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS:     return "DST_SPEAKERS";
127         case MIXERLINE_COMPONENTTYPE_DST_DIGITAL:      return "DST_DIGITAL";
128         case MIXERLINE_COMPONENTTYPE_DST_MONITOR:      return "DST_MONITOR";
129         case MIXERLINE_COMPONENTTYPE_DST_TELEPHONE:    return "DST_TELEPHONE";
130         case MIXERLINE_COMPONENTTYPE_DST_UNDEFINED:    return "DST_UNDEFINED";
131         case MIXERLINE_COMPONENTTYPE_DST_VOICEIN:      return "DST_VOICEIN";
132         case MIXERLINE_COMPONENTTYPE_DST_WAVEIN:       return "DST_WAVEIN";
133 
134         case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC:  return "SRC_COMPACTDISC";
135         case MIXERLINE_COMPONENTTYPE_SRC_LINE:         return "SRC_LINE";
136         case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE:   return "SRC_MICROPHONE";
137         case MIXERLINE_COMPONENTTYPE_SRC_ANALOG:       return "SRC_ANALOG";
138         case MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY:    return "SRC_AUXILIARY";
139         case MIXERLINE_COMPONENTTYPE_SRC_DIGITAL:      return "SRC_DIGITAL";
140         case MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER:    return "SRC_PCSPEAKER";
141         case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER:  return "SRC_SYNTHESIZER";
142         case MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE:    return "SRC_TELEPHONE";
143         case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED:    return "SRC_UNDEFINED";
144         case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT:      return "SRC_WAVEOUT";
145     }
146     return "";
147 }
148 
printMixerLine(MIXERLINE * mixerLine)149 void printMixerLine(MIXERLINE* mixerLine) {
150     TRACE2("MIXERLINE destination=%d, source=%d, ", mixerLine->dwDestination, mixerLine->dwSource);
151     TRACE3("channels=%d, connections=%d, controls=%d, ", mixerLine->cChannels, mixerLine->cConnections, mixerLine->cControls);
152     TRACE3("\"%s\", fdwLine=%s, componentType=%s\n", mixerLine->szName,  getLineFlags(mixerLine->fdwLine), getComponentType(mixerLine->dwComponentType));
153 }
154 
getControlClass(int controlType)155 char* getControlClass(int controlType) {
156     switch (controlType & MIXERCONTROL_CT_CLASS_MASK) {
157         case MIXERCONTROL_CT_CLASS_CUSTOM : return "CLASS_CUSTOM";
158         case MIXERCONTROL_CT_CLASS_FADER  : return "CLASS_FADER ";
159         case MIXERCONTROL_CT_CLASS_LIST   : return "CLASS_LIST  ";
160         case MIXERCONTROL_CT_CLASS_METER  : return "CLASS_METER ";
161         case MIXERCONTROL_CT_CLASS_NUMBER : return "CLASS_NUMBER";
162         case MIXERCONTROL_CT_CLASS_SLIDER : return "CLASS_SLIDER";
163         case MIXERCONTROL_CT_CLASS_SWITCH : return "CLASS_SWITCH";
164         case MIXERCONTROL_CT_CLASS_TIME   : return "CLASS_TIME  ";
165     }
166     return "unknown class";
167 }
168 
getControlType(int controlType)169 char* getControlType(int controlType) {
170     switch (controlType) {
171         case MIXERCONTROL_CONTROLTYPE_CUSTOM          : return "CUSTOM         ";
172         case MIXERCONTROL_CONTROLTYPE_BASS            : return "BASS           ";
173         case MIXERCONTROL_CONTROLTYPE_EQUALIZER       : return "EQUALIZER      ";
174         case MIXERCONTROL_CONTROLTYPE_FADER           : return "FADER          ";
175         case MIXERCONTROL_CONTROLTYPE_TREBLE          : return "TREBLE         ";
176         case MIXERCONTROL_CONTROLTYPE_VOLUME          : return "VOLUME         ";
177         case MIXERCONTROL_CONTROLTYPE_MIXER           : return "MIXER          ";
178         case MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT  : return "MULTIPLESELECT ";
179         case MIXERCONTROL_CONTROLTYPE_MUX             : return "MUX            ";
180         case MIXERCONTROL_CONTROLTYPE_SINGLESELECT    : return "SINGLESELECT   ";
181         case MIXERCONTROL_CONTROLTYPE_BOOLEANMETER    : return "BOOLEANMETER   ";
182         case MIXERCONTROL_CONTROLTYPE_PEAKMETER       : return "PEAKMETER      ";
183         case MIXERCONTROL_CONTROLTYPE_SIGNEDMETER     : return "SIGNEDMETER    ";
184         case MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER   : return "UNSIGNEDMETER  ";
185         case MIXERCONTROL_CONTROLTYPE_DECIBELS        : return "DECIBELS       ";
186         case MIXERCONTROL_CONTROLTYPE_PERCENT         : return "PERCENT        ";
187         case MIXERCONTROL_CONTROLTYPE_SIGNED          : return "SIGNED         ";
188         case MIXERCONTROL_CONTROLTYPE_UNSIGNED        : return "UNSIGNED       ";
189         case MIXERCONTROL_CONTROLTYPE_PAN             : return "PAN            ";
190         case MIXERCONTROL_CONTROLTYPE_QSOUNDPAN       : return "QSOUNDPAN      ";
191         case MIXERCONTROL_CONTROLTYPE_SLIDER          : return "SLIDER         ";
192         case MIXERCONTROL_CONTROLTYPE_BOOLEAN         : return "BOOLEAN        ";
193         case MIXERCONTROL_CONTROLTYPE_BUTTON          : return "BUTTON         ";
194         case MIXERCONTROL_CONTROLTYPE_LOUDNESS        : return "LOUDNESS       ";
195         case MIXERCONTROL_CONTROLTYPE_MONO            : return "MONO           ";
196         case MIXERCONTROL_CONTROLTYPE_MUTE            : return "MUTE           ";
197         case MIXERCONTROL_CONTROLTYPE_ONOFF           : return "ONOFF          ";
198         case MIXERCONTROL_CONTROLTYPE_STEREOENH       : return "STEREOENH      ";
199         case MIXERCONTROL_CONTROLTYPE_MICROTIME       : return "MICROTIME      ";
200         case MIXERCONTROL_CONTROLTYPE_MILLITIME       : return "MILLITIME      ";
201     }
202     return "unknown";
203 }
204 
getControlState(DWORD controlState)205 char* getControlState(DWORD controlState) {
206     static char ret[100];
207     ret[0]=0;
208     if (controlState & MIXERCONTROL_CONTROLF_DISABLED) {
209         strcat(ret, "DISABLED ");
210         controlState ^= MIXERCONTROL_CONTROLF_DISABLED;
211     }
212     if (controlState & MIXERCONTROL_CONTROLF_MULTIPLE) {
213         strcat(ret, "MULTIPLE ");
214         controlState ^= MIXERCONTROL_CONTROLF_MULTIPLE;
215     }
216     if (controlState & MIXERCONTROL_CONTROLF_UNIFORM) {
217         strcat(ret, "UNIFORM ");
218         controlState ^= MIXERCONTROL_CONTROLF_UNIFORM;
219     }
220     if (controlState!=0) {
221         UINT_PTR r = (UINT_PTR) ret;
222         r += strlen(ret);
223         sprintf((char*) r, "%d", controlState);
224     }
225     return ret;
226 }
227 
printControl(MIXERCONTROL * control)228 void printControl(MIXERCONTROL* control) {
229     TRACE3("    %s: dwControlType=%s/%s, ", control->szName, getControlClass(control->dwControlType), getControlType(control->dwControlType));
230     TRACE3("multpleItems=%d, state=%d, %s\n", control->cMultipleItems, control->fdwControl, getControlState(control->fdwControl));
231 }
232 
printMixerLineControls(HMIXER handle,MIXERLINE * mixerLine)233 void printMixerLineControls(HMIXER handle, MIXERLINE* mixerLine) {
234     MIXERLINECONTROLS controls;
235     DWORD i;
236     TRACE1("  Controls for %s:\n", mixerLine->szName);
237     if (getControlInfo(handle, mixerLine, &controls)) {
238         for (i = 0; i < controls.cControls; i++) {
239             printControl(&controls.pamxctrl[i]);
240         }
241         if (controls.pamxctrl) {
242             free(controls.pamxctrl);
243             controls.pamxctrl = NULL;
244         }
245     }
246 }
247 
printInfo(PortInfo * info)248 void printInfo(PortInfo* info) {
249     TRACE5(" PortInfo %p: handle=%p, mixerIndex=%d, dstLineCount=%d, dstLines=%p, ", info, (void*) info->handle, info->mixerIndex, info->dstLineCount, info->dstLines);
250     TRACE5("srcLineCount=%d, srcLines=%p, targetPortCount=%d, sourcePortCount=%d, ports=%p, ", info->srcLineCount, info->srcLines, info->targetPortCount, info->sourcePortCount, info->ports);
251     TRACE3("maxControlCount=%d, usedControlIDs=%d, controlIDs=%p \n", info->maxControlCount, info->usedControlIDs, info->controlIDs);
252     TRACE2("usedMuxData=%d, muxData=%p, controlIDs=%p \n", info->usedMuxData, info->muxData);
253 }
254 
255 #endif // USE_TRACE
256 
257 // internal utility functions
258 
getMixerLineByDestination(HMIXER handle,DWORD dstIndex,MIXERLINE * mixerLine)259 int getMixerLineByDestination(HMIXER handle, DWORD dstIndex, MIXERLINE* mixerLine) {
260     mixerLine->cbStruct = sizeof(MIXERLINE);
261     mixerLine->dwDestination = dstIndex;
262     if (mixerGetLineInfo((HMIXEROBJ) handle, mixerLine,
263                           MIXER_GETLINEINFOF_DESTINATION | MIXER_OBJECTF_HMIXER
264                          ) == MMSYSERR_NOERROR) {
265         return TRUE;
266     }
267     mixerLine->cControls = 0;
268     mixerLine->cConnections = 0;
269     return FALSE;
270 }
271 
getMixerLineByType(HMIXER handle,DWORD linetype,MIXERLINE * mixerLine)272 int getMixerLineByType(HMIXER handle, DWORD linetype, MIXERLINE* mixerLine) {
273     mixerLine->cbStruct = sizeof(MIXERLINE);
274     mixerLine->dwComponentType = linetype;
275     if (mixerGetLineInfo((HMIXEROBJ) handle, mixerLine,
276                           MIXER_GETLINEINFOF_COMPONENTTYPE | MIXER_OBJECTF_HMIXER
277                          ) == MMSYSERR_NOERROR) {
278         return TRUE;
279     }
280     mixerLine->cControls = 0;
281     mixerLine->cConnections = 0;
282     return FALSE;
283 }
284 
getMixerLineBySource(HMIXER handle,DWORD dstIndex,DWORD srcIndex,MIXERLINE * mixerLine)285 int getMixerLineBySource(HMIXER handle, DWORD dstIndex, DWORD srcIndex, MIXERLINE* mixerLine) {
286     mixerLine->cbStruct = sizeof(MIXERLINE);
287     mixerLine->dwDestination = dstIndex;
288     mixerLine->dwSource = srcIndex;
289     if (mixerGetLineInfo((HMIXEROBJ) handle, mixerLine,
290                           MIXER_GETLINEINFOF_SOURCE | MIXER_OBJECTF_HMIXER
291                          ) == MMSYSERR_NOERROR) {
292         return TRUE;
293     }
294     mixerLine->cControls = 0;
295     mixerLine->cConnections = 0;
296     return FALSE;
297 }
298 
getControlInfo(HMIXER handle,MIXERLINE * line,MIXERLINECONTROLS * controls)299 int getControlInfo(HMIXER handle, MIXERLINE* line, MIXERLINECONTROLS* controls) {
300     int ret = FALSE;
301 
302     //TRACE2(">getControlInfo for line %s with %d controls\n", line->szName, line->cControls);
303     controls->pamxctrl = NULL;
304     if (line->cControls > 0) {
305         // line points to the requested line.
306         // Reserve memory for the control infos
307         controls->cbStruct = sizeof(MIXERLINECONTROLS);
308         controls->dwLineID = line->dwLineID;
309         controls->cControls = line->cControls;
310         controls->cbmxctrl = sizeof(MIXERCONTROL);
311         controls->pamxctrl = (MIXERCONTROL*) malloc(sizeof(MIXERCONTROL) * line->cControls);
312         if (controls->pamxctrl) {
313             //TRACE0(" calling mixerGetLineControls\n");
314             ret = mixerGetLineControls((HMIXEROBJ) handle, controls,
315                                        MIXER_GETLINECONTROLSF_ALL | MIXER_OBJECTF_HMIXER) == MMSYSERR_NOERROR;
316         }
317     }
318     if (!ret) {
319         if (controls->pamxctrl) {
320             free(controls->pamxctrl);
321             controls->pamxctrl = NULL;
322         }
323     }
324     //TRACE0("<getControlInfo \n");
325     return ret;
326 }
327 
328 // returns TRUE if there are more than MIXER/MUX controls in this line
329 // if controls is non-NULL, it will be filled with the info
lineHasControls(HMIXER handle,MIXERLINE * line,MIXERLINECONTROLS * controls)330 int lineHasControls(HMIXER handle, MIXERLINE* line, MIXERLINECONTROLS* controls) {
331     MIXERLINECONTROLS localControls;
332     int ret = FALSE;
333     UINT i;
334 
335     localControls.pamxctrl = NULL;
336     if (controls == NULL) {
337         controls = &localControls;
338     }
339     if (getControlInfo(handle, line, controls)) {
340         for (i = 0; !ret && (i < controls->cControls); i++) {
341             switch (controls->pamxctrl[i].dwControlType & MIXERCONTROL_CT_CLASS_MASK) {
342                 case MIXERCONTROL_CT_CLASS_FADER  : // fall through
343                 case MIXERCONTROL_CT_CLASS_SLIDER : // fall through
344                 case MIXERCONTROL_CT_CLASS_SWITCH : ret = TRUE;
345             }
346         }
347     }
348     if (localControls.pamxctrl) {
349         free(localControls.pamxctrl);
350         localControls.pamxctrl = NULL;
351     }
352     return ret;
353 }
354 
355 
356 ///// implemented functions of Ports.h
357 
PORT_GetPortMixerDescription(INT32 mixerIndex,PortMixerDescription * description)358 INT32 PORT_GetPortMixerDescription(INT32 mixerIndex, PortMixerDescription* description) {
359     MIXERCAPSW mixerCaps;
360     if (mixerGetDevCapsW(mixerIndex, &mixerCaps, sizeof(MIXERCAPSW)) == MMSYSERR_NOERROR) {
361         UnicodeToUTF8AndCopy(description->name, mixerCaps.szPname, PORT_STRING_LENGTH);
362         sprintf(description->version, "%d.%d", (mixerCaps.vDriverVersion & 0xFF00) >> 8, mixerCaps.vDriverVersion & 0xFF);
363         strncpy(description->description, "Port Mixer", PORT_STRING_LENGTH-1);
364         return TRUE;
365     }
366     return FALSE;
367 }
368 
getDestinationCount(HMIXER handle)369 int getDestinationCount(HMIXER handle) {
370     int ret = 0;
371     MIXERCAPSW mixerCaps;
372 
373     if (mixerGetDevCapsW((UINT_PTR) handle, &mixerCaps, sizeof(MIXERCAPSW)) == MMSYSERR_NOERROR) {
374         ret = mixerCaps.cDestinations;
375     }
376     return ret;
377 }
378 
PORT_Open(INT32 mixerIndex)379 void* PORT_Open(INT32 mixerIndex) {
380     PortInfo* info = NULL;
381     MMRESULT mmres;
382     HMIXER handle;
383     MIXERLINE* waveInLine;
384     int success = FALSE;
385     int src, dst, srcIndex, waveInHasControls;
386     int dstCount;
387 
388     TRACE0("PORT_Open\n");
389     mmres = mixerOpen((LPHMIXER) &handle, mixerIndex, 0, 0, MIXER_OBJECTF_MIXER);
390     if (mmres != MMSYSERR_NOERROR) {
391         return NULL;
392     }
393 
394     info = (PortInfo*) malloc(sizeof(PortInfo));
395     if (info != NULL) {
396         success = TRUE;
397         memset(info, 0, sizeof(PortInfo));
398         info->handle = handle;
399         info->mixerIndex = mixerIndex;
400         waveInLine = NULL;
401         waveInHasControls = FALSE;
402         // number of destinations
403         dstCount = getDestinationCount(handle);
404         if (dstCount) {
405             info->dstLines = (MIXERLINE*) malloc(dstCount * sizeof(MIXERLINE));
406             success = (info->dstLines != NULL);
407         }
408         if (success && info->dstLines) {
409             // go through all destinations and fill the structures in PortInfo
410             for (dst = 0; dst < dstCount; dst++) {
411                 if (getMixerLineByDestination(handle, dst, &info->dstLines[info->dstLineCount])) {
412                     info->srcLineCount += info->dstLines[info->dstLineCount].cConnections;
413                     if (info->dstLines[info->dstLineCount].dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN && !waveInLine) {
414                         waveInLine = &info->dstLines[info->dstLineCount];
415                         info->sourcePortCount = waveInLine->cConnections;
416                         if (lineHasControls(handle, waveInLine, NULL)) {
417                             // add a single port for all the controls that do not show in the MUX/MIXER controls
418                             info->sourcePortCount++;
419                             waveInHasControls = TRUE;
420                         }
421                     } else {
422                         info->targetPortCount++;
423                     }
424                     info->dstLineCount++;
425                 }
426             }
427         }
428         if (info->srcLineCount) {
429             info->srcLines = (MIXERLINE*) malloc(info->srcLineCount * sizeof(MIXERLINE));
430             success = (info->srcLines != NULL);
431         }
432         if (success && info->srcLines) {
433             // go through all destinations and fill the source line structures in PortInfo
434             srcIndex = 0;
435             for (dst = 0; dst < info->dstLineCount; dst++) {
436                 // remember the srcIndex for mapping the srcLines to this destination line
437                 info->dstLines[dst].dwUser = srcIndex;
438                 for (src = 0; src < (int) info->dstLines[dst].cConnections; src++) {
439                     getMixerLineBySource(handle, dst, src, &info->srcLines[srcIndex++]);
440                 }
441             }
442         }
443         // now create the mapping to Java Sound
444         if ((info->targetPortCount + info->sourcePortCount) > 0) {
445             info->ports = (LPMIXERLINE*) malloc((info->targetPortCount + info->sourcePortCount) * sizeof(LPMIXERLINE));
446             success = (info->ports != NULL);
447         }
448         if (success && info->ports) {
449             // first add the target MIXERLINEs to the array
450             srcIndex = 0;
451             for (dst = 0; dst < info->dstLineCount; dst++) {
452                 if (waveInLine != &info->dstLines[dst]) {
453                     info->ports[srcIndex++] = &info->dstLines[dst];
454                 }
455             }
456             if (srcIndex != info->targetPortCount) {
457                 ERROR2("srcIndex=%d is NOT targetPortCount=%d !\n", srcIndex, info->targetPortCount);
458             }
459             //srcIndex = info->targetPortCount; // should be automatic!
460             if (waveInLine) {
461                 // if the recording destination line has controls, add the line
462                 if (waveInHasControls) {
463                     info->ports[srcIndex++] = waveInLine;
464                 }
465                 for (src = 0; src < (int) waveInLine->cConnections; src++) {
466                     info->ports[srcIndex++] = &info->srcLines[src + waveInLine->dwUser];
467                 }
468             }
469             if (srcIndex != (info->targetPortCount + info->sourcePortCount)) {
470                 ERROR2("srcIndex=%d is NOT PortCount=%d !\n", srcIndex, (info->targetPortCount + info->sourcePortCount));
471             }
472         }
473     }
474     if (!success) {
475         if (handle != NULL) {
476             mixerClose(handle);
477         }
478         PORT_Close((void*) info);
479         info = NULL;
480     }
481     return info;
482 }
483 
PORT_Close(void * id)484 void PORT_Close(void* id) {
485     TRACE0("PORT_Close\n");
486     if (id != NULL) {
487         PortInfo* info = (PortInfo*) id;
488         if (info->handle) {
489             mixerClose(info->handle);
490             info->handle = NULL;
491         }
492         if (info->dstLines) {
493             free(info->dstLines);
494             info->dstLines = NULL;
495         }
496         if (info->srcLines) {
497             free(info->srcLines);
498             info->srcLines=NULL;
499         }
500         if (info->ports) {
501             free(info->ports);
502             info->ports = NULL;
503         }
504         if (info->controlIDs) {
505             free(info->controlIDs);
506             info->controlIDs = NULL;
507         }
508         if (info->muxData) {
509             free(info->muxData);
510             info->muxData = NULL;
511         }
512         free(info);
513     }
514 }
515 
PORT_GetPortCount(void * id)516 INT32 PORT_GetPortCount(void* id) {
517     int ret = 0;
518     PortInfo* info = (PortInfo*) id;
519     if (info != NULL) {
520         ret = info->targetPortCount + info->sourcePortCount;
521     }
522     return ret;
523 }
524 
componentType2type(DWORD componentType)525 int componentType2type(DWORD componentType) {
526     int ret = 0;
527     if (componentType >= MIXERLINE_COMPONENTTYPE_DST_FIRST && componentType <= MIXERLINE_COMPONENTTYPE_DST_LAST) {
528         ret = PORT_DST_UNKNOWN;
529     }
530     else if (componentType >= MIXERLINE_COMPONENTTYPE_SRC_FIRST && componentType <= MIXERLINE_COMPONENTTYPE_SRC_LAST) {
531         ret = PORT_SRC_UNKNOWN;
532     }
533     // handle special cases
534     switch (componentType) {
535         case MIXERLINE_COMPONENTTYPE_DST_HEADPHONES:  ret = PORT_DST_HEADPHONE; break;
536         case MIXERLINE_COMPONENTTYPE_DST_LINE:        ret = PORT_DST_LINE_OUT; break;
537         case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS:    ret = PORT_DST_SPEAKER; break;
538         case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC: ret = PORT_SRC_COMPACT_DISC; break;
539         case MIXERLINE_COMPONENTTYPE_SRC_LINE:        ret = PORT_SRC_LINE_IN; break;
540         case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE:  ret = PORT_SRC_MICROPHONE; break;
541     }
542     return ret;
543 }
544 
PORT_GetPortType(void * id,INT32 portIndex)545 INT32 PORT_GetPortType(void* id, INT32 portIndex) {
546     MIXERLINE* line;
547     PortInfo* info = (PortInfo*) id;
548     if ((portIndex >= 0) && (portIndex < PORT_GetPortCount(id))) {
549         line = info->ports[portIndex];
550         if (line) {
551             return componentType2type(line->dwComponentType);
552         }
553     }
554     return 0;
555 }
556 
PORT_GetPortName(void * id,INT32 portIndex,char * name,INT32 len)557 INT32 PORT_GetPortName(void* id, INT32 portIndex, char* name, INT32 len) {
558     MIXERLINE* line;
559     PortInfo* info = (PortInfo*) id;
560 
561     if ((portIndex >= 0) && (portIndex < PORT_GetPortCount(id))) {
562         line = info->ports[portIndex];
563         if (line) {
564             strncpy(name, line->szName, len-1);
565             name[len-1] = 0;
566             return TRUE;
567         }
568     }
569     return FALSE;
570 }
571 
getControlCount(HMIXER handle,MIXERLINE * line,INT32 * muxCount)572 int getControlCount(HMIXER handle, MIXERLINE* line, INT32* muxCount) {
573     MIXERLINECONTROLS controls;
574     int ret = 0;
575     UINT i;
576 
577     controls.pamxctrl = NULL;
578     if (getControlInfo(handle, line, &controls)) {
579         for (i = 0; i < controls.cControls; i++) {
580             switch (controls.pamxctrl[i].dwControlType & MIXERCONTROL_CT_CLASS_MASK) {
581                 case MIXERCONTROL_CT_CLASS_FADER   : // fall through
582                 case MIXERCONTROL_CT_CLASS_SLIDER  : // fall through
583                 case MIXERCONTROL_CT_CLASS_SWITCH  : // fall through
584                 case MIXERCONTROL_CT_CLASS_LIST    : ret++; break;
585             }
586             if ((controls.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER)
587                  || (controls.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_MUX)) {
588                 ret += controls.pamxctrl[i].cMultipleItems;
589                 if (muxCount) {
590                     (*muxCount) += controls.pamxctrl[i].cMultipleItems;
591                 }
592             }
593             else if ((controls.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
594                     && (line->cChannels == 2)) {
595                 ret++; // for FAKE volume/balance pairs
596             }
597         }
598     }
599     if (controls.pamxctrl) {
600         free(controls.pamxctrl);
601         controls.pamxctrl = NULL;
602     }
603     return ret;
604 }
605 
findDestLine(PortInfo * info,DWORD dwDestination)606 MIXERLINE* findDestLine(PortInfo* info, DWORD dwDestination) {
607     int i;
608     TRACE0(">findDestLine\n");
609     for (i = 0; i < info->dstLineCount; i++) {
610         if (info->dstLines[i].dwDestination == dwDestination) {
611                 TRACE0("<findDestLine\n");
612             return &(info->dstLines[i]);
613         }
614     }
615     TRACE0("<findDestLine NULL\n");
616     return NULL;
617 }
618 
createMuxControl(PortInfo * info,PortControlCreator * creator,MIXERLINE * dstLine,DWORD srcLineID,void ** controlObjects,int * controlCount)619 void createMuxControl(PortInfo* info, PortControlCreator* creator, MIXERLINE* dstLine, DWORD srcLineID, void** controlObjects, int* controlCount) {
620     MIXERLINECONTROLS controlInfos;
621     MIXERCONTROLDETAILS* details;
622     MIXERCONTROLDETAILS_LISTTEXT* listTextDetails = NULL;
623     UINT listTextDetailCount = 0;
624     PortControlID* controlID;
625     UINT i, c;
626     int m;
627 
628     TRACE0(">createMuxControl\n");
629     // go through all controls of dstline
630     controlInfos.pamxctrl = NULL;
631     if (getControlInfo(info->handle, dstLine, &controlInfos)) {
632         for (i = 0; i < controlInfos.cControls; i++) {
633             if (((controlInfos.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER)
634                  || (controlInfos.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_MUX))
635                 && (controlInfos.pamxctrl[i].cMultipleItems > 0)) {
636                 if (info->usedControlIDs >= info->maxControlCount) {
637                     ERROR1("not enough free controlIDs !! maxControlIDs = %d\n", info->maxControlCount);
638                     break;
639                 }
640                 // get the details for this mux control
641                 controlID = &(info->controlIDs[info->usedControlIDs]);
642                 controlID->portInfo = info;
643                 if (controlInfos.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER) {
644                     controlID->controlType = PORT_CONTROL_TYPE_MIXER;
645                 } else {
646                     controlID->controlType = PORT_CONTROL_TYPE_MUX;
647                 }
648                 details = &(controlID->details);
649                 details->cbStruct = sizeof(MIXERCONTROLDETAILS);
650                 details->dwControlID = controlInfos.pamxctrl[i].dwControlID;
651                 details->cChannels = 1;
652                 details->cMultipleItems = controlInfos.pamxctrl[i].cMultipleItems;
653                 details->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXT);
654                 if (!listTextDetails || (listTextDetailCount < (details->cMultipleItems * details->cChannels))) {
655                     // need to allocate new listTextDetails
656                     if (listTextDetails) {
657                         free(listTextDetails);
658                         listTextDetails = NULL;
659                     }
660                     listTextDetailCount = details->cMultipleItems * details->cChannels;
661                     listTextDetails = (MIXERCONTROLDETAILS_LISTTEXT*) malloc(listTextDetailCount * sizeof(MIXERCONTROLDETAILS_LISTTEXT));
662                     if (!listTextDetails) {
663                         ERROR0("createMuxControl: unable to allocate listTextDetails!\n");
664                         if (controlInfos.pamxctrl) {
665                             free(controlInfos.pamxctrl);
666                             controlInfos.pamxctrl = NULL;
667                         }
668                         TRACE0("<createMuxControl ERROR\n");
669                         return;
670                     }
671                 }
672                 details->paDetails = listTextDetails;
673                 if (mixerGetControlDetails((HMIXEROBJ) info->handle, details, MIXER_GETCONTROLDETAILSF_LISTTEXT | MIXER_OBJECTF_HMIXER) != MMSYSERR_NOERROR) {
674                     ERROR0("createMuxControl: unable to get control details!\n");
675                     continue;
676                 }
677                 // prevent freeing this data
678                 details->paDetails = NULL;
679                 // go through all mux items. If the line matches, then add a BOOLEAN select control
680                 for (c = 0; c < details->cMultipleItems; c++) {
681                     if (listTextDetails[c].dwParam1 == srcLineID) {
682                         // we have found the line in the MUX lines.
683                         controlID->muxIndex = c;
684                         details->cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
685                         // now look if any other controlID was already part of this MUX line
686                         for (m = 0; m < info->usedControlIDs; m++) {
687                             if (info->controlIDs[m].details.dwControlID == details->dwControlID) {
688                                 // reuse the MUX Data
689                                 TRACE2("Reusing paDetails=%p of controlID[%d]\n", info->controlIDs[m].details.paDetails, m);
690                                 details->paDetails = info->controlIDs[m].details.paDetails;
691                                 break;
692                             }
693                         }
694                         if (!details->paDetails) {
695                             // first time this MUX control is used, allocate some of the muxData
696                             details->paDetails = &(info->muxData[info->usedMuxData]);
697                             TRACE2("Setting paDetails=%p to muxData[%d] \n", details->paDetails, info->usedMuxData);
698                             info->usedMuxData += details->cMultipleItems;
699                         }
700                         // finally this line can be added
701                         controlObjects[*controlCount] = (creator->newBooleanControl)(creator, controlID, CONTROL_TYPE_SELECT);
702                         (*controlCount)++;
703                         info->usedControlIDs++;
704                         break;
705                     }
706                 }
707             }
708         }
709     }
710     if (listTextDetails) {
711         free(listTextDetails);
712         listTextDetails = NULL;
713     }
714     if (controlInfos.pamxctrl) {
715         free(controlInfos.pamxctrl);
716         controlInfos.pamxctrl = NULL;
717     }
718     TRACE0("<createMuxControl\n");
719 }
720 
createPortControl(PortInfo * info,PortControlCreator * creator,MIXERCONTROL * mixerControl,INT32 type,void ** controlObjects,int * controlCount)721 void createPortControl(PortInfo* info, PortControlCreator* creator, MIXERCONTROL* mixerControl,
722                        INT32 type, void** controlObjects, int* controlCount) {
723     PortControlID* controlID;
724     void* newControl = NULL;
725     char* typeName = mixerControl->szName;
726     float min;
727     TRACE0(">createPortControl\n");
728 
729     // fill the ControlID structure and add this control
730     if (info->usedControlIDs >= info->maxControlCount) {
731         ERROR1("not enough free controlIDs !! maxControlIDs = %d\n", info->maxControlCount);
732         return;
733     }
734     controlID = &(info->controlIDs[info->usedControlIDs]);
735     controlID->portInfo = info;
736     controlID->controlType = type;
737     controlID->details.cbStruct = sizeof(MIXERCONTROLDETAILS);
738     controlID->details.dwControlID = mixerControl->dwControlID;
739     controlID->details.cChannels = 1; // uniform
740     controlID->details.cMultipleItems = 0;
741     switch (type) {
742         case PORT_CONTROL_TYPE_BOOLEAN:
743             TRACE0(" PORT_CONTROL_TYPE_BOOLEAN\n");
744             controlID->details.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
745             controlID->details.paDetails = &(controlID->boolValue);
746             if (mixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE) {
747                 typeName = CONTROL_TYPE_MUTE;
748             }
749             newControl = (creator->newBooleanControl)(creator, controlID, typeName);
750             break;
751         case PORT_CONTROL_TYPE_SIGNED:
752             TRACE0(" PORT_CONTROL_TYPE_SIGNED\n");
753             controlID->details.cbDetails = sizeof(MIXERCONTROLDETAILS_SIGNED);
754             controlID->details.paDetails = &(controlID->signedValue);
755             controlID->min = (INT32) mixerControl->Bounds.lMinimum;
756             controlID->max = (INT32) mixerControl->Bounds.lMaximum;
757             if (mixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_PAN) {
758                 typeName = CONTROL_TYPE_PAN;
759             }
760             newControl = (creator->newFloatControl)(creator, controlID, typeName,
761                 -1.0f, 1.0f, 2.0f / (controlID->max - controlID->min + 1), "");
762             break;
763         case PORT_CONTROL_TYPE_FAKE_VOLUME:  // fall through
764         case PORT_CONTROL_TYPE_FAKE_BALANCE: // fall through
765         case PORT_CONTROL_TYPE_UNSIGNED:
766             TRACE0(" PORT_CONTROL_TYPE_UNSIGNED\n");
767             controlID->details.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
768             controlID->details.paDetails = &(controlID->unsignedValue[0]);
769             controlID->min = (INT32) mixerControl->Bounds.dwMinimum;
770             controlID->max = (INT32) mixerControl->Bounds.dwMaximum;
771             min = 0.0f;
772             if ((type == PORT_CONTROL_TYPE_FAKE_VOLUME)
773                || (mixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)) {
774                 typeName = CONTROL_TYPE_VOLUME;
775             }
776             if (type == PORT_CONTROL_TYPE_FAKE_BALANCE) {
777                 typeName = CONTROL_TYPE_BALANCE;
778                 min = -1.0f;
779             }
780             if ((type == PORT_CONTROL_TYPE_FAKE_VOLUME)
781                || (type == PORT_CONTROL_TYPE_FAKE_BALANCE)) {
782                 controlID->details.cChannels = 2;
783             }
784             TRACE0(" ....PORT_CONTROL_TYPE_UNSIGNED\n");
785             newControl = (creator->newFloatControl)(creator, controlID, typeName,
786                 min, 1.0f, 1.0f / (controlID->max - controlID->min + 1), "");
787             break;
788         default:
789             ERROR1("createPortControl: unknown type %d !", type);
790             break;
791     }
792     if (newControl) {
793         controlObjects[*controlCount] = newControl;
794         (*controlCount)++;
795         info->usedControlIDs++;
796     }
797     TRACE0("<createPortControl\n");
798 }
799 
createLineControls(PortInfo * info,PortControlCreator * creator,MIXERLINE * line,void ** controlObjects,int * controlCount)800 void createLineControls(PortInfo* info, PortControlCreator* creator, MIXERLINE* line, void** controlObjects, int* controlCount) {
801     MIXERLINECONTROLS controlInfos;
802     MIXERCONTROL* mixerControl;
803     UINT i;
804     INT32 type;
805 
806     TRACE1(">createLineControls for line %s\n", line->szName);
807     // go through all controls of line
808     controlInfos.pamxctrl = NULL;
809     if (getControlInfo(info->handle, line, &controlInfos)) {
810         for (i = 0; i < controlInfos.cControls; i++) {
811             TRACE1("  %d\n", i);
812             mixerControl = &(controlInfos.pamxctrl[i]);
813             type = 0;
814             switch (mixerControl->dwControlType) {
815                 case MIXERCONTROL_CONTROLTYPE_BOOLEAN  : // fall through
816                 case MIXERCONTROL_CONTROLTYPE_BUTTON   : // fall through
817                 case MIXERCONTROL_CONTROLTYPE_LOUDNESS : // fall through
818                 case MIXERCONTROL_CONTROLTYPE_MONO     : // fall through
819                 case MIXERCONTROL_CONTROLTYPE_MUTE     : // fall through
820                 case MIXERCONTROL_CONTROLTYPE_ONOFF    : // fall through
821                 case MIXERCONTROL_CONTROLTYPE_STEREOENH: type = PORT_CONTROL_TYPE_BOOLEAN; break;
822 
823                 case MIXERCONTROL_CONTROLTYPE_PAN      : // fall through
824                 case MIXERCONTROL_CONTROLTYPE_QSOUNDPAN: // fall through
825                 case MIXERCONTROL_CONTROLTYPE_SLIDER   : type = PORT_CONTROL_TYPE_SIGNED; break;
826 
827                 case MIXERCONTROL_CONTROLTYPE_BASS     : // fall through
828                 //case MIXERCONTROL_CONTROLTYPE_EQUALIZER: // fall through
829                 case MIXERCONTROL_CONTROLTYPE_FADER    : // fall through
830                 case MIXERCONTROL_CONTROLTYPE_TREBLE   : type = PORT_CONTROL_TYPE_UNSIGNED; break;
831                 case MIXERCONTROL_CONTROLTYPE_VOLUME   :
832                     type = PORT_CONTROL_TYPE_UNSIGNED;
833                     if (line->cChannels == 2 && ((mixerControl->fdwControl & MIXERCONTROL_CONTROLF_UNIFORM) == 0)) {
834                         type = PORT_CONTROL_TYPE_FAKE_VOLUME;
835                     }
836                     break;
837             }
838             if (type != 0) {
839                 createPortControl(info, creator, mixerControl, type, controlObjects, controlCount);
840                 // create fake balance for fake volume
841                 if (type == PORT_CONTROL_TYPE_FAKE_VOLUME) {
842                     createPortControl(info, creator, mixerControl, PORT_CONTROL_TYPE_FAKE_BALANCE, controlObjects, controlCount);
843                 }
844             }
845         }
846     }
847     if (controlInfos.pamxctrl) {
848         free(controlInfos.pamxctrl);
849         controlInfos.pamxctrl = NULL;
850     }
851     TRACE0("<createLineControls\n");
852 }
853 
addCompoundControl(PortInfo * info,PortControlCreator * creator,char * name,void ** controlObjects,int * controlCount)854 void addCompoundControl(PortInfo* info, PortControlCreator* creator, char* name, void** controlObjects, int* controlCount) {
855     void* compControl;
856 
857     TRACE1(">addCompoundControl %d controls\n", *controlCount);
858     if (*controlCount) {
859         // create compound control and add it to the vector
860         compControl = (creator->newCompoundControl)(creator, name, controlObjects, *controlCount);
861         if (compControl) {
862             TRACE1(" addCompoundControl: calling addControl %p\n", compControl);
863             (creator->addControl)(creator, compControl);
864         }
865         *controlCount = 0;
866     }
867     TRACE0("<addCompoundControl\n");
868 }
869 
addAllControls(PortInfo * info,PortControlCreator * creator,void ** controlObjects,int * controlCount)870 void addAllControls(PortInfo* info, PortControlCreator* creator, void** controlObjects, int* controlCount) {
871     int i = 0;
872 
873     TRACE0(">addAllControl\n");
874     // go through all controls and add them to the vector
875     for (i = 0; i < *controlCount; i++) {
876         (creator->addControl)(creator, controlObjects[i]);
877     }
878     *controlCount = 0;
879     TRACE0("<addAllControl\n");
880 }
881 
882 
883 
PORT_GetControls(void * id,INT32 portIndex,PortControlCreator * creator)884 void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) {
885     MIXERLINE* line;
886     PortInfo* info = (PortInfo*) id;
887     int portCount = PORT_GetPortCount(id);
888     void** controls = NULL;
889     int controlCount;
890     UINT i;
891 
892     TRACE4(">PORT_GetControls(id=%p, portIndex=%d). controlIDs=%p, maxControlCount=%d\n", id, portIndex, info->controlIDs, info->maxControlCount);
893     if ((portIndex >= 0) && (portIndex < portCount)) {
894         line = info->ports[portIndex];
895         if (line) {
896             // if the memory isn't reserved for the control structures, allocate it
897             if (!info->controlIDs) {
898                 int i, maxCount = 0, muxCount = 0;
899                 TRACE0("getControl: allocate mem\n");
900                 // get a maximum number of controls
901                 // first for all destination lines
902                 for (i = 0; i < info->dstLineCount; i++) {
903                     MIXERLINE* thisLine = &(info->dstLines[i]);
904                     maxCount += getControlCount(info->handle, thisLine, &muxCount);
905                 }
906                 // then all source lines
907                 for (i = 0; i < info->srcLineCount; i++) {
908                     MIXERLINE* thisLine = &(info->srcLines[i]);
909                     maxCount += getControlCount(info->handle, thisLine, &muxCount);
910                 }
911                 info->maxControlCount = maxCount;
912                 if (maxCount > 0) {
913                     info->controlIDs = (PortControlID*) malloc(sizeof(PortControlID) * maxCount);
914                 } else {
915                     // no ports: nothing to do !
916                     return;
917                 }
918                 TRACE2("Creating muxData for %d elements and %d controlIDs.\n", muxCount, maxCount);
919                 if (muxCount > 0) {
920                     info->muxData = (MIXERCONTROLDETAILS_BOOLEAN*) malloc(sizeof(MIXERCONTROLDETAILS_BOOLEAN) * muxCount);
921                 }
922                 if (!info->controlIDs || (muxCount && !info->muxData)) {
923                     ERROR3("PORT_GetControls: info->controlIDs=%p, muxCount=%d,  info->muxData=%p !!\n", info->controlIDs, muxCount, info->muxData);
924                     return;
925                 }
926             }
927             if (info->maxControlCount == 0) {
928                 return;
929             }
930             controls = (void*) malloc(info->maxControlCount * sizeof(void*));
931             if (!controls) {
932                 ERROR0("PORT_GetControls: couldn't allocate controls!\n");
933                 return;
934             }
935 
936             // add controls of this line
937             controlCount = 0;
938             // if this line is part of MUX, add the respective BOOLEANCONTROL as a control
939             if ((line->fdwLine & MIXERLINE_LINEF_SOURCE) == MIXERLINE_LINEF_SOURCE) {
940                 MIXERLINE* dstLine = findDestLine(info, line->dwDestination);
941                 TRACE0("Port_getControls: this is a source line\n");
942                 if (dstLine) {
943                     // selection controls (implemented as Mute control)
944                     createMuxControl(info, creator, dstLine, line->dwLineID, controls, &controlCount);
945                 }
946                 // then add all controls in one compound control
947                 createLineControls(info, creator, line, controls, &controlCount);
948                 addCompoundControl(info, creator, line->szName, controls, &controlCount);
949             } else {
950                 TRACE0("getControl: this is a dest line\n");
951                 // if this is a destination line, add its controls
952                 createLineControls(info, creator, line, controls, &controlCount);
953                 addAllControls(info, creator, controls, &controlCount);
954                 // then add all controls of its source lines as one compound control
955                 for (i = 0; i < line->cConnections; i++) {
956                     // then add all controls
957                     MIXERLINE* srcLine = &(info->srcLines[line->dwUser + i]);
958                     TRACE1("PORT_getControls: add source line %d\n", i);
959                     createLineControls(info, creator, srcLine, controls, &controlCount);
960                     addCompoundControl(info, creator, srcLine->szName, controls, &controlCount);
961                 }
962             }
963         }
964     }
965     if (controls) {
966         free(controls);
967     }
968     TRACE0("< PORT_getControls\n");
969 }
970 
getControlValue(PortControlID * controlID)971 int getControlValue(PortControlID* controlID) {
972     if (mixerGetControlDetails((HMIXEROBJ) controlID->portInfo->handle, &(controlID->details),
973             MIXER_GETCONTROLDETAILSF_VALUE | MIXER_OBJECTF_HMIXER) != MMSYSERR_NOERROR) {
974         ERROR0("getControlValue: unable to get control details!\n");
975         //ERROR3("   cbStruct=%d, dwControlID=%d, cChannels=%d, ", controlID->details.cbStruct, controlID->details.dwControlID, controlID->details.cChannels);
976         //ERROR2("   cMultipleItems=%d, cbDetails=%d\n", controlID->details.cMultipleItems, controlID->details.cbDetails);
977         return FALSE;
978     }
979     return TRUE;
980 }
981 
setControlValue(PortControlID * controlID)982 int setControlValue(PortControlID* controlID) {
983     if (mixerSetControlDetails((HMIXEROBJ) controlID->portInfo->handle, &(controlID->details),
984             MIXER_SETCONTROLDETAILSF_VALUE | MIXER_OBJECTF_HMIXER) != MMSYSERR_NOERROR) {
985         ERROR0("setControlValue: unable to set control details!\n");
986         //ERROR3("   cbStruct=%d, dwControlID=%d, cChannels=%d, ", controlID->details.cbStruct, controlID->details.dwControlID, controlID->details.cChannels);
987         //ERROR2("   cMultipleItems=%d, cbDetails=%d\n", controlID->details.cMultipleItems, controlID->details.cbDetails);
988         return FALSE;
989     }
990     return TRUE;
991 }
992 
PORT_GetIntValue(void * controlIDV)993 INT32 PORT_GetIntValue(void* controlIDV) {
994     PortControlID* controlID = (PortControlID*) controlIDV;
995     MIXERCONTROLDETAILS_BOOLEAN* bools;
996     int ret = 0;
997     if (getControlValue(controlID)) {
998         switch (controlID->controlType) {
999         case PORT_CONTROL_TYPE_MUX:   // fall through
1000         case PORT_CONTROL_TYPE_MIXER:
1001                 bools = (MIXERCONTROLDETAILS_BOOLEAN*) controlID->details.paDetails;
1002                 ret = (bools[controlID->muxIndex].fValue)?TRUE:FALSE;
1003                 break;
1004         case PORT_CONTROL_TYPE_BOOLEAN:
1005                 ret = (controlID->boolValue.fValue)?TRUE:FALSE;
1006                 break;
1007         default: ERROR1("PORT_GetIntValue: wrong controlType=%d !\n", controlID->controlType);
1008         }
1009     }
1010     return ret;
1011 }
1012 
PORT_SetIntValue(void * controlIDV,INT32 value)1013 void PORT_SetIntValue(void* controlIDV, INT32 value) {
1014     PortControlID* controlID = (PortControlID*) controlIDV;
1015     MIXERCONTROLDETAILS_BOOLEAN* bools;
1016     UINT i;
1017 
1018     switch (controlID->controlType) {
1019     case PORT_CONTROL_TYPE_MUX:
1020         if (!value) {
1021             // cannot unselect a MUX line
1022             return;
1023         }
1024         if (!getControlValue(controlID)) {
1025             return;
1026         }
1027         bools = (MIXERCONTROLDETAILS_BOOLEAN*) controlID->details.paDetails;
1028         for (i = 0; i < controlID->details.cMultipleItems; i++) {
1029             bools[i].fValue = (i == (UINT) controlID->muxIndex)?TRUE:FALSE;
1030         }
1031         break;
1032     case PORT_CONTROL_TYPE_MIXER:
1033         if (!getControlValue(controlID)) {
1034             return;
1035         }
1036         bools = (MIXERCONTROLDETAILS_BOOLEAN*) controlID->details.paDetails;
1037         bools[controlID->muxIndex].fValue = (value?TRUE:FALSE);
1038         break;
1039     case PORT_CONTROL_TYPE_BOOLEAN:
1040         controlID->boolValue.fValue = (value?TRUE:FALSE);
1041         break;
1042     default:
1043         ERROR1("PORT_SetIntValue: wrong controlType=%d !\n", controlID->controlType);
1044         return;
1045     }
1046     setControlValue(controlID);
1047 }
1048 
getFakeBalance(PortControlID * controlID)1049 float getFakeBalance(PortControlID* controlID) {
1050     float volL, volR;
1051     float range = (float) (controlID->max - controlID->min);
1052     // pan is the ratio of left and right
1053     volL = (((float) (controlID->unsignedValue[0].dwValue - controlID->min)) / range);
1054     volR = (((float) (controlID->unsignedValue[1].dwValue - controlID->min)) / range);
1055     if (volL > volR) {
1056         return -1.0f + (volR / volL);
1057     }
1058     else if (volR > volL) {
1059         return 1.0f - (volL / volR);
1060     }
1061     return 0.0f;
1062 }
1063 
getFakeVolume(PortControlID * controlID)1064 float getFakeVolume(PortControlID* controlID) {
1065     // volume is the greater value of both
1066     UINT vol = controlID->unsignedValue[0].dwValue;
1067     if (controlID->unsignedValue[1].dwValue > vol) {
1068         vol = controlID->unsignedValue[1].dwValue;
1069     }
1070     return (((float) (vol - controlID->min)) / (controlID->max - controlID->min));
1071 }
1072 
1073 /*
1074  * sets the unsigned values for left and right volume according to
1075  * the given volume (0...1) and balance (-1..0..+1)
1076  */
setFakeVolume(PortControlID * controlID,float vol,float bal)1077 void setFakeVolume(PortControlID* controlID, float vol, float bal) {
1078     vol = vol * (controlID->max - controlID->min);
1079     if (bal < 0.0f) {
1080         controlID->unsignedValue[0].dwValue = (UINT) (vol  + 0.5f) + controlID->min;
1081         controlID->unsignedValue[1].dwValue = (UINT) ((vol * (bal + 1.0f)) + 0.5f) + controlID->min;
1082     } else {
1083         controlID->unsignedValue[1].dwValue = (UINT) (vol  + 0.5f) + controlID->min;
1084         controlID->unsignedValue[0].dwValue = (UINT) ((vol * (1.0f - bal)) + 0.5f) + controlID->min;
1085     }
1086 }
1087 
PORT_GetFloatValue(void * controlIDV)1088 float PORT_GetFloatValue(void* controlIDV) {
1089     PortControlID* controlID = (PortControlID*) controlIDV;
1090     float ret = 0.0f;
1091     float range = (float) (controlID->max - controlID->min);
1092     if (getControlValue(controlID)) {
1093         switch (controlID->controlType) {
1094         case PORT_CONTROL_TYPE_SIGNED:
1095                 ret = ((float) controlID->signedValue.lValue) / controlID->max;
1096                 break;
1097         case PORT_CONTROL_TYPE_UNSIGNED:
1098                 ret = (((float) (controlID->unsignedValue[0].dwValue - controlID->min)) / range);
1099                 break;
1100         case PORT_CONTROL_TYPE_FAKE_VOLUME:
1101                 ret = getFakeVolume(controlID);
1102                 break;
1103         case PORT_CONTROL_TYPE_FAKE_BALANCE:
1104                 ret = getFakeBalance(controlID);
1105                 break;
1106         default: ERROR1("PORT_GetFloatValue: wrong controlType=%d !\n", controlID->controlType);
1107         }
1108     }
1109     return ret;
1110 }
1111 
PORT_SetFloatValue(void * controlIDV,float value)1112 void PORT_SetFloatValue(void* controlIDV, float value) {
1113     PortControlID* controlID = (PortControlID*) controlIDV;
1114     float range = (float) (controlID->max - controlID->min);
1115     switch (controlID->controlType) {
1116     case PORT_CONTROL_TYPE_SIGNED:
1117         controlID->signedValue.lValue = (INT32) ((value * controlID->max) + 0.5f);
1118         break;
1119     case PORT_CONTROL_TYPE_UNSIGNED:
1120         controlID->unsignedValue[0].dwValue = (INT32) ((value * range) + 0.5f) + controlID->min;
1121         break;
1122     case PORT_CONTROL_TYPE_FAKE_VOLUME:
1123         if (!getControlValue(controlID)) {
1124             return;
1125         }
1126         setFakeVolume(controlID, value, getFakeBalance(controlID));
1127         break;
1128     case PORT_CONTROL_TYPE_FAKE_BALANCE:
1129         if (!getControlValue(controlID)) {
1130             return;
1131         }
1132         setFakeVolume(controlID, getFakeVolume(controlID), value);
1133         break;
1134     default:
1135         ERROR1("PORT_SetFloatValue: wrong controlType=%d !\n", controlID->controlType);
1136         return;
1137     }
1138     setControlValue(controlID);
1139 }
1140 
1141 #endif // USE_PORTS
1142