winmm: MCI open always creates an alias for subsequent commands.
[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 test_mixerClose(HMIXER mix)
178 {
179     MMRESULT rc;
180
181     rc = mixerClose(mix);
182     ok(rc == MMSYSERR_NOERROR || rc == MMSYSERR_INVALHANDLE,
183        "mixerClose: MMSYSERR_NOERROR or MMSYSERR_INVALHANDLE expected, got %s\n",
184        mmsys_error(rc));
185 }
186
187 static void mixer_test_controlA(HMIXER mix, LPMIXERCONTROLA control)
188 {
189     MMRESULT rc;
190
191     if ((control->dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME) ||
192         (control->dwControlType == MIXERCONTROL_CONTROLTYPE_UNSIGNED)) {
193         MIXERCONTROLDETAILS details;
194         MIXERCONTROLDETAILS_UNSIGNED value;
195
196         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
197         details.dwControlID = control->dwControlID;
198         details.cChannels = 1;
199         U(details).cMultipleItems = 0;
200         details.paDetails = &value;
201         details.cbDetails = sizeof(value);
202
203         /* read the current control value */
204         rc=mixerGetControlDetails((HMIXEROBJ)mix,&details,MIXER_GETCONTROLDETAILSF_VALUE);
205         ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
206            "MMSYSERR_NOERROR expected, got %s\n",
207            mmsys_error(rc));
208         if (rc==MMSYSERR_NOERROR && winetest_interactive) {
209             MIXERCONTROLDETAILS new_details;
210             MIXERCONTROLDETAILS_UNSIGNED new_value;
211
212             trace("            Value=%d\n",value.dwValue);
213
214             if (value.dwValue + control->Metrics.cSteps < S1(control->Bounds).dwMaximum)
215                 new_value.dwValue = value.dwValue + control->Metrics.cSteps;
216             else
217                 new_value.dwValue = value.dwValue - control->Metrics.cSteps;
218
219             new_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
220             new_details.dwControlID = control->dwControlID;
221             new_details.cChannels = 1;
222             U(new_details).cMultipleItems = 0;
223             new_details.paDetails = &new_value;
224             new_details.cbDetails = sizeof(new_value);
225
226             /* change the control value by one step */
227             rc=mixerSetControlDetails((HMIXEROBJ)mix,&new_details,MIXER_SETCONTROLDETAILSF_VALUE);
228             ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
229                "MMSYSERR_NOERROR expected, got %s\n",
230                mmsys_error(rc));
231             if (rc==MMSYSERR_NOERROR) {
232                 MIXERCONTROLDETAILS ret_details;
233                 MIXERCONTROLDETAILS_UNSIGNED ret_value;
234
235                 ret_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
236                 ret_details.dwControlID = control->dwControlID;
237                 ret_details.cChannels = 1;
238                 U(ret_details).cMultipleItems = 0;
239                 ret_details.paDetails = &ret_value;
240                 ret_details.cbDetails = sizeof(ret_value);
241
242                 /* read back the new control value */
243                 rc=mixerGetControlDetails((HMIXEROBJ)mix,&ret_details,MIXER_GETCONTROLDETAILSF_VALUE);
244                 ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
245                    "MMSYSERR_NOERROR expected, got %s\n",
246                    mmsys_error(rc));
247                 if (rc==MMSYSERR_NOERROR) {
248                     /* result may not match exactly because of rounding */
249                     ok(abs(ret_value.dwValue-new_value.dwValue)<=1,
250                        "Couldn't change value from %d to %d, returned %d\n",
251                        value.dwValue,new_value.dwValue,ret_value.dwValue);
252
253                     if (abs(ret_value.dwValue-new_value.dwValue)<=1) {
254                         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
255                         details.dwControlID = control->dwControlID;
256                         details.cChannels = 1;
257                         U(details).cMultipleItems = 0;
258                         details.paDetails = &value;
259                         details.cbDetails = sizeof(value);
260
261                         /* restore original value */
262                         rc=mixerSetControlDetails((HMIXEROBJ)mix,&details,MIXER_SETCONTROLDETAILSF_VALUE);
263                         ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
264                            "MMSYSERR_NOERROR expected, got %s\n",
265                            mmsys_error(rc));
266                     }
267                 }
268             }
269         }
270     } else if ((control->dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE) ||
271         (control->dwControlType == MIXERCONTROL_CONTROLTYPE_BOOLEAN) ||
272         (control->dwControlType == MIXERCONTROL_CONTROLTYPE_BUTTON)) {
273         MIXERCONTROLDETAILS details;
274         MIXERCONTROLDETAILS_BOOLEAN value;
275
276         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
277         details.dwControlID = control->dwControlID;
278         details.cChannels = 1;
279         U(details).cMultipleItems = 0;
280         details.paDetails = &value;
281         details.cbDetails = sizeof(value);
282
283         rc=mixerGetControlDetails((HMIXEROBJ)mix,&details,MIXER_GETCONTROLDETAILSF_VALUE);
284         ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
285            "MMSYSERR_NOERROR expected, got %s\n",
286            mmsys_error(rc));
287         if (rc==MMSYSERR_NOERROR && winetest_interactive) {
288             MIXERCONTROLDETAILS new_details;
289             MIXERCONTROLDETAILS_BOOLEAN new_value;
290
291             trace("            Value=%d\n",value.fValue);
292
293             if (value.fValue == FALSE)
294                 new_value.fValue = TRUE;
295             else
296                 new_value.fValue = FALSE;
297
298             new_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
299             new_details.dwControlID = control->dwControlID;
300             new_details.cChannels = 1;
301             U(new_details).cMultipleItems = 0;
302             new_details.paDetails = &new_value;
303             new_details.cbDetails = sizeof(new_value);
304
305             /* change the control value by one step */
306             rc=mixerSetControlDetails((HMIXEROBJ)mix,&new_details,MIXER_SETCONTROLDETAILSF_VALUE);
307             ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
308                "MMSYSERR_NOERROR expected, got %s\n",
309                mmsys_error(rc));
310             if (rc==MMSYSERR_NOERROR) {
311                 MIXERCONTROLDETAILS ret_details;
312                 MIXERCONTROLDETAILS_BOOLEAN ret_value;
313
314                 ret_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
315                 ret_details.dwControlID = control->dwControlID;
316                 ret_details.cChannels = 1;
317                 U(ret_details).cMultipleItems = 0;
318                 ret_details.paDetails = &ret_value;
319                 ret_details.cbDetails = sizeof(ret_value);
320
321                 /* read back the new control value */
322                 rc=mixerGetControlDetails((HMIXEROBJ)mix,&ret_details,MIXER_GETCONTROLDETAILSF_VALUE);
323                 ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
324                    "MMSYSERR_NOERROR expected, got %s\n",
325                    mmsys_error(rc));
326                 if (rc==MMSYSERR_NOERROR) {
327                     /* result may not match exactly because of rounding */
328                     ok(ret_value.fValue==new_value.fValue,
329                        "Couldn't change value from %d to %d, returned %d\n",
330                        value.fValue,new_value.fValue,ret_value.fValue);
331
332                     if (ret_value.fValue==new_value.fValue) {
333                         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
334                         details.dwControlID = control->dwControlID;
335                         details.cChannels = 1;
336                         U(details).cMultipleItems = 0;
337                         details.paDetails = &value;
338                         details.cbDetails = sizeof(value);
339
340                         /* restore original value */
341                         rc=mixerSetControlDetails((HMIXEROBJ)mix,&details,MIXER_SETCONTROLDETAILSF_VALUE);
342                         ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
343                            "MMSYSERR_NOERROR expected, got %s\n",
344                            mmsys_error(rc));
345                     }
346                 }
347             }
348         }
349     } else {
350         /* FIXME */
351     }
352 }
353
354 static void mixer_test_deviceA(int device)
355 {
356     MIXERCAPSA capsA;
357     HMIXER mix;
358     MMRESULT rc;
359     DWORD d,s,ns,nc;
360
361     rc=mixerGetDevCapsA(device,0,sizeof(capsA));
362     ok(rc==MMSYSERR_INVALPARAM,
363        "mixerGetDevCapsA: MMSYSERR_INVALPARAM expected, got %s\n",
364        mmsys_error(rc));
365
366     rc=mixerGetDevCapsA(device,&capsA,4);
367     ok(rc==MMSYSERR_NOERROR,
368        "mixerGetDevCapsA: MMSYSERR_NOERROR expected, got %s\n",
369        mmsys_error(rc));
370
371     rc=mixerGetDevCapsA(device,&capsA,sizeof(capsA));
372     ok(rc==MMSYSERR_NOERROR,
373        "mixerGetDevCapsA: MMSYSERR_NOERROR expected, got %s\n",
374        mmsys_error(rc));
375
376     if (winetest_interactive) {
377         trace("  %d: \"%s\" %d.%d (%d:%d) destinations=%d\n", device,
378               capsA.szPname, capsA.vDriverVersion >> 8,
379               capsA.vDriverVersion & 0xff,capsA.wMid,capsA.wPid,
380               capsA.cDestinations);
381     } else {
382         trace("  %d: \"%s\" %d.%d (%d:%d)\n", device,
383               capsA.szPname, capsA.vDriverVersion >> 8,
384               capsA.vDriverVersion & 0xff,capsA.wMid,capsA.wPid);
385     }
386
387     rc=mixerOpen(&mix, device, 0, 0, 0);
388     ok(rc==MMSYSERR_NOERROR,
389        "mixerOpen: MMSYSERR_NOERROR expected, got %s\n",mmsys_error(rc));
390     if (rc==MMSYSERR_NOERROR) {
391         for (d=0;d<capsA.cDestinations;d++) {
392             MIXERLINEA mixerlineA;
393             mixerlineA.cbStruct = 0;
394             mixerlineA.dwDestination=d;
395             rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA,
396                                  MIXER_GETLINEINFOF_DESTINATION);
397             ok(rc==MMSYSERR_INVALPARAM,
398                "mixerGetLineInfoA(MIXER_GETLINEINFOF_DESTINATION): "
399                "MMSYSERR_INVALPARAM expected, got %s\n",
400                mmsys_error(rc));
401
402             mixerlineA.cbStruct = sizeof(mixerlineA);
403             mixerlineA.dwDestination=capsA.cDestinations;
404             rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA,
405                                  MIXER_GETLINEINFOF_DESTINATION);
406             ok(rc==MMSYSERR_INVALPARAM||rc==MIXERR_INVALLINE,
407                "mixerGetLineInfoA(MIXER_GETLINEINFOF_DESTINATION): "
408                "MMSYSERR_INVALPARAM or MIXERR_INVALLINE expected, got %s\n",
409                mmsys_error(rc));
410
411             mixerlineA.cbStruct = sizeof(mixerlineA);
412             mixerlineA.dwDestination=d;
413             rc=mixerGetLineInfoA((HMIXEROBJ)mix,0,
414                                  MIXER_GETLINEINFOF_DESTINATION);
415             ok(rc==MMSYSERR_INVALPARAM,
416                "mixerGetLineInfoA(MIXER_GETLINEINFOF_DESTINATION): "
417                "MMSYSERR_INVALPARAM expected, got %s\n",
418                mmsys_error(rc));
419
420             mixerlineA.cbStruct = sizeof(mixerlineA);
421             mixerlineA.dwDestination=d;
422             rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA,-1);
423             ok(rc==MMSYSERR_INVALFLAG,
424                "mixerGetLineInfoA(-1): MMSYSERR_INVALFLAG expected, got %s\n",
425                mmsys_error(rc));
426
427             mixerlineA.cbStruct = sizeof(mixerlineA);
428             mixerlineA.dwDestination=d;
429             rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA,
430                                   MIXER_GETLINEINFOF_DESTINATION);
431             ok(rc==MMSYSERR_NOERROR||rc==MMSYSERR_NODRIVER,
432                "mixerGetLineInfoA(MIXER_GETLINEINFOF_DESTINATION): "
433                "MMSYSERR_NOERROR expected, got %s\n",
434                mmsys_error(rc));
435             if (rc==MMSYSERR_NODRIVER)
436                 trace("  No Driver\n");
437             else if (rc==MMSYSERR_NOERROR) {
438               if (winetest_interactive) {
439                 trace("    %d: \"%s\" (%s) Destination=%d Source=%d\n",
440                       d,mixerlineA.szShortName, mixerlineA.szName,
441                       mixerlineA.dwDestination,mixerlineA.dwSource);
442                 trace("        LineID=%08x Channels=%d "
443                       "Connections=%d Controls=%d\n",
444                       mixerlineA.dwLineID,mixerlineA.cChannels,
445                       mixerlineA.cConnections,mixerlineA.cControls);
446                 trace("        State=0x%08x(%s)\n",
447                       mixerlineA.fdwLine,line_flags(mixerlineA.fdwLine));
448                 trace("        ComponentType=%s\n",
449                       component_type(mixerlineA.dwComponentType));
450                 trace("        Type=%s\n",
451                       target_type(mixerlineA.Target.dwType));
452                 trace("        Device=%d (%s) %d.%d (%d:%d)\n",
453                       mixerlineA.Target.dwDeviceID,
454                       mixerlineA.Target.szPname,
455                       mixerlineA.Target.vDriverVersion >> 8,
456                       mixerlineA.Target.vDriverVersion & 0xff,
457                       mixerlineA.Target.wMid, mixerlineA.Target.wPid);
458               }
459               ns=mixerlineA.cConnections;
460               for(s=0;s<ns;s++) {
461                 mixerlineA.cbStruct = sizeof(mixerlineA);
462                 mixerlineA.dwDestination=d;
463                 mixerlineA.dwSource=s;
464                 rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA,
465                                      MIXER_GETLINEINFOF_SOURCE);
466                 ok(rc==MMSYSERR_NOERROR||rc==MMSYSERR_NODRIVER,
467                    "mixerGetLineInfoA(MIXER_GETLINEINFOF_SOURCE): "
468                    "MMSYSERR_NOERROR expected, got %s\n",
469                    mmsys_error(rc));
470                 if (rc==MMSYSERR_NODRIVER)
471                     trace("  No Driver\n");
472                 else if (rc==MMSYSERR_NOERROR) {
473                     LPMIXERCONTROLA    array;
474                     MIXERLINECONTROLSA controls;
475                     if (winetest_interactive) {
476                         trace("      %d: \"%s\" (%s) Destination=%d Source=%d\n",
477                               s,mixerlineA.szShortName, mixerlineA.szName,
478                               mixerlineA.dwDestination,mixerlineA.dwSource);
479                         trace("          LineID=%08x Channels=%d "
480                               "Connections=%d Controls=%d\n",
481                               mixerlineA.dwLineID,mixerlineA.cChannels,
482                               mixerlineA.cConnections,mixerlineA.cControls);
483                         trace("          State=0x%08x(%s)\n",
484                               mixerlineA.fdwLine,line_flags(mixerlineA.fdwLine));
485                         trace("          ComponentType=%s\n",
486                               component_type(mixerlineA.dwComponentType));
487                         trace("          Type=%s\n",
488                               target_type(mixerlineA.Target.dwType));
489                         trace("          Device=%d (%s) %d.%d (%d:%d)\n",
490                               mixerlineA.Target.dwDeviceID,
491                               mixerlineA.Target.szPname,
492                               mixerlineA.Target.vDriverVersion >> 8,
493                               mixerlineA.Target.vDriverVersion & 0xff,
494                               mixerlineA.Target.wMid, mixerlineA.Target.wPid);
495                     }
496                     if (mixerlineA.cControls) {
497                         array=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
498                             mixerlineA.cControls*sizeof(MIXERCONTROLA));
499                         if (array) {
500                             memset(&controls, 0, sizeof(controls));
501
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         test_mixerClose(mix);
560     }
561 }
562
563 static void mixer_test_controlW(HMIXER mix, LPMIXERCONTROLW control)
564 {
565     MMRESULT rc;
566
567     if ((control->dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME) ||
568         (control->dwControlType == MIXERCONTROL_CONTROLTYPE_UNSIGNED)) {
569         MIXERCONTROLDETAILS details;
570         MIXERCONTROLDETAILS_UNSIGNED value;
571
572         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
573         details.dwControlID = control->dwControlID;
574         details.cChannels = 1;
575         U(details).cMultipleItems = 0;
576         details.paDetails = &value;
577         details.cbDetails = sizeof(value);
578
579         /* read the current control value */
580         rc=mixerGetControlDetails((HMIXEROBJ)mix,&details,MIXER_GETCONTROLDETAILSF_VALUE);
581         ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
582            "MMSYSERR_NOERROR expected, got %s\n",
583            mmsys_error(rc));
584         if (rc==MMSYSERR_NOERROR && winetest_interactive) {
585             MIXERCONTROLDETAILS new_details;
586             MIXERCONTROLDETAILS_UNSIGNED new_value;
587
588             trace("            Value=%d\n",value.dwValue);
589
590             if (value.dwValue + control->Metrics.cSteps < S1(control->Bounds).dwMaximum)
591                 new_value.dwValue = value.dwValue + control->Metrics.cSteps;
592             else
593                 new_value.dwValue = value.dwValue - control->Metrics.cSteps;
594
595             new_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
596             new_details.dwControlID = control->dwControlID;
597             new_details.cChannels = 1;
598             U(new_details).cMultipleItems = 0;
599             new_details.paDetails = &new_value;
600             new_details.cbDetails = sizeof(new_value);
601
602             /* change the control value by one step */
603             rc=mixerSetControlDetails((HMIXEROBJ)mix,&new_details,MIXER_SETCONTROLDETAILSF_VALUE);
604             ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
605                "MMSYSERR_NOERROR expected, got %s\n",
606                mmsys_error(rc));
607             if (rc==MMSYSERR_NOERROR) {
608                 MIXERCONTROLDETAILS ret_details;
609                 MIXERCONTROLDETAILS_UNSIGNED ret_value;
610
611                 ret_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
612                 ret_details.dwControlID = control->dwControlID;
613                 ret_details.cChannels = 1;
614                 U(ret_details).cMultipleItems = 0;
615                 ret_details.paDetails = &ret_value;
616                 ret_details.cbDetails = sizeof(ret_value);
617
618                 /* read back the new control value */
619                 rc=mixerGetControlDetails((HMIXEROBJ)mix,&ret_details,MIXER_GETCONTROLDETAILSF_VALUE);
620                 ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
621                    "MMSYSERR_NOERROR expected, got %s\n",
622                    mmsys_error(rc));
623                 if (rc==MMSYSERR_NOERROR) {
624                     /* result may not match exactly because of rounding */
625                     ok(abs(ret_value.dwValue-new_value.dwValue)<=1,
626                        "Couldn't change value from %d to %d, returned %d\n",
627                        value.dwValue,new_value.dwValue,ret_value.dwValue);
628
629                     if (abs(ret_value.dwValue-new_value.dwValue)<=1) {
630                         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
631                         details.dwControlID = control->dwControlID;
632                         details.cChannels = 1;
633                         U(details).cMultipleItems = 0;
634                         details.paDetails = &value;
635                         details.cbDetails = sizeof(value);
636
637                         /* restore original value */
638                         rc=mixerSetControlDetails((HMIXEROBJ)mix,&details,MIXER_SETCONTROLDETAILSF_VALUE);
639                         ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
640                            "MMSYSERR_NOERROR expected, got %s\n",
641                            mmsys_error(rc));
642                     }
643                 }
644             }
645         }
646     } else if ((control->dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE) ||
647         (control->dwControlType == MIXERCONTROL_CONTROLTYPE_BOOLEAN) ||
648         (control->dwControlType == MIXERCONTROL_CONTROLTYPE_BUTTON)) {
649         MIXERCONTROLDETAILS details;
650         MIXERCONTROLDETAILS_BOOLEAN value;
651
652         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
653         details.dwControlID = control->dwControlID;
654         details.cChannels = 1;
655         U(details).cMultipleItems = 0;
656         details.paDetails = &value;
657         details.cbDetails = sizeof(value);
658
659         rc=mixerGetControlDetails((HMIXEROBJ)mix,&details,MIXER_GETCONTROLDETAILSF_VALUE);
660         ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
661            "MMSYSERR_NOERROR expected, got %s\n",
662            mmsys_error(rc));
663         if (rc==MMSYSERR_NOERROR && winetest_interactive) {
664             MIXERCONTROLDETAILS new_details;
665             MIXERCONTROLDETAILS_BOOLEAN new_value;
666
667             trace("            Value=%d\n",value.fValue);
668
669             if (value.fValue == FALSE)
670                 new_value.fValue = TRUE;
671             else
672                 new_value.fValue = FALSE;
673
674             new_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
675             new_details.dwControlID = control->dwControlID;
676             new_details.cChannels = 1;
677             U(new_details).cMultipleItems = 0;
678             new_details.paDetails = &new_value;
679             new_details.cbDetails = sizeof(new_value);
680
681             /* change the control value by one step */
682             rc=mixerSetControlDetails((HMIXEROBJ)mix,&new_details,MIXER_SETCONTROLDETAILSF_VALUE);
683             ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
684                "MMSYSERR_NOERROR expected, got %s\n",
685                mmsys_error(rc));
686             if (rc==MMSYSERR_NOERROR) {
687                 MIXERCONTROLDETAILS ret_details;
688                 MIXERCONTROLDETAILS_BOOLEAN ret_value;
689
690                 ret_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
691                 ret_details.dwControlID = control->dwControlID;
692                 ret_details.cChannels = 1;
693                 U(ret_details).cMultipleItems = 0;
694                 ret_details.paDetails = &ret_value;
695                 ret_details.cbDetails = sizeof(ret_value);
696
697                 /* read back the new control value */
698                 rc=mixerGetControlDetails((HMIXEROBJ)mix,&ret_details,MIXER_GETCONTROLDETAILSF_VALUE);
699                 ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
700                    "MMSYSERR_NOERROR expected, got %s\n",
701                    mmsys_error(rc));
702                 if (rc==MMSYSERR_NOERROR) {
703                     /* result may not match exactly because of rounding */
704                     ok(ret_value.fValue==new_value.fValue,
705                        "Couldn't change value from %d to %d, returned %d\n",
706                        value.fValue,new_value.fValue,ret_value.fValue);
707
708                     if (ret_value.fValue==new_value.fValue) {
709                         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
710                         details.dwControlID = control->dwControlID;
711                         details.cChannels = 1;
712                         U(details).cMultipleItems = 0;
713                         details.paDetails = &value;
714                         details.cbDetails = sizeof(value);
715
716                         /* restore original value */
717                         rc=mixerSetControlDetails((HMIXEROBJ)mix,&details,MIXER_SETCONTROLDETAILSF_VALUE);
718                         ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
719                            "MMSYSERR_NOERROR expected, got %s\n",
720                            mmsys_error(rc));
721                     }
722                 }
723             }
724         }
725     } else {
726         /* FIXME */
727     }
728 }
729
730 static void mixer_test_deviceW(int device)
731 {
732     MIXERCAPSW capsW;
733     HMIXER mix;
734     MMRESULT rc;
735     DWORD d,s,ns,nc;
736     char szShortName[MIXER_SHORT_NAME_CHARS];
737     char szName[MIXER_LONG_NAME_CHARS];
738     char szPname[MAXPNAMELEN];
739
740     rc=mixerGetDevCapsW(device,0,sizeof(capsW));
741     ok(rc==MMSYSERR_INVALPARAM,
742        "mixerGetDevCapsW: MMSYSERR_INVALPARAM expected, got %s\n",
743        mmsys_error(rc));
744
745     rc=mixerGetDevCapsW(device,&capsW,4);
746     ok(rc==MMSYSERR_NOERROR ||
747        rc==MMSYSERR_INVALPARAM, /* Vista and W2K8 */
748        "mixerGetDevCapsW: MMSYSERR_NOERROR or MMSYSERR_INVALPARAM expected, got %s\n",
749        mmsys_error(rc));
750
751     rc=mixerGetDevCapsW(device,&capsW,sizeof(capsW));
752     ok(rc==MMSYSERR_NOERROR,
753        "mixerGetDevCapsW: MMSYSERR_NOERROR expected, got %s\n",
754        mmsys_error(rc));
755
756     WideCharToMultiByte(CP_ACP,0,capsW.szPname, MAXPNAMELEN,szPname,
757                         MAXPNAMELEN,NULL,NULL);
758     if (winetest_interactive) {
759         trace("  %d: \"%s\" %d.%d (%d:%d) destinations=%d\n", device,
760               szPname, capsW.vDriverVersion >> 8,
761               capsW.vDriverVersion & 0xff,capsW.wMid,capsW.wPid,
762               capsW.cDestinations);
763     } else {
764         trace("  %d: \"%s\" %d.%d (%d:%d)\n", device,
765               szPname, capsW.vDriverVersion >> 8,
766               capsW.vDriverVersion & 0xff,capsW.wMid,capsW.wPid);
767     }
768
769
770     rc=mixerOpen(&mix, device, 0, 0, 0);
771     ok(rc==MMSYSERR_NOERROR,
772        "mixerOpen: MMSYSERR_NOERROR expected, got %s\n",mmsys_error(rc));
773     if (rc==MMSYSERR_NOERROR) {
774         for (d=0;d<capsW.cDestinations;d++) {
775             MIXERLINEW mixerlineW;
776             mixerlineW.cbStruct = 0;
777             mixerlineW.dwDestination=d;
778             rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
779                                  MIXER_GETLINEINFOF_DESTINATION);
780             ok(rc==MMSYSERR_INVALPARAM,
781                "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
782                "MMSYSERR_INVALPARAM expected, got %s\n",
783                mmsys_error(rc));
784
785             mixerlineW.cbStruct = sizeof(mixerlineW);
786             mixerlineW.dwDestination=capsW.cDestinations;
787             rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
788                                  MIXER_GETLINEINFOF_DESTINATION);
789             ok(rc==MMSYSERR_INVALPARAM||rc==MIXERR_INVALLINE,
790                "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
791                "MMSYSERR_INVALPARAM or MIXERR_INVALLINE expected, got %s\n",
792                mmsys_error(rc));
793
794             mixerlineW.cbStruct = sizeof(mixerlineW);
795             mixerlineW.dwDestination=d;
796             rc=mixerGetLineInfoW((HMIXEROBJ)mix,0,
797                                  MIXER_GETLINEINFOF_DESTINATION);
798             ok(rc==MMSYSERR_INVALPARAM,
799                "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
800                "MMSYSERR_INVALPARAM expected, got %s\n",
801                mmsys_error(rc));
802
803             mixerlineW.cbStruct = sizeof(mixerlineW);
804             mixerlineW.dwDestination=d;
805             rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,-1);
806             ok(rc==MMSYSERR_INVALFLAG,
807                "mixerGetLineInfoW(-1): MMSYSERR_INVALFLAG expected, got %s\n",
808                mmsys_error(rc));
809
810             mixerlineW.cbStruct = sizeof(mixerlineW);
811             mixerlineW.dwDestination=d;
812             rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
813                                   MIXER_GETLINEINFOF_DESTINATION);
814             ok(rc==MMSYSERR_NOERROR||rc==MMSYSERR_NODRIVER,
815                "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
816                "MMSYSERR_NOERROR expected, got %s\n",
817                mmsys_error(rc));
818             if (rc==MMSYSERR_NODRIVER)
819                 trace("  No Driver\n");
820             else if (rc==MMSYSERR_NOERROR && winetest_interactive) {
821                 WideCharToMultiByte(CP_ACP,0,mixerlineW.szShortName,
822                     MIXER_SHORT_NAME_CHARS,szShortName,
823                     MIXER_SHORT_NAME_CHARS,NULL,NULL);
824                 WideCharToMultiByte(CP_ACP,0,mixerlineW.szName,
825                     MIXER_LONG_NAME_CHARS,szName,
826                     MIXER_LONG_NAME_CHARS,NULL,NULL);
827                 WideCharToMultiByte(CP_ACP,0,mixerlineW.Target.szPname,
828                     MAXPNAMELEN,szPname,
829                     MAXPNAMELEN,NULL, NULL);
830                 trace("    %d: \"%s\" (%s) Destination=%d Source=%d\n",
831                       d,szShortName,szName,
832                       mixerlineW.dwDestination,mixerlineW.dwSource);
833                 trace("        LineID=%08x Channels=%d "
834                       "Connections=%d Controls=%d\n",
835                       mixerlineW.dwLineID,mixerlineW.cChannels,
836                       mixerlineW.cConnections,mixerlineW.cControls);
837                 trace("        State=0x%08x(%s)\n",
838                       mixerlineW.fdwLine,line_flags(mixerlineW.fdwLine));
839                 trace("        ComponentType=%s\n",
840                       component_type(mixerlineW.dwComponentType));
841                 trace("        Type=%s\n",
842                       target_type(mixerlineW.Target.dwType));
843                 trace("        Device=%d (%s) %d.%d (%d:%d)\n",
844                       mixerlineW.Target.dwDeviceID,szPname,
845                       mixerlineW.Target.vDriverVersion >> 8,
846                       mixerlineW.Target.vDriverVersion & 0xff,
847                       mixerlineW.Target.wMid, mixerlineW.Target.wPid);
848             }
849             ns=mixerlineW.cConnections;
850             for(s=0;s<ns;s++) {
851                 mixerlineW.cbStruct = sizeof(mixerlineW);
852                 mixerlineW.dwDestination=d;
853                 mixerlineW.dwSource=s;
854                 rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
855                                      MIXER_GETLINEINFOF_SOURCE);
856                 ok(rc==MMSYSERR_NOERROR||rc==MMSYSERR_NODRIVER,
857                    "mixerGetLineInfoW(MIXER_GETLINEINFOF_SOURCE): "
858                    "MMSYSERR_NOERROR expected, got %s\n",
859                    mmsys_error(rc));
860                 if (rc==MMSYSERR_NODRIVER)
861                     trace("  No Driver\n");
862                 else if (rc==MMSYSERR_NOERROR) {
863                     LPMIXERCONTROLW    array;
864                     MIXERLINECONTROLSW controls;
865                     if (winetest_interactive) {
866                         WideCharToMultiByte(CP_ACP,0,mixerlineW.szShortName,
867                             MIXER_SHORT_NAME_CHARS,szShortName,
868                             MIXER_SHORT_NAME_CHARS,NULL,NULL);
869                         WideCharToMultiByte(CP_ACP,0,mixerlineW.szName,
870                             MIXER_LONG_NAME_CHARS,szName,
871                             MIXER_LONG_NAME_CHARS,NULL,NULL);
872                         WideCharToMultiByte(CP_ACP,0,mixerlineW.Target.szPname,
873                             MAXPNAMELEN,szPname,
874                             MAXPNAMELEN,NULL, NULL);
875                         trace("      %d: \"%s\" (%s) Destination=%d Source=%d\n",
876                               s,szShortName,szName,
877                               mixerlineW.dwDestination,mixerlineW.dwSource);
878                         trace("          LineID=%08x Channels=%d "
879                               "Connections=%d Controls=%d\n",
880                               mixerlineW.dwLineID,mixerlineW.cChannels,
881                               mixerlineW.cConnections,mixerlineW.cControls);
882                         trace("          State=0x%08x(%s)\n",
883                               mixerlineW.fdwLine,line_flags(mixerlineW.fdwLine));
884                         trace("          ComponentType=%s\n",
885                               component_type(mixerlineW.dwComponentType));
886                         trace("          Type=%s\n",
887                               target_type(mixerlineW.Target.dwType));
888                         trace("          Device=%d (%s) %d.%d (%d:%d)\n",
889                               mixerlineW.Target.dwDeviceID,szPname,
890                               mixerlineW.Target.vDriverVersion >> 8,
891                               mixerlineW.Target.vDriverVersion & 0xff,
892                               mixerlineW.Target.wMid, mixerlineW.Target.wPid);
893                     }
894                     if (mixerlineW.cControls) {
895                         array=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
896                             mixerlineW.cControls*sizeof(MIXERCONTROLW));
897                         if (array) {
898                             rc=mixerGetLineControlsW((HMIXEROBJ)mix,0,
899                                                      MIXER_GETLINECONTROLSF_ALL);
900                             ok(rc==MMSYSERR_INVALPARAM,
901                                "mixerGetLineControlsW(MIXER_GETLINECONTROLSF_ALL): "
902                                "MMSYSERR_INVALPARAM expected, got %s\n",
903                                mmsys_error(rc));
904                             rc=mixerGetLineControlsW((HMIXEROBJ)mix,&controls,
905                                                      -1);
906                             ok(rc==MMSYSERR_INVALFLAG||rc==MMSYSERR_INVALPARAM,
907                                "mixerGetLineControlsA(-1): "
908                                "MMSYSERR_INVALFLAG or MMSYSERR_INVALPARAM expected, got %s\n",
909                                mmsys_error(rc));
910
911                             controls.cbStruct = sizeof(MIXERLINECONTROLSW);
912                             controls.cControls = mixerlineW.cControls;
913                             controls.dwLineID = mixerlineW.dwLineID;
914                             controls.pamxctrl = array;
915                             controls.cbmxctrl = sizeof(MIXERCONTROLW);
916
917                             /* FIXME: do MIXER_GETLINECONTROLSF_ONEBYID
918                              * and MIXER_GETLINECONTROLSF_ONEBYTYPE
919                              */
920                             rc=mixerGetLineControlsW((HMIXEROBJ)mix,&controls,
921                                                      MIXER_GETLINECONTROLSF_ALL);
922                             ok(rc==MMSYSERR_NOERROR,
923                                "mixerGetLineControlsW(MIXER_GETLINECONTROLSF_ALL): "
924                                "MMSYSERR_NOERROR expected, got %s\n",
925                                mmsys_error(rc));
926                             if (rc==MMSYSERR_NOERROR) {
927                                 for(nc=0;nc<mixerlineW.cControls;nc++) {
928                                     if (winetest_interactive) {
929                                         WideCharToMultiByte(CP_ACP,0,array[nc].szShortName,
930                                             MIXER_SHORT_NAME_CHARS,szShortName,
931                                             MIXER_SHORT_NAME_CHARS,NULL,NULL);
932                                         WideCharToMultiByte(CP_ACP,0,array[nc].szName,
933                                             MIXER_LONG_NAME_CHARS,szName,
934                                             MIXER_LONG_NAME_CHARS,NULL,NULL);
935                                         trace("        %d: \"%s\" (%s) ControlID=%d\n", nc,
936                                               szShortName, szName, array[nc].dwControlID);
937                                         trace("            ControlType=%s\n",
938                                                control_type(array[nc].dwControlType));
939                                         trace("            Control=0x%08x(%s)\n",
940                                               array[nc].fdwControl,
941                                               control_flags(array[nc].fdwControl));
942                                         trace("            Items=%d Min=%d Max=%d Step=%d\n",
943                                               array[nc].cMultipleItems,
944                                               S1(array[nc].Bounds).dwMinimum,
945                                               S1(array[nc].Bounds).dwMaximum,
946                                               array[nc].Metrics.cSteps);
947                                     }
948                                     mixer_test_controlW(mix, &array[nc]);
949                                 }
950                             }
951
952                             HeapFree(GetProcessHeap(),0,array);
953                         }
954                     }
955                 }
956             }
957         }
958         test_mixerClose(mix);
959     }
960 }
961
962 static void mixer_testsA(void)
963 {
964     MIXERCAPSA capsA;
965     MMRESULT rc;
966     UINT ndev, d;
967
968     trace("--- Testing ASCII functions ---\n");
969
970     ndev=mixerGetNumDevs();
971     trace("found %d Mixer devices\n",ndev);
972
973     rc=mixerGetDevCapsA(ndev+1,&capsA,sizeof(capsA));
974     ok(rc==MMSYSERR_BADDEVICEID,
975        "mixerGetDevCapsA: MMSYSERR_BADDEVICEID expected, got %s\n",
976        mmsys_error(rc));
977
978     for (d=0;d<ndev;d++)
979         mixer_test_deviceA(d);
980 }
981
982 static void mixer_testsW(void)
983 {
984     MIXERCAPSW capsW;
985     MMRESULT rc;
986     UINT ndev, d;
987
988     trace("--- Testing WCHAR functions ---\n");
989
990     ndev=mixerGetNumDevs();
991     trace("found %d Mixer devices\n",ndev);
992
993     rc=mixerGetDevCapsW(ndev+1,&capsW,sizeof(capsW));
994     ok(rc==MMSYSERR_BADDEVICEID||rc==MMSYSERR_NOTSUPPORTED,
995        "mixerGetDevCapsW: MMSYSERR_BADDEVICEID or MMSYSERR_NOTSUPPORTED "
996        "expected, got %s\n", mmsys_error(rc));
997     if (rc==MMSYSERR_NOTSUPPORTED)
998         return;
999
1000     for (d=0;d<ndev;d++)
1001         mixer_test_deviceW(d);
1002 }
1003
1004 static void test_mixerOpen(void)
1005 {
1006     HMIXER mix;
1007     MMRESULT rc;
1008     UINT ndev, d;
1009
1010     ndev = mixerGetNumDevs();
1011
1012     /* Test mixerOpen with invalid device ID values. */
1013     rc = mixerOpen(&mix, ndev + 1, 0, 0, 0);
1014     ok(rc == MMSYSERR_BADDEVICEID,
1015        "mixerOpen: MMSYSERR_BADDEVICEID expected, got %s\n",
1016        mmsys_error(rc));
1017
1018     rc = mixerOpen(&mix, -1, 0, 0, 0);
1019     ok(rc == MMSYSERR_BADDEVICEID ||
1020        rc == MMSYSERR_INVALHANDLE, /* NT4/W2K */
1021        "mixerOpen: MMSYSERR_BADDEVICEID or MMSYSERR_INVALHANDLE expected, got %s\n",
1022        mmsys_error(rc));
1023
1024     for (d = 0; d < ndev; d++) {
1025         /* Test mixerOpen with valid device ID values and invalid parameters. */
1026         rc = mixerOpen(&mix, d, 0, 0, CALLBACK_FUNCTION);
1027         ok(rc == MMSYSERR_INVALFLAG
1028            || rc == MMSYSERR_NOTSUPPORTED, /* 98/ME */
1029            "mixerOpen: MMSYSERR_INVALFLAG expected, got %s\n",
1030            mmsys_error(rc));
1031
1032         rc = mixerOpen(&mix, d, 0xdeadbeef, 0, CALLBACK_WINDOW);
1033         ok(rc == MMSYSERR_INVALPARAM ||
1034            rc == MMSYSERR_NOERROR, /* 98 */
1035            "mixerOpen: MMSYSERR_INVALPARAM expected, got %s\n",
1036            mmsys_error(rc));
1037         if (rc == MMSYSERR_NOERROR)
1038             test_mixerClose(mix);
1039
1040         /* Test mixerOpen with a NULL dwCallback and CALLBACK_WINDOW flag. */
1041         rc = mixerOpen(&mix, d, 0, 0, CALLBACK_WINDOW);
1042         ok(rc == MMSYSERR_NOERROR,
1043            "mixerOpen: MMSYSERR_NOERROR expected, got %s\n",
1044            mmsys_error(rc));
1045
1046         if (rc == MMSYSERR_NOERROR)
1047             test_mixerClose(mix);
1048
1049         /* Test mixerOpen with normal parameters. */
1050         rc = mixerOpen(&mix, d, 0, 0, 0);
1051         ok(rc == MMSYSERR_NOERROR,
1052            "mixerOpen: MMSYSERR_NOERROR expected, got %s\n",
1053            mmsys_error(rc));
1054
1055         if (rc == MMSYSERR_NOERROR)
1056             test_mixerClose(mix);
1057     }
1058 }
1059
1060 START_TEST(mixer)
1061 {
1062     test_mixerOpen();
1063     mixer_testsA();
1064     mixer_testsW();
1065 }