urlmon: Don't create stgmed_obj for binding to object.
[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 && winetest_interactive) {
199             MIXERCONTROLDETAILS new_details;
200             MIXERCONTROLDETAILS_UNSIGNED new_value;
201
202             trace("            Value=%d\n",value.dwValue);
203
204             if (value.dwValue + control->Metrics.cSteps < S1(control->Bounds).dwMaximum)
205                 new_value.dwValue = value.dwValue + control->Metrics.cSteps;
206             else
207                 new_value.dwValue = value.dwValue - control->Metrics.cSteps;
208
209             new_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
210             new_details.dwControlID = control->dwControlID;
211             new_details.cChannels = 1;
212             U(new_details).cMultipleItems = 0;
213             new_details.paDetails = &new_value;
214             new_details.cbDetails = sizeof(new_value);
215
216             /* change the control value by one step */
217             rc=mixerSetControlDetails((HMIXEROBJ)mix,&new_details,MIXER_SETCONTROLDETAILSF_VALUE);
218             ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
219                "MMSYSERR_NOERROR expected, got %s\n",
220                mmsys_error(rc));
221             if (rc==MMSYSERR_NOERROR) {
222                 MIXERCONTROLDETAILS ret_details;
223                 MIXERCONTROLDETAILS_UNSIGNED ret_value;
224
225                 ret_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
226                 ret_details.dwControlID = control->dwControlID;
227                 ret_details.cChannels = 1;
228                 U(ret_details).cMultipleItems = 0;
229                 ret_details.paDetails = &ret_value;
230                 ret_details.cbDetails = sizeof(ret_value);
231
232                 /* read back the new control value */
233                 rc=mixerGetControlDetails((HMIXEROBJ)mix,&ret_details,MIXER_GETCONTROLDETAILSF_VALUE);
234                 ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
235                    "MMSYSERR_NOERROR expected, got %s\n",
236                    mmsys_error(rc));
237                 if (rc==MMSYSERR_NOERROR) {
238                     /* result may not match exactly because of rounding */
239                     ok(abs(ret_value.dwValue-new_value.dwValue)<=1,
240                        "Couldn't change value from %d to %d, returned %d\n",
241                        value.dwValue,new_value.dwValue,ret_value.dwValue);
242
243                     if (abs(ret_value.dwValue-new_value.dwValue)<=1) {
244                         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
245                         details.dwControlID = control->dwControlID;
246                         details.cChannels = 1;
247                         U(details).cMultipleItems = 0;
248                         details.paDetails = &value;
249                         details.cbDetails = sizeof(value);
250
251                         /* restore original value */
252                         rc=mixerSetControlDetails((HMIXEROBJ)mix,&details,MIXER_SETCONTROLDETAILSF_VALUE);
253                         ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
254                            "MMSYSERR_NOERROR expected, got %s\n",
255                            mmsys_error(rc));
256                     }
257                 }
258             }
259         }
260     } else if ((control->dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE) ||
261         (control->dwControlType == MIXERCONTROL_CONTROLTYPE_BOOLEAN) ||
262         (control->dwControlType == MIXERCONTROL_CONTROLTYPE_BUTTON)) {
263         MIXERCONTROLDETAILS details;
264         MIXERCONTROLDETAILS_BOOLEAN value;
265
266         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
267         details.dwControlID = control->dwControlID;
268         details.cChannels = 1;
269         U(details).cMultipleItems = 0;
270         details.paDetails = &value;
271         details.cbDetails = sizeof(value);
272
273         rc=mixerGetControlDetails((HMIXEROBJ)mix,&details,MIXER_GETCONTROLDETAILSF_VALUE);
274         ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
275            "MMSYSERR_NOERROR expected, got %s\n",
276            mmsys_error(rc));
277         if (rc==MMSYSERR_NOERROR && winetest_interactive) {
278             MIXERCONTROLDETAILS new_details;
279             MIXERCONTROLDETAILS_BOOLEAN new_value;
280
281             trace("            Value=%d\n",value.fValue);
282
283             if (value.fValue == FALSE)
284                 new_value.fValue = TRUE;
285             else
286                 new_value.fValue = FALSE;
287
288             new_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
289             new_details.dwControlID = control->dwControlID;
290             new_details.cChannels = 1;
291             U(new_details).cMultipleItems = 0;
292             new_details.paDetails = &new_value;
293             new_details.cbDetails = sizeof(new_value);
294
295             /* change the control value by one step */
296             rc=mixerSetControlDetails((HMIXEROBJ)mix,&new_details,MIXER_SETCONTROLDETAILSF_VALUE);
297             ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
298                "MMSYSERR_NOERROR expected, got %s\n",
299                mmsys_error(rc));
300             if (rc==MMSYSERR_NOERROR) {
301                 MIXERCONTROLDETAILS ret_details;
302                 MIXERCONTROLDETAILS_BOOLEAN ret_value;
303
304                 ret_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
305                 ret_details.dwControlID = control->dwControlID;
306                 ret_details.cChannels = 1;
307                 U(ret_details).cMultipleItems = 0;
308                 ret_details.paDetails = &ret_value;
309                 ret_details.cbDetails = sizeof(ret_value);
310
311                 /* read back the new control value */
312                 rc=mixerGetControlDetails((HMIXEROBJ)mix,&ret_details,MIXER_GETCONTROLDETAILSF_VALUE);
313                 ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
314                    "MMSYSERR_NOERROR expected, got %s\n",
315                    mmsys_error(rc));
316                 if (rc==MMSYSERR_NOERROR) {
317                     /* result may not match exactly because of rounding */
318                     ok(ret_value.fValue==new_value.fValue,
319                        "Couldn't change value from %d to %d, returned %d\n",
320                        value.fValue,new_value.fValue,ret_value.fValue);
321
322                     if (ret_value.fValue==new_value.fValue) {
323                         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
324                         details.dwControlID = control->dwControlID;
325                         details.cChannels = 1;
326                         U(details).cMultipleItems = 0;
327                         details.paDetails = &value;
328                         details.cbDetails = sizeof(value);
329
330                         /* restore original value */
331                         rc=mixerSetControlDetails((HMIXEROBJ)mix,&details,MIXER_SETCONTROLDETAILSF_VALUE);
332                         ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
333                            "MMSYSERR_NOERROR expected, got %s\n",
334                            mmsys_error(rc));
335                     }
336                 }
337             }
338         }
339     } else {
340         /* FIXME */
341     }
342 }
343
344 static void mixer_test_deviceA(int device)
345 {
346     MIXERCAPSA capsA;
347     HMIXER mix;
348     MMRESULT rc;
349     DWORD d,s,ns,nc;
350
351     rc=mixerGetDevCapsA(device,0,sizeof(capsA));
352     ok(rc==MMSYSERR_INVALPARAM,
353        "mixerGetDevCapsA: MMSYSERR_INVALPARAM expected, got %s\n",
354        mmsys_error(rc));
355
356     rc=mixerGetDevCapsA(device,&capsA,4);
357     ok(rc==MMSYSERR_NOERROR,
358        "mixerGetDevCapsA: MMSYSERR_NOERROR expected, got %s\n",
359        mmsys_error(rc));
360
361     rc=mixerGetDevCapsA(device,&capsA,sizeof(capsA));
362     ok(rc==MMSYSERR_NOERROR,
363        "mixerGetDevCapsA: MMSYSERR_NOERROR expected, got %s\n",
364        mmsys_error(rc));
365
366     if (winetest_interactive) {
367         trace("  %d: \"%s\" %d.%d (%d:%d) destinations=%d\n", device,
368               capsA.szPname, capsA.vDriverVersion >> 8,
369               capsA.vDriverVersion & 0xff,capsA.wMid,capsA.wPid,
370               capsA.cDestinations);
371     } else {
372         trace("  %d: \"%s\" %d.%d (%d:%d)\n", device,
373               capsA.szPname, capsA.vDriverVersion >> 8,
374               capsA.vDriverVersion & 0xff,capsA.wMid,capsA.wPid);
375     }
376
377     rc=mixerOpen(&mix, device, 0, 0, 0);
378     ok(rc==MMSYSERR_NOERROR,
379        "mixerOpen: MMSYSERR_NOERROR expected, got %s\n",mmsys_error(rc));
380     if (rc==MMSYSERR_NOERROR) {
381         rc=mixerOpen(&mix, device, 0, 0, CALLBACK_FUNCTION);
382         ok(rc==MMSYSERR_INVALFLAG,
383            "mixerOpen: MMSYSERR_INVALFLAG expected, got %s\n", mmsys_error(rc));
384
385         /* Shouldn't open without a valid HWND */
386         rc=mixerOpen(&mix, device, 0, 0, CALLBACK_WINDOW);
387         ok(rc==MMSYSERR_INVALPARAM,
388            "mixerOpen: MMSYSERR_INVALPARAM expected, got %s\n", mmsys_error(rc));
389
390
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                             rc=mixerGetLineControlsA((HMIXEROBJ)mix,0,
501                                                       MIXER_GETLINECONTROLSF_ALL);
502                             ok(rc==MMSYSERR_INVALPARAM,
503                                "mixerGetLineControlsA(MIXER_GETLINECONTROLSF_ALL): "
504                                "MMSYSERR_INVALPARAM expected, got %s\n",
505                                mmsys_error(rc));
506
507                             rc=mixerGetLineControlsA((HMIXEROBJ)mix,&controls,-1);
508                             ok(rc==MMSYSERR_INVALFLAG||rc==MMSYSERR_INVALPARAM,
509                                "mixerGetLineControlsA(-1): "
510                                "MMSYSERR_INVALFLAG or MMSYSERR_INVALPARAM expected, got %s\n",
511                                mmsys_error(rc));
512
513                             controls.cbStruct = sizeof(MIXERLINECONTROLSA);
514                             controls.cControls = mixerlineA.cControls;
515                             controls.dwLineID = mixerlineA.dwLineID;
516                             controls.pamxctrl = array;
517                             controls.cbmxctrl = sizeof(MIXERCONTROLA);
518
519                             /* FIXME: do MIXER_GETLINECONTROLSF_ONEBYID
520                              * and MIXER_GETLINECONTROLSF_ONEBYTYPE
521                              */
522                             rc=mixerGetLineControlsA((HMIXEROBJ)mix,&controls,
523                                                      MIXER_GETLINECONTROLSF_ALL);
524                             ok(rc==MMSYSERR_NOERROR,
525                                "mixerGetLineControlsA(MIXER_GETLINECONTROLSF_ALL): "
526                                "MMSYSERR_NOERROR expected, got %s\n",
527                                mmsys_error(rc));
528                             if (rc==MMSYSERR_NOERROR) {
529                                 for(nc=0;nc<mixerlineA.cControls;nc++) {
530                                     if (winetest_interactive) {
531                                         trace("        %d: \"%s\" (%s) ControlID=%d\n", nc,
532                                               array[nc].szShortName,
533                                               array[nc].szName, array[nc].dwControlID);
534                                         trace("            ControlType=%s\n",
535                                                control_type(array[nc].dwControlType));
536                                         trace("            Control=0x%08x(%s)\n",
537                                               array[nc].fdwControl,
538                                               control_flags(array[nc].fdwControl));
539                                         trace("            Items=%d Min=%d Max=%d Step=%d\n",
540                                               array[nc].cMultipleItems,
541                                               S1(array[nc].Bounds).dwMinimum,
542                                               S1(array[nc].Bounds).dwMaximum,
543                                               array[nc].Metrics.cSteps);
544                                     }
545
546                                     mixer_test_controlA(mix, &array[nc]);
547                                 }
548                             }
549
550                             HeapFree(GetProcessHeap(),0,array);
551                         }
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 && winetest_interactive) {
586             MIXERCONTROLDETAILS new_details;
587             MIXERCONTROLDETAILS_UNSIGNED new_value;
588
589             trace("            Value=%d\n",value.dwValue);
590
591             if (value.dwValue + control->Metrics.cSteps < S1(control->Bounds).dwMaximum)
592                 new_value.dwValue = value.dwValue + control->Metrics.cSteps;
593             else
594                 new_value.dwValue = value.dwValue - control->Metrics.cSteps;
595
596             new_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
597             new_details.dwControlID = control->dwControlID;
598             new_details.cChannels = 1;
599             U(new_details).cMultipleItems = 0;
600             new_details.paDetails = &new_value;
601             new_details.cbDetails = sizeof(new_value);
602
603             /* change the control value by one step */
604             rc=mixerSetControlDetails((HMIXEROBJ)mix,&new_details,MIXER_SETCONTROLDETAILSF_VALUE);
605             ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
606                "MMSYSERR_NOERROR expected, got %s\n",
607                mmsys_error(rc));
608             if (rc==MMSYSERR_NOERROR) {
609                 MIXERCONTROLDETAILS ret_details;
610                 MIXERCONTROLDETAILS_UNSIGNED ret_value;
611
612                 ret_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
613                 ret_details.dwControlID = control->dwControlID;
614                 ret_details.cChannels = 1;
615                 U(ret_details).cMultipleItems = 0;
616                 ret_details.paDetails = &ret_value;
617                 ret_details.cbDetails = sizeof(ret_value);
618
619                 /* read back the new control value */
620                 rc=mixerGetControlDetails((HMIXEROBJ)mix,&ret_details,MIXER_GETCONTROLDETAILSF_VALUE);
621                 ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
622                    "MMSYSERR_NOERROR expected, got %s\n",
623                    mmsys_error(rc));
624                 if (rc==MMSYSERR_NOERROR) {
625                     /* result may not match exactly because of rounding */
626                     ok(abs(ret_value.dwValue-new_value.dwValue)<=1,
627                        "Couldn't change value from %d to %d, returned %d\n",
628                        value.dwValue,new_value.dwValue,ret_value.dwValue);
629
630                     if (abs(ret_value.dwValue-new_value.dwValue)<=1) {
631                         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
632                         details.dwControlID = control->dwControlID;
633                         details.cChannels = 1;
634                         U(details).cMultipleItems = 0;
635                         details.paDetails = &value;
636                         details.cbDetails = sizeof(value);
637
638                         /* restore original value */
639                         rc=mixerSetControlDetails((HMIXEROBJ)mix,&details,MIXER_SETCONTROLDETAILSF_VALUE);
640                         ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
641                            "MMSYSERR_NOERROR expected, got %s\n",
642                            mmsys_error(rc));
643                     }
644                 }
645             }
646         }
647     } else if ((control->dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE) ||
648         (control->dwControlType == MIXERCONTROL_CONTROLTYPE_BOOLEAN) ||
649         (control->dwControlType == MIXERCONTROL_CONTROLTYPE_BUTTON)) {
650         MIXERCONTROLDETAILS details;
651         MIXERCONTROLDETAILS_BOOLEAN value;
652
653         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
654         details.dwControlID = control->dwControlID;
655         details.cChannels = 1;
656         U(details).cMultipleItems = 0;
657         details.paDetails = &value;
658         details.cbDetails = sizeof(value);
659
660         rc=mixerGetControlDetails((HMIXEROBJ)mix,&details,MIXER_GETCONTROLDETAILSF_VALUE);
661         ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
662            "MMSYSERR_NOERROR expected, got %s\n",
663            mmsys_error(rc));
664         if (rc==MMSYSERR_NOERROR && winetest_interactive) {
665             MIXERCONTROLDETAILS new_details;
666             MIXERCONTROLDETAILS_BOOLEAN new_value;
667
668             trace("            Value=%d\n",value.fValue);
669
670             if (value.fValue == FALSE)
671                 new_value.fValue = TRUE;
672             else
673                 new_value.fValue = FALSE;
674
675             new_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
676             new_details.dwControlID = control->dwControlID;
677             new_details.cChannels = 1;
678             U(new_details).cMultipleItems = 0;
679             new_details.paDetails = &new_value;
680             new_details.cbDetails = sizeof(new_value);
681
682             /* change the control value by one step */
683             rc=mixerSetControlDetails((HMIXEROBJ)mix,&new_details,MIXER_SETCONTROLDETAILSF_VALUE);
684             ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
685                "MMSYSERR_NOERROR expected, got %s\n",
686                mmsys_error(rc));
687             if (rc==MMSYSERR_NOERROR) {
688                 MIXERCONTROLDETAILS ret_details;
689                 MIXERCONTROLDETAILS_BOOLEAN ret_value;
690
691                 ret_details.cbStruct = sizeof(MIXERCONTROLDETAILS);
692                 ret_details.dwControlID = control->dwControlID;
693                 ret_details.cChannels = 1;
694                 U(ret_details).cMultipleItems = 0;
695                 ret_details.paDetails = &ret_value;
696                 ret_details.cbDetails = sizeof(ret_value);
697
698                 /* read back the new control value */
699                 rc=mixerGetControlDetails((HMIXEROBJ)mix,&ret_details,MIXER_GETCONTROLDETAILSF_VALUE);
700                 ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): "
701                    "MMSYSERR_NOERROR expected, got %s\n",
702                    mmsys_error(rc));
703                 if (rc==MMSYSERR_NOERROR) {
704                     /* result may not match exactly because of rounding */
705                     ok(ret_value.fValue==new_value.fValue,
706                        "Couldn't change value from %d to %d, returned %d\n",
707                        value.fValue,new_value.fValue,ret_value.fValue);
708
709                     if (ret_value.fValue==new_value.fValue) {
710                         details.cbStruct = sizeof(MIXERCONTROLDETAILS);
711                         details.dwControlID = control->dwControlID;
712                         details.cChannels = 1;
713                         U(details).cMultipleItems = 0;
714                         details.paDetails = &value;
715                         details.cbDetails = sizeof(value);
716
717                         /* restore original value */
718                         rc=mixerSetControlDetails((HMIXEROBJ)mix,&details,MIXER_SETCONTROLDETAILSF_VALUE);
719                         ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): "
720                            "MMSYSERR_NOERROR expected, got %s\n",
721                            mmsys_error(rc));
722                     }
723                 }
724             }
725         }
726     } else {
727         /* FIXME */
728     }
729 }
730
731 static void mixer_test_deviceW(int device)
732 {
733     MIXERCAPSW capsW;
734     HMIXER mix;
735     MMRESULT rc;
736     DWORD d,s,ns,nc;
737     char szShortName[MIXER_SHORT_NAME_CHARS];
738     char szName[MIXER_LONG_NAME_CHARS];
739     char szPname[MAXPNAMELEN];
740
741     rc=mixerGetDevCapsW(device,0,sizeof(capsW));
742     ok(rc==MMSYSERR_INVALPARAM,
743        "mixerGetDevCapsW: MMSYSERR_INVALPARAM expected, got %s\n",
744        mmsys_error(rc));
745
746     rc=mixerGetDevCapsW(device,&capsW,4);
747     ok(rc==MMSYSERR_NOERROR,
748        "mixerGetDevCapsW: MMSYSERR_NOERROR 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_BADDEVICEID expected, got %s\n",mmsys_error(rc));
773     if (rc==MMSYSERR_NOERROR) {
774         rc=mixerOpen(&mix, device, 0, 0, CALLBACK_FUNCTION);
775         ok(rc==MMSYSERR_INVALFLAG,
776            "mixerOpen: MMSYSERR_INVALFLAG expected, got %s\n", mmsys_error(rc));
777
778         /* Shouldn't open without a valid HWND */
779         rc=mixerOpen(&mix, device, 0, 0, CALLBACK_WINDOW);
780         ok(rc==MMSYSERR_INVALPARAM,
781            "mixerOpen: MMSYSERR_INVALPARAM expected, got %s\n", mmsys_error(rc));
782
783         for (d=0;d<capsW.cDestinations;d++) {
784             MIXERLINEW mixerlineW;
785             mixerlineW.cbStruct = 0;
786             mixerlineW.dwDestination=d;
787             rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
788                                  MIXER_GETLINEINFOF_DESTINATION);
789             ok(rc==MMSYSERR_INVALPARAM,
790                "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
791                "MMSYSERR_INVALPARAM expected, got %s\n",
792                mmsys_error(rc));
793
794             mixerlineW.cbStruct = sizeof(mixerlineW);
795             mixerlineW.dwDestination=capsW.cDestinations;
796             rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
797                                  MIXER_GETLINEINFOF_DESTINATION);
798             ok(rc==MMSYSERR_INVALPARAM||rc==MIXERR_INVALLINE,
799                "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
800                "MMSYSERR_INVALPARAM or MIXERR_INVALLINE expected, got %s\n",
801                mmsys_error(rc));
802
803             mixerlineW.cbStruct = sizeof(mixerlineW);
804             mixerlineW.dwDestination=d;
805             rc=mixerGetLineInfoW((HMIXEROBJ)mix,0,
806                                  MIXER_GETLINEINFOF_DESTINATION);
807             ok(rc==MMSYSERR_INVALPARAM,
808                "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
809                "MMSYSERR_INVALPARAM expected, got %s\n",
810                mmsys_error(rc));
811
812             mixerlineW.cbStruct = sizeof(mixerlineW);
813             mixerlineW.dwDestination=d;
814             rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,-1);
815             ok(rc==MMSYSERR_INVALFLAG,
816                "mixerGetLineInfoW(-1): MMSYSERR_INVALFLAG expected, got %s\n",
817                mmsys_error(rc));
818
819             mixerlineW.cbStruct = sizeof(mixerlineW);
820             mixerlineW.dwDestination=d;
821             rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
822                                   MIXER_GETLINEINFOF_DESTINATION);
823             ok(rc==MMSYSERR_NOERROR||rc==MMSYSERR_NODRIVER,
824                "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
825                "MMSYSERR_NOERROR expected, got %s\n",
826                mmsys_error(rc));
827             if (rc==MMSYSERR_NODRIVER)
828                 trace("  No Driver\n");
829             else if (rc==MMSYSERR_NOERROR && winetest_interactive) {
830                 WideCharToMultiByte(CP_ACP,0,mixerlineW.szShortName,
831                     MIXER_SHORT_NAME_CHARS,szShortName,
832                     MIXER_SHORT_NAME_CHARS,NULL,NULL);
833                 WideCharToMultiByte(CP_ACP,0,mixerlineW.szName,
834                     MIXER_LONG_NAME_CHARS,szName,
835                     MIXER_LONG_NAME_CHARS,NULL,NULL);
836                 WideCharToMultiByte(CP_ACP,0,mixerlineW.Target.szPname,
837                     MAXPNAMELEN,szPname,
838                     MAXPNAMELEN,NULL, NULL);
839                 trace("    %d: \"%s\" (%s) Destination=%d Source=%d\n",
840                       d,szShortName,szName,
841                       mixerlineW.dwDestination,mixerlineW.dwSource);
842                 trace("        LineID=%08x Channels=%d "
843                       "Connections=%d Controls=%d\n",
844                       mixerlineW.dwLineID,mixerlineW.cChannels,
845                       mixerlineW.cConnections,mixerlineW.cControls);
846                 trace("        State=0x%08x(%s)\n",
847                       mixerlineW.fdwLine,line_flags(mixerlineW.fdwLine));
848                 trace("        ComponentType=%s\n",
849                       component_type(mixerlineW.dwComponentType));
850                 trace("        Type=%s\n",
851                       target_type(mixerlineW.Target.dwType));
852                 trace("        Device=%d (%s) %d.%d (%d:%d)\n",
853                       mixerlineW.Target.dwDeviceID,szPname,
854                       mixerlineW.Target.vDriverVersion >> 8,
855                       mixerlineW.Target.vDriverVersion & 0xff,
856                       mixerlineW.Target.wMid, mixerlineW.Target.wPid);
857             }
858             ns=mixerlineW.cConnections;
859             for(s=0;s<ns;s++) {
860                 mixerlineW.cbStruct = sizeof(mixerlineW);
861                 mixerlineW.dwDestination=d;
862                 mixerlineW.dwSource=s;
863                 rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
864                                      MIXER_GETLINEINFOF_SOURCE);
865                 ok(rc==MMSYSERR_NOERROR||rc==MMSYSERR_NODRIVER,
866                    "mixerGetLineInfoW(MIXER_GETLINEINFOF_SOURCE): "
867                    "MMSYSERR_NOERROR expected, got %s\n",
868                    mmsys_error(rc));
869                 if (rc==MMSYSERR_NODRIVER)
870                     trace("  No Driver\n");
871                 else if (rc==MMSYSERR_NOERROR) {
872                     LPMIXERCONTROLW    array;
873                     MIXERLINECONTROLSW controls;
874                     if (winetest_interactive) {
875                         WideCharToMultiByte(CP_ACP,0,mixerlineW.szShortName,
876                             MIXER_SHORT_NAME_CHARS,szShortName,
877                             MIXER_SHORT_NAME_CHARS,NULL,NULL);
878                         WideCharToMultiByte(CP_ACP,0,mixerlineW.szName,
879                             MIXER_LONG_NAME_CHARS,szName,
880                             MIXER_LONG_NAME_CHARS,NULL,NULL);
881                         WideCharToMultiByte(CP_ACP,0,mixerlineW.Target.szPname,
882                             MAXPNAMELEN,szPname,
883                             MAXPNAMELEN,NULL, NULL);
884                         trace("      %d: \"%s\" (%s) Destination=%d Source=%d\n",
885                               s,szShortName,szName,
886                               mixerlineW.dwDestination,mixerlineW.dwSource);
887                         trace("          LineID=%08x Channels=%d "
888                               "Connections=%d Controls=%d\n",
889                               mixerlineW.dwLineID,mixerlineW.cChannels,
890                               mixerlineW.cConnections,mixerlineW.cControls);
891                         trace("          State=0x%08x(%s)\n",
892                               mixerlineW.fdwLine,line_flags(mixerlineW.fdwLine));
893                         trace("          ComponentType=%s\n",
894                               component_type(mixerlineW.dwComponentType));
895                         trace("          Type=%s\n",
896                               target_type(mixerlineW.Target.dwType));
897                         trace("          Device=%d (%s) %d.%d (%d:%d)\n",
898                               mixerlineW.Target.dwDeviceID,szPname,
899                               mixerlineW.Target.vDriverVersion >> 8,
900                               mixerlineW.Target.vDriverVersion & 0xff,
901                               mixerlineW.Target.wMid, mixerlineW.Target.wPid);
902                     }
903                     if (mixerlineW.cControls) {
904                         array=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
905                             mixerlineW.cControls*sizeof(MIXERCONTROLW));
906                         if (array) {
907                             rc=mixerGetLineControlsW((HMIXEROBJ)mix,0,
908                                                      MIXER_GETLINECONTROLSF_ALL);
909                             ok(rc==MMSYSERR_INVALPARAM,
910                                "mixerGetLineControlsW(MIXER_GETLINECONTROLSF_ALL): "
911                                "MMSYSERR_INVALPARAM expected, got %s\n",
912                                mmsys_error(rc));
913                             rc=mixerGetLineControlsW((HMIXEROBJ)mix,&controls,
914                                                      -1);
915                             ok(rc==MMSYSERR_INVALFLAG||rc==MMSYSERR_INVALPARAM,
916                                "mixerGetLineControlsA(-1): "
917                                "MMSYSERR_INVALFLAG or MMSYSERR_INVALPARAM expected, got %s\n",
918                                mmsys_error(rc));
919
920                             controls.cbStruct = sizeof(MIXERLINECONTROLSW);
921                             controls.cControls = mixerlineW.cControls;
922                             controls.dwLineID = mixerlineW.dwLineID;
923                             controls.pamxctrl = array;
924                             controls.cbmxctrl = sizeof(MIXERCONTROLW);
925
926                             /* FIXME: do MIXER_GETLINECONTROLSF_ONEBYID
927                              * and MIXER_GETLINECONTROLSF_ONEBYTYPE
928                              */
929                             rc=mixerGetLineControlsW((HMIXEROBJ)mix,&controls,
930                                                      MIXER_GETLINECONTROLSF_ALL);
931                             ok(rc==MMSYSERR_NOERROR,
932                                "mixerGetLineControlsW(MIXER_GETLINECONTROLSF_ALL): "
933                                "MMSYSERR_NOERROR expected, got %s\n",
934                                mmsys_error(rc));
935                             if (rc==MMSYSERR_NOERROR) {
936                                 for(nc=0;nc<mixerlineW.cControls;nc++) {
937                                     if (winetest_interactive) {
938                                         WideCharToMultiByte(CP_ACP,0,array[nc].szShortName,
939                                             MIXER_SHORT_NAME_CHARS,szShortName,
940                                             MIXER_SHORT_NAME_CHARS,NULL,NULL);
941                                         WideCharToMultiByte(CP_ACP,0,array[nc].szName,
942                                             MIXER_LONG_NAME_CHARS,szName,
943                                             MIXER_LONG_NAME_CHARS,NULL,NULL);
944                                         trace("        %d: \"%s\" (%s) ControlID=%d\n", nc,
945                                               szShortName, szName, array[nc].dwControlID);
946                                         trace("            ControlType=%s\n",
947                                                control_type(array[nc].dwControlType));
948                                         trace("            Control=0x%08x(%s)\n",
949                                               array[nc].fdwControl,
950                                               control_flags(array[nc].fdwControl));
951                                         trace("            Items=%d Min=%d Max=%d Step=%d\n",
952                                               array[nc].cMultipleItems,
953                                               S1(array[nc].Bounds).dwMinimum,
954                                               S1(array[nc].Bounds).dwMaximum,
955                                               array[nc].Metrics.cSteps);
956                                     }
957                                     mixer_test_controlW(mix, &array[nc]);
958                                 }
959                             }
960
961                             HeapFree(GetProcessHeap(),0,array);
962                         }
963                     }
964                 }
965             }
966         }
967         rc=mixerClose(mix);
968         ok(rc==MMSYSERR_NOERROR,
969            "mixerClose: MMSYSERR_BADDEVICEID expected, got %s\n",
970            mmsys_error(rc));
971     }
972 }
973
974 static void mixer_testsA(void)
975 {
976     MIXERCAPSA capsA;
977     HMIXER mix;
978     MMRESULT rc;
979     UINT ndev, d;
980
981     trace("--- Testing ASCII functions ---\n");
982
983     ndev=mixerGetNumDevs();
984     trace("found %d Mixer devices\n",ndev);
985
986     rc=mixerGetDevCapsA(ndev+1,&capsA,sizeof(capsA));
987     ok(rc==MMSYSERR_BADDEVICEID,
988        "mixerGetDevCapsA: MMSYSERR_BADDEVICEID expected, got %s\n",
989        mmsys_error(rc));
990
991     rc=mixerOpen(&mix, ndev+1, 0, 0, 0);
992     ok(rc==MMSYSERR_BADDEVICEID,
993        "mixerOpen: MMSYSERR_BADDEVICEID expected, got %s\n",
994        mmsys_error(rc));
995
996     for (d=0;d<ndev;d++)
997         mixer_test_deviceA(d);
998 }
999
1000 static void mixer_testsW(void)
1001 {
1002     MIXERCAPSW capsW;
1003     HMIXER mix;
1004     MMRESULT rc;
1005     UINT ndev, d;
1006
1007     trace("--- Testing WCHAR functions ---\n");
1008
1009     ndev=mixerGetNumDevs();
1010     trace("found %d Mixer devices\n",ndev);
1011
1012     rc=mixerGetDevCapsW(ndev+1,&capsW,sizeof(capsW));
1013     ok(rc==MMSYSERR_BADDEVICEID||rc==MMSYSERR_NOTSUPPORTED,
1014        "mixerGetDevCapsW: MMSYSERR_BADDEVICEID or MMSYSERR_NOTSUPPORTED "
1015        "expected, got %s\n", mmsys_error(rc));
1016     if (rc==MMSYSERR_NOTSUPPORTED)
1017         return;
1018
1019     rc=mixerOpen(&mix, ndev+1, 0, 0, 0);
1020     ok(rc==MMSYSERR_BADDEVICEID,
1021        "mixerOpen: MMSYSERR_BADDEVICEID expected, got %s\n",
1022        mmsys_error(rc));
1023
1024     for (d=0;d<ndev;d++)
1025         mixer_test_deviceW(d);
1026 }
1027
1028 START_TEST(mixer)
1029 {
1030     mixer_testsA();
1031     mixer_testsW();
1032 }