mshtml: Fixed handling channels without container and necko channel.
[wine] / dlls / winmm / tests / mixer.c
1 /*
2  * Test mixer
3  *
4  * Copyright (c) 2004 Robert Reif
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 /*
22  * To Do:
23  * add interactive tests
24  */
25
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <math.h>
30
31 #include "wine/test.h"
32 #include "windef.h"
33 #include "winbase.h"
34 #include "winnls.h"
35 #include "mmsystem.h"
36
37 #include "winmm_test.h"
38
39 static const char * line_flags(DWORD fdwLine)
40 {
41     static char flags[100];
42     BOOL first=TRUE;
43     flags[0]=0;
44     if (fdwLine&MIXERLINE_LINEF_ACTIVE) {
45         strcat(flags,"MIXERLINE_LINEF_ACTIVE");
46         first=FALSE;
47     }
48     if (fdwLine&MIXERLINE_LINEF_DISCONNECTED) {
49         if (!first)
50             strcat(flags, "|");
51
52         strcat(flags,"MIXERLINE_LINEF_DISCONNECTED");
53         first=FALSE;
54     }
55
56     if (fdwLine&MIXERLINE_LINEF_SOURCE) {
57         if (!first)
58             strcat(flags, "|");
59
60         strcat(flags,"MIXERLINE_LINEF_SOURCE");
61     }
62
63     return flags;
64 }
65
66 static const char * component_type(DWORD dwComponentType)
67 {
68 #define TYPE_TO_STR(x) case x: return #x
69     switch (dwComponentType) {
70     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_UNDEFINED);
71     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_DIGITAL);
72     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_LINE);
73     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_MONITOR);
74     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_SPEAKERS);
75     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_HEADPHONES);
76     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_TELEPHONE);
77     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_WAVEIN);
78     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_VOICEIN);
79     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED);
80     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_DIGITAL);
81     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_LINE);
82     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE);
83     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER);
84     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC);
85     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE);
86     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER);
87     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT);
88     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY);
89     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_ANALOG);
90     }
91 #undef TYPE_TO_STR
92     return "UNKNOWN";
93 }
94
95 static const char * target_type(DWORD dwType)
96 {
97 #define TYPE_TO_STR(x) case x: return #x
98     switch (dwType) {
99     TYPE_TO_STR(MIXERLINE_TARGETTYPE_UNDEFINED);
100     TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEOUT);
101     TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEIN);
102     TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIOUT);
103     TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIIN);
104     TYPE_TO_STR(MIXERLINE_TARGETTYPE_AUX);
105     }
106 #undef TYPE_TO_STR
107     return "UNKNOWN";
108 }
109
110 static const char * control_type(DWORD dwControlType)
111 {
112 #define TYPE_TO_STR(x) case x: return #x
113     switch (dwControlType) {
114     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_CUSTOM);
115     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEANMETER);
116     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNEDMETER);
117     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PEAKMETER);
118     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER);
119     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEAN);
120     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_ONOFF);
121     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUTE);
122     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MONO);
123     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_LOUDNESS);
124     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_STEREOENH);
125     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS_BOOST);
126     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BUTTON);
127     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_DECIBELS);
128     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNED);
129     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNED);
130     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PERCENT);
131     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SLIDER);
132     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PAN);
133     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_QSOUNDPAN);
134     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_FADER);
135     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_VOLUME);
136     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS);
137     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_TREBLE);
138     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_EQUALIZER);
139     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SINGLESELECT);
140     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUX);
141     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT);
142     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MIXER);
143     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MICROTIME);
144     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MILLITIME);
145     }
146 #undef TYPE_TO_STR
147     return "UNKNOWN";
148 }
149
150 static const char * control_flags(DWORD fdwControl)
151 {
152     static char flags[100];
153     BOOL first=TRUE;
154     flags[0]=0;
155     if (fdwControl&MIXERCONTROL_CONTROLF_UNIFORM) {
156         strcat(flags,"MIXERCONTROL_CONTROLF_UNIFORM");
157         first=FALSE;
158     }
159     if (fdwControl&MIXERCONTROL_CONTROLF_MULTIPLE) {
160         if (!first)
161             strcat(flags, "|");
162
163         strcat(flags,"MIXERCONTROL_CONTROLF_MULTIPLE");
164         first=FALSE;
165     }
166
167     if (fdwControl&MIXERCONTROL_CONTROLF_DISABLED) {
168         if (!first)
169             strcat(flags, "|");
170
171         strcat(flags,"MIXERCONTROL_CONTROLF_DISABLED");
172     }
173
174     return flags;
175 }
176
177 static void mixer_test_controlA(HMIXER mix, LPMIXERCONTROLA control)
178 {
179     MMRESULT rc;
180
181     if ((control->dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME) ||
182         (control->dwControlType == MIXERCONTROL_CONTROLTYPE_UNSIGNED)) {
183         MIXERCONTROLDETAILS details;
184         MIXERCONTROLDETAILS_UNSIGNED value;
185
186         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
187         details.dwControlID = control->dwControlID;
188         details.cChannels = 1;
189         U(details).cMultipleItems = 0;
190         details.paDetails = &value;
191         details.cbDetails = sizeof(value);
192
193         /* read the current control value */
194         rc=mixerGetControlDetails((HMIXEROBJ)mix,&details,MIXER_GETCONTROLDETAILSF_VALUE);
195         ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
196            "MMSYSERR_NOERROR expected, got %s\n",
197            mmsys_error(rc));
198         if (rc==MMSYSERR_NOERROR) {
199             MIXERCONTROLDETAILS new_details;
200             MIXERCONTROLDETAILS_UNSIGNED new_value;
201
202             if (winetest_interactive)
203                 trace("            Value=%d\n",value.dwValue);
204
205             if (value.dwValue + control->Metrics.cSteps < S1(control->Bounds).dwMaximum)
206                 new_value.dwValue = value.dwValue + control->Metrics.cSteps;
207             else
208                 new_value.dwValue = value.dwValue - control->Metrics.cSteps;
209
210             new_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
211             new_details.dwControlID = control->dwControlID;
212             new_details.cChannels = 1;
213             U(new_details).cMultipleItems = 0;
214             new_details.paDetails = &new_value;
215             new_details.cbDetails = sizeof(new_value);
216
217             /* change the control value by one step */
218             rc=mixerSetControlDetails((HMIXEROBJ)mix,&new_details,MIXER_SETCONTROLDETAILSF_VALUE);
219             ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
220                "MMSYSERR_NOERROR expected, got %s\n",
221                mmsys_error(rc));
222             if (rc==MMSYSERR_NOERROR) {
223                 MIXERCONTROLDETAILS ret_details;
224                 MIXERCONTROLDETAILS_UNSIGNED ret_value;
225
226                 ret_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
227                 ret_details.dwControlID = control->dwControlID;
228                 ret_details.cChannels = 1;
229                 U(ret_details).cMultipleItems = 0;
230                 ret_details.paDetails = &ret_value;
231                 ret_details.cbDetails = sizeof(ret_value);
232
233                 /* read back the new control value */
234                 rc=mixerGetControlDetails((HMIXEROBJ)mix,&ret_details,MIXER_GETCONTROLDETAILSF_VALUE);
235                 ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
236                    "MMSYSERR_NOERROR expected, got %s\n",
237                    mmsys_error(rc));
238                 if (rc==MMSYSERR_NOERROR) {
239                     /* result may not match exactly because of rounding */
240                     ok(abs(ret_value.dwValue-new_value.dwValue)<=1,
241                        "Couldn't change value from %d to %d, returned %d\n",
242                        value.dwValue,new_value.dwValue,ret_value.dwValue);
243
244                     if (abs(ret_value.dwValue-new_value.dwValue)<=1) {
245                         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
246                         details.dwControlID = control->dwControlID;
247                         details.cChannels = 1;
248                         U(details).cMultipleItems = 0;
249                         details.paDetails = &value;
250                         details.cbDetails = sizeof(value);
251
252                         /* restore original value */
253                         rc=mixerSetControlDetails((HMIXEROBJ)mix,&details,MIXER_SETCONTROLDETAILSF_VALUE);
254                         ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
255                            "MMSYSERR_NOERROR expected, got %s\n",
256                            mmsys_error(rc));
257                     }
258                 }
259             }
260         }
261     } else if ((control->dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE) ||
262         (control->dwControlType == MIXERCONTROL_CONTROLTYPE_BOOLEAN) ||
263         (control->dwControlType == MIXERCONTROL_CONTROLTYPE_BUTTON)) {
264         MIXERCONTROLDETAILS details;
265         MIXERCONTROLDETAILS_BOOLEAN value;
266
267         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
268         details.dwControlID = control->dwControlID;
269         details.cChannels = 1;
270         U(details).cMultipleItems = 0;
271         details.paDetails = &value;
272         details.cbDetails = sizeof(value);
273
274         rc=mixerGetControlDetails((HMIXEROBJ)mix,&details,MIXER_GETCONTROLDETAILSF_VALUE);
275         ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
276            "MMSYSERR_NOERROR expected, got %s\n",
277            mmsys_error(rc));
278         if (rc==MMSYSERR_NOERROR) {
279             MIXERCONTROLDETAILS new_details;
280             MIXERCONTROLDETAILS_BOOLEAN new_value;
281
282             if (winetest_interactive)
283                 trace("            Value=%d\n",value.fValue);
284
285             if (value.fValue == FALSE)
286                 new_value.fValue = TRUE;
287             else
288                 new_value.fValue = FALSE;
289
290             new_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
291             new_details.dwControlID = control->dwControlID;
292             new_details.cChannels = 1;
293             U(new_details).cMultipleItems = 0;
294             new_details.paDetails = &new_value;
295             new_details.cbDetails = sizeof(new_value);
296
297             /* change the control value by one step */
298             rc=mixerSetControlDetails((HMIXEROBJ)mix,&new_details,MIXER_SETCONTROLDETAILSF_VALUE);
299             ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
300                "MMSYSERR_NOERROR expected, got %s\n",
301                mmsys_error(rc));
302             if (rc==MMSYSERR_NOERROR) {
303                 MIXERCONTROLDETAILS ret_details;
304                 MIXERCONTROLDETAILS_BOOLEAN ret_value;
305
306                 ret_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
307                 ret_details.dwControlID = control->dwControlID;
308                 ret_details.cChannels = 1;
309                 U(ret_details).cMultipleItems = 0;
310                 ret_details.paDetails = &ret_value;
311                 ret_details.cbDetails = sizeof(ret_value);
312
313                 /* read back the new control value */
314                 rc=mixerGetControlDetails((HMIXEROBJ)mix,&ret_details,MIXER_GETCONTROLDETAILSF_VALUE);
315                 ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
316                    "MMSYSERR_NOERROR expected, got %s\n",
317                    mmsys_error(rc));
318                 if (rc==MMSYSERR_NOERROR) {
319                     /* result may not match exactly because of rounding */
320                     ok(ret_value.fValue==new_value.fValue,
321                        "Couldn't change value from %d to %d, returned %d\n",
322                        value.fValue,new_value.fValue,ret_value.fValue);
323
324                     if (ret_value.fValue==new_value.fValue) {
325                         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
326                         details.dwControlID = control->dwControlID;
327                         details.cChannels = 1;
328                         U(details).cMultipleItems = 0;
329                         details.paDetails = &value;
330                         details.cbDetails = sizeof(value);
331
332                         /* restore original value */
333                         rc=mixerSetControlDetails((HMIXEROBJ)mix,&details,MIXER_SETCONTROLDETAILSF_VALUE);
334                         ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
335                            "MMSYSERR_NOERROR expected, got %s\n",
336                            mmsys_error(rc));
337                     }
338                 }
339             }
340         }
341     } else {
342         /* FIXME */
343     }
344 }
345
346 static void mixer_test_deviceA(int device)
347 {
348     MIXERCAPSA capsA;
349     HMIXER mix;
350     MMRESULT rc;
351     DWORD d,s,ns,nc;
352
353     rc=mixerGetDevCapsA(device,0,sizeof(capsA));
354     ok(rc==MMSYSERR_INVALPARAM,
355        "mixerGetDevCapsA: MMSYSERR_INVALPARAM expected, got %s\n",
356        mmsys_error(rc));
357
358     rc=mixerGetDevCapsA(device,&capsA,4);
359     ok(rc==MMSYSERR_NOERROR,
360        "mixerGetDevCapsA: MMSYSERR_NOERROR expected, got %s\n",
361        mmsys_error(rc));
362
363     rc=mixerGetDevCapsA(device,&capsA,sizeof(capsA));
364     ok(rc==MMSYSERR_NOERROR,
365        "mixerGetDevCapsA: MMSYSERR_NOERROR expected, got %s\n",
366        mmsys_error(rc));
367
368     if (winetest_interactive) {
369         trace("  %d: \"%s\" %d.%d (%d:%d) destinations=%d\n", device,
370               capsA.szPname, capsA.vDriverVersion >> 8,
371               capsA.vDriverVersion & 0xff,capsA.wMid,capsA.wPid,
372               capsA.cDestinations);
373     } else {
374         trace("  %d: \"%s\" %d.%d (%d:%d)\n", device,
375               capsA.szPname, capsA.vDriverVersion >> 8,
376               capsA.vDriverVersion & 0xff,capsA.wMid,capsA.wPid);
377     }
378
379     rc=mixerOpen(&mix, device, 0, 0, 0);
380     ok(rc==MMSYSERR_NOERROR,
381        "mixerOpen: MMSYSERR_NOERROR expected, got %s\n",mmsys_error(rc));
382     if (rc==MMSYSERR_NOERROR) {
383         rc=mixerOpen(&mix, device, 0, 0, CALLBACK_FUNCTION);
384         ok(rc==MMSYSERR_INVALFLAG,
385            "mixerOpen: MMSYSERR_INVALFLAG expected, got %s\n", mmsys_error(rc));
386
387         /* Shouldn't open without a valid HWND */
388         rc=mixerOpen(&mix, device, 0, 0, CALLBACK_WINDOW);
389         ok(rc==MMSYSERR_INVALPARAM,
390            "mixerOpen: MMSYSERR_INVALPARAM expected, got %s\n", mmsys_error(rc));
391
392
393         for (d=0;d<capsA.cDestinations;d++) {
394             MIXERLINEA mixerlineA;
395             mixerlineA.cbStruct = 0;
396             mixerlineA.dwDestination=d;
397             rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA,
398                                  MIXER_GETLINEINFOF_DESTINATION);
399             ok(rc==MMSYSERR_INVALPARAM,
400                "mixerGetLineInfoA(MIXER_GETLINEINFOF_DESTINATION): "
401                "MMSYSERR_INVALPARAM expected, got %s\n",
402                mmsys_error(rc));
403
404             mixerlineA.cbStruct = sizeof(mixerlineA);
405             mixerlineA.dwDestination=capsA.cDestinations;
406             rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA,
407                                  MIXER_GETLINEINFOF_DESTINATION);
408             ok(rc==MMSYSERR_INVALPARAM||rc==MIXERR_INVALLINE,
409                "mixerGetLineInfoA(MIXER_GETLINEINFOF_DESTINATION): "
410                "MMSYSERR_INVALPARAM or MIXERR_INVALLINE expected, got %s\n",
411                mmsys_error(rc));
412
413             mixerlineA.cbStruct = sizeof(mixerlineA);
414             mixerlineA.dwDestination=d;
415             rc=mixerGetLineInfoA((HMIXEROBJ)mix,0,
416                                  MIXER_GETLINEINFOF_DESTINATION);
417             ok(rc==MMSYSERR_INVALPARAM,
418                "mixerGetLineInfoA(MIXER_GETLINEINFOF_DESTINATION): "
419                "MMSYSERR_INVALPARAM expected, got %s\n",
420                mmsys_error(rc));
421
422             mixerlineA.cbStruct = sizeof(mixerlineA);
423             mixerlineA.dwDestination=d;
424             rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA,-1);
425             ok(rc==MMSYSERR_INVALFLAG,
426                "mixerGetLineInfoA(-1): MMSYSERR_INVALFLAG expected, got %s\n",
427                mmsys_error(rc));
428
429             mixerlineA.cbStruct = sizeof(mixerlineA);
430             mixerlineA.dwDestination=d;
431             rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA,
432                                   MIXER_GETLINEINFOF_DESTINATION);
433             ok(rc==MMSYSERR_NOERROR||rc==MMSYSERR_NODRIVER,
434                "mixerGetLineInfoA(MIXER_GETLINEINFOF_DESTINATION): "
435                "MMSYSERR_NOERROR expected, got %s\n",
436                mmsys_error(rc));
437             if (rc==MMSYSERR_NODRIVER)
438                 trace("  No Driver\n");
439             else if (rc==MMSYSERR_NOERROR) {
440               if (winetest_interactive) {
441                 trace("    %d: \"%s\" (%s) Destination=%d Source=%d\n",
442                       d,mixerlineA.szShortName, mixerlineA.szName,
443                       mixerlineA.dwDestination,mixerlineA.dwSource);
444                 trace("        LineID=%08x Channels=%d "
445                       "Connections=%d Controls=%d\n",
446                       mixerlineA.dwLineID,mixerlineA.cChannels,
447                       mixerlineA.cConnections,mixerlineA.cControls);
448                 trace("        State=0x%08x(%s)\n",
449                       mixerlineA.fdwLine,line_flags(mixerlineA.fdwLine));
450                 trace("        ComponentType=%s\n",
451                       component_type(mixerlineA.dwComponentType));
452                 trace("        Type=%s\n",
453                       target_type(mixerlineA.Target.dwType));
454                 trace("        Device=%d (%s) %d.%d (%d:%d)\n",
455                       mixerlineA.Target.dwDeviceID,
456                       mixerlineA.Target.szPname,
457                       mixerlineA.Target.vDriverVersion >> 8,
458                       mixerlineA.Target.vDriverVersion & 0xff,
459                       mixerlineA.Target.wMid, mixerlineA.Target.wPid);
460               }
461               ns=mixerlineA.cConnections;
462               for(s=0;s<ns;s++) {
463                 mixerlineA.cbStruct = sizeof(mixerlineA);
464                 mixerlineA.dwDestination=d;
465                 mixerlineA.dwSource=s;
466                 rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA,
467                                      MIXER_GETLINEINFOF_SOURCE);
468                 ok(rc==MMSYSERR_NOERROR||rc==MMSYSERR_NODRIVER,
469                    "mixerGetLineInfoA(MIXER_GETLINEINFOF_SOURCE): "
470                    "MMSYSERR_NOERROR expected, got %s\n",
471                    mmsys_error(rc));
472                 if (rc==MMSYSERR_NODRIVER)
473                     trace("  No Driver\n");
474                 else if (rc==MMSYSERR_NOERROR) {
475                     LPMIXERCONTROLA    array;
476                     MIXERLINECONTROLSA controls;
477                     if (winetest_interactive) {
478                         trace("      %d: \"%s\" (%s) Destination=%d Source=%d\n",
479                               s,mixerlineA.szShortName, mixerlineA.szName,
480                               mixerlineA.dwDestination,mixerlineA.dwSource);
481                         trace("          LineID=%08x Channels=%d "
482                               "Connections=%d Controls=%d\n",
483                               mixerlineA.dwLineID,mixerlineA.cChannels,
484                               mixerlineA.cConnections,mixerlineA.cControls);
485                         trace("          State=0x%08x(%s)\n",
486                               mixerlineA.fdwLine,line_flags(mixerlineA.fdwLine));
487                         trace("          ComponentType=%s\n",
488                               component_type(mixerlineA.dwComponentType));
489                         trace("          Type=%s\n",
490                               target_type(mixerlineA.Target.dwType));
491                         trace("          Device=%d (%s) %d.%d (%d:%d)\n",
492                               mixerlineA.Target.dwDeviceID,
493                               mixerlineA.Target.szPname,
494                               mixerlineA.Target.vDriverVersion >> 8,
495                               mixerlineA.Target.vDriverVersion & 0xff,
496                               mixerlineA.Target.wMid, mixerlineA.Target.wPid);
497                     }
498                     if (mixerlineA.cControls) {
499                         array=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
500                             mixerlineA.cControls*sizeof(MIXERCONTROLA));
501                         if (array) {
502                             rc=mixerGetLineControlsA((HMIXEROBJ)mix,0,
503                                                       MIXER_GETLINECONTROLSF_ALL);
504                             ok(rc==MMSYSERR_INVALPARAM,
505                                "mixerGetLineControlsA(MIXER_GETLINECONTROLSF_ALL): "
506                                "MMSYSERR_INVALPARAM expected, got %s\n",
507                                mmsys_error(rc));
508
509                             rc=mixerGetLineControlsA((HMIXEROBJ)mix,&controls,-1);
510                             ok(rc==MMSYSERR_INVALFLAG||rc==MMSYSERR_INVALPARAM,
511                                "mixerGetLineControlsA(-1): "
512                                "MMSYSERR_INVALFLAG or MMSYSERR_INVALPARAM expected, got %s\n",
513                                mmsys_error(rc));
514
515                             controls.cbStruct = sizeof(MIXERLINECONTROLSA);
516                             controls.cControls = mixerlineA.cControls;
517                             controls.dwLineID = mixerlineA.dwLineID;
518                             controls.pamxctrl = array;
519                             controls.cbmxctrl = sizeof(MIXERCONTROLA);
520
521                             /* FIXME: do MIXER_GETLINECONTROLSF_ONEBYID
522                              * and MIXER_GETLINECONTROLSF_ONEBYTYPE
523                              */
524                             rc=mixerGetLineControlsA((HMIXEROBJ)mix,&controls,
525                                                      MIXER_GETLINECONTROLSF_ALL);
526                             ok(rc==MMSYSERR_NOERROR,
527                                "mixerGetLineControlsA(MIXER_GETLINECONTROLSF_ALL): "
528                                "MMSYSERR_NOERROR expected, got %s\n",
529                                mmsys_error(rc));
530                             if (rc==MMSYSERR_NOERROR) {
531                                 for(nc=0;nc<mixerlineA.cControls;nc++) {
532                                     if (winetest_interactive) {
533                                         trace("        %d: \"%s\" (%s) ControlID=%d\n", nc,
534                                               array[nc].szShortName,
535                                               array[nc].szName, array[nc].dwControlID);
536                                         trace("            ControlType=%s\n",
537                                                control_type(array[nc].dwControlType));
538                                         trace("            Control=0x%08x(%s)\n",
539                                               array[nc].fdwControl,
540                                               control_flags(array[nc].fdwControl));
541                                         trace("            Items=%d Min=%d Max=%d Step=%d\n",
542                                               array[nc].cMultipleItems,
543                                               S1(array[nc].Bounds).dwMinimum,
544                                               S1(array[nc].Bounds).dwMaximum,
545                                               array[nc].Metrics.cSteps);
546                                     }
547
548                                     mixer_test_controlA(mix, &array[nc]);
549                                 }
550                             }
551
552                             HeapFree(GetProcessHeap(),0,array);
553                         }
554                     }
555                 }
556               }
557             }
558         }
559         rc=mixerClose(mix);
560         ok(rc==MMSYSERR_NOERROR,
561            "mixerClose: MMSYSERR_BADDEVICEID expected, got %s\n",
562            mmsys_error(rc));
563     }
564 }
565
566 static void mixer_test_controlW(HMIXER mix, LPMIXERCONTROLW control)
567 {
568     MMRESULT rc;
569
570     if ((control->dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME) ||
571         (control->dwControlType == MIXERCONTROL_CONTROLTYPE_UNSIGNED)) {
572         MIXERCONTROLDETAILS details;
573         MIXERCONTROLDETAILS_UNSIGNED value;
574
575         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
576         details.dwControlID = control->dwControlID;
577         details.cChannels = 1;
578         U(details).cMultipleItems = 0;
579         details.paDetails = &value;
580         details.cbDetails = sizeof(value);
581
582         /* read the current control value */
583         rc=mixerGetControlDetails((HMIXEROBJ)mix,&details,MIXER_GETCONTROLDETAILSF_VALUE);
584         ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
585            "MMSYSERR_NOERROR expected, got %s\n",
586            mmsys_error(rc));
587         if (rc==MMSYSERR_NOERROR) {
588             MIXERCONTROLDETAILS new_details;
589             MIXERCONTROLDETAILS_UNSIGNED new_value;
590
591             if (winetest_interactive)
592                 trace("            Value=%d\n",value.dwValue);
593
594             if (value.dwValue + control->Metrics.cSteps < S1(control->Bounds).dwMaximum)
595                 new_value.dwValue = value.dwValue + control->Metrics.cSteps;
596             else
597                 new_value.dwValue = value.dwValue - control->Metrics.cSteps;
598
599             new_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
600             new_details.dwControlID = control->dwControlID;
601             new_details.cChannels = 1;
602             U(new_details).cMultipleItems = 0;
603             new_details.paDetails = &new_value;
604             new_details.cbDetails = sizeof(new_value);
605
606             /* change the control value by one step */
607             rc=mixerSetControlDetails((HMIXEROBJ)mix,&new_details,MIXER_SETCONTROLDETAILSF_VALUE);
608             ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
609                "MMSYSERR_NOERROR expected, got %s\n",
610                mmsys_error(rc));
611             if (rc==MMSYSERR_NOERROR) {
612                 MIXERCONTROLDETAILS ret_details;
613                 MIXERCONTROLDETAILS_UNSIGNED ret_value;
614
615                 ret_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
616                 ret_details.dwControlID = control->dwControlID;
617                 ret_details.cChannels = 1;
618                 U(ret_details).cMultipleItems = 0;
619                 ret_details.paDetails = &ret_value;
620                 ret_details.cbDetails = sizeof(ret_value);
621
622                 /* read back the new control value */
623                 rc=mixerGetControlDetails((HMIXEROBJ)mix,&ret_details,MIXER_GETCONTROLDETAILSF_VALUE);
624                 ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
625                    "MMSYSERR_NOERROR expected, got %s\n",
626                    mmsys_error(rc));
627                 if (rc==MMSYSERR_NOERROR) {
628                     /* result may not match exactly because of rounding */
629                     ok(abs(ret_value.dwValue-new_value.dwValue)<=1,
630                        "Couldn't change value from %d to %d, returned %d\n",
631                        value.dwValue,new_value.dwValue,ret_value.dwValue);
632
633                     if (abs(ret_value.dwValue-new_value.dwValue)<=1) {
634                         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
635                         details.dwControlID = control->dwControlID;
636                         details.cChannels = 1;
637                         U(details).cMultipleItems = 0;
638                         details.paDetails = &value;
639                         details.cbDetails = sizeof(value);
640
641                         /* restore original value */
642                         rc=mixerSetControlDetails((HMIXEROBJ)mix,&details,MIXER_SETCONTROLDETAILSF_VALUE);
643                         ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
644                            "MMSYSERR_NOERROR expected, got %s\n",
645                            mmsys_error(rc));
646                     }
647                 }
648             }
649         }
650     } else if ((control->dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE) ||
651         (control->dwControlType == MIXERCONTROL_CONTROLTYPE_BOOLEAN) ||
652         (control->dwControlType == MIXERCONTROL_CONTROLTYPE_BUTTON)) {
653         MIXERCONTROLDETAILS details;
654         MIXERCONTROLDETAILS_BOOLEAN value;
655
656         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
657         details.dwControlID = control->dwControlID;
658         details.cChannels = 1;
659         U(details).cMultipleItems = 0;
660         details.paDetails = &value;
661         details.cbDetails = sizeof(value);
662
663         rc=mixerGetControlDetails((HMIXEROBJ)mix,&details,MIXER_GETCONTROLDETAILSF_VALUE);
664         ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
665            "MMSYSERR_NOERROR expected, got %s\n",
666            mmsys_error(rc));
667         if (rc==MMSYSERR_NOERROR) {
668             MIXERCONTROLDETAILS new_details;
669             MIXERCONTROLDETAILS_BOOLEAN new_value;
670
671             if (winetest_interactive)
672                 trace("            Value=%d\n",value.fValue);
673
674             if (value.fValue == FALSE)
675                 new_value.fValue = TRUE;
676             else
677                 new_value.fValue = FALSE;
678
679             new_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
680             new_details.dwControlID = control->dwControlID;
681             new_details.cChannels = 1;
682             U(new_details).cMultipleItems = 0;
683             new_details.paDetails = &new_value;
684             new_details.cbDetails = sizeof(new_value);
685
686             /* change the control value by one step */
687             rc=mixerSetControlDetails((HMIXEROBJ)mix,&new_details,MIXER_SETCONTROLDETAILSF_VALUE);
688             ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
689                "MMSYSERR_NOERROR expected, got %s\n",
690                mmsys_error(rc));
691             if (rc==MMSYSERR_NOERROR) {
692                 MIXERCONTROLDETAILS ret_details;
693                 MIXERCONTROLDETAILS_BOOLEAN ret_value;
694
695                 ret_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
696                 ret_details.dwControlID = control->dwControlID;
697                 ret_details.cChannels = 1;
698                 U(ret_details).cMultipleItems = 0;
699                 ret_details.paDetails = &ret_value;
700                 ret_details.cbDetails = sizeof(ret_value);
701
702                 /* read back the new control value */
703                 rc=mixerGetControlDetails((HMIXEROBJ)mix,&ret_details,MIXER_GETCONTROLDETAILSF_VALUE);
704                 ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
705                    "MMSYSERR_NOERROR expected, got %s\n",
706                    mmsys_error(rc));
707                 if (rc==MMSYSERR_NOERROR) {
708                     /* result may not match exactly because of rounding */
709                     ok(ret_value.fValue==new_value.fValue,
710                        "Couldn't change value from %d to %d, returned %d\n",
711                        value.fValue,new_value.fValue,ret_value.fValue);
712
713                     if (ret_value.fValue==new_value.fValue) {
714                         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
715                         details.dwControlID = control->dwControlID;
716                         details.cChannels = 1;
717                         U(details).cMultipleItems = 0;
718                         details.paDetails = &value;
719                         details.cbDetails = sizeof(value);
720
721                         /* restore original value */
722                         rc=mixerSetControlDetails((HMIXEROBJ)mix,&details,MIXER_SETCONTROLDETAILSF_VALUE);
723                         ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
724                            "MMSYSERR_NOERROR expected, got %s\n",
725                            mmsys_error(rc));
726                     }
727                 }
728             }
729         }
730     } else {
731         /* FIXME */
732     }
733 }
734
735 static void mixer_test_deviceW(int device)
736 {
737     MIXERCAPSW capsW;
738     HMIXER mix;
739     MMRESULT rc;
740     DWORD d,s,ns,nc;
741     char szShortName[MIXER_SHORT_NAME_CHARS];
742     char szName[MIXER_LONG_NAME_CHARS];
743     char szPname[MAXPNAMELEN];
744
745     rc=mixerGetDevCapsW(device,0,sizeof(capsW));
746     ok(rc==MMSYSERR_INVALPARAM,
747        "mixerGetDevCapsW: MMSYSERR_INVALPARAM expected, got %s\n",
748        mmsys_error(rc));
749
750     rc=mixerGetDevCapsW(device,&capsW,4);
751     ok(rc==MMSYSERR_NOERROR,
752        "mixerGetDevCapsW: MMSYSERR_NOERROR expected, got %s\n",
753        mmsys_error(rc));
754
755     rc=mixerGetDevCapsW(device,&capsW,sizeof(capsW));
756     ok(rc==MMSYSERR_NOERROR,
757        "mixerGetDevCapsW: MMSYSERR_NOERROR expected, got %s\n",
758        mmsys_error(rc));
759
760     WideCharToMultiByte(CP_ACP,0,capsW.szPname, MAXPNAMELEN,szPname,
761                         MAXPNAMELEN,NULL,NULL);
762     if (winetest_interactive) {
763         trace("  %d: \"%s\" %d.%d (%d:%d) destinations=%d\n", device,
764               szPname, capsW.vDriverVersion >> 8,
765               capsW.vDriverVersion & 0xff,capsW.wMid,capsW.wPid,
766               capsW.cDestinations);
767     } else {
768         trace("  %d: \"%s\" %d.%d (%d:%d)\n", device,
769               szPname, capsW.vDriverVersion >> 8,
770               capsW.vDriverVersion & 0xff,capsW.wMid,capsW.wPid);
771     }
772
773
774     rc=mixerOpen(&mix, device, 0, 0, 0);
775     ok(rc==MMSYSERR_NOERROR,
776        "mixerOpen: MMSYSERR_BADDEVICEID expected, got %s\n",mmsys_error(rc));
777     if (rc==MMSYSERR_NOERROR) {
778         rc=mixerOpen(&mix, device, 0, 0, CALLBACK_FUNCTION);
779         ok(rc==MMSYSERR_INVALFLAG,
780            "mixerOpen: MMSYSERR_INVALFLAG expected, got %s\n", mmsys_error(rc));
781
782         /* Shouldn't open without a valid HWND */
783         rc=mixerOpen(&mix, device, 0, 0, CALLBACK_WINDOW);
784         ok(rc==MMSYSERR_INVALPARAM,
785            "mixerOpen: MMSYSERR_INVALPARAM expected, got %s\n", mmsys_error(rc));
786
787         for (d=0;d<capsW.cDestinations;d++) {
788             MIXERLINEW mixerlineW;
789             mixerlineW.cbStruct = 0;
790             mixerlineW.dwDestination=d;
791             rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
792                                  MIXER_GETLINEINFOF_DESTINATION);
793             ok(rc==MMSYSERR_INVALPARAM,
794                "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
795                "MMSYSERR_INVALPARAM expected, got %s\n",
796                mmsys_error(rc));
797
798             mixerlineW.cbStruct = sizeof(mixerlineW);
799             mixerlineW.dwDestination=capsW.cDestinations;
800             rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
801                                  MIXER_GETLINEINFOF_DESTINATION);
802             ok(rc==MMSYSERR_INVALPARAM||rc==MIXERR_INVALLINE,
803                "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
804                "MMSYSERR_INVALPARAM or MIXERR_INVALLINE expected, got %s\n",
805                mmsys_error(rc));
806
807             mixerlineW.cbStruct = sizeof(mixerlineW);
808             mixerlineW.dwDestination=d;
809             rc=mixerGetLineInfoW((HMIXEROBJ)mix,0,
810                                  MIXER_GETLINEINFOF_DESTINATION);
811             ok(rc==MMSYSERR_INVALPARAM,
812                "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
813                "MMSYSERR_INVALPARAM expected, got %s\n",
814                mmsys_error(rc));
815
816             mixerlineW.cbStruct = sizeof(mixerlineW);
817             mixerlineW.dwDestination=d;
818             rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,-1);
819             ok(rc==MMSYSERR_INVALFLAG,
820                "mixerGetLineInfoW(-1): MMSYSERR_INVALFLAG expected, got %s\n",
821                mmsys_error(rc));
822
823             mixerlineW.cbStruct = sizeof(mixerlineW);
824             mixerlineW.dwDestination=d;
825             rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
826                                   MIXER_GETLINEINFOF_DESTINATION);
827             ok(rc==MMSYSERR_NOERROR||rc==MMSYSERR_NODRIVER,
828                "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
829                "MMSYSERR_NOERROR expected, got %s\n",
830                mmsys_error(rc));
831             if (rc==MMSYSERR_NODRIVER)
832                 trace("  No Driver\n");
833             else if (rc==MMSYSERR_NOERROR && winetest_interactive) {
834                 WideCharToMultiByte(CP_ACP,0,mixerlineW.szShortName,
835                     MIXER_SHORT_NAME_CHARS,szShortName,
836                     MIXER_SHORT_NAME_CHARS,NULL,NULL);
837                 WideCharToMultiByte(CP_ACP,0,mixerlineW.szName,
838                     MIXER_LONG_NAME_CHARS,szName,
839                     MIXER_LONG_NAME_CHARS,NULL,NULL);
840                 WideCharToMultiByte(CP_ACP,0,mixerlineW.Target.szPname,
841                     MAXPNAMELEN,szPname,
842                     MAXPNAMELEN,NULL, NULL);
843                 trace("    %d: \"%s\" (%s) Destination=%d Source=%d\n",
844                       d,szShortName,szName,
845                       mixerlineW.dwDestination,mixerlineW.dwSource);
846                 trace("        LineID=%08x Channels=%d "
847                       "Connections=%d Controls=%d\n",
848                       mixerlineW.dwLineID,mixerlineW.cChannels,
849                       mixerlineW.cConnections,mixerlineW.cControls);
850                 trace("        State=0x%08x(%s)\n",
851                       mixerlineW.fdwLine,line_flags(mixerlineW.fdwLine));
852                 trace("        ComponentType=%s\n",
853                       component_type(mixerlineW.dwComponentType));
854                 trace("        Type=%s\n",
855                       target_type(mixerlineW.Target.dwType));
856                 trace("        Device=%d (%s) %d.%d (%d:%d)\n",
857                       mixerlineW.Target.dwDeviceID,szPname,
858                       mixerlineW.Target.vDriverVersion >> 8,
859                       mixerlineW.Target.vDriverVersion & 0xff,
860                       mixerlineW.Target.wMid, mixerlineW.Target.wPid);
861             }
862             ns=mixerlineW.cConnections;
863             for(s=0;s<ns;s++) {
864                 mixerlineW.cbStruct = sizeof(mixerlineW);
865                 mixerlineW.dwDestination=d;
866                 mixerlineW.dwSource=s;
867                 rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
868                                      MIXER_GETLINEINFOF_SOURCE);
869                 ok(rc==MMSYSERR_NOERROR||rc==MMSYSERR_NODRIVER,
870                    "mixerGetLineInfoW(MIXER_GETLINEINFOF_SOURCE): "
871                    "MMSYSERR_NOERROR expected, got %s\n",
872                    mmsys_error(rc));
873                 if (rc==MMSYSERR_NODRIVER)
874                     trace("  No Driver\n");
875                 else if (rc==MMSYSERR_NOERROR) {
876                     LPMIXERCONTROLW    array;
877                     MIXERLINECONTROLSW controls;
878                     if (winetest_interactive) {
879                         WideCharToMultiByte(CP_ACP,0,mixerlineW.szShortName,
880                             MIXER_SHORT_NAME_CHARS,szShortName,
881                             MIXER_SHORT_NAME_CHARS,NULL,NULL);
882                         WideCharToMultiByte(CP_ACP,0,mixerlineW.szName,
883                             MIXER_LONG_NAME_CHARS,szName,
884                             MIXER_LONG_NAME_CHARS,NULL,NULL);
885                         WideCharToMultiByte(CP_ACP,0,mixerlineW.Target.szPname,
886                             MAXPNAMELEN,szPname,
887                             MAXPNAMELEN,NULL, NULL);
888                         trace("      %d: \"%s\" (%s) Destination=%d Source=%d\n",
889                               s,szShortName,szName,
890                               mixerlineW.dwDestination,mixerlineW.dwSource);
891                         trace("          LineID=%08x Channels=%d "
892                               "Connections=%d Controls=%d\n",
893                               mixerlineW.dwLineID,mixerlineW.cChannels,
894                               mixerlineW.cConnections,mixerlineW.cControls);
895                         trace("          State=0x%08x(%s)\n",
896                               mixerlineW.fdwLine,line_flags(mixerlineW.fdwLine));
897                         trace("          ComponentType=%s\n",
898                               component_type(mixerlineW.dwComponentType));
899                         trace("          Type=%s\n",
900                               target_type(mixerlineW.Target.dwType));
901                         trace("          Device=%d (%s) %d.%d (%d:%d)\n",
902                               mixerlineW.Target.dwDeviceID,szPname,
903                               mixerlineW.Target.vDriverVersion >> 8,
904                               mixerlineW.Target.vDriverVersion & 0xff,
905                               mixerlineW.Target.wMid, mixerlineW.Target.wPid);
906                     }
907                     if (mixerlineW.cControls) {
908                         array=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
909                             mixerlineW.cControls*sizeof(MIXERCONTROLW));
910                         if (array) {
911                             rc=mixerGetLineControlsW((HMIXEROBJ)mix,0,
912                                                      MIXER_GETLINECONTROLSF_ALL);
913                             ok(rc==MMSYSERR_INVALPARAM,
914                                "mixerGetLineControlsW(MIXER_GETLINECONTROLSF_ALL): "
915                                "MMSYSERR_INVALPARAM expected, got %s\n",
916                                mmsys_error(rc));
917                             rc=mixerGetLineControlsW((HMIXEROBJ)mix,&controls,
918                                                      -1);
919                             ok(rc==MMSYSERR_INVALFLAG||rc==MMSYSERR_INVALPARAM,
920                                "mixerGetLineControlsA(-1): "
921                                "MMSYSERR_INVALFLAG or MMSYSERR_INVALPARAM expected, got %s\n",
922                                mmsys_error(rc));
923
924                             controls.cbStruct = sizeof(MIXERLINECONTROLSW);
925                             controls.cControls = mixerlineW.cControls;
926                             controls.dwLineID = mixerlineW.dwLineID;
927                             controls.pamxctrl = array;
928                             controls.cbmxctrl = sizeof(MIXERCONTROLW);
929
930                             /* FIXME: do MIXER_GETLINECONTROLSF_ONEBYID
931                              * and MIXER_GETLINECONTROLSF_ONEBYTYPE
932                              */
933                             rc=mixerGetLineControlsW((HMIXEROBJ)mix,&controls,
934                                                      MIXER_GETLINECONTROLSF_ALL);
935                             ok(rc==MMSYSERR_NOERROR,
936                                "mixerGetLineControlsW(MIXER_GETLINECONTROLSF_ALL): "
937                                "MMSYSERR_NOERROR expected, got %s\n",
938                                mmsys_error(rc));
939                             if (rc==MMSYSERR_NOERROR) {
940                                 for(nc=0;nc<mixerlineW.cControls;nc++) {
941                                     if (winetest_interactive) {
942                                         WideCharToMultiByte(CP_ACP,0,array[nc].szShortName,
943                                             MIXER_SHORT_NAME_CHARS,szShortName,
944                                             MIXER_SHORT_NAME_CHARS,NULL,NULL);
945                                         WideCharToMultiByte(CP_ACP,0,array[nc].szName,
946                                             MIXER_LONG_NAME_CHARS,szName,
947                                             MIXER_LONG_NAME_CHARS,NULL,NULL);
948                                         trace("        %d: \"%s\" (%s) ControlID=%d\n", nc,
949                                               szShortName, szName, array[nc].dwControlID);
950                                         trace("            ControlType=%s\n",
951                                                control_type(array[nc].dwControlType));
952                                         trace("            Control=0x%08x(%s)\n",
953                                               array[nc].fdwControl,
954                                               control_flags(array[nc].fdwControl));
955                                         trace("            Items=%d Min=%d Max=%d Step=%d\n",
956                                               array[nc].cMultipleItems,
957                                               S1(array[nc].Bounds).dwMinimum,
958                                               S1(array[nc].Bounds).dwMaximum,
959                                               array[nc].Metrics.cSteps);
960                                     }
961                                     mixer_test_controlW(mix, &array[nc]);
962                                 }
963                             }
964
965                             HeapFree(GetProcessHeap(),0,array);
966                         }
967                     }
968                 }
969             }
970         }
971         rc=mixerClose(mix);
972         ok(rc==MMSYSERR_NOERROR,
973            "mixerClose: MMSYSERR_BADDEVICEID expected, got %s\n",
974            mmsys_error(rc));
975     }
976 }
977
978 static void mixer_testsA(void)
979 {
980     MIXERCAPSA capsA;
981     HMIXER mix;
982     MMRESULT rc;
983     UINT ndev, d;
984
985     trace("--- Testing ASCII functions ---\n");
986
987     ndev=mixerGetNumDevs();
988     trace("found %d Mixer devices\n",ndev);
989
990     rc=mixerGetDevCapsA(ndev+1,&capsA,sizeof(capsA));
991     ok(rc==MMSYSERR_BADDEVICEID,
992        "mixerGetDevCapsA: MMSYSERR_BADDEVICEID expected, got %s\n",
993        mmsys_error(rc));
994
995     rc=mixerOpen(&mix, ndev+1, 0, 0, 0);
996     ok(rc==MMSYSERR_BADDEVICEID,
997        "mixerOpen: MMSYSERR_BADDEVICEID expected, got %s\n",
998        mmsys_error(rc));
999
1000     for (d=0;d<ndev;d++)
1001         mixer_test_deviceA(d);
1002 }
1003
1004 static void mixer_testsW(void)
1005 {
1006     MIXERCAPSW capsW;
1007     HMIXER mix;
1008     MMRESULT rc;
1009     UINT ndev, d;
1010
1011     trace("--- Testing WCHAR functions ---\n");
1012
1013     ndev=mixerGetNumDevs();
1014     trace("found %d Mixer devices\n",ndev);
1015
1016     rc=mixerGetDevCapsW(ndev+1,&capsW,sizeof(capsW));
1017     ok(rc==MMSYSERR_BADDEVICEID||rc==MMSYSERR_NOTSUPPORTED,
1018        "mixerGetDevCapsW: MMSYSERR_BADDEVICEID or MMSYSERR_NOTSUPPORTED "
1019        "expected, got %s\n", mmsys_error(rc));
1020     if (rc==MMSYSERR_NOTSUPPORTED)
1021         return;
1022
1023     rc=mixerOpen(&mix, ndev+1, 0, 0, 0);
1024     ok(rc==MMSYSERR_BADDEVICEID,
1025        "mixerOpen: MMSYSERR_BADDEVICEID expected, got %s\n",
1026        mmsys_error(rc));
1027
1028     for (d=0;d<ndev;d++)
1029         mixer_test_deviceW(d);
1030 }
1031
1032 START_TEST(mixer)
1033 {
1034     mixer_testsA();
1035     mixer_testsW();
1036 }