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