2007-04-24 00:23:34 +02:00
/*
* Alsa MIXER Wine Driver for Linux
* Very loosely based on wineoss mixer driver
*
* Copyright 2007 Maarten Lankhorst
*
* 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
*/
# include "config.h"
# include "wine/port.h"
# include <stdlib.h>
# include <stdarg.h>
# include <stdio.h>
# include <string.h>
# ifdef HAVE_UNISTD_H
# include <unistd.h>
# endif
# include <fcntl.h>
# include <errno.h>
# include <assert.h>
# ifdef HAVE_SYS_IOCTL_H
# include <sys / ioctl.h>
# endif
# define NONAMELESSUNION
# define NONAMELESSSTRUCT
# include "windef.h"
# include "winbase.h"
# include "wingdi.h"
# include "winerror.h"
# include "winuser.h"
# include "winnls.h"
# include "mmddk.h"
# include "mmsystem.h"
# include "alsa.h"
# include "wine/unicode.h"
# include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL ( mixer ) ;
# ifdef HAVE_ALSA
2007-04-24 00:23:42 +02:00
# define WINE_MIXER_MANUF_ID 0xAA
# define WINE_MIXER_PRODUCT_ID 0x55
# define WINE_MIXER_VERSION 0x0100
2007-04-24 00:23:34 +02:00
/* Generic notes:
* In windows it seems to be required for all controls to have a volume switch
* In alsa that ' s optional
*
* I assume for playback controls , that there is always a playback volume switch available
* Mute is optional
*
* For capture controls , it is needed that there is a capture switch and a volume switch ,
* It doesn ' t matter wether it is a playback volume switch or a capture volume switch .
* The code will first try to get / adjust capture volume , if that fails it tries playback volume
* It is not pretty , but under my 3 test cards it seems that there is no other choice :
* Most capture controls don ' t have a capture volume setting
*
* MUX means that only capture source can be exclusively selected ,
* MIXER means that multiple sources can be selected simultaneously .
*/
static const char * getMessage ( UINT uMsg )
{
static char str [ 64 ] ;
# define MSG_TO_STR(x) case x: return #x;
switch ( uMsg ) {
MSG_TO_STR ( DRVM_INIT ) ;
MSG_TO_STR ( DRVM_EXIT ) ;
MSG_TO_STR ( DRVM_ENABLE ) ;
MSG_TO_STR ( DRVM_DISABLE ) ;
MSG_TO_STR ( MXDM_GETDEVCAPS ) ;
MSG_TO_STR ( MXDM_GETLINEINFO ) ;
MSG_TO_STR ( MXDM_GETNUMDEVS ) ;
MSG_TO_STR ( MXDM_OPEN ) ;
MSG_TO_STR ( MXDM_CLOSE ) ;
MSG_TO_STR ( MXDM_GETLINECONTROLS ) ;
MSG_TO_STR ( MXDM_GETCONTROLDETAILS ) ;
MSG_TO_STR ( MXDM_SETCONTROLDETAILS ) ;
default : break ;
}
# undef MSG_TO_STR
sprintf ( str , " UNKNOWN(%08x) " , uMsg ) ;
return str ;
}
2007-04-24 00:23:42 +02:00
/* A simple declaration of a line control
* These are each of the channels that show up
*/
typedef struct line {
/* Name we present to outside world */
WCHAR name [ MAXPNAMELEN ] ;
DWORD component ;
DWORD dst ;
DWORD capt ;
DWORD chans ;
snd_mixer_elem_t * elem ;
} line ;
2007-04-24 00:23:34 +02:00
/* Mixer device */
typedef struct mixer
{
snd_mixer_t * mix ;
WCHAR mixername [ MAXPNAMELEN ] ;
int chans , dests ;
2007-04-24 00:23:42 +02:00
LPDRVCALLBACK callback ;
DWORD_PTR callbackpriv ;
HDRVR hmx ;
line * lines ;
2007-04-24 00:23:34 +02:00
} mixer ;
# define MAX_MIXERS 32
static int cards = 0 ;
static mixer mixdev [ MAX_MIXERS ] ;
2007-04-24 00:23:42 +02:00
static HANDLE thread ;
static int elem_callback ( snd_mixer_elem_t * elem , unsigned int mask ) ;
static DWORD WINAPI ALSA_MixerPollThread ( LPVOID lParam ) ;
static CRITICAL_SECTION elem_crst ;
static int msg_pipe [ 2 ] ;
static LONG refcnt ;
/* found channel names in alsa lib, alsa api doesn't have another way for this
* map name - > componenttype , worst case we get a wrong componenttype which is
* mostly harmless
*/
static const struct mixerlinetype {
const char * name ; DWORD cmpt ;
} converttable [ ] = {
{ " Master " , MIXERLINE_COMPONENTTYPE_DST_SPEAKERS , } ,
{ " Capture " , MIXERLINE_COMPONENTTYPE_DST_WAVEIN , } ,
{ " PCM " , MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT , } ,
{ " PC Speaker " , MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER , } ,
{ " Synth " , MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER , } ,
{ " Headphone " , MIXERLINE_COMPONENTTYPE_DST_HEADPHONES , } ,
{ " Mic " , MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE , } ,
{ " Aux " , MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED , } ,
{ " CD " , MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC , } ,
{ " Line " , MIXERLINE_COMPONENTTYPE_SRC_LINE , } ,
{ " Phone " , MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE , } ,
} ;
/* Map name to MIXERLINE_COMPONENTTYPE_XXX */
static int getcomponenttype ( const char * name )
{
int x ;
for ( x = 0 ; x < sizeof ( converttable ) / sizeof ( converttable [ 0 ] ) ; + + x )
if ( ! strcasecmp ( name , converttable [ x ] . name ) )
{
TRACE ( " %d -> %s \n " , x , name ) ;
return converttable [ x ] . cmpt ;
}
WARN ( " Unknown mixer name %s, probably harmless \n " , name ) ;
return MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED ;
}
2007-04-24 00:23:34 +02:00
/* Is this control suited for showing up? */
static int blacklisted ( snd_mixer_elem_t * elem )
{
const char * name = snd_mixer_selem_get_name ( elem ) ;
BOOL blisted = 0 ;
if ( ! snd_mixer_selem_has_playback_volume ( elem ) & &
( ! snd_mixer_selem_has_capture_volume ( elem ) | |
! snd_mixer_selem_has_capture_switch ( elem ) ) )
blisted = 1 ;
TRACE ( " %s: %x \n " , name , blisted ) ;
return blisted ;
}
2007-04-24 00:23:42 +02:00
/* get amount of channels for elem */
/* Officially we should keep capture/playback seperated,
* but that ' s not going to work in the alsa api */
static int chans ( mixer * mmixer , snd_mixer_elem_t * elem , DWORD capt )
{
int ret = 0 , chn ;
if ( capt & & snd_mixer_selem_has_capture_volume ( elem ) ) {
for ( chn = 0 ; chn < = SND_MIXER_SCHN_LAST ; + + chn )
if ( snd_mixer_selem_has_capture_channel ( elem , chn ) )
+ + ret ;
} else {
for ( chn = 0 ; chn < = SND_MIXER_SCHN_LAST ; + + chn )
if ( snd_mixer_selem_has_playback_channel ( elem , chn ) )
+ + ret ;
}
if ( ! ret )
FIXME ( " Mixer channel %s was found for %s, but no channels were found? Wrong selection! \n " , snd_mixer_selem_get_name ( elem ) , ( snd_mixer_selem_has_playback_volume ( elem ) ? " playback " : " capture " ) ) ;
return ret ;
}
2007-04-24 00:23:34 +02:00
static void ALSA_MixerInit ( void )
{
int x , mixnum = 0 ;
for ( x = 0 ; x < MAX_MIXERS ; + + x )
{
2007-04-24 00:23:42 +02:00
int card , err , y ;
2007-04-24 00:23:34 +02:00
char cardind [ 6 ] , cardname [ 10 ] ;
BOOL hascapt = 0 , hasmast = 0 ;
2007-04-24 00:23:42 +02:00
line * mline ;
2007-04-24 00:23:34 +02:00
snd_ctl_t * ctl ;
snd_mixer_elem_t * elem , * mastelem = NULL , * captelem = NULL ;
snd_ctl_card_info_t * info = NULL ;
snd_ctl_card_info_alloca ( & info ) ;
2007-04-24 00:23:42 +02:00
mixdev [ mixnum ] . lines = NULL ;
mixdev [ mixnum ] . callback = 0 ;
2007-04-24 00:23:34 +02:00
snprintf ( cardind , sizeof ( cardind ) , " %d " , x ) ;
card = snd_card_get_index ( cardind ) ;
2007-04-24 00:23:42 +02:00
if ( card < 0 )
2007-04-24 00:23:34 +02:00
continue ;
snprintf ( cardname , sizeof ( cardname ) , " hw:%d " , card ) ;
err = snd_ctl_open ( & ctl , cardname , 0 ) ;
if ( err < 0 )
{
WARN ( " Cannot open card: %s \n " , snd_strerror ( err ) ) ;
continue ;
}
err = snd_ctl_card_info ( ctl , info ) ;
if ( err < 0 )
{
WARN ( " Cannot get card info: %s \n " , snd_strerror ( err ) ) ;
snd_ctl_close ( ctl ) ;
continue ;
}
MultiByteToWideChar ( CP_UNIXCP , 0 , snd_ctl_card_info_get_name ( info ) , - 1 , mixdev [ mixnum ] . mixername , sizeof ( mixdev [ mixnum ] . mixername ) / sizeof ( WCHAR ) ) ;
snd_ctl_close ( ctl ) ;
err = snd_mixer_open ( & mixdev [ mixnum ] . mix , 0 ) ;
if ( err < 0 )
{
WARN ( " Error occured opening mixer: %s \n " , snd_strerror ( err ) ) ;
continue ;
}
err = snd_mixer_attach ( mixdev [ mixnum ] . mix , cardname ) ;
if ( err < 0 )
goto eclose ;
err = snd_mixer_selem_register ( mixdev [ mixnum ] . mix , NULL , NULL ) ;
if ( err < 0 )
goto eclose ;
err = snd_mixer_load ( mixdev [ mixnum ] . mix ) ;
if ( err < 0 )
goto eclose ;
mixdev [ mixnum ] . chans = 0 ;
mixdev [ mixnum ] . dests = 1 ; /* Master, Capture will be enabled if needed */
for ( elem = snd_mixer_first_elem ( mixdev [ mixnum ] . mix ) ; elem ; elem = snd_mixer_elem_next ( elem ) )
if ( ! strcasecmp ( snd_mixer_selem_get_name ( elem ) , " Master " ) )
{
mastelem = elem ;
+ + hasmast ;
}
else if ( ! strcasecmp ( snd_mixer_selem_get_name ( elem ) , " Capture " ) )
{
captelem = elem ;
+ + hascapt ;
}
else if ( ! blacklisted ( elem ) )
{
if ( snd_mixer_selem_has_capture_switch ( elem ) )
{
+ + mixdev [ mixnum ] . chans ;
mixdev [ mixnum ] . dests = 2 ;
}
if ( snd_mixer_selem_has_playback_volume ( elem ) )
+ + mixdev [ mixnum ] . chans ;
}
/* If there is only 'Capture' and 'Master', this device is not worth it */
if ( ! mixdev [ mixnum ] . chans )
{
WARN ( " No channels found, skipping device! \n " ) ;
snd_mixer_close ( mixdev [ mixnum ] . mix ) ;
continue ;
}
/* If there are no 'Capture' and 'Master', something is wrong */
if ( hasmast ! = 1 | | hascapt ! = 1 )
{
if ( hasmast ! = 1 )
FIXME ( " Should have found 1 channel for 'Master', but instead found %d \n " , hasmast ) ;
if ( hascapt ! = 1 )
FIXME ( " Should have found 1 channel for 'Capture', but instead found %d \n " , hascapt ) ;
goto eclose ;
}
mixdev [ mixnum ] . chans + = 2 ; /* Capture/Master */
2007-04-24 00:23:42 +02:00
mixdev [ mixnum ] . lines = calloc ( sizeof ( MIXERLINEW ) , mixdev [ mixnum ] . chans ) ;
err = - ENOMEM ;
if ( ! mixdev [ mixnum ] . lines )
goto eclose ;
/* Master control */
mline = & mixdev [ mixnum ] . lines [ 0 ] ;
MultiByteToWideChar ( CP_UNIXCP , 0 , snd_mixer_selem_get_name ( mastelem ) , - 1 , mline - > name , sizeof ( mline - > name ) / sizeof ( WCHAR ) ) ;
mline - > component = getcomponenttype ( " Master " ) ;
mline - > dst = 0 ;
mline - > capt = 0 ;
mline - > elem = mastelem ;
mline - > chans = chans ( & mixdev [ mixnum ] , mastelem , 0 ) ;
/* Capture control
* Note : since mmixer - > dests = 1 , it means only playback control is visible
* This makes sense , because if there are no capture sources capture control
* can ' t do anything and should be invisible */
mline + + ;
MultiByteToWideChar ( CP_UNIXCP , 0 , snd_mixer_selem_get_name ( captelem ) , - 1 , mline - > name , sizeof ( mline - > name ) / sizeof ( WCHAR ) ) ;
mline - > component = getcomponenttype ( " Capture " ) ;
mline - > dst = 1 ;
mline - > capt = 1 ;
mline - > elem = captelem ;
mline - > chans = chans ( & mixdev [ mixnum ] , captelem , 1 ) ;
snd_mixer_elem_set_callback ( mastelem , & elem_callback ) ;
snd_mixer_elem_set_callback_private ( mastelem , & mixdev [ mixnum ] ) ;
if ( mixdev [ mixnum ] . dests = = 2 )
{
snd_mixer_elem_set_callback ( captelem , & elem_callback ) ;
snd_mixer_elem_set_callback_private ( captelem , & mixdev [ mixnum ] ) ;
}
y = 2 ;
for ( elem = snd_mixer_first_elem ( mixdev [ mixnum ] . mix ) ; elem ; elem = snd_mixer_elem_next ( elem ) )
if ( elem ! = mastelem & & elem ! = captelem & & ! blacklisted ( elem ) )
{
const char * name = snd_mixer_selem_get_name ( elem ) ;
DWORD comp = getcomponenttype ( name ) ;
snd_mixer_elem_set_callback ( elem , & elem_callback ) ;
snd_mixer_elem_set_callback_private ( elem , & mixdev [ mixnum ] ) ;
if ( snd_mixer_selem_has_playback_volume ( elem ) )
{
mline = & mixdev [ mixnum ] . lines [ y + + ] ;
mline - > component = comp ;
MultiByteToWideChar ( CP_UNIXCP , 0 , name , - 1 , mline - > name , MAXPNAMELEN ) ;
mline - > capt = mline - > dst = 0 ;
mline - > elem = elem ;
mline - > chans = chans ( & mixdev [ mixnum ] , elem , 0 ) ;
}
if ( snd_mixer_selem_has_capture_switch ( elem ) )
{
mline = & mixdev [ mixnum ] . lines [ y + + ] ;
mline - > component = comp ;
MultiByteToWideChar ( CP_UNIXCP , 0 , name , - 1 , mline - > name , MAXPNAMELEN ) ;
mline - > capt = mline - > dst = 1 ;
mline - > elem = elem ;
mline - > chans = chans ( & mixdev [ mixnum ] , elem , 1 ) ;
}
}
2007-04-24 00:23:34 +02:00
TRACE ( " %s: Amount of controls: %i/%i, name: %s \n " , cardname , mixdev [ mixnum ] . dests , mixdev [ mixnum ] . chans , debugstr_w ( mixdev [ mixnum ] . mixername ) ) ;
mixnum + + ;
continue ;
eclose :
WARN ( " Error occured initialising mixer: %s \n " , snd_strerror ( err ) ) ;
2007-04-24 00:23:42 +02:00
if ( mixdev [ mixnum ] . lines )
free ( mixdev [ mixnum ] . lines ) ;
2007-04-24 00:23:34 +02:00
snd_mixer_close ( mixdev [ mixnum ] . mix ) ;
}
cards = mixnum ;
2007-04-24 00:23:42 +02:00
InitializeCriticalSection ( & elem_crst ) ;
elem_crst . DebugInfo - > Spare [ 0 ] = ( DWORD_PTR ) ( __FILE__ " : ALSA_MIXER.elem_crst " ) ;
2007-04-24 00:23:34 +02:00
TRACE ( " \n " ) ;
}
static void ALSA_MixerExit ( void )
{
int x ;
2007-04-24 00:23:42 +02:00
if ( refcnt )
{
WARN ( " Callback thread still alive, terminating uncleanly, refcnt: %d \n " , refcnt ) ;
/* Least we can do is making sure we're not in 'foreign' code */
EnterCriticalSection ( & elem_crst ) ;
TerminateThread ( thread , 1 ) ;
refcnt = 0 ;
}
TRACE ( " Cleaning up \n " ) ;
elem_crst . DebugInfo - > Spare [ 0 ] = 0 ;
DeleteCriticalSection ( & elem_crst ) ;
2007-04-24 00:23:34 +02:00
for ( x = 0 ; x < cards ; + + x )
2007-04-24 00:23:42 +02:00
{
2007-04-24 00:23:34 +02:00
snd_mixer_close ( mixdev [ x ] . mix ) ;
2007-04-24 00:23:42 +02:00
free ( mixdev [ x ] . lines ) ;
}
2007-04-24 00:23:34 +02:00
cards = 0 ;
}
2007-04-24 00:23:42 +02:00
static mixer * MIX_GetMix ( UINT wDevID )
{
mixer * mmixer ;
if ( wDevID < 0 | | wDevID > = cards )
{
WARN ( " Invalid mixer id: %d \n " , wDevID ) ;
return NULL ;
}
mmixer = & mixdev [ wDevID ] ;
return mmixer ;
}
/* Since alsa doesn't tell what exactly changed, just assume all affected controls changed */
static int elem_callback ( snd_mixer_elem_t * elem , unsigned int type )
{
mixer * mmixer = snd_mixer_elem_get_callback_private ( elem ) ;
int x ;
if ( type ! = SND_CTL_EVENT_MASK_VALUE )
return 0 ;
assert ( mmixer ) ;
EnterCriticalSection ( & elem_crst ) ;
if ( ! mmixer - > callback )
goto out ;
for ( x = 0 ; x < mmixer - > chans ; + + x )
{
if ( elem ! = mmixer - > lines [ x ] . elem )
continue ;
TRACE ( " Found changed control %s \n " , debugstr_w ( mmixer - > lines [ x ] . name ) ) ;
mmixer - > callback ( mmixer - > hmx , MM_MIXM_LINE_CHANGE , mmixer - > callbackpriv , x , 0 ) ;
}
out :
LeaveCriticalSection ( & elem_crst ) ;
return 0 ;
}
static DWORD WINAPI ALSA_MixerPollThread ( LPVOID lParam )
{
struct pollfd * pfds = NULL ;
int x , y , err , mcnt , count = 1 ;
TRACE ( " %p \n " , lParam ) ;
for ( x = 0 ; x < cards ; + + x )
count + = snd_mixer_poll_descriptors_count ( mixdev [ x ] . mix ) ;
TRACE ( " Counted %d descriptors \n " , count ) ;
pfds = HeapAlloc ( GetProcessHeap ( ) , 0 , count * sizeof ( struct pollfd ) ) ;
if ( ! pfds )
{
WARN ( " Out of memory \n " ) ;
goto die ;
}
pfds [ 0 ] . fd = msg_pipe [ 0 ] ;
pfds [ 0 ] . events = POLLIN ;
y = 1 ;
for ( x = 0 ; x < cards ; + + x )
y + = snd_mixer_poll_descriptors ( mixdev [ x ] . mix , & pfds [ y ] , count - y ) ;
while ( ( err = poll ( pfds , ( unsigned int ) count , - 1 ) ) > = 0 | | errno = = EINTR | | errno = = EAGAIN )
{
if ( pfds [ 0 ] . revents & POLLIN )
break ;
mcnt = 1 ;
for ( x = y = 0 ; x < cards ; + + x )
{
int j , max = snd_mixer_poll_descriptors_count ( mixdev [ x ] . mix ) ;
for ( j = 0 ; j < max ; + + j )
if ( pfds [ mcnt + j ] . revents )
{
y + = snd_mixer_handle_events ( mixdev [ x ] . mix ) ;
break ;
}
mcnt + = max ;
}
if ( y )
TRACE ( " Handled %d events \n " , y ) ;
}
die :
TRACE ( " Shutting down \n " ) ;
if ( pfds )
HeapFree ( GetProcessHeap ( ) , 0 , pfds ) ;
y = read ( msg_pipe [ 0 ] , & x , sizeof ( x ) ) ;
close ( msg_pipe [ 1 ] ) ;
close ( msg_pipe [ 0 ] ) ;
return 0 ;
}
static DWORD MIX_Open ( UINT wDevID , LPMIXEROPENDESC desc , DWORD_PTR flags )
{
mixer * mmixer = MIX_GetMix ( wDevID ) ;
if ( ! mmixer )
return MMSYSERR_BADDEVICEID ;
flags & = CALLBACK_TYPEMASK ;
switch ( flags )
{
case CALLBACK_NULL :
goto done ;
case CALLBACK_FUNCTION :
break ;
default :
FIXME ( " Unhandled callback type: %08lx \n " , flags & CALLBACK_TYPEMASK ) ;
return MIXERR_INVALVALUE ;
}
mmixer - > callback = ( LPDRVCALLBACK ) desc - > dwCallback ;
mmixer - > callbackpriv = desc - > dwInstance ;
mmixer - > hmx = ( HDRVR ) desc - > hmx ;
done :
if ( InterlockedIncrement ( & refcnt ) = = 1 )
{
if ( pipe ( msg_pipe ) > = 0 )
{
thread = CreateThread ( NULL , 0 , ALSA_MixerPollThread , NULL , 0 , NULL ) ;
if ( ! thread )
{
close ( msg_pipe [ 0 ] ) ;
close ( msg_pipe [ 1 ] ) ;
msg_pipe [ 0 ] = msg_pipe [ 1 ] = - 1 ;
}
}
else
msg_pipe [ 0 ] = msg_pipe [ 1 ] = - 1 ;
}
return MMSYSERR_NOERROR ;
}
static DWORD MIX_Close ( UINT wDevID )
{
int x ;
mixer * mmixer = MIX_GetMix ( wDevID ) ;
if ( ! mmixer )
return MMSYSERR_BADDEVICEID ;
EnterCriticalSection ( & elem_crst ) ;
mmixer - > callback = 0 ;
LeaveCriticalSection ( & elem_crst ) ;
if ( ! InterlockedDecrement ( & refcnt ) )
{
if ( write ( msg_pipe [ 1 ] , & x , sizeof ( x ) ) > 0 )
{
TRACE ( " Shutting down thread... \n " ) ;
WaitForSingleObject ( thread , INFINITE ) ;
TRACE ( " Done \n " ) ;
}
}
return MMSYSERR_NOERROR ;
}
static DWORD MIX_GetDevCaps ( UINT wDevID , LPMIXERCAPS2W caps , DWORD_PTR parm2 )
{
mixer * mmixer = MIX_GetMix ( wDevID ) ;
MIXERCAPS2W capsW ;
if ( ! caps )
return MMSYSERR_INVALPARAM ;
if ( ! mmixer )
return MMSYSERR_BADDEVICEID ;
memset ( & capsW , 0 , sizeof ( MIXERCAPS2W ) ) ;
capsW . wMid = WINE_MIXER_MANUF_ID ;
capsW . wPid = WINE_MIXER_PRODUCT_ID ;
capsW . vDriverVersion = WINE_MIXER_VERSION ;
lstrcpynW ( capsW . szPname , mmixer - > mixername , sizeof ( capsW . szPname ) / sizeof ( WCHAR ) ) ;
capsW . cDestinations = mmixer - > dests ;
memcpy ( caps , & capsW , min ( parm2 , sizeof ( capsW ) ) ) ;
return MMSYSERR_NOERROR ;
}
2007-04-24 00:23:34 +02:00
# endif /*HAVE_ALSA*/
/**************************************************************************
* mxdMessage ( WINEALSA .3 )
*/
DWORD WINAPI ALSA_mxdMessage ( UINT wDevID , UINT wMsg , DWORD_PTR dwUser ,
DWORD_PTR dwParam1 , DWORD_PTR dwParam2 )
{
# ifdef HAVE_ALSA
DWORD ret ;
TRACE ( " (%04X, %s, %08lX, %08lX, %08lX); \n " , wDevID , getMessage ( wMsg ) ,
dwUser , dwParam1 , dwParam2 ) ;
switch ( wMsg )
{
case DRVM_INIT : ALSA_MixerInit ( ) ; ret = MMSYSERR_NOERROR ; break ;
case DRVM_EXIT : ALSA_MixerExit ( ) ; ret = MMSYSERR_NOERROR ; break ;
/* All taken care of by driver initialisation */
/* Unimplemented, and not needed */
case DRVM_ENABLE :
case DRVM_DISABLE :
ret = MMSYSERR_NOERROR ; break ;
2007-04-24 00:23:42 +02:00
case MXDM_OPEN :
ret = MIX_Open ( wDevID , ( LPMIXEROPENDESC ) dwParam1 , dwParam2 ) ; break ;
case MXDM_CLOSE :
ret = MIX_Close ( wDevID ) ; break ;
case MXDM_GETDEVCAPS :
ret = MIX_GetDevCaps ( wDevID , ( LPMIXERCAPS2W ) dwParam1 , dwParam2 ) ; break ;
2007-04-24 00:23:34 +02:00
case MXDM_GETNUMDEVS :
ret = cards ; break ;
default :
WARN ( " unknown message %s! \n " , getMessage ( wMsg ) ) ;
return MMSYSERR_NOTSUPPORTED ;
}
TRACE ( " Returning %08X \n " , ret ) ;
return ret ;
# else /*HAVE_ALSA*/
TRACE ( " (%04X, %04X, %08lX, %08lX, %08lX); \n " , wDevID , wMsg , dwUser , dwParam1 , dwParam2 ) ;
return MMSYSERR_NOTENABLED ;
# endif /*HAVE_ALSA*/
}