mcicda: Exclude unused headers.
[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 && winetest_interactive) {
440                 trace("    %d: \"%s\" (%s) Destination=%d Source=%d\n",
441                       d,mixerlineA.szShortName, mixerlineA.szName,
442                       mixerlineA.dwDestination,mixerlineA.dwSource);
443                 trace("        LineID=%08x Channels=%d "
444                       "Connections=%d Controls=%d\n",
445                       mixerlineA.dwLineID,mixerlineA.cChannels,
446                       mixerlineA.cConnections,mixerlineA.cControls);
447                 trace("        State=0x%08x(%s)\n",
448                       mixerlineA.fdwLine,line_flags(mixerlineA.fdwLine));
449                 trace("        ComponentType=%s\n",
450                       component_type(mixerlineA.dwComponentType));
451                 trace("        Type=%s\n",
452                       target_type(mixerlineA.Target.dwType));
453                 trace("        Device=%d (%s) %d.%d (%d:%d)\n",
454                       mixerlineA.Target.dwDeviceID,
455                       mixerlineA.Target.szPname,
456                       mixerlineA.Target.vDriverVersion >> 8,
457                       mixerlineA.Target.vDriverVersion & 0xff,
458                       mixerlineA.Target.wMid, mixerlineA.Target.wPid);
459             }
460             ns=mixerlineA.cConnections;
461             for(s=0;s<ns;s++) {
462                 mixerlineA.cbStruct = sizeof(mixerlineA);
463                 mixerlineA.dwDestination=d;
464                 mixerlineA.dwSource=s;
465                 rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA,
466                                      MIXER_GETLINEINFOF_SOURCE);
467                 ok(rc==MMSYSERR_NOERROR||rc==MMSYSERR_NODRIVER,
468                    "mixerGetLineInfoA(MIXER_GETLINEINFOF_SOURCE): "
469                    "MMSYSERR_NOERROR expected, got %s\n",
470                    mmsys_error(rc));
471                 if (rc==MMSYSERR_NODRIVER)
472                     trace("  No Driver\n");
473                 else if (rc==MMSYSERR_NOERROR) {
474                     LPMIXERCONTROLA    array;
475                     MIXERLINECONTROLSA controls;
476                     if (winetest_interactive) {
477                         trace("      %d: \"%s\" (%s) Destination=%d Source=%d\n",
478                               s,mixerlineA.szShortName, mixerlineA.szName,
479                               mixerlineA.dwDestination,mixerlineA.dwSource);
480                         trace("          LineID=%08x Channels=%d "
481                               "Connections=%d Controls=%d\n",
482                               mixerlineA.dwLineID,mixerlineA.cChannels,
483                               mixerlineA.cConnections,mixerlineA.cControls);
484                         trace("          State=0x%08x(%s)\n",
485                               mixerlineA.fdwLine,line_flags(mixerlineA.fdwLine));
486                         trace("          ComponentType=%s\n",
487                               component_type(mixerlineA.dwComponentType));
488                         trace("          Type=%s\n",
489                               target_type(mixerlineA.Target.dwType));
490                         trace("          Device=%d (%s) %d.%d (%d:%d)\n",
491                               mixerlineA.Target.dwDeviceID,
492                               mixerlineA.Target.szPname,
493                               mixerlineA.Target.vDriverVersion >> 8,
494                               mixerlineA.Target.vDriverVersion & 0xff,
495                               mixerlineA.Target.wMid, mixerlineA.Target.wPid);
496                     }
497                     if (mixerlineA.cControls) {
498                         array=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
499                             mixerlineA.cControls*sizeof(MIXERCONTROLA));
500                         if (array) {
501                             rc=mixerGetLineControlsA((HMIXEROBJ)mix,0,
502                                                       MIXER_GETLINECONTROLSF_ALL);
503                             ok(rc==MMSYSERR_INVALPARAM,
504                                "mixerGetLineControlsA(MIXER_GETLINECONTROLSF_ALL): "
505                                "MMSYSERR_INVALPARAM expected, got %s\n",
506                                mmsys_error(rc));
507
508                             rc=mixerGetLineControlsA((HMIXEROBJ)mix,&controls,-1);
509                             ok(rc==MMSYSERR_INVALFLAG||rc==MMSYSERR_INVALPARAM,
510                                "mixerGetLineControlsA(-1): "
511                                "MMSYSERR_INVALFLAG or MMSYSERR_INVALPARAM expected, got %s\n",
512                                mmsys_error(rc));
513
514                             controls.cbStruct = sizeof(MIXERLINECONTROLSA);
515                             controls.cControls = mixerlineA.cControls;
516                             controls.dwLineID = mixerlineA.dwLineID;
517                             controls.pamxctrl = array;
518                             controls.cbmxctrl = sizeof(MIXERCONTROLA);
519
520                             /* FIXME: do MIXER_GETLINECONTROLSF_ONEBYID
521                              * and MIXER_GETLINECONTROLSF_ONEBYTYPE
522                              */
523                             rc=mixerGetLineControlsA((HMIXEROBJ)mix,&controls,
524                                                      MIXER_GETLINECONTROLSF_ALL);
525                             ok(rc==MMSYSERR_NOERROR,
526                                "mixerGetLineControlsA(MIXER_GETLINECONTROLSF_ALL): "
527                                "MMSYSERR_NOERROR expected, got %s\n",
528                                mmsys_error(rc));
529                             if (rc==MMSYSERR_NOERROR) {
530                                 for(nc=0;nc<mixerlineA.cControls;nc++) {
531                                     if (winetest_interactive) {
532                                         trace("        %d: \"%s\" (%s) ControlID=%d\n", nc,
533                                               array[nc].szShortName,
534                                               array[nc].szName, array[nc].dwControlID);
535                                         trace("            ControlType=%s\n",
536                                                control_type(array[nc].dwControlType));
537                                         trace("            Control=0x%08x(%s)\n",
538                                               array[nc].fdwControl,
539                                               control_flags(array[nc].fdwControl));
540                                         trace("            Items=%d Min=%d Max=%d Step=%d\n",
541                                               array[nc].cMultipleItems,
542                                               S1(array[nc].Bounds).dwMinimum,
543                                               S1(array[nc].Bounds).dwMaximum,
544                                               array[nc].Metrics.cSteps);
545                                     }
546
547                                     mixer_test_controlA(mix, &array[nc]);
548                                 }
549                             }
550
551                             HeapFree(GetProcessHeap(),0,array);
552                         }
553                     }
554                 }
555             }
556         }
557         rc=mixerClose(mix);
558         ok(rc==MMSYSERR_NOERROR,
559            "mixerClose: MMSYSERR_BADDEVICEID expected, got %s\n",
560            mmsys_error(rc));
561     }
562 }
563
564 static void mixer_test_controlW(HMIXER mix, LPMIXERCONTROLW control)
565 {
566     MMRESULT rc;
567
568     if ((control->dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME) ||
569         (control->dwControlType == MIXERCONTROL_CONTROLTYPE_UNSIGNED)) {
570         MIXERCONTROLDETAILS details;
571         MIXERCONTROLDETAILS_UNSIGNED value;
572
573         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
574         details.dwControlID = control->dwControlID;
575         details.cChannels = 1;
576         U(details).cMultipleItems = 0;
577         details.paDetails = &value;
578         details.cbDetails = sizeof(value);
579
580         /* read the current control value */
581         rc=mixerGetControlDetails((HMIXEROBJ)mix,&details,MIXER_GETCONTROLDETAILSF_VALUE);
582         ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
583            "MMSYSERR_NOERROR expected, got %s\n",
584            mmsys_error(rc));
585         if (rc==MMSYSERR_NOERROR) {
586             MIXERCONTROLDETAILS new_details;
587             MIXERCONTROLDETAILS_UNSIGNED new_value;
588
589             if (winetest_interactive)
590                 trace("            Value=%d\n",value.dwValue);
591
592             if (value.dwValue + control->Metrics.cSteps < S1(control->Bounds).dwMaximum)
593                 new_value.dwValue = value.dwValue + control->Metrics.cSteps;
594             else
595                 new_value.dwValue = value.dwValue - control->Metrics.cSteps;
596
597             new_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
598             new_details.dwControlID = control->dwControlID;
599             new_details.cChannels = 1;
600             U(new_details).cMultipleItems = 0;
601             new_details.paDetails = &new_value;
602             new_details.cbDetails = sizeof(new_value);
603
604             /* change the control value by one step */
605             rc=mixerSetControlDetails((HMIXEROBJ)mix,&new_details,MIXER_SETCONTROLDETAILSF_VALUE);
606             ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
607                "MMSYSERR_NOERROR expected, got %s\n",
608                mmsys_error(rc));
609             if (rc==MMSYSERR_NOERROR) {
610                 MIXERCONTROLDETAILS ret_details;
611                 MIXERCONTROLDETAILS_UNSIGNED ret_value;
612
613                 ret_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
614                 ret_details.dwControlID = control->dwControlID;
615                 ret_details.cChannels = 1;
616                 U(ret_details).cMultipleItems = 0;
617                 ret_details.paDetails = &ret_value;
618                 ret_details.cbDetails = sizeof(ret_value);
619
620                 /* read back the new control value */
621                 rc=mixerGetControlDetails((HMIXEROBJ)mix,&ret_details,MIXER_GETCONTROLDETAILSF_VALUE);
622                 ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
623                    "MMSYSERR_NOERROR expected, got %s\n",
624                    mmsys_error(rc));
625                 if (rc==MMSYSERR_NOERROR) {
626                     /* result may not match exactly because of rounding */
627                     ok(abs(ret_value.dwValue-new_value.dwValue)<=1,
628                        "Couldn't change value from %d to %d, returned %d\n",
629                        value.dwValue,new_value.dwValue,ret_value.dwValue);
630
631                     if (abs(ret_value.dwValue-new_value.dwValue)<=1) {
632                         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
633                         details.dwControlID = control->dwControlID;
634                         details.cChannels = 1;
635                         U(details).cMultipleItems = 0;
636                         details.paDetails = &value;
637                         details.cbDetails = sizeof(value);
638
639                         /* restore original value */
640                         rc=mixerSetControlDetails((HMIXEROBJ)mix,&details,MIXER_SETCONTROLDETAILSF_VALUE);
641                         ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
642                            "MMSYSERR_NOERROR expected, got %s\n",
643                            mmsys_error(rc));
644                     }
645                 }
646             }
647         }
648     } else if ((control->dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE) ||
649         (control->dwControlType == MIXERCONTROL_CONTROLTYPE_BOOLEAN) ||
650         (control->dwControlType == MIXERCONTROL_CONTROLTYPE_BUTTON)) {
651         MIXERCONTROLDETAILS details;
652         MIXERCONTROLDETAILS_BOOLEAN value;
653
654         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
655         details.dwControlID = control->dwControlID;
656         details.cChannels = 1;
657         U(details).cMultipleItems = 0;
658         details.paDetails = &value;
659         details.cbDetails = sizeof(value);
660
661         rc=mixerGetControlDetails((HMIXEROBJ)mix,&details,MIXER_GETCONTROLDETAILSF_VALUE);
662         ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
663            "MMSYSERR_NOERROR expected, got %s\n",
664            mmsys_error(rc));
665         if (rc==MMSYSERR_NOERROR) {
666             MIXERCONTROLDETAILS new_details;
667             MIXERCONTROLDETAILS_BOOLEAN new_value;
668
669             if (winetest_interactive)
670                 trace("            Value=%d\n",value.fValue);
671
672             if (value.fValue == FALSE)
673                 new_value.fValue = TRUE;
674             else
675                 new_value.fValue = FALSE;
676
677             new_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
678             new_details.dwControlID = control->dwControlID;
679             new_details.cChannels = 1;
680             U(new_details).cMultipleItems = 0;
681             new_details.paDetails = &new_value;
682             new_details.cbDetails = sizeof(new_value);
683
684             /* change the control value by one step */
685             rc=mixerSetControlDetails((HMIXEROBJ)mix,&new_details,MIXER_SETCONTROLDETAILSF_VALUE);
686             ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
687                "MMSYSERR_NOERROR expected, got %s\n",
688                mmsys_error(rc));
689             if (rc==MMSYSERR_NOERROR) {
690                 MIXERCONTROLDETAILS ret_details;
691                 MIXERCONTROLDETAILS_BOOLEAN ret_value;
692
693                 ret_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
694                 ret_details.dwControlID = control->dwControlID;
695                 ret_details.cChannels = 1;
696                 U(ret_details).cMultipleItems = 0;
697                 ret_details.paDetails = &ret_value;
698                 ret_details.cbDetails = sizeof(ret_value);
699
700                 /* read back the new control value */
701                 rc=mixerGetControlDetails((HMIXEROBJ)mix,&ret_details,MIXER_GETCONTROLDETAILSF_VALUE);
702                 ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
703                    "MMSYSERR_NOERROR expected, got %s\n",
704                    mmsys_error(rc));
705                 if (rc==MMSYSERR_NOERROR) {
706                     /* result may not match exactly because of rounding */
707                     ok(ret_value.fValue==new_value.fValue,
708                        "Couldn't change value from %d to %d, returned %d\n",
709                        value.fValue,new_value.fValue,ret_value.fValue);
710
711                     if (ret_value.fValue==new_value.fValue) {
712                         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
713                         details.dwControlID = control->dwControlID;
714                         details.cChannels = 1;
715                         U(details).cMultipleItems = 0;
716                         details.paDetails = &value;
717                         details.cbDetails = sizeof(value);
718
719                         /* restore original value */
720                         rc=mixerSetControlDetails((HMIXEROBJ)mix,&details,MIXER_SETCONTROLDETAILSF_VALUE);
721                         ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
722                            "MMSYSERR_NOERROR expected, got %s\n",
723                            mmsys_error(rc));
724                     }
725                 }
726             }
727         }
728     } else {
729         /* FIXME */
730     }
731 }
732
733 static void mixer_test_deviceW(int device)
734 {
735     MIXERCAPSW capsW;
736     HMIXER mix;
737     MMRESULT rc;
738     DWORD d,s,ns,nc;
739     char szShortName[MIXER_SHORT_NAME_CHARS];
740     char szName[MIXER_LONG_NAME_CHARS];
741     char szPname[MAXPNAMELEN];
742
743     rc=mixerGetDevCapsW(device,0,sizeof(capsW));
744     ok(rc==MMSYSERR_INVALPARAM,
745        "mixerGetDevCapsW: MMSYSERR_INVALPARAM expected, got %s\n",
746        mmsys_error(rc));
747
748     rc=mixerGetDevCapsW(device,&capsW,4);
749     ok(rc==MMSYSERR_NOERROR,
750        "mixerGetDevCapsW: MMSYSERR_NOERROR expected, got %s\n",
751        mmsys_error(rc));
752
753     rc=mixerGetDevCapsW(device,&capsW,sizeof(capsW));
754     ok(rc==MMSYSERR_NOERROR,
755        "mixerGetDevCapsW: MMSYSERR_NOERROR expected, got %s\n",
756        mmsys_error(rc));
757
758     WideCharToMultiByte(CP_ACP,0,capsW.szPname, MAXPNAMELEN,szPname,
759                         MAXPNAMELEN,NULL,NULL);
760     if (winetest_interactive) {
761         trace("  %d: \"%s\" %d.%d (%d:%d) destinations=%d\n", device,
762               szPname, capsW.vDriverVersion >> 8,
763               capsW.vDriverVersion & 0xff,capsW.wMid,capsW.wPid,
764               capsW.cDestinations);
765     } else {
766         trace("  %d: \"%s\" %d.%d (%d:%d)\n", device,
767               szPname, capsW.vDriverVersion >> 8,
768               capsW.vDriverVersion & 0xff,capsW.wMid,capsW.wPid);
769     }
770
771
772     rc=mixerOpen(&mix, device, 0, 0, 0);
773     ok(rc==MMSYSERR_NOERROR,
774        "mixerOpen: MMSYSERR_BADDEVICEID expected, got %s\n",mmsys_error(rc));
775     if (rc==MMSYSERR_NOERROR) {
776         rc=mixerOpen(&mix, device, 0, 0, CALLBACK_FUNCTION);
777         ok(rc==MMSYSERR_INVALFLAG,
778            "mixerOpen: MMSYSERR_INVALFLAG expected, got %s\n", mmsys_error(rc));
779
780         /* Shouldn't open without a valid HWND */
781         rc=mixerOpen(&mix, device, 0, 0, CALLBACK_WINDOW);
782         ok(rc==MMSYSERR_INVALPARAM,
783            "mixerOpen: MMSYSERR_INVALPARAM expected, got %s\n", mmsys_error(rc));
784
785         for (d=0;d<capsW.cDestinations;d++) {
786             MIXERLINEW mixerlineW;
787             mixerlineW.cbStruct = 0;
788             mixerlineW.dwDestination=d;
789             rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
790                                  MIXER_GETLINEINFOF_DESTINATION);
791             ok(rc==MMSYSERR_INVALPARAM,
792                "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
793                "MMSYSERR_INVALPARAM expected, got %s\n",
794                mmsys_error(rc));
795
796             mixerlineW.cbStruct = sizeof(mixerlineW);
797             mixerlineW.dwDestination=capsW.cDestinations;
798             rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
799                                  MIXER_GETLINEINFOF_DESTINATION);
800             ok(rc==MMSYSERR_INVALPARAM||rc==MIXERR_INVALLINE,
801                "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
802                "MMSYSERR_INVALPARAM or MIXERR_INVALLINE expected, got %s\n",
803                mmsys_error(rc));
804
805             mixerlineW.cbStruct = sizeof(mixerlineW);
806             mixerlineW.dwDestination=d;
807             rc=mixerGetLineInfoW((HMIXEROBJ)mix,0,
808                                  MIXER_GETLINEINFOF_DESTINATION);
809             ok(rc==MMSYSERR_INVALPARAM,
810                "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
811                "MMSYSERR_INVALPARAM expected, got %s\n",
812                mmsys_error(rc));
813
814             mixerlineW.cbStruct = sizeof(mixerlineW);
815             mixerlineW.dwDestination=d;
816             rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,-1);
817             ok(rc==MMSYSERR_INVALFLAG,
818                "mixerGetLineInfoW(-1): MMSYSERR_INVALFLAG expected, got %s\n",
819                mmsys_error(rc));
820
821             mixerlineW.cbStruct = sizeof(mixerlineW);
822             mixerlineW.dwDestination=d;
823             rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
824                                   MIXER_GETLINEINFOF_DESTINATION);
825             ok(rc==MMSYSERR_NOERROR||rc==MMSYSERR_NODRIVER,
826                "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
827                "MMSYSERR_NOERROR expected, got %s\n",
828                mmsys_error(rc));
829             if (rc==MMSYSERR_NODRIVER)
830                 trace("  No Driver\n");
831             else if (rc==MMSYSERR_NOERROR && winetest_interactive) {
832                 WideCharToMultiByte(CP_ACP,0,mixerlineW.szShortName,
833                     MIXER_SHORT_NAME_CHARS,szShortName,
834                     MIXER_SHORT_NAME_CHARS,NULL,NULL);
835                 WideCharToMultiByte(CP_ACP,0,mixerlineW.szName,
836                     MIXER_LONG_NAME_CHARS,szName,
837                     MIXER_LONG_NAME_CHARS,NULL,NULL);
838                 WideCharToMultiByte(CP_ACP,0,mixerlineW.Target.szPname,
839                     MAXPNAMELEN,szPname,
840                     MAXPNAMELEN,NULL, NULL);
841                 trace("    %d: \"%s\" (%s) Destination=%d Source=%d\n",
842                       d,szShortName,szName,
843                       mixerlineW.dwDestination,mixerlineW.dwSource);
844                 trace("        LineID=%08x Channels=%d "
845                       "Connections=%d Controls=%d\n",
846                       mixerlineW.dwLineID,mixerlineW.cChannels,
847                       mixerlineW.cConnections,mixerlineW.cControls);
848                 trace("        State=0x%08x(%s)\n",
849                       mixerlineW.fdwLine,line_flags(mixerlineW.fdwLine));
850                 trace("        ComponentType=%s\n",
851                       component_type(mixerlineW.dwComponentType));
852                 trace("        Type=%s\n",
853                       target_type(mixerlineW.Target.dwType));
854                 trace("        Device=%d (%s) %d.%d (%d:%d)\n",
855                       mixerlineW.Target.dwDeviceID,szPname,
856                       mixerlineW.Target.vDriverVersion >> 8,
857                       mixerlineW.Target.vDriverVersion & 0xff,
858                       mixerlineW.Target.wMid, mixerlineW.Target.wPid);
859             }
860             ns=mixerlineW.cConnections;
861             for(s=0;s<ns;s++) {
862                 mixerlineW.cbStruct = sizeof(mixerlineW);
863                 mixerlineW.dwDestination=d;
864                 mixerlineW.dwSource=s;
865                 rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
866                                      MIXER_GETLINEINFOF_SOURCE);
867                 ok(rc==MMSYSERR_NOERROR||rc==MMSYSERR_NODRIVER,
868                    "mixerGetLineInfoW(MIXER_GETLINEINFOF_SOURCE): "
869                    "MMSYSERR_NOERROR expected, got %s\n",
870                    mmsys_error(rc));
871                 if (rc==MMSYSERR_NODRIVER)
872                     trace("  No Driver\n");
873                 else if (rc==MMSYSERR_NOERROR) {
874                     LPMIXERCONTROLW    array;
875                     MIXERLINECONTROLSW controls;
876                     if (winetest_interactive) {
877                         WideCharToMultiByte(CP_ACP,0,mixerlineW.szShortName,
878                             MIXER_SHORT_NAME_CHARS,szShortName,
879                             MIXER_SHORT_NAME_CHARS,NULL,NULL);
880                         WideCharToMultiByte(CP_ACP,0,mixerlineW.szName,
881                             MIXER_LONG_NAME_CHARS,szName,
882                             MIXER_LONG_NAME_CHARS,NULL,NULL);
883                         WideCharToMultiByte(CP_ACP,0,mixerlineW.Target.szPname,
884                             MAXPNAMELEN,szPname,
885                             MAXPNAMELEN,NULL, NULL);
886                         trace("      %d: \"%s\" (%s) Destination=%d Source=%d\n",
887                               s,szShortName,szName,
888                               mixerlineW.dwDestination,mixerlineW.dwSource);
889                         trace("          LineID=%08x Channels=%d "
890                               "Connections=%d Controls=%d\n",
891                               mixerlineW.dwLineID,mixerlineW.cChannels,
892                               mixerlineW.cConnections,mixerlineW.cControls);
893                         trace("          State=0x%08x(%s)\n",
894                               mixerlineW.fdwLine,line_flags(mixerlineW.fdwLine));
895                         trace("          ComponentType=%s\n",
896                               component_type(mixerlineW.dwComponentType));
897                         trace("          Type=%s\n",
898                               target_type(mixerlineW.Target.dwType));
899                         trace("          Device=%d (%s) %d.%d (%d:%d)\n",
900                               mixerlineW.Target.dwDeviceID,szPname,
901                               mixerlineW.Target.vDriverVersion >> 8,
902                               mixerlineW.Target.vDriverVersion & 0xff,
903                               mixerlineW.Target.wMid, mixerlineW.Target.wPid);
904                     }
905                     if (mixerlineW.cControls) {
906                         array=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
907                             mixerlineW.cControls*sizeof(MIXERCONTROLW));
908                         if (array) {
909                             rc=mixerGetLineControlsW((HMIXEROBJ)mix,0,
910                                                      MIXER_GETLINECONTROLSF_ALL);
911                             ok(rc==MMSYSERR_INVALPARAM,
912                                "mixerGetLineControlsW(MIXER_GETLINECONTROLSF_ALL): "
913                                "MMSYSERR_INVALPARAM expected, got %s\n",
914                                mmsys_error(rc));
915                             rc=mixerGetLineControlsW((HMIXEROBJ)mix,&controls,
916                                                      -1);
917                             ok(rc==MMSYSERR_INVALFLAG||rc==MMSYSERR_INVALPARAM,
918                                "mixerGetLineControlsA(-1): "
919                                "MMSYSERR_INVALFLAG or MMSYSERR_INVALPARAM expected, got %s\n",
920                                mmsys_error(rc));
921
922                             controls.cbStruct = sizeof(MIXERLINECONTROLSW);
923                             controls.cControls = mixerlineW.cControls;
924                             controls.dwLineID = mixerlineW.dwLineID;
925                             controls.pamxctrl = array;
926                             controls.cbmxctrl = sizeof(MIXERCONTROLW);
927
928                             /* FIXME: do MIXER_GETLINECONTROLSF_ONEBYID
929                              * and MIXER_GETLINECONTROLSF_ONEBYTYPE
930                              */
931                             rc=mixerGetLineControlsW((HMIXEROBJ)mix,&controls,
932                                                      MIXER_GETLINECONTROLSF_ALL);
933                             ok(rc==MMSYSERR_NOERROR,
934                                "mixerGetLineControlsW(MIXER_GETLINECONTROLSF_ALL): "
935                                "MMSYSERR_NOERROR expected, got %s\n",
936                                mmsys_error(rc));
937                             if (rc==MMSYSERR_NOERROR) {
938                                 for(nc=0;nc<mixerlineW.cControls;nc++) {
939                                     if (winetest_interactive) {
940                                         WideCharToMultiByte(CP_ACP,0,array[nc].szShortName,
941                                             MIXER_SHORT_NAME_CHARS,szShortName,
942                                             MIXER_SHORT_NAME_CHARS,NULL,NULL);
943                                         WideCharToMultiByte(CP_ACP,0,array[nc].szName,
944                                             MIXER_LONG_NAME_CHARS,szName,
945                                             MIXER_LONG_NAME_CHARS,NULL,NULL);
946                                         trace("        %d: \"%s\" (%s) ControlID=%d\n", nc,
947                                               szShortName, szName, array[nc].dwControlID);
948                                         trace("            ControlType=%s\n",
949                                                control_type(array[nc].dwControlType));
950                                         trace("            Control=0x%08x(%s)\n",
951                                               array[nc].fdwControl,
952                                               control_flags(array[nc].fdwControl));
953                                         trace("            Items=%d Min=%d Max=%d Step=%d\n",
954                                               array[nc].cMultipleItems,
955                                               S1(array[nc].Bounds).dwMinimum,
956                                               S1(array[nc].Bounds).dwMaximum,
957                                               array[nc].Metrics.cSteps);
958                                     }
959                                     mixer_test_controlW(mix, &array[nc]);
960                                 }
961                             }
962
963                             HeapFree(GetProcessHeap(),0,array);
964                         }
965                     }
966                 }
967             }
968         }
969         rc=mixerClose(mix);
970         ok(rc==MMSYSERR_NOERROR,
971            "mixerClose: MMSYSERR_BADDEVICEID expected, got %s\n",
972            mmsys_error(rc));
973     }
974 }
975
976 static void mixer_testsA(void)
977 {
978     MIXERCAPSA capsA;
979     HMIXER mix;
980     MMRESULT rc;
981     UINT ndev, d;
982
983     trace("--- Testing ASCII functions ---\n");
984
985     ndev=mixerGetNumDevs();
986     trace("found %d Mixer devices\n",ndev);
987
988     rc=mixerGetDevCapsA(ndev+1,&capsA,sizeof(capsA));
989     ok(rc==MMSYSERR_BADDEVICEID,
990        "mixerGetDevCapsA: MMSYSERR_BADDEVICEID expected, got %s\n",
991        mmsys_error(rc));
992
993     rc=mixerOpen(&mix, ndev+1, 0, 0, 0);
994     ok(rc==MMSYSERR_BADDEVICEID,
995        "mixerOpen: MMSYSERR_BADDEVICEID expected, got %s\n",
996        mmsys_error(rc));
997
998     for (d=0;d<ndev;d++)
999         mixer_test_deviceA(d);
1000 }
1001
1002 static void mixer_testsW(void)
1003 {
1004     MIXERCAPSW capsW;
1005     HMIXER mix;
1006     MMRESULT rc;
1007     UINT ndev, d;
1008
1009     trace("--- Testing WCHAR functions ---\n");
1010
1011     ndev=mixerGetNumDevs();
1012     trace("found %d Mixer devices\n",ndev);
1013
1014     rc=mixerGetDevCapsW(ndev+1,&capsW,sizeof(capsW));
1015     ok(rc==MMSYSERR_BADDEVICEID||rc==MMSYSERR_NOTSUPPORTED,
1016        "mixerGetDevCapsW: MMSYSERR_BADDEVICEID or MMSYSERR_NOTSUPPORTED "
1017        "expected, got %s\n", mmsys_error(rc));
1018     if (rc==MMSYSERR_NOTSUPPORTED)
1019         return;
1020
1021     rc=mixerOpen(&mix, ndev+1, 0, 0, 0);
1022     ok(rc==MMSYSERR_BADDEVICEID,
1023        "mixerOpen: MMSYSERR_BADDEVICEID expected, got %s\n",
1024        mmsys_error(rc));
1025
1026     for (d=0;d<ndev;d++)
1027         mixer_test_deviceW(d);
1028 }
1029
1030 START_TEST(mixer)
1031 {
1032     mixer_testsA();
1033     mixer_testsW();
1034 }