|  | /* | 
|  | * Test mixer | 
|  | * | 
|  | * Copyright (c) 2004 Robert Reif | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * License along with this library; if not, write to the Free Software | 
|  | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * To Do: | 
|  | * add interactive tests | 
|  | */ | 
|  |  | 
|  | #include <stdarg.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <math.h> | 
|  |  | 
|  | #include "wine/test.h" | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "winnls.h" | 
|  | #include "mmsystem.h" | 
|  |  | 
|  | #include "winmm_test.h" | 
|  |  | 
|  | static const char * line_flags(DWORD fdwLine) | 
|  | { | 
|  | static char flags[100]; | 
|  | BOOL first=TRUE; | 
|  | flags[0]=0; | 
|  | if (fdwLine&MIXERLINE_LINEF_ACTIVE) { | 
|  | strcat(flags,"MIXERLINE_LINEF_ACTIVE"); | 
|  | first=FALSE; | 
|  | } | 
|  | if (fdwLine&MIXERLINE_LINEF_DISCONNECTED) { | 
|  | if (!first) | 
|  | strcat(flags, "|"); | 
|  |  | 
|  | strcat(flags,"MIXERLINE_LINEF_DISCONNECTED"); | 
|  | first=FALSE; | 
|  | } | 
|  |  | 
|  | if (fdwLine&MIXERLINE_LINEF_SOURCE) { | 
|  | if (!first) | 
|  | strcat(flags, "|"); | 
|  |  | 
|  | strcat(flags,"MIXERLINE_LINEF_SOURCE"); | 
|  | } | 
|  |  | 
|  | return flags; | 
|  | } | 
|  |  | 
|  | static const char * component_type(DWORD dwComponentType) | 
|  | { | 
|  | #define TYPE_TO_STR(x) case x: return #x | 
|  | switch (dwComponentType) { | 
|  | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_UNDEFINED); | 
|  | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_DIGITAL); | 
|  | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_LINE); | 
|  | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_MONITOR); | 
|  | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_SPEAKERS); | 
|  | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_HEADPHONES); | 
|  | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_TELEPHONE); | 
|  | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_WAVEIN); | 
|  | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_VOICEIN); | 
|  | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED); | 
|  | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_DIGITAL); | 
|  | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_LINE); | 
|  | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE); | 
|  | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER); | 
|  | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC); | 
|  | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE); | 
|  | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER); | 
|  | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT); | 
|  | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY); | 
|  | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_ANALOG); | 
|  | } | 
|  | #undef TYPE_TO_STR | 
|  | return "UNKNOWN"; | 
|  | } | 
|  |  | 
|  | static const char * target_type(DWORD dwType) | 
|  | { | 
|  | #define TYPE_TO_STR(x) case x: return #x | 
|  | switch (dwType) { | 
|  | TYPE_TO_STR(MIXERLINE_TARGETTYPE_UNDEFINED); | 
|  | TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEOUT); | 
|  | TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEIN); | 
|  | TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIOUT); | 
|  | TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIIN); | 
|  | TYPE_TO_STR(MIXERLINE_TARGETTYPE_AUX); | 
|  | } | 
|  | #undef TYPE_TO_STR | 
|  | return "UNKNOWN"; | 
|  | } | 
|  |  | 
|  | static const char * control_type(DWORD dwControlType) | 
|  | { | 
|  | #define TYPE_TO_STR(x) case x: return #x | 
|  | switch (dwControlType) { | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_CUSTOM); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEANMETER); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNEDMETER); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PEAKMETER); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEAN); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_ONOFF); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUTE); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MONO); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_LOUDNESS); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_STEREOENH); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS_BOOST); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BUTTON); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_DECIBELS); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNED); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNED); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PERCENT); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SLIDER); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PAN); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_QSOUNDPAN); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_FADER); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_VOLUME); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_TREBLE); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_EQUALIZER); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SINGLESELECT); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUX); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MIXER); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MICROTIME); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MILLITIME); | 
|  | } | 
|  | #undef TYPE_TO_STR | 
|  | return "UNKNOWN"; | 
|  | } | 
|  |  | 
|  | static const char * control_flags(DWORD fdwControl) | 
|  | { | 
|  | static char flags[100]; | 
|  | BOOL first=TRUE; | 
|  | flags[0]=0; | 
|  | if (fdwControl&MIXERCONTROL_CONTROLF_UNIFORM) { | 
|  | strcat(flags,"MIXERCONTROL_CONTROLF_UNIFORM"); | 
|  | first=FALSE; | 
|  | } | 
|  | if (fdwControl&MIXERCONTROL_CONTROLF_MULTIPLE) { | 
|  | if (!first) | 
|  | strcat(flags, "|"); | 
|  |  | 
|  | strcat(flags,"MIXERCONTROL_CONTROLF_MULTIPLE"); | 
|  | first=FALSE; | 
|  | } | 
|  |  | 
|  | if (fdwControl&MIXERCONTROL_CONTROLF_DISABLED) { | 
|  | if (!first) | 
|  | strcat(flags, "|"); | 
|  |  | 
|  | strcat(flags,"MIXERCONTROL_CONTROLF_DISABLED"); | 
|  | } | 
|  |  | 
|  | return flags; | 
|  | } | 
|  |  | 
|  | static void mixer_test_controlA(HMIXER mix, LPMIXERCONTROLA control) | 
|  | { | 
|  | MMRESULT rc; | 
|  |  | 
|  | if ((control->dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME) || | 
|  | (control->dwControlType == MIXERCONTROL_CONTROLTYPE_UNSIGNED)) { | 
|  | MIXERCONTROLDETAILS details; | 
|  | MIXERCONTROLDETAILS_UNSIGNED value; | 
|  |  | 
|  | details.cbStruct = sizeof(MIXERCONTROLDETAILS); | 
|  | details.dwControlID = control->dwControlID; | 
|  | details.cChannels = 1; | 
|  | U(details).cMultipleItems = 0; | 
|  | details.paDetails = &value; | 
|  | details.cbDetails = sizeof(value); | 
|  |  | 
|  | /* read the current control value */ | 
|  | rc=mixerGetControlDetails((HMIXEROBJ)mix,&details,MIXER_GETCONTROLDETAILSF_VALUE); | 
|  | ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): " | 
|  | "MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | if (rc==MMSYSERR_NOERROR) { | 
|  | MIXERCONTROLDETAILS new_details; | 
|  | MIXERCONTROLDETAILS_UNSIGNED new_value; | 
|  |  | 
|  | if (winetest_interactive) | 
|  | trace("            Value=%d\n",value.dwValue); | 
|  |  | 
|  | if (value.dwValue + control->Metrics.cSteps < S1(control->Bounds).dwMaximum) | 
|  | new_value.dwValue = value.dwValue + control->Metrics.cSteps; | 
|  | else | 
|  | new_value.dwValue = value.dwValue - control->Metrics.cSteps; | 
|  |  | 
|  | new_details.cbStruct = sizeof(MIXERCONTROLDETAILS); | 
|  | new_details.dwControlID = control->dwControlID; | 
|  | new_details.cChannels = 1; | 
|  | U(new_details).cMultipleItems = 0; | 
|  | new_details.paDetails = &new_value; | 
|  | new_details.cbDetails = sizeof(new_value); | 
|  |  | 
|  | /* change the control value by one step */ | 
|  | rc=mixerSetControlDetails((HMIXEROBJ)mix,&new_details,MIXER_SETCONTROLDETAILSF_VALUE); | 
|  | ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): " | 
|  | "MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | if (rc==MMSYSERR_NOERROR) { | 
|  | MIXERCONTROLDETAILS ret_details; | 
|  | MIXERCONTROLDETAILS_UNSIGNED ret_value; | 
|  |  | 
|  | ret_details.cbStruct = sizeof(MIXERCONTROLDETAILS); | 
|  | ret_details.dwControlID = control->dwControlID; | 
|  | ret_details.cChannels = 1; | 
|  | U(ret_details).cMultipleItems = 0; | 
|  | ret_details.paDetails = &ret_value; | 
|  | ret_details.cbDetails = sizeof(ret_value); | 
|  |  | 
|  | /* read back the new control value */ | 
|  | rc=mixerGetControlDetails((HMIXEROBJ)mix,&ret_details,MIXER_GETCONTROLDETAILSF_VALUE); | 
|  | ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): " | 
|  | "MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | if (rc==MMSYSERR_NOERROR) { | 
|  | /* result may not match exactly because of rounding */ | 
|  | ok(abs(ret_value.dwValue-new_value.dwValue)<=1, | 
|  | "Couldn't change value from %d to %d, returned %d\n", | 
|  | value.dwValue,new_value.dwValue,ret_value.dwValue); | 
|  |  | 
|  | if (abs(ret_value.dwValue-new_value.dwValue)<=1) { | 
|  | details.cbStruct = sizeof(MIXERCONTROLDETAILS); | 
|  | details.dwControlID = control->dwControlID; | 
|  | details.cChannels = 1; | 
|  | U(details).cMultipleItems = 0; | 
|  | details.paDetails = &value; | 
|  | details.cbDetails = sizeof(value); | 
|  |  | 
|  | /* restore original value */ | 
|  | rc=mixerSetControlDetails((HMIXEROBJ)mix,&details,MIXER_SETCONTROLDETAILSF_VALUE); | 
|  | ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): " | 
|  | "MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } else if ((control->dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE) || | 
|  | (control->dwControlType == MIXERCONTROL_CONTROLTYPE_BOOLEAN) || | 
|  | (control->dwControlType == MIXERCONTROL_CONTROLTYPE_BUTTON)) { | 
|  | MIXERCONTROLDETAILS details; | 
|  | MIXERCONTROLDETAILS_BOOLEAN value; | 
|  |  | 
|  | details.cbStruct = sizeof(MIXERCONTROLDETAILS); | 
|  | details.dwControlID = control->dwControlID; | 
|  | details.cChannels = 1; | 
|  | U(details).cMultipleItems = 0; | 
|  | details.paDetails = &value; | 
|  | details.cbDetails = sizeof(value); | 
|  |  | 
|  | rc=mixerGetControlDetails((HMIXEROBJ)mix,&details,MIXER_GETCONTROLDETAILSF_VALUE); | 
|  | ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): " | 
|  | "MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | if (rc==MMSYSERR_NOERROR) { | 
|  | MIXERCONTROLDETAILS new_details; | 
|  | MIXERCONTROLDETAILS_BOOLEAN new_value; | 
|  |  | 
|  | if (winetest_interactive) | 
|  | trace("            Value=%d\n",value.fValue); | 
|  |  | 
|  | if (value.fValue == FALSE) | 
|  | new_value.fValue = TRUE; | 
|  | else | 
|  | new_value.fValue = FALSE; | 
|  |  | 
|  | new_details.cbStruct = sizeof(MIXERCONTROLDETAILS); | 
|  | new_details.dwControlID = control->dwControlID; | 
|  | new_details.cChannels = 1; | 
|  | U(new_details).cMultipleItems = 0; | 
|  | new_details.paDetails = &new_value; | 
|  | new_details.cbDetails = sizeof(new_value); | 
|  |  | 
|  | /* change the control value by one step */ | 
|  | rc=mixerSetControlDetails((HMIXEROBJ)mix,&new_details,MIXER_SETCONTROLDETAILSF_VALUE); | 
|  | ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): " | 
|  | "MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | if (rc==MMSYSERR_NOERROR) { | 
|  | MIXERCONTROLDETAILS ret_details; | 
|  | MIXERCONTROLDETAILS_BOOLEAN ret_value; | 
|  |  | 
|  | ret_details.cbStruct = sizeof(MIXERCONTROLDETAILS); | 
|  | ret_details.dwControlID = control->dwControlID; | 
|  | ret_details.cChannels = 1; | 
|  | U(ret_details).cMultipleItems = 0; | 
|  | ret_details.paDetails = &ret_value; | 
|  | ret_details.cbDetails = sizeof(ret_value); | 
|  |  | 
|  | /* read back the new control value */ | 
|  | rc=mixerGetControlDetails((HMIXEROBJ)mix,&ret_details,MIXER_GETCONTROLDETAILSF_VALUE); | 
|  | ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): " | 
|  | "MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | if (rc==MMSYSERR_NOERROR) { | 
|  | /* result may not match exactly because of rounding */ | 
|  | ok(ret_value.fValue==new_value.fValue, | 
|  | "Couldn't change value from %d to %d, returned %d\n", | 
|  | value.fValue,new_value.fValue,ret_value.fValue); | 
|  |  | 
|  | if (ret_value.fValue==new_value.fValue) { | 
|  | details.cbStruct = sizeof(MIXERCONTROLDETAILS); | 
|  | details.dwControlID = control->dwControlID; | 
|  | details.cChannels = 1; | 
|  | U(details).cMultipleItems = 0; | 
|  | details.paDetails = &value; | 
|  | details.cbDetails = sizeof(value); | 
|  |  | 
|  | /* restore original value */ | 
|  | rc=mixerSetControlDetails((HMIXEROBJ)mix,&details,MIXER_SETCONTROLDETAILSF_VALUE); | 
|  | ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): " | 
|  | "MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } else { | 
|  | /* FIXME */ | 
|  | } | 
|  | } | 
|  |  | 
|  | static void mixer_test_deviceA(int device) | 
|  | { | 
|  | MIXERCAPSA capsA; | 
|  | HMIXER mix; | 
|  | MMRESULT rc; | 
|  | DWORD d,s,ns,nc; | 
|  |  | 
|  | rc=mixerGetDevCapsA(device,0,sizeof(capsA)); | 
|  | ok(rc==MMSYSERR_INVALPARAM, | 
|  | "mixerGetDevCapsA: MMSYSERR_INVALPARAM expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  |  | 
|  | rc=mixerGetDevCapsA(device,&capsA,4); | 
|  | ok(rc==MMSYSERR_NOERROR, | 
|  | "mixerGetDevCapsA: MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  |  | 
|  | rc=mixerGetDevCapsA(device,&capsA,sizeof(capsA)); | 
|  | ok(rc==MMSYSERR_NOERROR, | 
|  | "mixerGetDevCapsA: MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  |  | 
|  | if (winetest_interactive) { | 
|  | trace("  %d: \"%s\" %d.%d (%d:%d) destinations=%d\n", device, | 
|  | capsA.szPname, capsA.vDriverVersion >> 8, | 
|  | capsA.vDriverVersion & 0xff,capsA.wMid,capsA.wPid, | 
|  | capsA.cDestinations); | 
|  | } else { | 
|  | trace("  %d: \"%s\" %d.%d (%d:%d)\n", device, | 
|  | capsA.szPname, capsA.vDriverVersion >> 8, | 
|  | capsA.vDriverVersion & 0xff,capsA.wMid,capsA.wPid); | 
|  | } | 
|  |  | 
|  | rc=mixerOpen(&mix, device, 0, 0, 0); | 
|  | ok(rc==MMSYSERR_NOERROR, | 
|  | "mixerOpen: MMSYSERR_NOERROR expected, got %s\n",mmsys_error(rc)); | 
|  | if (rc==MMSYSERR_NOERROR) { | 
|  | rc=mixerOpen(&mix, device, 0, 0, CALLBACK_FUNCTION); | 
|  | ok(rc==MMSYSERR_INVALFLAG, | 
|  | "mixerOpen: MMSYSERR_INVALFLAG expected, got %s\n", mmsys_error(rc)); | 
|  |  | 
|  | /* Shouldn't open without a valid HWND */ | 
|  | rc=mixerOpen(&mix, device, 0, 0, CALLBACK_WINDOW); | 
|  | ok(rc==MMSYSERR_INVALPARAM, | 
|  | "mixerOpen: MMSYSERR_INVALPARAM expected, got %s\n", mmsys_error(rc)); | 
|  |  | 
|  |  | 
|  | for (d=0;d<capsA.cDestinations;d++) { | 
|  | MIXERLINEA mixerlineA; | 
|  | mixerlineA.cbStruct = 0; | 
|  | mixerlineA.dwDestination=d; | 
|  | rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA, | 
|  | MIXER_GETLINEINFOF_DESTINATION); | 
|  | ok(rc==MMSYSERR_INVALPARAM, | 
|  | "mixerGetLineInfoA(MIXER_GETLINEINFOF_DESTINATION): " | 
|  | "MMSYSERR_INVALPARAM expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  |  | 
|  | mixerlineA.cbStruct = sizeof(mixerlineA); | 
|  | mixerlineA.dwDestination=capsA.cDestinations; | 
|  | rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA, | 
|  | MIXER_GETLINEINFOF_DESTINATION); | 
|  | ok(rc==MMSYSERR_INVALPARAM||rc==MIXERR_INVALLINE, | 
|  | "mixerGetLineInfoA(MIXER_GETLINEINFOF_DESTINATION): " | 
|  | "MMSYSERR_INVALPARAM or MIXERR_INVALLINE expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  |  | 
|  | mixerlineA.cbStruct = sizeof(mixerlineA); | 
|  | mixerlineA.dwDestination=d; | 
|  | rc=mixerGetLineInfoA((HMIXEROBJ)mix,0, | 
|  | MIXER_GETLINEINFOF_DESTINATION); | 
|  | ok(rc==MMSYSERR_INVALPARAM, | 
|  | "mixerGetLineInfoA(MIXER_GETLINEINFOF_DESTINATION): " | 
|  | "MMSYSERR_INVALPARAM expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  |  | 
|  | mixerlineA.cbStruct = sizeof(mixerlineA); | 
|  | mixerlineA.dwDestination=d; | 
|  | rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA,-1); | 
|  | ok(rc==MMSYSERR_INVALFLAG, | 
|  | "mixerGetLineInfoA(-1): MMSYSERR_INVALFLAG expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  |  | 
|  | mixerlineA.cbStruct = sizeof(mixerlineA); | 
|  | mixerlineA.dwDestination=d; | 
|  | rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA, | 
|  | MIXER_GETLINEINFOF_DESTINATION); | 
|  | ok(rc==MMSYSERR_NOERROR||rc==MMSYSERR_NODRIVER, | 
|  | "mixerGetLineInfoA(MIXER_GETLINEINFOF_DESTINATION): " | 
|  | "MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | if (rc==MMSYSERR_NODRIVER) | 
|  | trace("  No Driver\n"); | 
|  | else if (rc==MMSYSERR_NOERROR && winetest_interactive) { | 
|  | trace("    %d: \"%s\" (%s) Destination=%d Source=%d\n", | 
|  | d,mixerlineA.szShortName, mixerlineA.szName, | 
|  | mixerlineA.dwDestination,mixerlineA.dwSource); | 
|  | trace("        LineID=%08x Channels=%d " | 
|  | "Connections=%d Controls=%d\n", | 
|  | mixerlineA.dwLineID,mixerlineA.cChannels, | 
|  | mixerlineA.cConnections,mixerlineA.cControls); | 
|  | trace("        State=0x%08x(%s)\n", | 
|  | mixerlineA.fdwLine,line_flags(mixerlineA.fdwLine)); | 
|  | trace("        ComponentType=%s\n", | 
|  | component_type(mixerlineA.dwComponentType)); | 
|  | trace("        Type=%s\n", | 
|  | target_type(mixerlineA.Target.dwType)); | 
|  | trace("        Device=%d (%s) %d.%d (%d:%d)\n", | 
|  | mixerlineA.Target.dwDeviceID, | 
|  | mixerlineA.Target.szPname, | 
|  | mixerlineA.Target.vDriverVersion >> 8, | 
|  | mixerlineA.Target.vDriverVersion & 0xff, | 
|  | mixerlineA.Target.wMid, mixerlineA.Target.wPid); | 
|  | } | 
|  | ns=mixerlineA.cConnections; | 
|  | for(s=0;s<ns;s++) { | 
|  | mixerlineA.cbStruct = sizeof(mixerlineA); | 
|  | mixerlineA.dwDestination=d; | 
|  | mixerlineA.dwSource=s; | 
|  | rc=mixerGetLineInfoA((HMIXEROBJ)mix,&mixerlineA, | 
|  | MIXER_GETLINEINFOF_SOURCE); | 
|  | ok(rc==MMSYSERR_NOERROR||rc==MMSYSERR_NODRIVER, | 
|  | "mixerGetLineInfoA(MIXER_GETLINEINFOF_SOURCE): " | 
|  | "MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | if (rc==MMSYSERR_NODRIVER) | 
|  | trace("  No Driver\n"); | 
|  | else if (rc==MMSYSERR_NOERROR) { | 
|  | LPMIXERCONTROLA    array; | 
|  | MIXERLINECONTROLSA controls; | 
|  | if (winetest_interactive) { | 
|  | trace("      %d: \"%s\" (%s) Destination=%d Source=%d\n", | 
|  | s,mixerlineA.szShortName, mixerlineA.szName, | 
|  | mixerlineA.dwDestination,mixerlineA.dwSource); | 
|  | trace("          LineID=%08x Channels=%d " | 
|  | "Connections=%d Controls=%d\n", | 
|  | mixerlineA.dwLineID,mixerlineA.cChannels, | 
|  | mixerlineA.cConnections,mixerlineA.cControls); | 
|  | trace("          State=0x%08x(%s)\n", | 
|  | mixerlineA.fdwLine,line_flags(mixerlineA.fdwLine)); | 
|  | trace("          ComponentType=%s\n", | 
|  | component_type(mixerlineA.dwComponentType)); | 
|  | trace("          Type=%s\n", | 
|  | target_type(mixerlineA.Target.dwType)); | 
|  | trace("          Device=%d (%s) %d.%d (%d:%d)\n", | 
|  | mixerlineA.Target.dwDeviceID, | 
|  | mixerlineA.Target.szPname, | 
|  | mixerlineA.Target.vDriverVersion >> 8, | 
|  | mixerlineA.Target.vDriverVersion & 0xff, | 
|  | mixerlineA.Target.wMid, mixerlineA.Target.wPid); | 
|  | } | 
|  | if (mixerlineA.cControls) { | 
|  | array=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, | 
|  | mixerlineA.cControls*sizeof(MIXERCONTROLA)); | 
|  | if (array) { | 
|  | rc=mixerGetLineControlsA((HMIXEROBJ)mix,0, | 
|  | MIXER_GETLINECONTROLSF_ALL); | 
|  | ok(rc==MMSYSERR_INVALPARAM, | 
|  | "mixerGetLineControlsA(MIXER_GETLINECONTROLSF_ALL): " | 
|  | "MMSYSERR_INVALPARAM expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  |  | 
|  | rc=mixerGetLineControlsA((HMIXEROBJ)mix,&controls,-1); | 
|  | ok(rc==MMSYSERR_INVALFLAG||rc==MMSYSERR_INVALPARAM, | 
|  | "mixerGetLineControlsA(-1): " | 
|  | "MMSYSERR_INVALFLAG or MMSYSERR_INVALPARAM expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  |  | 
|  | controls.cbStruct = sizeof(MIXERLINECONTROLSA); | 
|  | controls.cControls = mixerlineA.cControls; | 
|  | controls.dwLineID = mixerlineA.dwLineID; | 
|  | controls.pamxctrl = array; | 
|  | controls.cbmxctrl = sizeof(MIXERCONTROLA); | 
|  |  | 
|  | /* FIXME: do MIXER_GETLINECONTROLSF_ONEBYID | 
|  | * and MIXER_GETLINECONTROLSF_ONEBYTYPE | 
|  | */ | 
|  | rc=mixerGetLineControlsA((HMIXEROBJ)mix,&controls, | 
|  | MIXER_GETLINECONTROLSF_ALL); | 
|  | ok(rc==MMSYSERR_NOERROR, | 
|  | "mixerGetLineControlsA(MIXER_GETLINECONTROLSF_ALL): " | 
|  | "MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | if (rc==MMSYSERR_NOERROR) { | 
|  | for(nc=0;nc<mixerlineA.cControls;nc++) { | 
|  | if (winetest_interactive) { | 
|  | trace("        %d: \"%s\" (%s) ControlID=%d\n", nc, | 
|  | array[nc].szShortName, | 
|  | array[nc].szName, array[nc].dwControlID); | 
|  | trace("            ControlType=%s\n", | 
|  | control_type(array[nc].dwControlType)); | 
|  | trace("            Control=0x%08x(%s)\n", | 
|  | array[nc].fdwControl, | 
|  | control_flags(array[nc].fdwControl)); | 
|  | trace("            Items=%d Min=%d Max=%d Step=%d\n", | 
|  | array[nc].cMultipleItems, | 
|  | S1(array[nc].Bounds).dwMinimum, | 
|  | S1(array[nc].Bounds).dwMaximum, | 
|  | array[nc].Metrics.cSteps); | 
|  | } | 
|  |  | 
|  | mixer_test_controlA(mix, &array[nc]); | 
|  | } | 
|  | } | 
|  |  | 
|  | HeapFree(GetProcessHeap(),0,array); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | rc=mixerClose(mix); | 
|  | ok(rc==MMSYSERR_NOERROR, | 
|  | "mixerClose: MMSYSERR_BADDEVICEID expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void mixer_test_controlW(HMIXER mix, LPMIXERCONTROLW control) | 
|  | { | 
|  | MMRESULT rc; | 
|  |  | 
|  | if ((control->dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME) || | 
|  | (control->dwControlType == MIXERCONTROL_CONTROLTYPE_UNSIGNED)) { | 
|  | MIXERCONTROLDETAILS details; | 
|  | MIXERCONTROLDETAILS_UNSIGNED value; | 
|  |  | 
|  | details.cbStruct = sizeof(MIXERCONTROLDETAILS); | 
|  | details.dwControlID = control->dwControlID; | 
|  | details.cChannels = 1; | 
|  | U(details).cMultipleItems = 0; | 
|  | details.paDetails = &value; | 
|  | details.cbDetails = sizeof(value); | 
|  |  | 
|  | /* read the current control value */ | 
|  | rc=mixerGetControlDetails((HMIXEROBJ)mix,&details,MIXER_GETCONTROLDETAILSF_VALUE); | 
|  | ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): " | 
|  | "MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | if (rc==MMSYSERR_NOERROR) { | 
|  | MIXERCONTROLDETAILS new_details; | 
|  | MIXERCONTROLDETAILS_UNSIGNED new_value; | 
|  |  | 
|  | if (winetest_interactive) | 
|  | trace("            Value=%d\n",value.dwValue); | 
|  |  | 
|  | if (value.dwValue + control->Metrics.cSteps < S1(control->Bounds).dwMaximum) | 
|  | new_value.dwValue = value.dwValue + control->Metrics.cSteps; | 
|  | else | 
|  | new_value.dwValue = value.dwValue - control->Metrics.cSteps; | 
|  |  | 
|  | new_details.cbStruct = sizeof(MIXERCONTROLDETAILS); | 
|  | new_details.dwControlID = control->dwControlID; | 
|  | new_details.cChannels = 1; | 
|  | U(new_details).cMultipleItems = 0; | 
|  | new_details.paDetails = &new_value; | 
|  | new_details.cbDetails = sizeof(new_value); | 
|  |  | 
|  | /* change the control value by one step */ | 
|  | rc=mixerSetControlDetails((HMIXEROBJ)mix,&new_details,MIXER_SETCONTROLDETAILSF_VALUE); | 
|  | ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): " | 
|  | "MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | if (rc==MMSYSERR_NOERROR) { | 
|  | MIXERCONTROLDETAILS ret_details; | 
|  | MIXERCONTROLDETAILS_UNSIGNED ret_value; | 
|  |  | 
|  | ret_details.cbStruct = sizeof(MIXERCONTROLDETAILS); | 
|  | ret_details.dwControlID = control->dwControlID; | 
|  | ret_details.cChannels = 1; | 
|  | U(ret_details).cMultipleItems = 0; | 
|  | ret_details.paDetails = &ret_value; | 
|  | ret_details.cbDetails = sizeof(ret_value); | 
|  |  | 
|  | /* read back the new control value */ | 
|  | rc=mixerGetControlDetails((HMIXEROBJ)mix,&ret_details,MIXER_GETCONTROLDETAILSF_VALUE); | 
|  | ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): " | 
|  | "MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | if (rc==MMSYSERR_NOERROR) { | 
|  | /* result may not match exactly because of rounding */ | 
|  | ok(abs(ret_value.dwValue-new_value.dwValue)<=1, | 
|  | "Couldn't change value from %d to %d, returned %d\n", | 
|  | value.dwValue,new_value.dwValue,ret_value.dwValue); | 
|  |  | 
|  | if (abs(ret_value.dwValue-new_value.dwValue)<=1) { | 
|  | details.cbStruct = sizeof(MIXERCONTROLDETAILS); | 
|  | details.dwControlID = control->dwControlID; | 
|  | details.cChannels = 1; | 
|  | U(details).cMultipleItems = 0; | 
|  | details.paDetails = &value; | 
|  | details.cbDetails = sizeof(value); | 
|  |  | 
|  | /* restore original value */ | 
|  | rc=mixerSetControlDetails((HMIXEROBJ)mix,&details,MIXER_SETCONTROLDETAILSF_VALUE); | 
|  | ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): " | 
|  | "MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } else if ((control->dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE) || | 
|  | (control->dwControlType == MIXERCONTROL_CONTROLTYPE_BOOLEAN) || | 
|  | (control->dwControlType == MIXERCONTROL_CONTROLTYPE_BUTTON)) { | 
|  | MIXERCONTROLDETAILS details; | 
|  | MIXERCONTROLDETAILS_BOOLEAN value; | 
|  |  | 
|  | details.cbStruct = sizeof(MIXERCONTROLDETAILS); | 
|  | details.dwControlID = control->dwControlID; | 
|  | details.cChannels = 1; | 
|  | U(details).cMultipleItems = 0; | 
|  | details.paDetails = &value; | 
|  | details.cbDetails = sizeof(value); | 
|  |  | 
|  | rc=mixerGetControlDetails((HMIXEROBJ)mix,&details,MIXER_GETCONTROLDETAILSF_VALUE); | 
|  | ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): " | 
|  | "MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | if (rc==MMSYSERR_NOERROR) { | 
|  | MIXERCONTROLDETAILS new_details; | 
|  | MIXERCONTROLDETAILS_BOOLEAN new_value; | 
|  |  | 
|  | if (winetest_interactive) | 
|  | trace("            Value=%d\n",value.fValue); | 
|  |  | 
|  | if (value.fValue == FALSE) | 
|  | new_value.fValue = TRUE; | 
|  | else | 
|  | new_value.fValue = FALSE; | 
|  |  | 
|  | new_details.cbStruct = sizeof(MIXERCONTROLDETAILS); | 
|  | new_details.dwControlID = control->dwControlID; | 
|  | new_details.cChannels = 1; | 
|  | U(new_details).cMultipleItems = 0; | 
|  | new_details.paDetails = &new_value; | 
|  | new_details.cbDetails = sizeof(new_value); | 
|  |  | 
|  | /* change the control value by one step */ | 
|  | rc=mixerSetControlDetails((HMIXEROBJ)mix,&new_details,MIXER_SETCONTROLDETAILSF_VALUE); | 
|  | ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): " | 
|  | "MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | if (rc==MMSYSERR_NOERROR) { | 
|  | MIXERCONTROLDETAILS ret_details; | 
|  | MIXERCONTROLDETAILS_BOOLEAN ret_value; | 
|  |  | 
|  | ret_details.cbStruct = sizeof(MIXERCONTROLDETAILS); | 
|  | ret_details.dwControlID = control->dwControlID; | 
|  | ret_details.cChannels = 1; | 
|  | U(ret_details).cMultipleItems = 0; | 
|  | ret_details.paDetails = &ret_value; | 
|  | ret_details.cbDetails = sizeof(ret_value); | 
|  |  | 
|  | /* read back the new control value */ | 
|  | rc=mixerGetControlDetails((HMIXEROBJ)mix,&ret_details,MIXER_GETCONTROLDETAILSF_VALUE); | 
|  | ok(rc==MMSYSERR_NOERROR,"mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE): " | 
|  | "MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | if (rc==MMSYSERR_NOERROR) { | 
|  | /* result may not match exactly because of rounding */ | 
|  | ok(ret_value.fValue==new_value.fValue, | 
|  | "Couldn't change value from %d to %d, returned %d\n", | 
|  | value.fValue,new_value.fValue,ret_value.fValue); | 
|  |  | 
|  | if (ret_value.fValue==new_value.fValue) { | 
|  | details.cbStruct = sizeof(MIXERCONTROLDETAILS); | 
|  | details.dwControlID = control->dwControlID; | 
|  | details.cChannels = 1; | 
|  | U(details).cMultipleItems = 0; | 
|  | details.paDetails = &value; | 
|  | details.cbDetails = sizeof(value); | 
|  |  | 
|  | /* restore original value */ | 
|  | rc=mixerSetControlDetails((HMIXEROBJ)mix,&details,MIXER_SETCONTROLDETAILSF_VALUE); | 
|  | ok(rc==MMSYSERR_NOERROR,"mixerSetControlDetails(MIXER_SETCONTROLDETAILSF_VALUE): " | 
|  | "MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } else { | 
|  | /* FIXME */ | 
|  | } | 
|  | } | 
|  |  | 
|  | static void mixer_test_deviceW(int device) | 
|  | { | 
|  | MIXERCAPSW capsW; | 
|  | HMIXER mix; | 
|  | MMRESULT rc; | 
|  | DWORD d,s,ns,nc; | 
|  | char szShortName[MIXER_SHORT_NAME_CHARS]; | 
|  | char szName[MIXER_LONG_NAME_CHARS]; | 
|  | char szPname[MAXPNAMELEN]; | 
|  |  | 
|  | rc=mixerGetDevCapsW(device,0,sizeof(capsW)); | 
|  | ok(rc==MMSYSERR_INVALPARAM, | 
|  | "mixerGetDevCapsW: MMSYSERR_INVALPARAM expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  |  | 
|  | rc=mixerGetDevCapsW(device,&capsW,4); | 
|  | ok(rc==MMSYSERR_NOERROR, | 
|  | "mixerGetDevCapsW: MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  |  | 
|  | rc=mixerGetDevCapsW(device,&capsW,sizeof(capsW)); | 
|  | ok(rc==MMSYSERR_NOERROR, | 
|  | "mixerGetDevCapsW: MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  |  | 
|  | WideCharToMultiByte(CP_ACP,0,capsW.szPname, MAXPNAMELEN,szPname, | 
|  | MAXPNAMELEN,NULL,NULL); | 
|  | if (winetest_interactive) { | 
|  | trace("  %d: \"%s\" %d.%d (%d:%d) destinations=%d\n", device, | 
|  | szPname, capsW.vDriverVersion >> 8, | 
|  | capsW.vDriverVersion & 0xff,capsW.wMid,capsW.wPid, | 
|  | capsW.cDestinations); | 
|  | } else { | 
|  | trace("  %d: \"%s\" %d.%d (%d:%d)\n", device, | 
|  | szPname, capsW.vDriverVersion >> 8, | 
|  | capsW.vDriverVersion & 0xff,capsW.wMid,capsW.wPid); | 
|  | } | 
|  |  | 
|  |  | 
|  | rc=mixerOpen(&mix, device, 0, 0, 0); | 
|  | ok(rc==MMSYSERR_NOERROR, | 
|  | "mixerOpen: MMSYSERR_BADDEVICEID expected, got %s\n",mmsys_error(rc)); | 
|  | if (rc==MMSYSERR_NOERROR) { | 
|  | rc=mixerOpen(&mix, device, 0, 0, CALLBACK_FUNCTION); | 
|  | ok(rc==MMSYSERR_INVALFLAG, | 
|  | "mixerOpen: MMSYSERR_INVALFLAG expected, got %s\n", mmsys_error(rc)); | 
|  |  | 
|  | /* Shouldn't open without a valid HWND */ | 
|  | rc=mixerOpen(&mix, device, 0, 0, CALLBACK_WINDOW); | 
|  | ok(rc==MMSYSERR_INVALPARAM, | 
|  | "mixerOpen: MMSYSERR_INVALPARAM expected, got %s\n", mmsys_error(rc)); | 
|  |  | 
|  | for (d=0;d<capsW.cDestinations;d++) { | 
|  | MIXERLINEW mixerlineW; | 
|  | mixerlineW.cbStruct = 0; | 
|  | mixerlineW.dwDestination=d; | 
|  | rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW, | 
|  | MIXER_GETLINEINFOF_DESTINATION); | 
|  | ok(rc==MMSYSERR_INVALPARAM, | 
|  | "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): " | 
|  | "MMSYSERR_INVALPARAM expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  |  | 
|  | mixerlineW.cbStruct = sizeof(mixerlineW); | 
|  | mixerlineW.dwDestination=capsW.cDestinations; | 
|  | rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW, | 
|  | MIXER_GETLINEINFOF_DESTINATION); | 
|  | ok(rc==MMSYSERR_INVALPARAM||rc==MIXERR_INVALLINE, | 
|  | "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): " | 
|  | "MMSYSERR_INVALPARAM or MIXERR_INVALLINE expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  |  | 
|  | mixerlineW.cbStruct = sizeof(mixerlineW); | 
|  | mixerlineW.dwDestination=d; | 
|  | rc=mixerGetLineInfoW((HMIXEROBJ)mix,0, | 
|  | MIXER_GETLINEINFOF_DESTINATION); | 
|  | ok(rc==MMSYSERR_INVALPARAM, | 
|  | "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): " | 
|  | "MMSYSERR_INVALPARAM expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  |  | 
|  | mixerlineW.cbStruct = sizeof(mixerlineW); | 
|  | mixerlineW.dwDestination=d; | 
|  | rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW,-1); | 
|  | ok(rc==MMSYSERR_INVALFLAG, | 
|  | "mixerGetLineInfoW(-1): MMSYSERR_INVALFLAG expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  |  | 
|  | mixerlineW.cbStruct = sizeof(mixerlineW); | 
|  | mixerlineW.dwDestination=d; | 
|  | rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW, | 
|  | MIXER_GETLINEINFOF_DESTINATION); | 
|  | ok(rc==MMSYSERR_NOERROR||rc==MMSYSERR_NODRIVER, | 
|  | "mixerGetLineInfoW(MIXER_GETLINEINFOF_DESTINATION): " | 
|  | "MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | if (rc==MMSYSERR_NODRIVER) | 
|  | trace("  No Driver\n"); | 
|  | else if (rc==MMSYSERR_NOERROR && winetest_interactive) { | 
|  | WideCharToMultiByte(CP_ACP,0,mixerlineW.szShortName, | 
|  | MIXER_SHORT_NAME_CHARS,szShortName, | 
|  | MIXER_SHORT_NAME_CHARS,NULL,NULL); | 
|  | WideCharToMultiByte(CP_ACP,0,mixerlineW.szName, | 
|  | MIXER_LONG_NAME_CHARS,szName, | 
|  | MIXER_LONG_NAME_CHARS,NULL,NULL); | 
|  | WideCharToMultiByte(CP_ACP,0,mixerlineW.Target.szPname, | 
|  | MAXPNAMELEN,szPname, | 
|  | MAXPNAMELEN,NULL, NULL); | 
|  | trace("    %d: \"%s\" (%s) Destination=%d Source=%d\n", | 
|  | d,szShortName,szName, | 
|  | mixerlineW.dwDestination,mixerlineW.dwSource); | 
|  | trace("        LineID=%08x Channels=%d " | 
|  | "Connections=%d Controls=%d\n", | 
|  | mixerlineW.dwLineID,mixerlineW.cChannels, | 
|  | mixerlineW.cConnections,mixerlineW.cControls); | 
|  | trace("        State=0x%08x(%s)\n", | 
|  | mixerlineW.fdwLine,line_flags(mixerlineW.fdwLine)); | 
|  | trace("        ComponentType=%s\n", | 
|  | component_type(mixerlineW.dwComponentType)); | 
|  | trace("        Type=%s\n", | 
|  | target_type(mixerlineW.Target.dwType)); | 
|  | trace("        Device=%d (%s) %d.%d (%d:%d)\n", | 
|  | mixerlineW.Target.dwDeviceID,szPname, | 
|  | mixerlineW.Target.vDriverVersion >> 8, | 
|  | mixerlineW.Target.vDriverVersion & 0xff, | 
|  | mixerlineW.Target.wMid, mixerlineW.Target.wPid); | 
|  | } | 
|  | ns=mixerlineW.cConnections; | 
|  | for(s=0;s<ns;s++) { | 
|  | mixerlineW.cbStruct = sizeof(mixerlineW); | 
|  | mixerlineW.dwDestination=d; | 
|  | mixerlineW.dwSource=s; | 
|  | rc=mixerGetLineInfoW((HMIXEROBJ)mix,&mixerlineW, | 
|  | MIXER_GETLINEINFOF_SOURCE); | 
|  | ok(rc==MMSYSERR_NOERROR||rc==MMSYSERR_NODRIVER, | 
|  | "mixerGetLineInfoW(MIXER_GETLINEINFOF_SOURCE): " | 
|  | "MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | if (rc==MMSYSERR_NODRIVER) | 
|  | trace("  No Driver\n"); | 
|  | else if (rc==MMSYSERR_NOERROR) { | 
|  | LPMIXERCONTROLW    array; | 
|  | MIXERLINECONTROLSW controls; | 
|  | if (winetest_interactive) { | 
|  | WideCharToMultiByte(CP_ACP,0,mixerlineW.szShortName, | 
|  | MIXER_SHORT_NAME_CHARS,szShortName, | 
|  | MIXER_SHORT_NAME_CHARS,NULL,NULL); | 
|  | WideCharToMultiByte(CP_ACP,0,mixerlineW.szName, | 
|  | MIXER_LONG_NAME_CHARS,szName, | 
|  | MIXER_LONG_NAME_CHARS,NULL,NULL); | 
|  | WideCharToMultiByte(CP_ACP,0,mixerlineW.Target.szPname, | 
|  | MAXPNAMELEN,szPname, | 
|  | MAXPNAMELEN,NULL, NULL); | 
|  | trace("      %d: \"%s\" (%s) Destination=%d Source=%d\n", | 
|  | s,szShortName,szName, | 
|  | mixerlineW.dwDestination,mixerlineW.dwSource); | 
|  | trace("          LineID=%08x Channels=%d " | 
|  | "Connections=%d Controls=%d\n", | 
|  | mixerlineW.dwLineID,mixerlineW.cChannels, | 
|  | mixerlineW.cConnections,mixerlineW.cControls); | 
|  | trace("          State=0x%08x(%s)\n", | 
|  | mixerlineW.fdwLine,line_flags(mixerlineW.fdwLine)); | 
|  | trace("          ComponentType=%s\n", | 
|  | component_type(mixerlineW.dwComponentType)); | 
|  | trace("          Type=%s\n", | 
|  | target_type(mixerlineW.Target.dwType)); | 
|  | trace("          Device=%d (%s) %d.%d (%d:%d)\n", | 
|  | mixerlineW.Target.dwDeviceID,szPname, | 
|  | mixerlineW.Target.vDriverVersion >> 8, | 
|  | mixerlineW.Target.vDriverVersion & 0xff, | 
|  | mixerlineW.Target.wMid, mixerlineW.Target.wPid); | 
|  | } | 
|  | if (mixerlineW.cControls) { | 
|  | array=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, | 
|  | mixerlineW.cControls*sizeof(MIXERCONTROLW)); | 
|  | if (array) { | 
|  | rc=mixerGetLineControlsW((HMIXEROBJ)mix,0, | 
|  | MIXER_GETLINECONTROLSF_ALL); | 
|  | ok(rc==MMSYSERR_INVALPARAM, | 
|  | "mixerGetLineControlsW(MIXER_GETLINECONTROLSF_ALL): " | 
|  | "MMSYSERR_INVALPARAM expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | rc=mixerGetLineControlsW((HMIXEROBJ)mix,&controls, | 
|  | -1); | 
|  | ok(rc==MMSYSERR_INVALFLAG||rc==MMSYSERR_INVALPARAM, | 
|  | "mixerGetLineControlsA(-1): " | 
|  | "MMSYSERR_INVALFLAG or MMSYSERR_INVALPARAM expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  |  | 
|  | controls.cbStruct = sizeof(MIXERLINECONTROLSW); | 
|  | controls.cControls = mixerlineW.cControls; | 
|  | controls.dwLineID = mixerlineW.dwLineID; | 
|  | controls.pamxctrl = array; | 
|  | controls.cbmxctrl = sizeof(MIXERCONTROLW); | 
|  |  | 
|  | /* FIXME: do MIXER_GETLINECONTROLSF_ONEBYID | 
|  | * and MIXER_GETLINECONTROLSF_ONEBYTYPE | 
|  | */ | 
|  | rc=mixerGetLineControlsW((HMIXEROBJ)mix,&controls, | 
|  | MIXER_GETLINECONTROLSF_ALL); | 
|  | ok(rc==MMSYSERR_NOERROR, | 
|  | "mixerGetLineControlsW(MIXER_GETLINECONTROLSF_ALL): " | 
|  | "MMSYSERR_NOERROR expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | if (rc==MMSYSERR_NOERROR) { | 
|  | for(nc=0;nc<mixerlineW.cControls;nc++) { | 
|  | if (winetest_interactive) { | 
|  | WideCharToMultiByte(CP_ACP,0,array[nc].szShortName, | 
|  | MIXER_SHORT_NAME_CHARS,szShortName, | 
|  | MIXER_SHORT_NAME_CHARS,NULL,NULL); | 
|  | WideCharToMultiByte(CP_ACP,0,array[nc].szName, | 
|  | MIXER_LONG_NAME_CHARS,szName, | 
|  | MIXER_LONG_NAME_CHARS,NULL,NULL); | 
|  | trace("        %d: \"%s\" (%s) ControlID=%d\n", nc, | 
|  | szShortName, szName, array[nc].dwControlID); | 
|  | trace("            ControlType=%s\n", | 
|  | control_type(array[nc].dwControlType)); | 
|  | trace("            Control=0x%08x(%s)\n", | 
|  | array[nc].fdwControl, | 
|  | control_flags(array[nc].fdwControl)); | 
|  | trace("            Items=%d Min=%d Max=%d Step=%d\n", | 
|  | array[nc].cMultipleItems, | 
|  | S1(array[nc].Bounds).dwMinimum, | 
|  | S1(array[nc].Bounds).dwMaximum, | 
|  | array[nc].Metrics.cSteps); | 
|  | } | 
|  | mixer_test_controlW(mix, &array[nc]); | 
|  | } | 
|  | } | 
|  |  | 
|  | HeapFree(GetProcessHeap(),0,array); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | rc=mixerClose(mix); | 
|  | ok(rc==MMSYSERR_NOERROR, | 
|  | "mixerClose: MMSYSERR_BADDEVICEID expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void mixer_testsA(void) | 
|  | { | 
|  | MIXERCAPSA capsA; | 
|  | HMIXER mix; | 
|  | MMRESULT rc; | 
|  | UINT ndev, d; | 
|  |  | 
|  | trace("--- Testing ASCII functions ---\n"); | 
|  |  | 
|  | ndev=mixerGetNumDevs(); | 
|  | trace("found %d Mixer devices\n",ndev); | 
|  |  | 
|  | rc=mixerGetDevCapsA(ndev+1,&capsA,sizeof(capsA)); | 
|  | ok(rc==MMSYSERR_BADDEVICEID, | 
|  | "mixerGetDevCapsA: MMSYSERR_BADDEVICEID expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  |  | 
|  | rc=mixerOpen(&mix, ndev+1, 0, 0, 0); | 
|  | ok(rc==MMSYSERR_BADDEVICEID, | 
|  | "mixerOpen: MMSYSERR_BADDEVICEID expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  |  | 
|  | for (d=0;d<ndev;d++) | 
|  | mixer_test_deviceA(d); | 
|  | } | 
|  |  | 
|  | static void mixer_testsW(void) | 
|  | { | 
|  | MIXERCAPSW capsW; | 
|  | HMIXER mix; | 
|  | MMRESULT rc; | 
|  | UINT ndev, d; | 
|  |  | 
|  | trace("--- Testing WCHAR functions ---\n"); | 
|  |  | 
|  | ndev=mixerGetNumDevs(); | 
|  | trace("found %d Mixer devices\n",ndev); | 
|  |  | 
|  | rc=mixerGetDevCapsW(ndev+1,&capsW,sizeof(capsW)); | 
|  | ok(rc==MMSYSERR_BADDEVICEID||rc==MMSYSERR_NOTSUPPORTED, | 
|  | "mixerGetDevCapsW: MMSYSERR_BADDEVICEID or MMSYSERR_NOTSUPPORTED " | 
|  | "expected, got %s\n", mmsys_error(rc)); | 
|  | if (rc==MMSYSERR_NOTSUPPORTED) | 
|  | return; | 
|  |  | 
|  | rc=mixerOpen(&mix, ndev+1, 0, 0, 0); | 
|  | ok(rc==MMSYSERR_BADDEVICEID, | 
|  | "mixerOpen: MMSYSERR_BADDEVICEID expected, got %s\n", | 
|  | mmsys_error(rc)); | 
|  |  | 
|  | for (d=0;d<ndev;d++) | 
|  | mixer_test_deviceW(d); | 
|  | } | 
|  |  | 
|  | START_TEST(mixer) | 
|  | { | 
|  | mixer_testsA(); | 
|  | mixer_testsW(); | 
|  | } |