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