Added test for WAVEFORMATEXTENSIBLE support.
[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  * examine and update control details
24  * add interactive tests
25  */
26
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <math.h>
31
32 #include "wine/test.h"
33 #include "windef.h"
34 #include "winbase.h"
35 #include "winnls.h"
36 #include "mmsystem.h"
37 #include "mmddk.h"
38
39 #include "winmm_test.h"
40
41 #ifdef NONAMELESSSTRUCT
42 # define S1(x) (x).s1
43 #else
44 # define S1(x) (x)
45 #endif
46
47 static const char * line_flags(DWORD fdwLine)
48 {
49     static char flags[100];
50     BOOL first=TRUE;
51     flags[0]=0;
52     if (fdwLine&MIXERLINE_LINEF_ACTIVE) {
53         strcat(flags,"MIXERLINE_LINEF_ACTIVE");
54         first=FALSE;
55     }
56     if (fdwLine&MIXERLINE_LINEF_DISCONNECTED) {
57         if (!first)
58             strcat(flags, "|");
59
60         strcat(flags,"MIXERLINE_LINEF_DISCONNECTED");
61         first=FALSE;
62     }
63
64     if (fdwLine&MIXERLINE_LINEF_SOURCE) {
65         if (!first)
66             strcat(flags, "|");
67
68         strcat(flags,"MIXERLINE_LINEF_SOURCE");
69     }
70
71     return flags;
72 }
73
74 static const char * component_type(DWORD dwComponentType)
75 {
76 #define TYPE_TO_STR(x) case x: return #x
77     switch (dwComponentType) {
78     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_UNDEFINED);
79     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_DIGITAL);
80     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_LINE);
81     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_MONITOR);
82     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_SPEAKERS);
83     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_HEADPHONES);
84     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_TELEPHONE);
85     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_WAVEIN);
86     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_VOICEIN);
87     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED);
88     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_DIGITAL);
89     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_LINE);
90     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE);
91     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER);
92     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC);
93     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE);
94     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER);
95     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT);
96     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY);
97     TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_ANALOG);
98     }
99 #undef TYPE_TO_STR
100     return "UNKNOWN";
101 }
102
103 static const char * target_type(DWORD dwType)
104 {
105 #define TYPE_TO_STR(x) case x: return #x
106     switch (dwType) {
107     TYPE_TO_STR(MIXERLINE_TARGETTYPE_UNDEFINED);
108     TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEOUT);
109     TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEIN);
110     TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIOUT);
111     TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIIN);
112     TYPE_TO_STR(MIXERLINE_TARGETTYPE_AUX);
113     }
114 #undef TYPE_TO_STR
115     return "UNKNOWN";
116 }
117
118 static const char * control_type(DWORD dwControlType)
119 {
120 #define TYPE_TO_STR(x) case x: return #x
121     switch (dwControlType) {
122     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_CUSTOM);
123     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEANMETER);
124     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNEDMETER);
125     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PEAKMETER);
126     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER);
127     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEAN);
128     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_ONOFF);
129     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUTE);
130     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MONO);
131     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_LOUDNESS);
132     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_STEREOENH);
133     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS_BOOST);
134     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BUTTON);
135     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_DECIBELS);
136     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNED);
137     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNED);
138     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PERCENT);
139     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SLIDER);
140     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PAN);
141     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_QSOUNDPAN);
142     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_FADER);
143     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_VOLUME);
144     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS);
145     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_TREBLE);
146     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_EQUALIZER);
147     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SINGLESELECT);
148     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUX);
149     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT);
150     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MIXER);
151     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MICROTIME);
152     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MILLITIME);
153     }
154 #undef TYPE_TO_STR
155     return "UNKNOWN";
156 }
157
158 static const char * control_flags(DWORD fdwControl)
159 {
160     static char flags[100];
161     BOOL first=TRUE;
162     flags[0]=0;
163     if (fdwControl&MIXERCONTROL_CONTROLF_UNIFORM) {
164         strcat(flags,"MIXERCONTROL_CONTROLF_UNIFORM");
165         first=FALSE;
166     }
167     if (fdwControl&MIXERCONTROL_CONTROLF_MULTIPLE) {
168         if (!first)
169             strcat(flags, "|");
170
171         strcat(flags,"MIXERCONTROL_CONTROLF_MULTIPLE");
172         first=FALSE;
173     }
174
175     if (fdwControl&MIXERCONTROL_CONTROLF_DISABLED) {
176         if (!first)
177             strcat(flags, "|");
178
179         strcat(flags,"MIXERCONTROL_CONTROLF_DISABLED");
180     }
181
182     return flags;
183 }
184 void mixer_test_deviceA(int device)
185 {
186     MIXERCAPSA capsA;
187     HMIXER mix;
188     MMRESULT rc;
189     DWORD d,s,ns,nc;
190
191     rc=mixerGetDevCapsA(device,0,sizeof(capsA));
192     ok(rc==MMSYSERR_INVALPARAM,
193        "mixerGetDevCapsA: MMSYSERR_INVALPARAM expected, got %s\n",
194        mmsys_error(rc));
195
196     rc=mixerGetDevCapsA(device,&capsA,4);
197     ok(rc==MMSYSERR_NOERROR,
198        "mixerGetDevCapsA: MMSYSERR_NOERROR expected, got %s\n",
199        mmsys_error(rc));
200
201     rc=mixerGetDevCapsA(device,&capsA,sizeof(capsA));
202     ok(rc==MMSYSERR_NOERROR,
203        "mixerGetDevCapsA: MMSYSERR_NOERROR expected, got %s\n",
204        mmsys_error(rc));
205
206     if (winetest_interactive) {
207         trace("  %d: \"%s\" %d.%d (%d:%d) destinations=%ld\n", device,
208               capsA.szPname, capsA.vDriverVersion >> 8,
209               capsA.vDriverVersion & 0xff,capsA.wMid,capsA.wPid,
210               capsA.cDestinations);
211     }
212
213     rc=mixerOpen(&mix, device, 0, 0, 0);
214     ok(rc==MMSYSERR_NOERROR,
215        "mixerOpen: MMSYSERR_BADDEVICEID expected, got %s\n",mmsys_error(rc));
216     if (rc==MMSYSERR_NOERROR) {
217         for (d=0;d<capsA.cDestinations;d++) {
218             MIXERLINEA mixerlineA;
219             mixerlineA.cbStruct = 0;
220             mixerlineA.dwDestination=d;
221             rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA,
222                                  MIXER_GETLINEINFOF_DESTINATION);
223             ok(rc==MMSYSERR_INVALPARAM,
224                "mixerGetLineInfoA(MIXER_GETLINEINFOF_DESTINATION): "
225                "MMSYSERR_INVALPARAM expected, got %s\n",
226                mmsys_error(rc));
227
228             mixerlineA.cbStruct = sizeof(mixerlineA);
229             mixerlineA.dwDestination=capsA.cDestinations;
230             rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA,
231                                  MIXER_GETLINEINFOF_DESTINATION);
232             ok(rc==MMSYSERR_INVALPARAM||rc==MIXERR_INVALLINE,
233                "mixerGetLineInfoA(MIXER_GETLINEINFOF_DESTINATION): "
234                "MMSYSERR_INVALPARAM or MIXERR_INVALLINE expected, got %s\n",
235                mmsys_error(rc));
236
237             mixerlineA.cbStruct = sizeof(mixerlineA);
238             mixerlineA.dwDestination=d;
239             rc=mixerGetLineInfoA((HMIXEROBJ)mix,0,
240                                  MIXER_GETLINEINFOF_DESTINATION);
241             ok(rc==MMSYSERR_INVALPARAM,
242                "mixerGetLineInfoA(MIXER_GETLINEINFOF_DESTINATION): "
243                "MMSYSERR_INVALPARAM expected, got %s\n",
244                mmsys_error(rc));
245
246             mixerlineA.cbStruct = sizeof(mixerlineA);
247             mixerlineA.dwDestination=d;
248             rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA,-1);
249             ok(rc==MMSYSERR_INVALFLAG,
250                "mixerGetLineInfoA(-1): MMSYSERR_INVALFLAG expected, got %s\n",
251                mmsys_error(rc));
252
253             mixerlineA.cbStruct = sizeof(mixerlineA);
254             mixerlineA.dwDestination=d;
255             rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA,
256                                   MIXER_GETLINEINFOF_DESTINATION);
257             ok(rc==MMSYSERR_NOERROR,
258                "mixerGetLineInfoA(MIXER_GETLINEINFOF_DESTINATION): "
259                "MMSYSERR_NOERROR expected, got %s\n",
260                mmsys_error(rc));
261             if (rc==MMSYSERR_NOERROR && winetest_interactive) {
262                 trace("    %ld: \"%s\" (%s) Destination=%ld Source=%ld\n",
263                       d,mixerlineA.szShortName, mixerlineA.szName,
264                       mixerlineA.dwDestination,mixerlineA.dwSource);
265                 trace("        LineID=%08lx Channels=%ld "
266                       "Connections=%ld Controls=%ld \n",
267                       mixerlineA.dwLineID,mixerlineA.cChannels,
268                       mixerlineA.cConnections,mixerlineA.cControls);
269                 trace("        State=0x%08lx(%s)\n",
270                       mixerlineA.fdwLine,line_flags(mixerlineA.fdwLine));
271                 trace("        ComponentType=%s\n",
272                       component_type(mixerlineA.dwComponentType));
273                 trace("        Type=%s\n",
274                       target_type(mixerlineA.Target.dwType));
275                 trace("        Device=%ld (%s) %d.%d (%d:%d)\n",
276                       mixerlineA.Target.dwDeviceID,
277                       mixerlineA.Target.szPname,
278                       mixerlineA.Target.vDriverVersion >> 8,
279                       mixerlineA.Target.vDriverVersion & 0xff,
280                       mixerlineA.Target.wMid, mixerlineA.Target.wPid);
281             }
282             ns=mixerlineA.cConnections;
283             for(s=0;s<ns;s++) {
284                 mixerlineA.cbStruct = sizeof(mixerlineA);
285                 mixerlineA.dwDestination=d;
286                 mixerlineA.dwSource=s;
287                 rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA,
288                                      MIXER_GETLINEINFOF_SOURCE);
289                 ok(rc==MMSYSERR_NOERROR,
290                    "mixerGetLineInfoA(MIXER_GETLINEINFOF_SOURCE): "
291                    "MMSYSERR_NOERROR expected, got %s\n",
292                    mmsys_error(rc));
293                 if (rc==MMSYSERR_NOERROR) {
294                     LPMIXERCONTROLA    array;
295                     MIXERLINECONTROLSA controls;
296                     if (winetest_interactive) {
297                         trace("      %ld: \"%s\" (%s) Destination=%ld Source=%ld\n",
298                               s,mixerlineA.szShortName, mixerlineA.szName,
299                               mixerlineA.dwDestination,mixerlineA.dwSource);
300                         trace("          LineID=%08lx Channels=%ld "
301                               "Connections=%ld Controls=%ld \n",
302                               mixerlineA.dwLineID,mixerlineA.cChannels,
303                               mixerlineA.cConnections,mixerlineA.cControls);
304                         trace("          State=0x%08lx(%s)\n",
305                               mixerlineA.fdwLine,line_flags(mixerlineA.fdwLine));
306                         trace("          ComponentType=%s\n",
307                               component_type(mixerlineA.dwComponentType));
308                         trace("          Type=%s\n",
309                               target_type(mixerlineA.Target.dwType));
310                         trace("          Device=%ld (%s) %d.%d (%d:%d)\n",
311                               mixerlineA.Target.dwDeviceID,
312                               mixerlineA.Target.szPname,
313                               mixerlineA.Target.vDriverVersion >> 8,
314                               mixerlineA.Target.vDriverVersion & 0xff,
315                               mixerlineA.Target.wMid, mixerlineA.Target.wPid);
316                     }
317                     if (mixerlineA.cControls) {
318                         array=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
319                             mixerlineA.cControls*sizeof(MIXERCONTROLA));
320                         if (array) {
321                             rc=mixerGetLineControlsA((HMIXEROBJ)mix,0,
322                                                       MIXER_GETLINECONTROLSF_ALL);
323                             ok(rc==MMSYSERR_INVALPARAM,
324                                "mixerGetLineControlsA(MIXER_GETLINECONTROLSF_ALL): "
325                                "MMSYSERR_INVALPARAM expected, got %s\n",
326                                mmsys_error(rc));
327
328                             rc=mixerGetLineControlsA((HMIXEROBJ)mix,&controls,-1);
329                             ok(rc==MMSYSERR_INVALFLAG||rc==MMSYSERR_INVALPARAM,
330                                "mixerGetLineControlsA(-1): "
331                                "MMSYSERR_INVALFLAG or MMSYSERR_INVALPARAM expected, got %s\n",
332                                mmsys_error(rc));
333
334                             controls.cbStruct = sizeof(MIXERLINECONTROLSA);
335                             controls.cControls = mixerlineA.cControls;
336                             controls.dwLineID = mixerlineA.dwLineID;
337                             controls.pamxctrl = array;
338                             controls.cbmxctrl = sizeof(MIXERCONTROLA);
339
340                             /* FIXME: do MIXER_GETLINECONTROLSF_ONEBYID
341                              * and MIXER_GETLINECONTROLSF_ONEBYTYPE
342                              */
343                             rc=mixerGetLineControlsA((HMIXEROBJ)mix,&controls,
344                                                      MIXER_GETLINECONTROLSF_ALL);
345                             ok(rc==MMSYSERR_NOERROR,
346                                "mixerGetLineControlsA(MIXER_GETLINECONTROLSF_ALL): "
347                                "MMSYSERR_NOERROR expected, got %s\n",
348                                mmsys_error(rc));
349                             if (rc==MMSYSERR_NOERROR) {
350                                 for(nc=0;nc<mixerlineA.cControls;nc++) {
351                                     if (winetest_interactive) {
352                                         trace("        %ld: \"%s\" (%s) ControlID=%ld\n", nc,
353                                               array[nc].szShortName,
354                                               array[nc].szName, array[nc].dwControlID);
355                                         trace("            ControlType=%s\n",
356                                                control_type(array[nc].dwControlType));
357                                         trace("            Control=0x%08lx(%s)\n",
358                                               array[nc].fdwControl,
359                                               control_flags(array[nc].fdwControl));
360                                         trace("            Items=%ld Min=%ld Max=%ld Step=%ld\n",
361                                               array[nc].cMultipleItems,
362                                               S1(array[nc].Bounds).dwMinimum,
363                                               S1(array[nc].Bounds).dwMaximum,
364                                               array[nc].Metrics.cSteps);
365                                     }
366                                 }
367                             }
368
369                             HeapFree(GetProcessHeap(),0,array);
370                         }
371                     }
372                 }
373             }
374         }
375         rc=mixerClose(mix);
376         ok(rc==MMSYSERR_NOERROR,
377            "mixerClose: MMSYSERR_BADDEVICEID expected, got %s\n",
378            mmsys_error(rc));
379     }
380 }
381
382 void mixer_test_deviceW(int device)
383 {
384     MIXERCAPSW capsW;
385     HMIXER mix;
386     MMRESULT rc;
387     DWORD d,s,ns,nc;
388     char szShortName[MIXER_SHORT_NAME_CHARS];
389     char szName[MIXER_LONG_NAME_CHARS];
390     char szPname[MAXPNAMELEN];
391
392     rc=mixerGetDevCapsW(device,0,sizeof(capsW));
393     ok(rc==MMSYSERR_INVALPARAM,
394        "mixerGetDevCapsW: MMSYSERR_INVALPARAM expected, got %s\n",
395        mmsys_error(rc));
396
397     rc=mixerGetDevCapsW(device,&capsW,4);
398     ok(rc==MMSYSERR_NOERROR,
399        "mixerGetDevCapsW: MMSYSERR_NOERROR expected, got %s\n",
400        mmsys_error(rc));
401
402     rc=mixerGetDevCapsW(device,&capsW,sizeof(capsW));
403     ok(rc==MMSYSERR_NOERROR,
404        "mixerGetDevCapsW: MMSYSERR_NOERROR expected, got %s\n",
405        mmsys_error(rc));
406
407     WideCharToMultiByte(CP_ACP,0,capsW.szPname, MAXPNAMELEN,szPname,
408                         MAXPNAMELEN,NULL,NULL);
409     if (winetest_interactive) {
410         trace("  %d: \"%s\" %d.%d (%d:%d) destinations=%ld\n", device,
411               szPname, capsW.vDriverVersion >> 8,
412               capsW.vDriverVersion & 0xff,capsW.wMid,capsW.wPid,
413               capsW.cDestinations);
414     }
415     rc=mixerOpen(&mix, device, 0, 0, 0);
416     ok(rc==MMSYSERR_NOERROR,
417        "mixerOpen: MMSYSERR_BADDEVICEID expected, got %s\n",mmsys_error(rc));
418     if (rc==MMSYSERR_NOERROR) {
419         for (d=0;d<capsW.cDestinations;d++) {
420             MIXERLINEW mixerlineW;
421             mixerlineW.cbStruct = 0;
422             mixerlineW.dwDestination=d;
423             rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
424                                  MIXER_GETLINEINFOF_DESTINATION);
425             ok(rc==MMSYSERR_INVALPARAM,
426                "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
427                "MMSYSERR_INVALPARAM expected, got %s\n",
428                mmsys_error(rc));
429
430             mixerlineW.cbStruct = sizeof(mixerlineW);
431             mixerlineW.dwDestination=capsW.cDestinations;
432             rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
433                                  MIXER_GETLINEINFOF_DESTINATION);
434             ok(rc==MMSYSERR_INVALPARAM||rc==MIXERR_INVALLINE,
435                "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
436                "MMSYSERR_INVALPARAM or MIXERR_INVALLINE expected, got %s\n",
437                mmsys_error(rc));
438
439             mixerlineW.cbStruct = sizeof(mixerlineW);
440             mixerlineW.dwDestination=d;
441             rc=mixerGetLineInfoW((HMIXEROBJ)mix,0,
442                                  MIXER_GETLINEINFOF_DESTINATION);
443             ok(rc==MMSYSERR_INVALPARAM,
444                "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
445                "MMSYSERR_INVALPARAM expected, got %s\n",
446                mmsys_error(rc));
447
448             mixerlineW.cbStruct = sizeof(mixerlineW);
449             mixerlineW.dwDestination=d;
450             rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,-1);
451             ok(rc==MMSYSERR_INVALFLAG,
452                "mixerGetLineInfoW(-1): MMSYSERR_INVALFLAG expected, got %s\n",
453                mmsys_error(rc));
454
455             mixerlineW.cbStruct = sizeof(mixerlineW);
456             mixerlineW.dwDestination=d;
457             rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
458                                   MIXER_GETLINEINFOF_DESTINATION);
459             ok(rc==MMSYSERR_NOERROR,
460                "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): "
461                "MMSYSERR_NOERROR expected, got %s\n",
462                mmsys_error(rc));
463             if (rc==MMSYSERR_NOERROR && winetest_interactive) {
464                 WideCharToMultiByte(CP_ACP,0,mixerlineW.szShortName,
465                     MIXER_SHORT_NAME_CHARS,szShortName,
466                     MIXER_SHORT_NAME_CHARS,NULL,NULL);
467                 WideCharToMultiByte(CP_ACP,0,mixerlineW.szName,
468                     MIXER_LONG_NAME_CHARS,szName,
469                     MIXER_LONG_NAME_CHARS,NULL,NULL);
470                 WideCharToMultiByte(CP_ACP,0,mixerlineW.Target.szPname,
471                     MAXPNAMELEN,szPname,
472                     MAXPNAMELEN,NULL, NULL);
473                 trace("    %ld: \"%s\" (%s) Destination=%ld Source=%ld\n",
474                       d,szShortName,szName,
475                       mixerlineW.dwDestination,mixerlineW.dwSource);
476                 trace("        LineID=%08lx Channels=%ld "
477                       "Connections=%ld Controls=%ld \n",
478                       mixerlineW.dwLineID,mixerlineW.cChannels,
479                       mixerlineW.cConnections,mixerlineW.cControls);
480                 trace("        State=0x%08lx(%s)\n",
481                       mixerlineW.fdwLine,line_flags(mixerlineW.fdwLine));
482                 trace("        ComponentType=%s\n",
483                       component_type(mixerlineW.dwComponentType));
484                 trace("        Type=%s\n",
485                       target_type(mixerlineW.Target.dwType));
486                 trace("        Device=%ld (%s) %d.%d (%d:%d)\n",
487                       mixerlineW.Target.dwDeviceID,szPname,
488                       mixerlineW.Target.vDriverVersion >> 8,
489                       mixerlineW.Target.vDriverVersion & 0xff,
490                       mixerlineW.Target.wMid, mixerlineW.Target.wPid);
491             }
492             ns=mixerlineW.cConnections;
493             for(s=0;s<ns;s++) {
494                 mixerlineW.cbStruct = sizeof(mixerlineW);
495                 mixerlineW.dwDestination=d;
496                 mixerlineW.dwSource=s;
497                 rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,
498                                      MIXER_GETLINEINFOF_SOURCE);
499                 ok(rc==MMSYSERR_NOERROR,
500                    "mixerGetLineInfoW(MIXER_GETLINEINFOF_SOURCE): "
501                    "MMSYSERR_NOERROR expected, got %s\n",
502                    mmsys_error(rc));
503                 if (rc==MMSYSERR_NOERROR) {
504                     LPMIXERCONTROLW    array;
505                     MIXERLINECONTROLSW controls;
506                     if (winetest_interactive) {
507                         WideCharToMultiByte(CP_ACP,0,mixerlineW.szShortName,
508                             MIXER_SHORT_NAME_CHARS,szShortName,
509                             MIXER_SHORT_NAME_CHARS,NULL,NULL);
510                         WideCharToMultiByte(CP_ACP,0,mixerlineW.szName,
511                             MIXER_LONG_NAME_CHARS,szName,
512                             MIXER_LONG_NAME_CHARS,NULL,NULL);
513                         WideCharToMultiByte(CP_ACP,0,mixerlineW.Target.szPname,
514                             MAXPNAMELEN,szPname,
515                             MAXPNAMELEN,NULL, NULL);
516                         trace("      %ld: \"%s\" (%s) Destination=%ld Source=%ld\n",
517                               s,szShortName,szName,
518                               mixerlineW.dwDestination,mixerlineW.dwSource);
519                         trace("          LineID=%08lx Channels=%ld "
520                               "Connections=%ld Controls=%ld \n",
521                               mixerlineW.dwLineID,mixerlineW.cChannels,
522                               mixerlineW.cConnections,mixerlineW.cControls);
523                         trace("          State=0x%08lx(%s)\n",
524                               mixerlineW.fdwLine,line_flags(mixerlineW.fdwLine));
525                         trace("          ComponentType=%s\n",
526                               component_type(mixerlineW.dwComponentType));
527                         trace("          Type=%s\n",
528                               target_type(mixerlineW.Target.dwType));
529                         trace("          Device=%ld (%s) %d.%d (%d:%d)\n",
530                               mixerlineW.Target.dwDeviceID,szPname,
531                               mixerlineW.Target.vDriverVersion >> 8,
532                               mixerlineW.Target.vDriverVersion & 0xff,
533                               mixerlineW.Target.wMid, mixerlineW.Target.wPid);
534                     }
535                     if (mixerlineW.cControls) {
536                         array=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
537                             mixerlineW.cControls*sizeof(MIXERCONTROLW));
538                         if (array) {
539                             rc=mixerGetLineControlsW((HMIXEROBJ)mix,0,
540                                                      MIXER_GETLINECONTROLSF_ALL);
541                             ok(rc==MMSYSERR_INVALPARAM,
542                                "mixerGetLineControlsW(MIXER_GETLINECONTROLSF_ALL): "
543                                "MMSYSERR_INVALPARAM expected, got %s\n",
544                                mmsys_error(rc));
545                             rc=mixerGetLineControlsW((HMIXEROBJ)mix,&controls,
546                                                      -1);
547                             ok(rc==MMSYSERR_INVALFLAG||rc==MMSYSERR_INVALPARAM,
548                                "mixerGetLineControlsA(-1): "
549                                "MMSYSERR_INVALFLAG or MMSYSERR_INVALPARAM expected, got %s\n",
550                                mmsys_error(rc));
551
552                             controls.cbStruct = sizeof(MIXERLINECONTROLSW);
553                             controls.cControls = mixerlineW.cControls;
554                             controls.dwLineID = mixerlineW.dwLineID;
555                             controls.pamxctrl = array;
556                             controls.cbmxctrl = sizeof(MIXERCONTROLW);
557
558                             /* FIXME: do MIXER_GETLINECONTROLSF_ONEBYID
559                              * and MIXER_GETLINECONTROLSF_ONEBYTYPE
560                              */
561                             rc=mixerGetLineControlsW((HMIXEROBJ)mix,&controls,
562                                                      MIXER_GETLINECONTROLSF_ALL);
563                             ok(rc==MMSYSERR_NOERROR,
564                                "mixerGetLineControlsW(MIXER_GETLINECONTROLSF_ALL): "
565                                "MMSYSERR_NOERROR expected, got %s\n",
566                                mmsys_error(rc));
567                             if (rc==MMSYSERR_NOERROR) {
568                                 for(nc=0;nc<mixerlineW.cControls;nc++) {
569                                     if (winetest_interactive) {
570                                         WideCharToMultiByte(CP_ACP,0,array[nc].szShortName,
571                                             MIXER_SHORT_NAME_CHARS,szShortName,
572                                             MIXER_SHORT_NAME_CHARS,NULL,NULL);
573                                         WideCharToMultiByte(CP_ACP,0,array[nc].szName,
574                                             MIXER_LONG_NAME_CHARS,szName,
575                                             MIXER_LONG_NAME_CHARS,NULL,NULL);
576                                         trace("        %ld: \"%s\" (%s) ControlID=%ld\n", nc,
577                                               szShortName, szName, array[nc].dwControlID);
578                                         trace("            ControlType=%s\n",
579                                                control_type(array[nc].dwControlType));
580                                         trace("            Control=0x%08lx(%s)\n",
581                                               array[nc].fdwControl,
582                                               control_flags(array[nc].fdwControl));
583                                         trace("            Items=%ld Min=%ld Max=%ld Step=%ld\n",
584                                               array[nc].cMultipleItems,
585                                               S1(array[nc].Bounds).dwMinimum,
586                                               S1(array[nc].Bounds).dwMaximum,
587                                               array[nc].Metrics.cSteps);
588                                     }
589                                 }
590                             }
591
592                             HeapFree(GetProcessHeap(),0,array);
593                         }
594                     }
595                 }
596             }
597         }
598         rc=mixerClose(mix);
599         ok(rc==MMSYSERR_NOERROR,
600            "mixerClose: MMSYSERR_BADDEVICEID expected, got %s\n",
601            mmsys_error(rc));
602     }
603 }
604
605 void mixer_testsA()
606 {
607     MIXERCAPSA capsA;
608     HMIXER mix;
609     MMRESULT rc;
610     UINT ndev, d;
611
612     trace("--- Testing ASCII functions ---\n");
613
614     ndev=mixerGetNumDevs();
615     trace("found %d Mixer devices\n",ndev);
616
617     rc=mixerGetDevCapsA(ndev+1,&capsA,sizeof(capsA));
618     ok(rc==MMSYSERR_BADDEVICEID,
619        "mixerGetDevCapsA: MMSYSERR_BADDEVICEID expected, got %s\n",
620        mmsys_error(rc));
621
622     rc=mixerOpen(&mix, ndev+1, 0, 0, 0);
623     ok(rc==MMSYSERR_BADDEVICEID,
624        "mixerOpen: MMSYSERR_BADDEVICEID expected, got %s\n",
625        mmsys_error(rc));
626
627     for (d=0;d<ndev;d++)
628         mixer_test_deviceA(d);
629 }
630
631 void mixer_testsW()
632 {
633     MIXERCAPSW capsW;
634     HMIXER mix;
635     MMRESULT rc;
636     UINT ndev, d;
637
638     trace("--- Testing WCHAR functions ---\n");
639
640     ndev=mixerGetNumDevs();
641     trace("found %d Mixer devices\n",ndev);
642
643     rc=mixerGetDevCapsW(ndev+1,&capsW,sizeof(capsW));
644     ok(rc==MMSYSERR_BADDEVICEID,
645        "mixerGetDevCapsW: MMSYSERR_BADDEVICEID expected, got %s\n",
646        mmsys_error(rc));
647
648     rc=mixerOpen(&mix, ndev+1, 0, 0, 0);
649     ok(rc==MMSYSERR_BADDEVICEID,
650        "mixerOpen: MMSYSERR_BADDEVICEID expected, got %s\n",
651        mmsys_error(rc));
652
653     for (d=0;d<ndev;d++)
654         mixer_test_deviceW(d);
655 }
656
657 START_TEST(mixer)
658 {
659     mixer_testsA();
660     mixer_testsW();
661 }