Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wine-cw
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wine
wine-cw
Commits
151015fa
Commit
151015fa
authored
Jan 10, 2005
by
Vincent Béron
Committed by
Alexandre Julliard
Jan 10, 2005
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Only use Alsa if 1.0 is detected.
parent
ed3ad885
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
76 additions
and
1807 deletions
+76
-1807
configure
configure
+57
-11
configure.ac
configure.ac
+10
-4
Makefile.in
dlls/winmm/winealsa/Makefile.in
+0
-1
audio.c
dlls/winmm/winealsa/audio.c
+2
-8
audio_05.c
dlls/winmm/winealsa/audio_05.c
+0
-1774
midi.c
dlls/winmm/winealsa/midi.c
+6
-8
config.h.in
include/config.h.in
+1
-1
No files found.
configure
View file @
151015fa
...
...
@@ -11391,9 +11391,9 @@ done
if
test
"
$ac_cv_header_sys_asoundlib_h
"
=
"yes"
-o
"
$ac_cv_header_alsa_asoundlib_h
"
=
"yes"
then
echo
"
$as_me
:
$LINENO
: checking for snd_pcm_
open
in -lasound"
>
&5
echo
$ECHO_N
"checking for snd_pcm_
open
in -lasound...
$ECHO_C
"
>
&6
if
test
"
${
ac_cv_lib_asound_snd_pcm_
open
+set
}
"
=
set
;
then
echo
"
$as_me
:
$LINENO
: checking for snd_pcm_
hw_params_get_access
in -lasound"
>
&5
echo
$ECHO_N
"checking for snd_pcm_
hw_params_get_access
in -lasound...
$ECHO_C
"
>
&6
if
test
"
${
ac_cv_lib_asound_snd_pcm_
hw_params_get_access
+set
}
"
=
set
;
then
echo
$ECHO_N
"(cached)
$ECHO_C
"
>
&6
else
ac_check_lib_save_LIBS
=
$LIBS
...
...
@@ -11411,11 +11411,11 @@ extern "C"
#endif
/* We use char because int might match the return type of a gcc2
builtin and then its argument prototype would still apply. */
char snd_pcm_
open
();
char snd_pcm_
hw_params_get_access
();
int
main ()
{
snd_pcm_
open
();
snd_pcm_
hw_params_get_access
();
;
return 0;
}
...
...
@@ -11441,26 +11441,72 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
ac_status
=
$?
echo
"
$as_me
:
$LINENO
:
\$
? =
$ac_status
"
>
&5
(
exit
$ac_status
)
;
}
;
}
;
then
ac_cv_lib_asound_snd_pcm_
open
=
yes
ac_cv_lib_asound_snd_pcm_
hw_params_get_access
=
yes
else
echo
"
$as_me
: failed program was:"
>
&5
sed
's/^/| /'
conftest.
$ac_ext
>
&5
ac_cv_lib_asound_snd_pcm_
open
=
no
ac_cv_lib_asound_snd_pcm_
hw_params_get_access
=
no
fi
rm
-f
conftest.err conftest.
$ac_objext
\
conftest
$ac_exeext
conftest.
$ac_ext
LIBS
=
$ac_check_lib_save_LIBS
fi
echo
"
$as_me
:
$LINENO
: result:
$ac_cv_lib_asound_snd_pcm_open
"
>
&5
echo
"
${
ECHO_T
}
$ac_cv_lib_asound_snd_pcm_open
"
>
&6
if
test
$ac_cv_lib_asound_snd_pcm_open
=
yes
;
then
echo
"
$as_me
:
$LINENO
: result:
$ac_cv_lib_asound_snd_pcm_hw_params_get_access
"
>
&5
echo
"
${
ECHO_T
}
$ac_cv_lib_asound_snd_pcm_hw_params_get_access
"
>
&6
if
test
$ac_cv_lib_asound_snd_pcm_hw_params_get_access
=
yes
;
then
cat
>
conftest.
$ac_ext
<<
_ACEOF
/* confdefs.h. */
_ACEOF
cat
confdefs.h
>>
conftest.
$ac_ext
cat
>>
conftest.
$ac_ext
<<
_ACEOF
/* end confdefs.h. */
#ifdef HAVE_ALSA_ASOUNDLIB_H
#include <alsa/asoundlib.h>
#elif defined(HAVE_SYS_ASOUNDLIB_H)
#include <sys/asoundlib.h>
#endif
int
main ()
{
int ret = snd_pcm_hw_params_get_access(NULL, NULL)
;
return 0;
}
_ACEOF
rm
-f
conftest.
$ac_objext
if
{
(
eval echo
"
$as_me
:
$LINENO
:
\"
$ac_compile
\"
"
)
>
&5
(
eval
$ac_compile
)
2>conftest.er1
ac_status
=
$?
grep
-v
'^ *+'
conftest.er1
>
conftest.err
rm
-f
conftest.er1
cat
conftest.err
>
&5
echo
"
$as_me
:
$LINENO
:
\$
? =
$ac_status
"
>
&5
(
exit
$ac_status
)
;
}
&&
{
ac_try
=
'test -z "$ac_c_werror_flag" || test ! -s conftest.err'
{
(
eval echo
"
$as_me
:
$LINENO
:
\"
$ac_try
\"
"
)
>
&5
(
eval
$ac_try
)
2>&5
ac_status
=
$?
echo
"
$as_me
:
$LINENO
:
\$
? =
$ac_status
"
>
&5
(
exit
$ac_status
)
;
}
;
}
&&
{
ac_try
=
'test -s conftest.$ac_objext'
{
(
eval echo
"
$as_me
:
$LINENO
:
\"
$ac_try
\"
"
)
>
&5
(
eval
$ac_try
)
2>&5
ac_status
=
$?
echo
"
$as_me
:
$LINENO
:
\$
? =
$ac_status
"
>
&5
(
exit
$ac_status
)
;
}
;
}
;
then
cat
>>
confdefs.h
<<
\
_ACEOF
#define HAVE_ALSA 1
_ACEOF
ALSALIBS
=
"-lasound"
ALSALIBS
=
"-lasound"
else
echo
"
$as_me
: failed program was:"
>
&5
sed
's/^/| /'
conftest.
$ac_ext
>
&5
fi
rm
-f
conftest.err conftest.
$ac_objext
conftest.
$ac_ext
fi
fi
...
...
configure.ac
View file @
151015fa
...
...
@@ -676,14 +676,20 @@ then
CFLAGS="$save_CFLAGS"
fi
dnl **** Check for ALSA ****
dnl **** Check for ALSA
1.x
****
AC_SUBST(ALSALIBS,"")
AC_CHECK_HEADERS(alsa/asoundlib.h sys/asoundlib.h, break)
if test "$ac_cv_header_sys_asoundlib_h" = "yes" -o "$ac_cv_header_alsa_asoundlib_h" = "yes"
then
AC_CHECK_LIB(asound,snd_pcm_open,
AC_DEFINE(HAVE_ALSA,1,[Define if you have ALSA including devel headers])
ALSALIBS="-lasound")
AC_CHECK_LIB(asound,snd_pcm_hw_params_get_access,
[AC_TRY_COMPILE([#ifdef HAVE_ALSA_ASOUNDLIB_H
#include <alsa/asoundlib.h>
#elif defined(HAVE_SYS_ASOUNDLIB_H)
#include <sys/asoundlib.h>
#endif],
[int ret = snd_pcm_hw_params_get_access(NULL, NULL)],
[AC_DEFINE(HAVE_ALSA,1,[Define if you have ALSA 1.x including devel headers])
ALSALIBS="-lasound"])])
fi
dnl **** Check for libaudioio (which can be used to get solaris audio support) ****
...
...
dlls/winmm/winealsa/Makefile.in
View file @
151015fa
...
...
@@ -8,7 +8,6 @@ EXTRALIBS = -ldxguid -luuid @ALSALIBS@
C_SRCS
=
\
audio.c
\
audio_05.c
\
alsa.c
\
midi.c
...
...
dlls/winmm/winealsa/audio.c
View file @
151015fa
...
...
@@ -68,7 +68,7 @@
WINE_DEFAULT_DEBUG_CHANNEL
(
wave
);
#if
defined(HAVE_ALSA) && ((SND_LIB_MAJOR == 0 && SND_LIB_MINOR >= 9) || SND_LIB_MAJOR >= 1)
#if
def HAVE_ALSA
/* internal ALSALIB functions */
snd_pcm_uframes_t
_snd_pcm_mmap_hw_ptr
(
snd_pcm_t
*
pcm
);
...
...
@@ -3580,9 +3580,7 @@ DWORD WINAPI ALSA_widMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
return
MMSYSERR_NOTSUPPORTED
;
}
#endif
#if !(defined(HAVE_ALSA) && ((SND_LIB_MAJOR == 0 && SND_LIB_MINOR >= 9) || SND_LIB_MAJOR >= 1))
#else
/**************************************************************************
* widMessage (WINEALSA.@)
...
...
@@ -3594,10 +3592,6 @@ DWORD WINAPI ALSA_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
return
MMSYSERR_NOTENABLED
;
}
#endif
#ifndef HAVE_ALSA
/**************************************************************************
* wodMessage (WINEALSA.@)
*/
...
...
dlls/winmm/winealsa/audio_05.c
deleted
100644 → 0
View file @
ed3ad885
/* -*- tab-width: 8; c-basic-offset: 4 -*- */
/*
* Sample Wine Driver for Advanced Linux Sound System (ALSA)
* Based on version 0.5 of the ALSA API
*
* Copyright 2002 Eric Pouech
* 2002 David Hammerton
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <errno.h>
#include <fcntl.h>
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#ifdef HAVE_SYS_MMAN_H
# include <sys/mman.h>
#endif
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winerror.h"
#include "winuser.h"
#include "mmddk.h"
#include "dsound.h"
#include "dsdriver.h"
#include "alsa.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL
(
wave
);
#if defined(HAVE_ALSA) && (SND_LIB_MAJOR == 0) && (SND_LIB_MINOR == 5)
#define MAX_WAVEOUTDRV (1)
#define MAX_WAVEINDRV (1)
/* state diagram for waveOut writing:
*
* +---------+-------------+---------------+---------------------------------+
* | state | function | event | new state |
* +---------+-------------+---------------+---------------------------------+
* | | open() | | STOPPED |
* | PAUSED | write() | | PAUSED |
* | STOPPED | write() | <thrd create> | PLAYING |
* | PLAYING | write() | HEADER | PLAYING |
* | (other) | write() | <error> | |
* | (any) | pause() | PAUSING | PAUSED |
* | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
* | (any) | reset() | RESETTING | STOPPED |
* | (any) | close() | CLOSING | CLOSED |
* +---------+-------------+---------------+---------------------------------+
*/
/* states of the playing device */
#define WINE_WS_PLAYING 0
#define WINE_WS_PAUSED 1
#define WINE_WS_STOPPED 2
#define WINE_WS_CLOSED 3
/* events to be send to device */
enum
win_wm_message
{
WINE_WM_PAUSING
=
WM_USER
+
1
,
WINE_WM_RESTARTING
,
WINE_WM_RESETTING
,
WINE_WM_HEADER
,
WINE_WM_UPDATE
,
WINE_WM_BREAKLOOP
,
WINE_WM_CLOSING
};
typedef
struct
{
enum
win_wm_message
msg
;
/* message identifier */
DWORD
param
;
/* parameter for this message */
HANDLE
hEvent
;
/* if message is synchronous, handle of event for synchro */
}
ALSA_MSG
;
/* implement an in-process message ring for better performance
* (compared to passing thru the server)
* this ring will be used by the input (resp output) record (resp playback) routine
*/
typedef
struct
{
/* FIXME: this could be made a dynamically growing array (if needed) */
#define ALSA_RING_BUFFER_SIZE 30
ALSA_MSG
messages
[
ALSA_RING_BUFFER_SIZE
];
int
msg_tosave
;
int
msg_toget
;
HANDLE
msg_event
;
CRITICAL_SECTION
msg_crst
;
}
ALSA_MSG_RING
;
typedef
struct
{
/* Windows information */
volatile
int
state
;
/* one of the WINE_WS_ manifest constants */
WAVEOPENDESC
waveDesc
;
WORD
wFlags
;
PCMWAVEFORMAT
format
;
WAVEOUTCAPSW
caps
;
/* ALSA information */
snd_pcm_t
*
handle
;
/* handle to ALSA device */
DWORD
dwFragmentSize
;
/* size of ALSA buffer fragment */
DWORD
dwBufferSize
;
/* size of whole ALSA buffer in bytes */
LPWAVEHDR
lpQueuePtr
;
/* start of queued WAVEHDRs (waiting to be notified) */
LPWAVEHDR
lpPlayPtr
;
/* start of not yet fully played buffers */
DWORD
dwPartialOffset
;
/* Offset of not yet written bytes in lpPlayPtr */
LPWAVEHDR
lpLoopPtr
;
/* pointer of first buffer in loop, if any */
DWORD
dwLoops
;
/* private copy of loop counter */
DWORD
dwPlayedTotal
;
/* number of bytes actually played since opening */
DWORD
dwWrittenTotal
;
/* number of bytes written to ALSA buffer since opening */
/* synchronization stuff */
HANDLE
hStartUpEvent
;
HANDLE
hThread
;
DWORD
dwThreadID
;
ALSA_MSG_RING
msgRing
;
/* DirectSound stuff */
void
*
mmap_buffer
;
snd_pcm_mmap_control_t
*
mmap_control
;
unsigned
mmap_block_size
;
unsigned
mmap_block_number
;
}
WINE_WAVEOUT
;
static
WINE_WAVEOUT
WOutDev
[
MAX_WAVEOUTDRV
];
static
DWORD
ALSA_WodNumDevs
;
static
DWORD
wodDsCreate
(
UINT
wDevID
,
PIDSDRIVER
*
drv
);
/* These strings used only for tracing */
static
const
char
*
wodPlayerCmdString
[]
=
{
"WINE_WM_PAUSING"
,
"WINE_WM_RESTARTING"
,
"WINE_WM_RESETTING"
,
"WINE_WM_HEADER"
,
"WINE_WM_UPDATE"
,
"WINE_WM_BREAKLOOP"
,
"WINE_WM_CLOSING"
,
};
/*======================================================================*
* Low level WAVE implementation *
*======================================================================*/
/******************************************************************
* ALSA_WaveInit
*
* Initialize internal structures from ALSA information
*/
LONG
ALSA_WaveInit
(
void
)
{
snd_pcm_t
*
h
;
snd_pcm_info_t
info
;
snd_pcm_channel_info_t
chn_info
;
static
const
WCHAR
ini_out
[]
=
{
'A'
,
'L'
,
'S'
,
'A'
,
' '
,
'W'
,
'a'
,
'v'
,
'e'
,
'O'
,
'u'
,
't'
,
' '
,
'D'
,
'r'
,
'i'
,
'v'
,
'e'
,
'r'
,
0
};
TRACE
(
"There are %d cards
\n
"
,
snd_cards
());
ALSA_WodNumDevs
=
0
;
if
(
snd_pcm_open
(
&
h
,
0
,
0
,
SND_PCM_OPEN_DUPLEX
|
SND_PCM_OPEN_NONBLOCK
))
{
ERR
(
"Error open: %s
\n
"
,
snd_strerror
(
errno
));
return
-
1
;
}
if
(
snd_pcm_info
(
h
,
&
info
))
{
ERR
(
"Error info: %s
\n
"
,
snd_strerror
(
errno
));
return
-
1
;
}
ALSA_WodNumDevs
++
;
TRACE
(
"type=%u, flags=%s%s%s name=%s #pb=%d cp=%d
\n
"
,
info
.
type
,
(
info
.
flags
&
SND_PCM_INFO_PLAYBACK
)
?
"playback "
:
""
,
(
info
.
flags
&
SND_PCM_INFO_PLAYBACK
)
?
"capture "
:
""
,
(
info
.
flags
&
SND_PCM_INFO_DUPLEX
)
?
"duplex "
:
""
,
info
.
name
,
info
.
playback
,
info
.
capture
);
memset
(
&
chn_info
,
0
,
sizeof
(
chn_info
));
if
(
snd_pcm_channel_info
(
h
,
&
chn_info
))
{
ERR
(
"Error chn info: %s
\n
"
,
snd_strerror
(
errno
));
return
-
1
;
}
#define X(f,s) ((chn_info.flags & (f)) ? #s " " : "")
#define Y(f,s) ((chn_info.rates & (f)) ? #s " " : "")
TRACE
(
"subdevice=%d name=%s chn=%d mode=%d
\n
"
"
\t
flags=%s%s%s%s%s%s%s%s%s%s%s
\n
"
"
\t
fmts=%u rates=%s%s%s%s%s%s%s%s%s%s%s%s%s
\n
"
"
\t
rates=[%d,%d] voices=[%d,%d] buf_size=%d fg_size=[%d,%d] fg_align=%u
\n
"
,
chn_info
.
subdevice
,
chn_info
.
subname
,
chn_info
.
channel
,
chn_info
.
mode
,
X
(
SND_PCM_CHNINFO_MMAP
,
MMAP
),
X
(
SND_PCM_CHNINFO_STREAM
,
STREAM
),
X
(
SND_PCM_CHNINFO_BLOCK
,
BLOCK
),
X
(
SND_PCM_CHNINFO_BATCH
,
BATCH
),
X
(
SND_PCM_CHNINFO_INTERLEAVE
,
INTERLEAVE
),
X
(
SND_PCM_CHNINFO_NONINTERLEAVE
,
NONINTERLEAVE
),
X
(
SND_PCM_CHNINFO_BLOCK_TRANSFER
,
BLOCK_TRANSFER
),
X
(
SND_PCM_CHNINFO_OVERRANGE
,
OVERRANGE
),
X
(
SND_PCM_CHNINFO_MMAP_VALID
,
MMAP_VALID
),
X
(
SND_PCM_CHNINFO_PAUSE
,
PAUSE
),
X
(
SND_PCM_CHNINFO_GLOBAL_PARAMS
,
GLOBAL_PARAMS
),
chn_info
.
formats
,
Y
(
SND_PCM_RATE_CONTINUOUS
,
CONTINUOUS
),
Y
(
SND_PCM_RATE_KNOT
,
KNOT
),
Y
(
SND_PCM_RATE_8000
,
8000
),
Y
(
SND_PCM_RATE_11025
,
11025
),
Y
(
SND_PCM_RATE_16000
,
16000
),
Y
(
SND_PCM_RATE_22050
,
22050
),
Y
(
SND_PCM_RATE_32000
,
32000
),
Y
(
SND_PCM_RATE_44100
,
44100
),
Y
(
SND_PCM_RATE_48000
,
48000
),
Y
(
SND_PCM_RATE_88200
,
88200
),
Y
(
SND_PCM_RATE_96000
,
96000
),
Y
(
SND_PCM_RATE_176400
,
176400
),
Y
(
SND_PCM_RATE_192000
,
192000
),
chn_info
.
min_rate
,
chn_info
.
max_rate
,
chn_info
.
min_voices
,
chn_info
.
max_voices
,
chn_info
.
buffer_size
,
chn_info
.
min_fragment_size
,
chn_info
.
max_fragment_size
,
chn_info
.
fragment_align
);
#undef X
#undef Y
/* FIXME: use better values */
WOutDev
[
0
].
caps
.
wMid
=
0x0002
;
WOutDev
[
0
].
caps
.
wPid
=
0x0104
;
strcpyW
(
WOutDev
[
0
].
caps
.
szPname
,
ini
);
WOutDev
[
0
].
caps
.
vDriverVersion
=
0x0100
;
WOutDev
[
0
].
caps
.
dwFormats
=
0x00000000
;
WOutDev
[
0
].
caps
.
dwSupport
=
WAVECAPS_VOLUME
;
#define X(r,v) \
if (chn_info.rates & SND_PCM_RATE_##r) \
{ \
if (chn_info.formats & SND_PCM_FMT_U8) \
{ \
if (chn_info.min_voices <= 1 && 1 <= chn_info.max_voices) \
WOutDev[0].caps.dwFormats |= WAVE_FORMAT_##v##S08; \
if (chn_info.min_voices <= 2 && 2 <= chn_info.max_voices) \
WOutDev[0].caps.dwFormats |= WAVE_FORMAT_##v##S08; \
} \
if (chn_info.formats & SND_PCM_FMT_S16_LE) \
{ \
if (chn_info.min_voices <= 1 && 1 <= chn_info.max_voices) \
WOutDev[0].caps.dwFormats |= WAVE_FORMAT_##v##S16; \
if (chn_info.min_voices <= 2 && 2 <= chn_info.max_voices) \
WOutDev[0].caps.dwFormats |= WAVE_FORMAT_##v##S16; \
} \
}
X
(
11025
,
1
);
X
(
22050
,
2
);
X
(
44100
,
4
);
#undef X
if
(
chn_info
.
min_voices
>
1
)
FIXME
(
"-
\n
"
);
WOutDev
[
0
].
caps
.
wChannels
=
(
chn_info
.
max_voices
>=
2
)
?
2
:
1
;
if
(
chn_info
.
min_voices
<=
2
&&
2
<=
chn_info
.
max_voices
)
WOutDev
[
0
].
caps
.
dwSupport
|=
WAVECAPS_LRVOLUME
;
/* FIXME: always true ? */
WOutDev
[
0
].
caps
.
dwSupport
|=
WAVECAPS_SAMPLEACCURATE
;
/* FIXME: is test sufficient ? */
if
(
chn_info
.
flags
&
SND_PCM_CHNINFO_MMAP
)
WOutDev
[
0
].
caps
.
dwSupport
|=
WAVECAPS_DIRECTSOUND
;
TRACE
(
"Configured with dwFmts=%08lx dwSupport=%08lx
\n
"
,
WOutDev
[
0
].
caps
.
dwFormats
,
WOutDev
[
0
].
caps
.
dwSupport
);
snd_pcm_close
(
h
);
return
0
;
}
/******************************************************************
* ALSA_InitRingMessage
*
* Initialize the ring of messages for passing between driver's caller and playback/record
* thread
*/
static
int
ALSA_InitRingMessage
(
ALSA_MSG_RING
*
omr
)
{
omr
->
msg_toget
=
0
;
omr
->
msg_tosave
=
0
;
omr
->
msg_event
=
CreateEventW
(
NULL
,
FALSE
,
FALSE
,
NULL
);
memset
(
omr
->
messages
,
0
,
sizeof
(
ALSA_MSG
)
*
ALSA_RING_BUFFER_SIZE
);
InitializeCriticalSection
(
&
omr
->
msg_crst
);
return
0
;
}
/******************************************************************
* ALSA_DestroyRingMessage
*
*/
static
int
ALSA_DestroyRingMessage
(
ALSA_MSG_RING
*
omr
)
{
CloseHandle
(
omr
->
msg_event
);
DeleteCriticalSection
(
&
omr
->
msg_crst
);
return
0
;
}
/******************************************************************
* ALSA_AddRingMessage
*
* Inserts a new message into the ring (should be called from DriverProc derivated routines)
*/
static
int
ALSA_AddRingMessage
(
ALSA_MSG_RING
*
omr
,
enum
win_wm_message
msg
,
DWORD
param
,
BOOL
wait
)
{
HANDLE
hEvent
=
INVALID_HANDLE_VALUE
;
EnterCriticalSection
(
&
omr
->
msg_crst
);
if
((
omr
->
msg_toget
==
((
omr
->
msg_tosave
+
1
)
%
ALSA_RING_BUFFER_SIZE
)))
/* buffer overflow ? */
{
ERR
(
"buffer overflow !?
\n
"
);
LeaveCriticalSection
(
&
omr
->
msg_crst
);
return
0
;
}
if
(
wait
)
{
hEvent
=
CreateEventW
(
NULL
,
FALSE
,
FALSE
,
NULL
);
if
(
hEvent
==
INVALID_HANDLE_VALUE
)
{
ERR
(
"can't create event !?
\n
"
);
LeaveCriticalSection
(
&
omr
->
msg_crst
);
return
0
;
}
if
(
omr
->
msg_toget
!=
omr
->
msg_tosave
&&
omr
->
messages
[
omr
->
msg_toget
].
msg
!=
WINE_WM_HEADER
)
FIXME
(
"two fast messages in the queue!!!!
\n
"
);
/* fast messages have to be added at the start of the queue */
omr
->
msg_toget
=
(
omr
->
msg_toget
+
ALSA_RING_BUFFER_SIZE
-
1
)
%
ALSA_RING_BUFFER_SIZE
;
omr
->
messages
[
omr
->
msg_toget
].
msg
=
msg
;
omr
->
messages
[
omr
->
msg_toget
].
param
=
param
;
omr
->
messages
[
omr
->
msg_toget
].
hEvent
=
hEvent
;
}
else
{
omr
->
messages
[
omr
->
msg_tosave
].
msg
=
msg
;
omr
->
messages
[
omr
->
msg_tosave
].
param
=
param
;
omr
->
messages
[
omr
->
msg_tosave
].
hEvent
=
INVALID_HANDLE_VALUE
;
omr
->
msg_tosave
=
(
omr
->
msg_tosave
+
1
)
%
ALSA_RING_BUFFER_SIZE
;
}
LeaveCriticalSection
(
&
omr
->
msg_crst
);
/* signal a new message */
SetEvent
(
omr
->
msg_event
);
if
(
wait
)
{
/* wait for playback/record thread to have processed the message */
WaitForSingleObject
(
hEvent
,
INFINITE
);
CloseHandle
(
hEvent
);
}
return
1
;
}
/******************************************************************
* ALSA_RetrieveRingMessage
*
* Get a message from the ring. Should be called by the playback/record thread.
*/
static
int
ALSA_RetrieveRingMessage
(
ALSA_MSG_RING
*
omr
,
enum
win_wm_message
*
msg
,
DWORD
*
param
,
HANDLE
*
hEvent
)
{
EnterCriticalSection
(
&
omr
->
msg_crst
);
if
(
omr
->
msg_toget
==
omr
->
msg_tosave
)
/* buffer empty ? */
{
LeaveCriticalSection
(
&
omr
->
msg_crst
);
return
0
;
}
*
msg
=
omr
->
messages
[
omr
->
msg_toget
].
msg
;
omr
->
messages
[
omr
->
msg_toget
].
msg
=
0
;
*
param
=
omr
->
messages
[
omr
->
msg_toget
].
param
;
*
hEvent
=
omr
->
messages
[
omr
->
msg_toget
].
hEvent
;
omr
->
msg_toget
=
(
omr
->
msg_toget
+
1
)
%
ALSA_RING_BUFFER_SIZE
;
LeaveCriticalSection
(
&
omr
->
msg_crst
);
return
1
;
}
/*======================================================================*
* Low level WAVE OUT implementation *
*======================================================================*/
/**************************************************************************
* wodNotifyClient [internal]
*/
static
DWORD
wodNotifyClient
(
WINE_WAVEOUT
*
wwo
,
WORD
wMsg
,
DWORD
dwParam1
,
DWORD
dwParam2
)
{
TRACE
(
"wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX
\n
"
,
wMsg
,
dwParam1
,
dwParam2
);
switch
(
wMsg
)
{
case
WOM_OPEN
:
case
WOM_CLOSE
:
case
WOM_DONE
:
if
(
wwo
->
wFlags
!=
DCB_NULL
&&
!
DriverCallback
(
wwo
->
waveDesc
.
dwCallback
,
wwo
->
wFlags
,
(
HDRVR
)
wwo
->
waveDesc
.
hWave
,
wMsg
,
wwo
->
waveDesc
.
dwInstance
,
dwParam1
,
dwParam2
))
{
WARN
(
"can't notify client !
\n
"
);
return
MMSYSERR_ERROR
;
}
break
;
default:
FIXME
(
"Unknown callback message %u
\n
"
,
wMsg
);
return
MMSYSERR_INVALPARAM
;
}
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodUpdatePlayedTotal [internal]
*
*/
static
BOOL
wodUpdatePlayedTotal
(
WINE_WAVEOUT
*
wwo
,
snd_pcm_channel_status_t
*
ps
)
{
snd_pcm_channel_status_t
s
;
snd_pcm_channel_status_t
*
status
=
(
ps
)
?
ps
:
&
s
;
if
(
snd_pcm_channel_status
(
wwo
->
handle
,
status
))
{
ERR
(
"Can't get channel status: %s
\n
"
,
snd_strerror
(
errno
));
return
FALSE
;
}
wwo
->
dwPlayedTotal
=
wwo
->
dwWrittenTotal
-
(
wwo
->
dwBufferSize
-
status
->
count
);
if
(
wwo
->
dwPlayedTotal
!=
status
->
scount
)
{
FIXME
(
"Ooch: %u played by ALSA, %lu counted by driver
\n
"
,
status
->
scount
,
wwo
->
dwPlayedTotal
);
if
(
wwo
->
dwPlayedTotal
&
0x8000000
)
wwo
->
dwPlayedTotal
=
0
;
}
return
TRUE
;
}
/**************************************************************************
* wodPlayer_BeginWaveHdr [internal]
*
* Makes the specified lpWaveHdr the currently playing wave header.
* If the specified wave header is a begin loop and we're not already in
* a loop, setup the loop.
*/
static
void
wodPlayer_BeginWaveHdr
(
WINE_WAVEOUT
*
wwo
,
LPWAVEHDR
lpWaveHdr
)
{
wwo
->
lpPlayPtr
=
lpWaveHdr
;
if
(
!
lpWaveHdr
)
return
;
if
(
lpWaveHdr
->
dwFlags
&
WHDR_BEGINLOOP
)
{
if
(
wwo
->
lpLoopPtr
)
{
WARN
(
"Already in a loop. Discarding loop on this header (%p)
\n
"
,
lpWaveHdr
);
}
else
{
TRACE
(
"Starting loop (%ldx) with %p
\n
"
,
lpWaveHdr
->
dwLoops
,
lpWaveHdr
);
wwo
->
lpLoopPtr
=
lpWaveHdr
;
/* Windows does not touch WAVEHDR.dwLoops,
* so we need to make an internal copy */
wwo
->
dwLoops
=
lpWaveHdr
->
dwLoops
;
}
}
wwo
->
dwPartialOffset
=
0
;
}
/**************************************************************************
* wodPlayer_PlayPtrNext [internal]
*
* Advance the play pointer to the next waveheader, looping if required.
*/
static
LPWAVEHDR
wodPlayer_PlayPtrNext
(
WINE_WAVEOUT
*
wwo
)
{
LPWAVEHDR
lpWaveHdr
=
wwo
->
lpPlayPtr
;
wwo
->
dwPartialOffset
=
0
;
if
((
lpWaveHdr
->
dwFlags
&
WHDR_ENDLOOP
)
&&
wwo
->
lpLoopPtr
)
{
/* We're at the end of a loop, loop if required */
if
(
--
wwo
->
dwLoops
>
0
)
{
wwo
->
lpPlayPtr
=
wwo
->
lpLoopPtr
;
}
else
{
/* Handle overlapping loops correctly */
if
(
wwo
->
lpLoopPtr
!=
lpWaveHdr
&&
(
lpWaveHdr
->
dwFlags
&
WHDR_BEGINLOOP
))
{
FIXME
(
"Correctly handled case ? (ending loop buffer also starts a new loop)
\n
"
);
/* shall we consider the END flag for the closing loop or for
* the opening one or for both ???
* code assumes for closing loop only
*/
}
else
{
lpWaveHdr
=
lpWaveHdr
->
lpNext
;
}
wwo
->
lpLoopPtr
=
NULL
;
wodPlayer_BeginWaveHdr
(
wwo
,
lpWaveHdr
);
}
}
else
{
/* We're not in a loop. Advance to the next wave header */
wodPlayer_BeginWaveHdr
(
wwo
,
lpWaveHdr
=
lpWaveHdr
->
lpNext
);
}
return
lpWaveHdr
;
}
/**************************************************************************
* wodPlayer_DSPWait [internal]
* Returns the number of milliseconds to wait for the DSP buffer to write
* one fragment.
*/
static
DWORD
wodPlayer_DSPWait
(
const
WINE_WAVEOUT
*
wwo
)
{
/* time for one fragment to be played */
return
wwo
->
dwFragmentSize
*
1000
/
wwo
->
format
.
wf
.
nAvgBytesPerSec
;
}
/**************************************************************************
* wodPlayer_NotifyWait [internal]
* Returns the number of milliseconds to wait before attempting to notify
* completion of the specified wavehdr.
* This is based on the number of bytes remaining to be written in the
* wave.
*/
static
DWORD
wodPlayer_NotifyWait
(
const
WINE_WAVEOUT
*
wwo
,
LPWAVEHDR
lpWaveHdr
)
{
DWORD
dwMillis
;
if
(
lpWaveHdr
->
reserved
<
wwo
->
dwPlayedTotal
)
{
dwMillis
=
1
;
}
else
{
dwMillis
=
(
lpWaveHdr
->
reserved
-
wwo
->
dwPlayedTotal
)
*
1000
/
wwo
->
format
.
wf
.
nAvgBytesPerSec
;
if
(
!
dwMillis
)
dwMillis
=
1
;
}
return
dwMillis
;
}
/**************************************************************************
* wodPlayer_WriteMaxFrags [internal]
* Writes the maximum number of bytes palsaible to the DSP and returns
* the number of bytes written.
*/
static
int
wodPlayer_WriteMaxFrags
(
WINE_WAVEOUT
*
wwo
,
DWORD
*
bytes
)
{
/* Only attempt to write to free bytes */
DWORD
dwLength
=
wwo
->
lpPlayPtr
->
dwBufferLength
-
wwo
->
dwPartialOffset
;
int
toWrite
=
min
(
dwLength
,
*
bytes
);
int
written
;
TRACE
(
"Writing wavehdr %p.%lu[%lu]
\n
"
,
wwo
->
lpPlayPtr
,
wwo
->
dwPartialOffset
,
wwo
->
lpPlayPtr
->
dwBufferLength
);
written
=
snd_pcm_write
(
wwo
->
handle
,
wwo
->
lpPlayPtr
->
lpData
+
wwo
->
dwPartialOffset
,
toWrite
);
if
(
written
<=
0
)
{
ERR
(
"Wrote: %d bytes (%s)
\n
"
,
written
,
snd_strerror
(
errno
));
return
written
;
}
if
(
written
>=
dwLength
)
{
/* If we wrote all current wavehdr, skip to the next one */
wodPlayer_PlayPtrNext
(
wwo
);
}
else
{
/* Remove the amount written */
wwo
->
dwPartialOffset
+=
written
;
}
*
bytes
-=
written
;
wwo
->
dwWrittenTotal
+=
written
;
return
written
;
}
/**************************************************************************
* wodPlayer_NotifyCompletions [internal]
*
* Notifies and remove from queue all wavehdrs which have been played to
* the speaker (ie. they have cleared the ALSA buffer). If force is true,
* we notify all wavehdrs and remove them all from the queue even if they
* are unplayed or part of a loop.
*/
static
DWORD
wodPlayer_NotifyCompletions
(
WINE_WAVEOUT
*
wwo
,
BOOL
force
)
{
LPWAVEHDR
lpWaveHdr
;
/* Start from lpQueuePtr and keep notifying until:
* - we hit an unwritten wavehdr
* - we hit the beginning of a running loop
* - we hit a wavehdr which hasn't finished playing
*/
while
((
lpWaveHdr
=
wwo
->
lpQueuePtr
)
&&
(
force
||
(
lpWaveHdr
!=
wwo
->
lpPlayPtr
&&
lpWaveHdr
!=
wwo
->
lpLoopPtr
&&
lpWaveHdr
->
reserved
<=
wwo
->
dwPlayedTotal
)))
{
wwo
->
lpQueuePtr
=
lpWaveHdr
->
lpNext
;
lpWaveHdr
->
dwFlags
&=
~
WHDR_INQUEUE
;
lpWaveHdr
->
dwFlags
|=
WHDR_DONE
;
wodNotifyClient
(
wwo
,
WOM_DONE
,
(
DWORD
)
lpWaveHdr
,
0
);
}
return
(
lpWaveHdr
&&
lpWaveHdr
!=
wwo
->
lpPlayPtr
&&
lpWaveHdr
!=
wwo
->
lpLoopPtr
)
?
wodPlayer_NotifyWait
(
wwo
,
lpWaveHdr
)
:
INFINITE
;
}
/**************************************************************************
* wodPlayer_Reset [internal]
*
* wodPlayer helper. Resets current output stream.
*/
static
void
wodPlayer_Reset
(
WINE_WAVEOUT
*
wwo
,
BOOL
reset
)
{
wodUpdatePlayedTotal
(
wwo
,
NULL
);
/* updates current notify list */
wodPlayer_NotifyCompletions
(
wwo
,
FALSE
);
if
(
snd_pcm_playback_flush
(
wwo
->
handle
)
!=
0
)
{
FIXME
(
"flush: %s
\n
"
,
snd_strerror
(
errno
));
wwo
->
hThread
=
0
;
wwo
->
state
=
WINE_WS_STOPPED
;
ExitThread
(
-
1
);
}
if
(
reset
)
{
enum
win_wm_message
msg
;
DWORD
param
;
HANDLE
ev
;
/* remove any buffer */
wodPlayer_NotifyCompletions
(
wwo
,
TRUE
);
wwo
->
lpPlayPtr
=
wwo
->
lpQueuePtr
=
wwo
->
lpLoopPtr
=
NULL
;
wwo
->
state
=
WINE_WS_STOPPED
;
wwo
->
dwPlayedTotal
=
wwo
->
dwWrittenTotal
=
0
;
/* Clear partial wavehdr */
wwo
->
dwPartialOffset
=
0
;
/* remove any existing message in the ring */
EnterCriticalSection
(
&
wwo
->
msgRing
.
msg_crst
);
/* return all pending headers in queue */
while
(
ALSA_RetrieveRingMessage
(
&
wwo
->
msgRing
,
&
msg
,
&
param
,
&
ev
))
{
if
(
msg
!=
WINE_WM_HEADER
)
{
FIXME
(
"shouldn't have headers left
\n
"
);
SetEvent
(
ev
);
continue
;
}
((
LPWAVEHDR
)
param
)
->
dwFlags
&=
~
WHDR_INQUEUE
;
((
LPWAVEHDR
)
param
)
->
dwFlags
|=
WHDR_DONE
;
wodNotifyClient
(
wwo
,
WOM_DONE
,
param
,
0
);
}
ResetEvent
(
wwo
->
msgRing
.
msg_event
);
LeaveCriticalSection
(
&
wwo
->
msgRing
.
msg_crst
);
}
else
{
if
(
wwo
->
lpLoopPtr
)
{
/* complicated case, not handled yet (could imply modifying the loop counter */
FIXME
(
"Pausing while in loop isn't correctly handled yet, except strange results
\n
"
);
wwo
->
lpPlayPtr
=
wwo
->
lpLoopPtr
;
wwo
->
dwPartialOffset
=
0
;
wwo
->
dwWrittenTotal
=
wwo
->
dwPlayedTotal
;
/* this is wrong !!! */
}
else
{
LPWAVEHDR
ptr
;
DWORD
sz
=
wwo
->
dwPartialOffset
;
/* reset all the data as if we had written only up to lpPlayedTotal bytes */
/* compute the max size playable from lpQueuePtr */
for
(
ptr
=
wwo
->
lpQueuePtr
;
ptr
!=
wwo
->
lpPlayPtr
;
ptr
=
ptr
->
lpNext
)
{
sz
+=
ptr
->
dwBufferLength
;
}
/* because the reset lpPlayPtr will be lpQueuePtr */
if
(
wwo
->
dwWrittenTotal
>
wwo
->
dwPlayedTotal
+
sz
)
ERR
(
"grin
\n
"
);
wwo
->
dwPartialOffset
=
sz
-
(
wwo
->
dwWrittenTotal
-
wwo
->
dwPlayedTotal
);
wwo
->
dwWrittenTotal
=
wwo
->
dwPlayedTotal
;
wwo
->
lpPlayPtr
=
wwo
->
lpQueuePtr
;
}
wwo
->
state
=
WINE_WS_PAUSED
;
}
}
/**************************************************************************
* wodPlayer_ProcessMessages [internal]
*/
static
void
wodPlayer_ProcessMessages
(
WINE_WAVEOUT
*
wwo
)
{
LPWAVEHDR
lpWaveHdr
;
enum
win_wm_message
msg
;
DWORD
param
;
HANDLE
ev
;
while
(
ALSA_RetrieveRingMessage
(
&
wwo
->
msgRing
,
&
msg
,
&
param
,
&
ev
))
{
TRACE
(
"Received %s %lx
\n
"
,
wodPlayerCmdString
[
msg
-
WM_USER
-
1
],
param
);
switch
(
msg
)
{
case
WINE_WM_PAUSING
:
wodPlayer_Reset
(
wwo
,
FALSE
);
SetEvent
(
ev
);
break
;
case
WINE_WM_RESTARTING
:
if
(
wwo
->
state
==
WINE_WS_PAUSED
)
{
snd_pcm_playback_prepare
(
wwo
->
handle
);
wwo
->
state
=
WINE_WS_PLAYING
;
}
SetEvent
(
ev
);
break
;
case
WINE_WM_HEADER
:
lpWaveHdr
=
(
LPWAVEHDR
)
param
;
/* insert buffer at the end of queue */
{
LPWAVEHDR
*
wh
;
for
(
wh
=
&
(
wwo
->
lpQueuePtr
);
*
wh
;
wh
=
&
((
*
wh
)
->
lpNext
));
*
wh
=
lpWaveHdr
;
}
if
(
!
wwo
->
lpPlayPtr
)
wodPlayer_BeginWaveHdr
(
wwo
,
lpWaveHdr
);
if
(
wwo
->
state
==
WINE_WS_STOPPED
)
wwo
->
state
=
WINE_WS_PLAYING
;
break
;
case
WINE_WM_RESETTING
:
wodPlayer_Reset
(
wwo
,
TRUE
);
SetEvent
(
ev
);
break
;
case
WINE_WM_UPDATE
:
wodUpdatePlayedTotal
(
wwo
,
NULL
);
SetEvent
(
ev
);
break
;
case
WINE_WM_BREAKLOOP
:
if
(
wwo
->
state
==
WINE_WS_PLAYING
&&
wwo
->
lpLoopPtr
!=
NULL
)
{
/* ensure exit at end of current loop */
wwo
->
dwLoops
=
1
;
}
SetEvent
(
ev
);
break
;
case
WINE_WM_CLOSING
:
/* sanity check: this should not happen since the device must have been reset before */
if
(
wwo
->
lpQueuePtr
||
wwo
->
lpPlayPtr
)
ERR
(
"out of sync
\n
"
);
wwo
->
hThread
=
0
;
wwo
->
state
=
WINE_WS_CLOSED
;
SetEvent
(
ev
);
ExitThread
(
0
);
/* shouldn't go here */
default:
FIXME
(
"unknown message %d
\n
"
,
msg
);
break
;
}
}
}
/**************************************************************************
* wodPlayer_FeedDSP [internal]
* Feed as much sound data as we can into the DSP and return the number of
* milliseconds before it will be necessary to feed the DSP again.
*/
static
DWORD
wodPlayer_FeedDSP
(
WINE_WAVEOUT
*
wwo
)
{
snd_pcm_channel_status_t
status
;
DWORD
availInQ
;
wodUpdatePlayedTotal
(
wwo
,
&
status
);
availInQ
=
status
.
free
;
#if 0
TODO;
TRACE("fragments=%d/%d, fragsize=%d, bytes=%d\n",
dspspace.fragments, dspspace.fragstotal, dspspace.fragsize, dspspace.bytes);
#endif
/* input queue empty and output buffer with less than one fragment to play */
/* FIXME: we should be able to catch OVERRUN errors */
if
(
!
wwo
->
lpPlayPtr
&&
wwo
->
dwBufferSize
<
availInQ
+
2
*
wwo
->
dwFragmentSize
)
{
TRACE
(
"Run out of wavehdr:s... flushing
\n
"
);
snd_pcm_playback_drain
(
wwo
->
handle
);
wwo
->
dwPlayedTotal
=
wwo
->
dwWrittenTotal
;
return
INFINITE
;
}
/* no more room... no need to try to feed */
if
(
status
.
free
>
0
)
{
/* Feed from partial wavehdr */
if
(
wwo
->
lpPlayPtr
&&
wwo
->
dwPartialOffset
!=
0
)
{
wodPlayer_WriteMaxFrags
(
wwo
,
&
availInQ
);
}
/* Feed wavehdrs until we run out of wavehdrs or DSP space */
if
(
wwo
->
dwPartialOffset
==
0
)
{
while
(
wwo
->
lpPlayPtr
&&
availInQ
>
0
)
{
/* note the value that dwPlayedTotal will return when this wave finishes playing */
wwo
->
lpPlayPtr
->
reserved
=
wwo
->
dwWrittenTotal
+
wwo
->
lpPlayPtr
->
dwBufferLength
;
wodPlayer_WriteMaxFrags
(
wwo
,
&
availInQ
);
}
}
}
return
wodPlayer_DSPWait
(
wwo
);
}
/**************************************************************************
* wodPlayer [internal]
*/
static
DWORD
CALLBACK
wodPlayer
(
LPVOID
pmt
)
{
WORD
uDevID
=
(
DWORD
)
pmt
;
WINE_WAVEOUT
*
wwo
=
(
WINE_WAVEOUT
*
)
&
WOutDev
[
uDevID
];
DWORD
dwNextFeedTime
=
INFINITE
;
/* Time before DSP needs feeding */
DWORD
dwNextNotifyTime
=
INFINITE
;
/* Time before next wave completion */
DWORD
dwSleepTime
;
wwo
->
state
=
WINE_WS_STOPPED
;
SetEvent
(
wwo
->
hStartUpEvent
);
for
(;;)
{
/** Wait for the shortest time before an action is required. If there
* are no pending actions, wait forever for a command.
*/
dwSleepTime
=
min
(
dwNextFeedTime
,
dwNextNotifyTime
);
TRACE
(
"waiting %lums (%lu,%lu)
\n
"
,
dwSleepTime
,
dwNextFeedTime
,
dwNextNotifyTime
);
WaitForSingleObject
(
wwo
->
msgRing
.
msg_event
,
dwSleepTime
);
wodPlayer_ProcessMessages
(
wwo
);
if
(
wwo
->
state
==
WINE_WS_PLAYING
)
{
dwNextFeedTime
=
wodPlayer_FeedDSP
(
wwo
);
dwNextNotifyTime
=
wodPlayer_NotifyCompletions
(
wwo
,
FALSE
);
}
else
{
dwNextFeedTime
=
dwNextNotifyTime
=
INFINITE
;
}
}
}
/**************************************************************************
* wodGetDevCaps [internal]
*/
static
DWORD
wodGetDevCaps
(
WORD
wDevID
,
LPWAVEOUTCAPSW
lpCaps
,
DWORD
dwSize
)
{
TRACE
(
"(%u, %p, %lu);
\n
"
,
wDevID
,
lpCaps
,
dwSize
);
if
(
lpCaps
==
NULL
)
return
MMSYSERR_NOTENABLED
;
if
(
wDevID
>=
MAX_WAVEOUTDRV
)
{
TRACE
(
"MAX_WAVOUTDRV reached !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
memcpy
(
lpCaps
,
&
WOutDev
[
wDevID
].
caps
,
min
(
dwSize
,
sizeof
(
*
lpCaps
)));
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodOpen [internal]
*/
static
DWORD
wodOpen
(
WORD
wDevID
,
LPWAVEOPENDESC
lpDesc
,
DWORD
dwFlags
)
{
WINE_WAVEOUT
*
wwo
;
snd_pcm_channel_params_t
params
;
TRACE
(
"(%u, %p, %08lX);
\n
"
,
wDevID
,
lpDesc
,
dwFlags
);
if
(
lpDesc
==
NULL
)
{
WARN
(
"Invalid Parameter !
\n
"
);
return
MMSYSERR_INVALPARAM
;
}
if
(
wDevID
>=
MAX_WAVEOUTDRV
)
{
TRACE
(
"MAX_WAVOUTDRV reached !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
/* only PCM format is supported so far... */
if
(
lpDesc
->
lpFormat
->
wFormatTag
!=
WAVE_FORMAT_PCM
||
lpDesc
->
lpFormat
->
nChannels
==
0
||
lpDesc
->
lpFormat
->
nSamplesPerSec
==
0
)
{
WARN
(
"Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !
\n
"
,
lpDesc
->
lpFormat
->
wFormatTag
,
lpDesc
->
lpFormat
->
nChannels
,
lpDesc
->
lpFormat
->
nSamplesPerSec
);
return
WAVERR_BADFORMAT
;
}
if
(
dwFlags
&
WAVE_FORMAT_QUERY
)
{
TRACE
(
"Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !
\n
"
,
lpDesc
->
lpFormat
->
wFormatTag
,
lpDesc
->
lpFormat
->
nChannels
,
lpDesc
->
lpFormat
->
nSamplesPerSec
);
return
MMSYSERR_NOERROR
;
}
wwo
=
&
WOutDev
[
wDevID
];
if
((
dwFlags
&
WAVE_DIRECTSOUND
)
&&
!
(
wwo
->
caps
.
dwSupport
&
WAVECAPS_DIRECTSOUND
))
/* not supported, ignore it */
dwFlags
&=
~
WAVE_DIRECTSOUND
;
wwo
->
handle
=
0
;
if
(
snd_pcm_open
(
&
wwo
->
handle
,
wDevID
,
0
,
SND_PCM_OPEN_DUPLEX
|
SND_PCM_OPEN_NONBLOCK
))
{
ERR
(
"Error open: %s
\n
"
,
snd_strerror
(
errno
));
return
MMSYSERR_NOTENABLED
;
}
memset
(
&
params
,
0
,
sizeof
(
params
));
params
.
channel
=
SND_PCM_CHANNEL_PLAYBACK
;
params
.
start_mode
=
SND_PCM_START_DATA
;
params
.
stop_mode
=
SND_PCM_STOP_STOP
;
params
.
mode
=
SND_PCM_MODE_STREAM
;
params
.
buf
.
stream
.
queue_size
=
0x1000
;
params
.
buf
.
stream
.
fill
=
SND_PCM_FILL_SILENCE
;
params
.
buf
.
stream
.
max_fill
=
0x800
;
wwo
->
wFlags
=
HIWORD
(
dwFlags
&
CALLBACK_TYPEMASK
);
memcpy
(
&
wwo
->
waveDesc
,
lpDesc
,
sizeof
(
WAVEOPENDESC
));
memcpy
(
&
wwo
->
format
,
lpDesc
->
lpFormat
,
sizeof
(
PCMWAVEFORMAT
));
if
(
wwo
->
format
.
wBitsPerSample
==
0
)
{
WARN
(
"Resetting zeroed wBitsPerSample
\n
"
);
wwo
->
format
.
wBitsPerSample
=
8
*
(
wwo
->
format
.
wf
.
nAvgBytesPerSec
/
wwo
->
format
.
wf
.
nSamplesPerSec
)
/
wwo
->
format
.
wf
.
nChannels
;
}
params
.
format
.
interleave
=
1
;
params
.
format
.
format
=
(
wwo
->
format
.
wBitsPerSample
==
16
)
?
SND_PCM_SFMT_S16_LE
:
SND_PCM_SFMT_U8
;
params
.
format
.
rate
=
wwo
->
format
.
wf
.
nSamplesPerSec
;
params
.
format
.
voices
=
(
wwo
->
format
.
wf
.
nChannels
>
1
)
?
2
:
1
;
params
.
format
.
special
=
0
;
if
(
snd_pcm_channel_params
(
wwo
->
handle
,
&
params
))
{
ERR
(
"Can't set params: %s
\n
"
,
snd_strerror
(
errno
));
snd_pcm_close
(
wwo
->
handle
);
wwo
->
handle
=
NULL
;
return
MMSYSERR_INVALPARAM
;
}
#if 0
TODO;
if (params.format.rate != format != ((wwo->format.wBitsPerSample == 16) ? AFMT_S16_LE : AFMT_U8))
ERR("Can't set format to %d (%d)\n",
(wwo->format.wBitsPerSample == 16) ? AFMT_S16_LE : AFMT_U8, format);
if (dsp_stereo != (wwo->format.wf.nChannels > 1) ? 1 : 0)
ERR("Can't set stereo to %u (%d)\n",
(wwo->format.wf.nChannels > 1) ? 1 : 0, dsp_stereo);
if (!NEAR_MATCH(sample_rate, wwo->format.wf.nSamplesPerSec))
ERR("Can't set sample_rate to %lu (%d)\n",
wwo->format.wf.nSamplesPerSec, sample_rate);
#endif
snd_pcm_playback_prepare
(
wwo
->
handle
);
/* Remember fragsize and total buffer size for future use */
wwo
->
dwBufferSize
=
params
.
buf
.
stream
.
queue_size
;
/* FIXME: should get rid off fragment size */
wwo
->
dwFragmentSize
=
wwo
->
dwBufferSize
>>
4
;
/* why not */
wwo
->
dwPlayedTotal
=
0
;
wwo
->
dwWrittenTotal
=
0
;
wwo
->
lpQueuePtr
=
wwo
->
lpPlayPtr
=
wwo
->
lpLoopPtr
=
NULL
;
ALSA_InitRingMessage
(
&
wwo
->
msgRing
);
if
(
!
(
dwFlags
&
WAVE_DIRECTSOUND
))
{
wwo
->
hStartUpEvent
=
CreateEventW
(
NULL
,
FALSE
,
FALSE
,
NULL
);
wwo
->
hThread
=
CreateThread
(
NULL
,
0
,
wodPlayer
,
(
LPVOID
)(
DWORD
)
wDevID
,
0
,
&
(
wwo
->
dwThreadID
));
WaitForSingleObject
(
wwo
->
hStartUpEvent
,
INFINITE
);
CloseHandle
(
wwo
->
hStartUpEvent
);
}
else
{
wwo
->
hThread
=
INVALID_HANDLE_VALUE
;
wwo
->
dwThreadID
=
0
;
}
wwo
->
hStartUpEvent
=
INVALID_HANDLE_VALUE
;
TRACE
(
"handle=%08lx fragmentSize=%ld
\n
"
,
(
DWORD
)
wwo
->
handle
,
wwo
->
dwFragmentSize
);
if
(
wwo
->
dwFragmentSize
%
wwo
->
format
.
wf
.
nBlockAlign
)
ERR
(
"Fragment doesn't contain an integral number of data blocks
\n
"
);
TRACE
(
"wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!
\n
"
,
wwo
->
format
.
wBitsPerSample
,
wwo
->
format
.
wf
.
nAvgBytesPerSec
,
wwo
->
format
.
wf
.
nSamplesPerSec
,
wwo
->
format
.
wf
.
nChannels
,
wwo
->
format
.
wf
.
nBlockAlign
);
return
wodNotifyClient
(
wwo
,
WOM_OPEN
,
0L
,
0L
);
}
/**************************************************************************
* wodClose [internal]
*/
static
DWORD
wodClose
(
WORD
wDevID
)
{
DWORD
ret
=
MMSYSERR_NOERROR
;
WINE_WAVEOUT
*
wwo
;
TRACE
(
"(%u);
\n
"
,
wDevID
);
if
(
wDevID
>=
MAX_WAVEOUTDRV
||
WOutDev
[
wDevID
].
handle
==
NULL
)
{
WARN
(
"bad device ID !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
wwo
=
&
WOutDev
[
wDevID
];
if
(
wwo
->
lpQueuePtr
)
{
WARN
(
"buffers still playing !
\n
"
);
ret
=
WAVERR_STILLPLAYING
;
}
else
{
if
(
wwo
->
hThread
!=
INVALID_HANDLE_VALUE
)
{
ALSA_AddRingMessage
(
&
wwo
->
msgRing
,
WINE_WM_CLOSING
,
0
,
TRUE
);
}
if
(
wwo
->
mmap_buffer
)
{
snd_pcm_munmap
(
wwo
->
handle
,
SND_PCM_CHANNEL_PLAYBACK
);
wwo
->
mmap_buffer
=
wwo
->
mmap_control
=
NULL
;
}
ALSA_DestroyRingMessage
(
&
wwo
->
msgRing
);
snd_pcm_close
(
wwo
->
handle
);
wwo
->
handle
=
NULL
;
wwo
->
dwFragmentSize
=
0
;
ret
=
wodNotifyClient
(
wwo
,
WOM_CLOSE
,
0L
,
0L
);
}
return
ret
;
}
/**************************************************************************
* wodWrite [internal]
*
*/
static
DWORD
wodWrite
(
WORD
wDevID
,
LPWAVEHDR
lpWaveHdr
,
DWORD
dwSize
)
{
TRACE
(
"(%u, %p, %08lX);
\n
"
,
wDevID
,
lpWaveHdr
,
dwSize
);
/* first, do the sanity checks... */
if
(
wDevID
>=
MAX_WAVEOUTDRV
||
WOutDev
[
wDevID
].
handle
==
NULL
)
{
WARN
(
"bad dev ID !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
if
(
lpWaveHdr
->
lpData
==
NULL
||
!
(
lpWaveHdr
->
dwFlags
&
WHDR_PREPARED
))
return
WAVERR_UNPREPARED
;
if
(
lpWaveHdr
->
dwFlags
&
WHDR_INQUEUE
)
return
WAVERR_STILLPLAYING
;
lpWaveHdr
->
dwFlags
&=
~
WHDR_DONE
;
lpWaveHdr
->
dwFlags
|=
WHDR_INQUEUE
;
lpWaveHdr
->
lpNext
=
0
;
ALSA_AddRingMessage
(
&
WOutDev
[
wDevID
].
msgRing
,
WINE_WM_HEADER
,
(
DWORD
)
lpWaveHdr
,
FALSE
);
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodPrepare [internal]
*/
static
DWORD
wodPrepare
(
WORD
wDevID
,
LPWAVEHDR
lpWaveHdr
,
DWORD
dwSize
)
{
TRACE
(
"(%u, %p, %08lX);
\n
"
,
wDevID
,
lpWaveHdr
,
dwSize
);
if
(
wDevID
>=
MAX_WAVEOUTDRV
)
{
WARN
(
"bad device ID !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
if
(
lpWaveHdr
->
dwFlags
&
WHDR_INQUEUE
)
return
WAVERR_STILLPLAYING
;
lpWaveHdr
->
dwFlags
|=
WHDR_PREPARED
;
lpWaveHdr
->
dwFlags
&=
~
WHDR_DONE
;
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodUnprepare [internal]
*/
static
DWORD
wodUnprepare
(
WORD
wDevID
,
LPWAVEHDR
lpWaveHdr
,
DWORD
dwSize
)
{
TRACE
(
"(%u, %p, %08lX);
\n
"
,
wDevID
,
lpWaveHdr
,
dwSize
);
if
(
wDevID
>=
MAX_WAVEOUTDRV
)
{
WARN
(
"bad device ID !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
if
(
lpWaveHdr
->
dwFlags
&
WHDR_INQUEUE
)
return
WAVERR_STILLPLAYING
;
lpWaveHdr
->
dwFlags
&=
~
WHDR_PREPARED
;
lpWaveHdr
->
dwFlags
|=
WHDR_DONE
;
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodPause [internal]
*/
static
DWORD
wodPause
(
WORD
wDevID
)
{
TRACE
(
"(%u);!
\n
"
,
wDevID
);
if
(
wDevID
>=
MAX_WAVEOUTDRV
||
WOutDev
[
wDevID
].
handle
==
NULL
)
{
WARN
(
"bad device ID !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
ALSA_AddRingMessage
(
&
WOutDev
[
wDevID
].
msgRing
,
WINE_WM_PAUSING
,
0
,
TRUE
);
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodRestart [internal]
*/
static
DWORD
wodRestart
(
WORD
wDevID
)
{
TRACE
(
"(%u);
\n
"
,
wDevID
);
if
(
wDevID
>=
MAX_WAVEOUTDRV
||
WOutDev
[
wDevID
].
handle
==
NULL
)
{
WARN
(
"bad device ID !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
if
(
WOutDev
[
wDevID
].
state
==
WINE_WS_PAUSED
)
{
ALSA_AddRingMessage
(
&
WOutDev
[
wDevID
].
msgRing
,
WINE_WM_RESTARTING
,
0
,
TRUE
);
}
/* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
/* FIXME: Myst crashes with this ... hmm -MM
return wodNotifyClient(wwo, WOM_DONE, 0L, 0L);
*/
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodReset [internal]
*/
static
DWORD
wodReset
(
WORD
wDevID
)
{
TRACE
(
"(%u);
\n
"
,
wDevID
);
if
(
wDevID
>=
MAX_WAVEOUTDRV
||
WOutDev
[
wDevID
].
handle
==
NULL
)
{
WARN
(
"bad device ID !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
ALSA_AddRingMessage
(
&
WOutDev
[
wDevID
].
msgRing
,
WINE_WM_RESETTING
,
0
,
TRUE
);
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodGetPosition [internal]
*/
static
DWORD
wodGetPosition
(
WORD
wDevID
,
LPMMTIME
lpTime
,
DWORD
uSize
)
{
int
time
;
DWORD
val
;
WINE_WAVEOUT
*
wwo
;
TRACE
(
"(%u, %p, %lu);
\n
"
,
wDevID
,
lpTime
,
uSize
);
if
(
wDevID
>=
MAX_WAVEOUTDRV
||
WOutDev
[
wDevID
].
handle
==
NULL
)
{
WARN
(
"bad device ID !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
if
(
lpTime
==
NULL
)
return
MMSYSERR_INVALPARAM
;
wwo
=
&
WOutDev
[
wDevID
];
ALSA_AddRingMessage
(
&
wwo
->
msgRing
,
WINE_WM_UPDATE
,
0
,
TRUE
);
val
=
wwo
->
dwPlayedTotal
;
TRACE
(
"wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu
\n
"
,
lpTime
->
wType
,
wwo
->
format
.
wBitsPerSample
,
wwo
->
format
.
wf
.
nSamplesPerSec
,
wwo
->
format
.
wf
.
nChannels
,
wwo
->
format
.
wf
.
nAvgBytesPerSec
);
TRACE
(
"dwPlayedTotal=%lu
\n
"
,
val
);
switch
(
lpTime
->
wType
)
{
case
TIME_BYTES
:
lpTime
->
u
.
cb
=
val
;
TRACE
(
"TIME_BYTES=%lu
\n
"
,
lpTime
->
u
.
cb
);
break
;
case
TIME_SAMPLES
:
lpTime
->
u
.
sample
=
val
*
8
/
wwo
->
format
.
wBitsPerSample
/
wwo
->
format
.
wf
.
nChannels
;
TRACE
(
"TIME_SAMPLES=%lu
\n
"
,
lpTime
->
u
.
sample
);
break
;
case
TIME_SMPTE
:
time
=
val
/
(
wwo
->
format
.
wf
.
nAvgBytesPerSec
/
1000
);
lpTime
->
u
.
smpte
.
hour
=
time
/
108000
;
time
-=
lpTime
->
u
.
smpte
.
hour
*
108000
;
lpTime
->
u
.
smpte
.
min
=
time
/
1800
;
time
-=
lpTime
->
u
.
smpte
.
min
*
1800
;
lpTime
->
u
.
smpte
.
sec
=
time
/
30
;
time
-=
lpTime
->
u
.
smpte
.
sec
*
30
;
lpTime
->
u
.
smpte
.
frame
=
time
;
lpTime
->
u
.
smpte
.
fps
=
30
;
TRACE
(
"TIME_SMPTE=%02u:%02u:%02u:%02u
\n
"
,
lpTime
->
u
.
smpte
.
hour
,
lpTime
->
u
.
smpte
.
min
,
lpTime
->
u
.
smpte
.
sec
,
lpTime
->
u
.
smpte
.
frame
);
break
;
default:
FIXME
(
"Format %d not supported ! use TIME_MS !
\n
"
,
lpTime
->
wType
);
lpTime
->
wType
=
TIME_MS
;
case
TIME_MS
:
lpTime
->
u
.
ms
=
val
/
(
wwo
->
format
.
wf
.
nAvgBytesPerSec
/
1000
);
TRACE
(
"TIME_MS=%lu
\n
"
,
lpTime
->
u
.
ms
);
break
;
}
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodBreakLoop [internal]
*/
static
DWORD
wodBreakLoop
(
WORD
wDevID
)
{
TRACE
(
"(%u);
\n
"
,
wDevID
);
if
(
wDevID
>=
MAX_WAVEOUTDRV
||
WOutDev
[
wDevID
].
handle
==
NULL
)
{
WARN
(
"bad device ID !
\n
"
);
return
MMSYSERR_BADDEVICEID
;
}
ALSA_AddRingMessage
(
&
WOutDev
[
wDevID
].
msgRing
,
WINE_WM_BREAKLOOP
,
0
,
TRUE
);
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodGetVolume [internal]
*/
static
DWORD
wodGetVolume
(
WORD
wDevID
,
LPDWORD
lpdwVol
)
{
#if 0
int mixer;
#endif
int
volume
;
DWORD
left
,
right
;
TRACE
(
"(%u, %p);
\n
"
,
wDevID
,
lpdwVol
);
if
(
lpdwVol
==
NULL
)
return
MMSYSERR_NOTENABLED
;
#if 0
TODO;
if ((mixer = open(MIXER_DEV, O_RDONLY|O_NDELAY)) < 0) {
WARN("mixer device not available !\n");
return MMSYSERR_NOTENABLED;
}
if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) {
WARN("unable to read mixer !\n");
return MMSYSERR_NOTENABLED;
}
close(mixer);
#else
volume
=
0x2020
;
#endif
left
=
LOBYTE
(
volume
);
right
=
HIBYTE
(
volume
);
TRACE
(
"left=%ld right=%ld !
\n
"
,
left
,
right
);
*
lpdwVol
=
((
left
*
0xFFFFl
)
/
100
)
+
(((
right
*
0xFFFFl
)
/
100
)
<<
16
);
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodSetVolume [internal]
*/
static
DWORD
wodSetVolume
(
WORD
wDevID
,
DWORD
dwParam
)
{
#if 0
int mixer;
#endif
int
volume
;
DWORD
left
,
right
;
TRACE
(
"(%u, %08lX);
\n
"
,
wDevID
,
dwParam
);
left
=
(
LOWORD
(
dwParam
)
*
100
)
/
0xFFFFl
;
right
=
(
HIWORD
(
dwParam
)
*
100
)
/
0xFFFFl
;
volume
=
left
+
(
right
<<
8
);
#if 0
TODO;
if ((mixer = open(MIXER_DEV, O_WRONLY|O_NDELAY)) < 0) {
WARN("mixer device not available !\n");
return MMSYSERR_NOTENABLED;
}
if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
WARN("unable to set mixer !\n");
return MMSYSERR_NOTENABLED;
} else {
TRACE("volume=%04x\n", (unsigned)volume);
}
close(mixer);
#endif
return
MMSYSERR_NOERROR
;
}
/**************************************************************************
* wodGetNumDevs [internal]
*/
static
DWORD
wodGetNumDevs
(
void
)
{
return
ALSA_WodNumDevs
;
}
/**************************************************************************
* wodMessage (WINEALSA.@)
*/
DWORD
WINAPI
ALSA_wodMessage
(
UINT
wDevID
,
UINT
wMsg
,
DWORD
dwUser
,
DWORD
dwParam1
,
DWORD
dwParam2
)
{
TRACE
(
"(%u, %04X, %08lX, %08lX, %08lX);
\n
"
,
wDevID
,
wMsg
,
dwUser
,
dwParam1
,
dwParam2
);
switch
(
wMsg
)
{
case
DRVM_INIT
:
case
DRVM_EXIT
:
case
DRVM_ENABLE
:
case
DRVM_DISABLE
:
/* FIXME: Pretend this is supported */
return
0
;
case
WODM_OPEN
:
return
wodOpen
(
wDevID
,
(
LPWAVEOPENDESC
)
dwParam1
,
dwParam2
);
case
WODM_CLOSE
:
return
wodClose
(
wDevID
);
case
WODM_WRITE
:
return
wodWrite
(
wDevID
,
(
LPWAVEHDR
)
dwParam1
,
dwParam2
);
case
WODM_PAUSE
:
return
wodPause
(
wDevID
);
case
WODM_GETPOS
:
return
wodGetPosition
(
wDevID
,
(
LPMMTIME
)
dwParam1
,
dwParam2
);
case
WODM_BREAKLOOP
:
return
wodBreakLoop
(
wDevID
);
case
WODM_PREPARE
:
return
wodPrepare
(
wDevID
,
(
LPWAVEHDR
)
dwParam1
,
dwParam2
);
case
WODM_UNPREPARE
:
return
wodUnprepare
(
wDevID
,
(
LPWAVEHDR
)
dwParam1
,
dwParam2
);
case
WODM_GETDEVCAPS
:
return
wodGetDevCaps
(
wDevID
,
(
LPWAVEOUTCAPSW
)
dwParam1
,
dwParam2
);
case
WODM_GETNUMDEVS
:
return
wodGetNumDevs
();
case
WODM_GETPITCH
:
return
MMSYSERR_NOTSUPPORTED
;
case
WODM_SETPITCH
:
return
MMSYSERR_NOTSUPPORTED
;
case
WODM_GETPLAYBACKRATE
:
return
MMSYSERR_NOTSUPPORTED
;
case
WODM_SETPLAYBACKRATE
:
return
MMSYSERR_NOTSUPPORTED
;
case
WODM_GETVOLUME
:
return
wodGetVolume
(
wDevID
,
(
LPDWORD
)
dwParam1
);
case
WODM_SETVOLUME
:
return
wodSetVolume
(
wDevID
,
dwParam1
);
case
WODM_RESTART
:
return
wodRestart
(
wDevID
);
case
WODM_RESET
:
return
wodReset
(
wDevID
);
case
DRV_QUERYDSOUNDIFACE
:
return
wodDsCreate
(
wDevID
,
(
PIDSDRIVER
*
)
dwParam1
);
default:
FIXME
(
"unknown message %d!
\n
"
,
wMsg
);
}
return
MMSYSERR_NOTSUPPORTED
;
}
/*======================================================================*
* Low level DSOUND implementation *
*======================================================================*/
typedef
struct
IDsDriverImpl
IDsDriverImpl
;
typedef
struct
IDsDriverBufferImpl
IDsDriverBufferImpl
;
struct
IDsDriverImpl
{
/* IUnknown fields */
IDsDriverVtbl
*
lpVtbl
;
DWORD
ref
;
/* IDsDriverImpl fields */
UINT
wDevID
;
IDsDriverBufferImpl
*
primary
;
};
struct
IDsDriverBufferImpl
{
/* IUnknown fields */
IDsDriverBufferVtbl
*
lpVtbl
;
DWORD
ref
;
/* IDsDriverBufferImpl fields */
IDsDriverImpl
*
drv
;
DWORD
buflen
;
};
static
HRESULT
DSDB_UnmapPrimary
(
IDsDriverBufferImpl
*
dsdb
)
{
WINE_WAVEOUT
*
wwo
=
&
(
WOutDev
[
dsdb
->
drv
->
wDevID
]);
if
(
wwo
->
mmap_buffer
)
{
if
(
snd_pcm_munmap
(
wwo
->
handle
,
SND_PCM_CHANNEL_PLAYBACK
)
<
0
)
{
ERR
(
"(%p): Could not unmap sound device (errno=%d)
\n
"
,
dsdb
,
errno
);
return
DSERR_GENERIC
;
}
wwo
->
mmap_buffer
=
wwo
->
mmap_control
=
NULL
;
TRACE
(
"(%p): sound device unmapped
\n
"
,
dsdb
);
}
return
DS_OK
;
}
static
HRESULT
WINAPI
IDsDriverBufferImpl_QueryInterface
(
PIDSDRIVERBUFFER
iface
,
REFIID
riid
,
LPVOID
*
ppobj
)
{
/* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
FIXME
(
"(): stub!
\n
"
);
return
DSERR_UNSUPPORTED
;
}
static
ULONG
WINAPI
IDsDriverBufferImpl_AddRef
(
PIDSDRIVERBUFFER
iface
)
{
IDsDriverBufferImpl
*
This
=
(
IDsDriverBufferImpl
*
)
iface
;
This
->
ref
++
;
return
This
->
ref
;
}
static
ULONG
WINAPI
IDsDriverBufferImpl_Release
(
PIDSDRIVERBUFFER
iface
)
{
IDsDriverBufferImpl
*
This
=
(
IDsDriverBufferImpl
*
)
iface
;
if
(
--
This
->
ref
)
return
This
->
ref
;
if
(
This
==
This
->
drv
->
primary
)
This
->
drv
->
primary
=
NULL
;
DSDB_UnmapPrimary
(
This
);
HeapFree
(
GetProcessHeap
(),
0
,
This
);
return
0
;
}
static
HRESULT
WINAPI
IDsDriverBufferImpl_Lock
(
PIDSDRIVERBUFFER
iface
,
LPVOID
*
ppvAudio1
,
LPDWORD
pdwLen1
,
LPVOID
*
ppvAudio2
,
LPDWORD
pdwLen2
,
DWORD
dwWritePosition
,
DWORD
dwWriteLen
,
DWORD
dwFlags
)
{
/* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
/* FIXME: we need to implement it */
TRACE
(
"(%p)
\n
"
,
iface
);
return
DSERR_UNSUPPORTED
;
}
static
HRESULT
WINAPI
IDsDriverBufferImpl_Unlock
(
PIDSDRIVERBUFFER
iface
,
LPVOID
pvAudio1
,
DWORD
dwLen1
,
LPVOID
pvAudio2
,
DWORD
dwLen2
)
{
/* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
TRACE
(
"(%p)
\n
"
,
iface
);
return
DSERR_UNSUPPORTED
;
}
static
HRESULT
WINAPI
IDsDriverBufferImpl_SetFormat
(
PIDSDRIVERBUFFER
iface
,
LPWAVEFORMATEX
pwfx
)
{
/* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
TRACE
(
"(%p,%p)
\n
"
,
iface
,
pwfx
);
/* On our request (GetDriverDesc flags), DirectSound has by now used
* waveOutClose/waveOutOpen to set the format...
* unfortunately, this means our mmap() is now gone...
* so we need to somehow signal to our DirectSound implementation
* that it should completely recreate this HW buffer...
* this unexpected error code should do the trick... */
return
DSERR_BUFFERLOST
;
}
static
HRESULT
WINAPI
IDsDriverBufferImpl_SetFrequency
(
PIDSDRIVERBUFFER
iface
,
DWORD
dwFreq
)
{
/* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
TRACE
(
"(%p,%ld): stub
\n
"
,
iface
,
dwFreq
);
return
DSERR_UNSUPPORTED
;
}
static
HRESULT
WINAPI
IDsDriverBufferImpl_SetVolumePan
(
PIDSDRIVERBUFFER
iface
,
PDSVOLUMEPAN
pVolPan
)
{
/* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
FIXME
(
"(%p,%p): stub!
\n
"
,
iface
,
pVolPan
);
return
DSERR_UNSUPPORTED
;
}
static
HRESULT
WINAPI
IDsDriverBufferImpl_SetPosition
(
PIDSDRIVERBUFFER
iface
,
DWORD
dwNewPos
)
{
/* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
TRACE
(
"(%p,%ld): stub
\n
"
,
iface
,
dwNewPos
);
return
DSERR_UNSUPPORTED
;
}
static
HRESULT
WINAPI
IDsDriverBufferImpl_GetPosition
(
PIDSDRIVERBUFFER
iface
,
LPDWORD
lpdwPlay
,
LPDWORD
lpdwWrite
)
{
#if 0
IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
TODO;
count_info info;
DWORD ptr;
TRACE("(%p)\n",iface);
if (WOutDev[This->drv->wDevID].handle == NULL) {
ERR("device not open, but accessing?\n");
return DSERR_UNINITIALIZED;
}
if (ioctl(WOutDev[This->drv->wDevID].unixdev, SNDCTL_DSP_GETOPTR, &info) < 0) {
ERR("ioctl failed (%d)\n", errno);
return DSERR_GENERIC;
}
ptr = info.ptr & ~3; /* align the pointer, just in case */
if (lpdwPlay) *lpdwPlay = ptr;
if (lpdwWrite) {
/* add some safety margin (not strictly necessary, but...) */
if (WOutDev[This->drv->wDevID].caps.dwSupport & WAVECAPS_SAMPLEACCURATE)
*lpdwWrite = ptr + 32;
else
*lpdwWrite = ptr + WOutDev[This->drv->wDevID].dwFragmentSize;
while (*lpdwWrite > This->buflen)
*lpdwWrite -= This->buflen;
}
#endif
TRACE
(
"playpos=%ld, writepos=%ld
\n
"
,
lpdwPlay
?*
lpdwPlay
:
0
,
lpdwWrite
?*
lpdwWrite
:
0
);
return
DS_OK
;
}
static
HRESULT
WINAPI
IDsDriverBufferImpl_Play
(
PIDSDRIVERBUFFER
iface
,
DWORD
dwRes1
,
DWORD
dwRes2
,
DWORD
dwFlags
)
{
IDsDriverBufferImpl
*
This
=
(
IDsDriverBufferImpl
*
)
iface
;
TRACE
(
"(%p,%lx,%lx,%lx)
\n
"
,
iface
,
dwRes1
,
dwRes2
,
dwFlags
);
/* FIXME: error handling */
snd_pcm_playback_go
(
WOutDev
[
This
->
drv
->
wDevID
].
handle
);
return
DS_OK
;
}
static
HRESULT
WINAPI
IDsDriverBufferImpl_Stop
(
PIDSDRIVERBUFFER
iface
)
{
IDsDriverBufferImpl
*
This
=
(
IDsDriverBufferImpl
*
)
iface
;
TRACE
(
"(%p)
\n
"
,
iface
);
/* no more playing */
/* FIXME: error handling */
snd_pcm_playback_drain
(
WOutDev
[
This
->
drv
->
wDevID
].
handle
);
/* Most ALSA drivers just can't stop the playback without closing the device...
* so we need to somehow signal to our DirectSound implementation
* that it should completely recreate this HW buffer...
* this unexpected error code should do the trick... */
return
DSERR_BUFFERLOST
;
}
static
IDsDriverBufferVtbl
dsdbvt
=
{
IDsDriverBufferImpl_QueryInterface
,
IDsDriverBufferImpl_AddRef
,
IDsDriverBufferImpl_Release
,
IDsDriverBufferImpl_Lock
,
IDsDriverBufferImpl_Unlock
,
IDsDriverBufferImpl_SetFormat
,
IDsDriverBufferImpl_SetFrequency
,
IDsDriverBufferImpl_SetVolumePan
,
IDsDriverBufferImpl_SetPosition
,
IDsDriverBufferImpl_GetPosition
,
IDsDriverBufferImpl_Play
,
IDsDriverBufferImpl_Stop
};
static
HRESULT
WINAPI
IDsDriverImpl_QueryInterface
(
PIDSDRIVER
iface
,
REFIID
riid
,
LPVOID
*
ppobj
)
{
/* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
FIXME
(
"(%p): stub!
\n
"
,
iface
);
return
DSERR_UNSUPPORTED
;
}
static
ULONG
WINAPI
IDsDriverImpl_AddRef
(
PIDSDRIVER
iface
)
{
IDsDriverImpl
*
This
=
(
IDsDriverImpl
*
)
iface
;
This
->
ref
++
;
return
This
->
ref
;
}
static
ULONG
WINAPI
IDsDriverImpl_Release
(
PIDSDRIVER
iface
)
{
IDsDriverImpl
*
This
=
(
IDsDriverImpl
*
)
iface
;
if
(
--
This
->
ref
)
return
This
->
ref
;
HeapFree
(
GetProcessHeap
(),
0
,
This
);
return
0
;
}
static
HRESULT
WINAPI
IDsDriverImpl_GetDriverDesc
(
PIDSDRIVER
iface
,
PDSDRIVERDESC
pDesc
)
{
IDsDriverImpl
*
This
=
(
IDsDriverImpl
*
)
iface
;
TRACE
(
"(%p,%p)
\n
"
,
iface
,
pDesc
);
pDesc
->
dwFlags
=
DSDDESC_DOMMSYSTEMOPEN
|
DSDDESC_DOMMSYSTEMSETFORMAT
|
DSDDESC_USESYSTEMMEMORY
;
strcpy
(
pDesc
->
szDesc
,
"WineALSA DirectSound Driver"
);
strcpy
(
pDesc
->
szDrvName
,
"winealsa.drv"
);
pDesc
->
dnDevNode
=
WOutDev
[
This
->
wDevID
].
waveDesc
.
dnDevNode
;
pDesc
->
wVxdId
=
0
;
pDesc
->
wReserved
=
0
;
pDesc
->
ulDeviceNum
=
This
->
wDevID
;
pDesc
->
dwHeapType
=
DSDHEAP_NOHEAP
;
pDesc
->
pvDirectDrawHeap
=
NULL
;
pDesc
->
dwMemStartAddress
=
0
;
pDesc
->
dwMemEndAddress
=
0
;
pDesc
->
dwMemAllocExtra
=
0
;
pDesc
->
pvReserved1
=
NULL
;
pDesc
->
pvReserved2
=
NULL
;
return
DS_OK
;
}
static
HRESULT
WINAPI
IDsDriverImpl_Open
(
PIDSDRIVER
iface
)
{
IDsDriverImpl
*
This
=
(
IDsDriverImpl
*
)
iface
;
TRACE
(
"(%p)
\n
"
,
iface
);
/* FIXME: error handling */
snd_pcm_channel_prepare
(
WOutDev
[
This
->
wDevID
].
handle
,
SND_PCM_CHANNEL_PLAYBACK
);
return
DS_OK
;
}
static
HRESULT
WINAPI
IDsDriverImpl_Close
(
PIDSDRIVER
iface
)
{
IDsDriverImpl
*
This
=
(
IDsDriverImpl
*
)
iface
;
TRACE
(
"(%p)
\n
"
,
iface
);
if
(
This
->
primary
)
{
ERR
(
"problem with DirectSound: primary not released
\n
"
);
return
DSERR_GENERIC
;
}
return
DS_OK
;
}
static
HRESULT
WINAPI
IDsDriverImpl_GetCaps
(
PIDSDRIVER
iface
,
PDSDRIVERCAPS
pCaps
)
{
/* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
TRACE
(
"(%p,%p)
\n
"
,
iface
,
pCaps
);
memset
(
pCaps
,
0
,
sizeof
(
*
pCaps
));
/* FIXME: need to check actual capabilities */
pCaps
->
dwFlags
=
DSCAPS_PRIMARYMONO
|
DSCAPS_PRIMARYSTEREO
|
DSCAPS_PRIMARY8BIT
|
DSCAPS_PRIMARY16BIT
;
pCaps
->
dwPrimaryBuffers
=
1
;
/* the other fields only apply to secondary buffers, which we don't support
* (unless we want to mess with wavetable synthesizers and MIDI) */
return
DS_OK
;
}
static
HRESULT
WINAPI
IDsDriverImpl_CreateSoundBuffer
(
PIDSDRIVER
iface
,
LPWAVEFORMATEX
pwfx
,
DWORD
dwFlags
,
DWORD
dwCardAddress
,
LPDWORD
pdwcbBufferSize
,
LPBYTE
*
ppbBuffer
,
LPVOID
*
ppvObj
)
{
IDsDriverImpl
*
This
=
(
IDsDriverImpl
*
)
iface
;
IDsDriverBufferImpl
**
ippdsdb
=
(
IDsDriverBufferImpl
**
)
ppvObj
;
WINE_WAVEOUT
*
wwo
=
&
(
WOutDev
[
This
->
wDevID
]);
struct
snd_pcm_channel_setup
setup
;
TRACE
(
"(%p,%p,%lx,%lx)
\n
"
,
iface
,
pwfx
,
dwFlags
,
dwCardAddress
);
/* we only support primary buffers */
if
(
!
(
dwFlags
&
DSBCAPS_PRIMARYBUFFER
))
return
DSERR_UNSUPPORTED
;
if
(
This
->
primary
)
return
DSERR_ALLOCATED
;
if
(
dwFlags
&
(
DSBCAPS_CTRLFREQUENCY
|
DSBCAPS_CTRLPAN
))
return
DSERR_CONTROLUNAVAIL
;
*
ippdsdb
=
(
IDsDriverBufferImpl
*
)
HeapAlloc
(
GetProcessHeap
(),
0
,
sizeof
(
IDsDriverBufferImpl
));
if
(
*
ippdsdb
==
NULL
)
return
DSERR_OUTOFMEMORY
;
(
*
ippdsdb
)
->
lpVtbl
=
&
dsdbvt
;
(
*
ippdsdb
)
->
ref
=
1
;
(
*
ippdsdb
)
->
drv
=
This
;
if
(
!
wwo
->
mmap_buffer
)
{
if
(
snd_pcm_mmap
(
wwo
->
handle
,
SND_PCM_CHANNEL_PLAYBACK
,
&
wwo
->
mmap_control
,
&
wwo
->
mmap_buffer
))
{
ERR
(
"(%p): Could not map sound device for direct access (%s)
\n
"
,
*
ippdsdb
,
snd_strerror
(
errno
));
return
DSERR_GENERIC
;
}
setup
.
mode
=
SND_PCM_MODE_BLOCK
;
setup
.
channel
=
SND_PCM_CHANNEL_PLAYBACK
;
if
(
snd_pcm_channel_setup
(
wwo
->
handle
,
&
setup
)
<
0
)
{
ERR
(
"Unable to obtain setup
\n
"
);
/* FIXME: resource cleanup */
return
DSERR_GENERIC
;
}
wwo
->
mmap_block_size
=
setup
.
buf
.
block
.
frag_size
;
wwo
->
mmap_block_number
=
setup
.
buf
.
block
.
frags
;
TRACE
(
"(%p): sound device has been mapped for direct access at %p, size=%d
\n
"
,
*
ippdsdb
,
wwo
->
mmap_buffer
,
setup
.
buf
.
block
.
frags
*
setup
.
buf
.
block
.
frag_size
);
#if 0
/* for some reason, es1371 and sblive! sometimes have junk in here.
* clear it, or we get junk noise */
/* some libc implementations are buggy: their memset reads from the buffer...
* to work around it, we have to zero the block by hand. We don't do the expected:
* memset(wwo->mapping, 0, wwo->maplen);
*/
{
char* p1 = wwo->mapping;
unsigned len = wwo->maplen;
if (len >= 16) /* so we can have at least a 4 long area to store... */
{
/* the mmap:ed value is (at least) dword aligned
* so, start filling the complete unsigned long:s
*/
int b = len >> 2;
unsigned long* p4 = (unsigned long*)p1;
while (b--) *p4++ = 0;
/* prepare for filling the rest */
len &= 3;
p1 = (unsigned char*)p4;
}
/* in all cases, fill the remaining bytes */
while (len-- != 0) *p1++ = 0;
}
#endif
}
/* primary buffer is ready to go */
*
pdwcbBufferSize
=
wwo
->
mmap_block_size
*
wwo
->
mmap_block_number
;
*
ppbBuffer
=
wwo
->
mmap_buffer
;
This
->
primary
=
*
ippdsdb
;
return
DS_OK
;
}
static
HRESULT
WINAPI
IDsDriverImpl_DuplicateSoundBuffer
(
PIDSDRIVER
iface
,
PIDSDRIVERBUFFER
pBuffer
,
LPVOID
*
ppvObj
)
{
/* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
TRACE
(
"(%p,%p): stub
\n
"
,
iface
,
pBuffer
);
return
DSERR_INVALIDCALL
;
}
static
IDsDriverVtbl
dsdvt
=
{
IDsDriverImpl_QueryInterface
,
IDsDriverImpl_AddRef
,
IDsDriverImpl_Release
,
IDsDriverImpl_GetDriverDesc
,
IDsDriverImpl_Open
,
IDsDriverImpl_Close
,
IDsDriverImpl_GetCaps
,
IDsDriverImpl_CreateSoundBuffer
,
IDsDriverImpl_DuplicateSoundBuffer
};
static
DWORD
wodDsCreate
(
UINT
wDevID
,
PIDSDRIVER
*
drv
)
{
IDsDriverImpl
**
idrv
=
(
IDsDriverImpl
**
)
drv
;
/* the HAL isn't much better than the HEL if we can't do mmap() */
if
(
!
(
WOutDev
[
wDevID
].
caps
.
dwSupport
&
WAVECAPS_DIRECTSOUND
))
{
ERR
(
"DirectSound flag not set
\n
"
);
MESSAGE
(
"This sound card's driver does not support direct access
\n
"
);
MESSAGE
(
"The (slower) DirectSound HEL mode will be used instead.
\n
"
);
return
MMSYSERR_NOTSUPPORTED
;
}
*
idrv
=
(
IDsDriverImpl
*
)
HeapAlloc
(
GetProcessHeap
(),
0
,
sizeof
(
IDsDriverImpl
));
if
(
!*
idrv
)
return
MMSYSERR_NOMEM
;
(
*
idrv
)
->
lpVtbl
=
&
dsdvt
;
(
*
idrv
)
->
ref
=
1
;
(
*
idrv
)
->
wDevID
=
wDevID
;
(
*
idrv
)
->
primary
=
NULL
;
return
MMSYSERR_NOERROR
;
}
/* we don't need a default wodMessage for audio when we don't have ALSA, the
* audio.c file will provide it for us
*/
#endif
/* HAVE_ALSA && interface == 0.5 */
dlls/winmm/winealsa/midi.c
View file @
151015fa
...
...
@@ -48,14 +48,12 @@
#include "winuser.h"
#include "winnls.h"
#include "mmddk.h"
#ifdef HAVE_ALSA
# include "alsa.h"
#endif
#include "alsa.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL
(
midi
);
#if
defined(HAVE_ALSA) && ((SND_LIB_MAJOR == 0 && SND_LIB_MINOR >= 9) || SND_LIB_MAJOR >= 1)
#if
def HAVE_ALSA
typedef
struct
{
int
state
;
/* -1 disabled, 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
...
...
@@ -1186,7 +1184,7 @@ static void ALSA_AddMidiPort(snd_seq_client_info_t* cinfo, snd_seq_port_info_t*
}
}
#endif
/*
defined(HAVE_ALSA) && ((SND_LIB_MAJOR == 0 && SND_LIB_MINOR >= 9) || SND_LIB_MAJOR >= 1)
*/
#endif
/*
HAVE_ALSA
*/
/*======================================================================*
...
...
@@ -1200,7 +1198,7 @@ static void ALSA_AddMidiPort(snd_seq_client_info_t* cinfo, snd_seq_port_info_t*
*/
LONG
ALSA_MidiInit
(
void
)
{
#if
defined(HAVE_ALSA) && ((SND_LIB_MAJOR == 0 && SND_LIB_MINOR >= 9) || SND_LIB_MAJOR >= 1)
#if
def HAVE_ALSA
static
BOOL
bInitDone
=
FALSE
;
snd_seq_client_info_t
*
cinfo
;
snd_seq_port_info_t
*
pinfo
;
...
...
@@ -1266,7 +1264,7 @@ DWORD WINAPI ALSA_midMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
TRACE
(
"(%04X, %04X, %08lX, %08lX, %08lX);
\n
"
,
wDevID
,
wMsg
,
dwUser
,
dwParam1
,
dwParam2
);
switch
(
wMsg
)
{
#if
defined(HAVE_ALSA) && ((SND_LIB_MAJOR == 0 && SND_LIB_MINOR >= 9) || SND_LIB_MAJOR >= 1)
#if
def HAVE_ALSA
case
DRVM_INIT
:
case
DRVM_EXIT
:
case
DRVM_ENABLE
:
...
...
@@ -1310,7 +1308,7 @@ DWORD WINAPI ALSA_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
wDevID
,
wMsg
,
dwUser
,
dwParam1
,
dwParam2
);
switch
(
wMsg
)
{
#if
defined(HAVE_ALSA) && ((SND_LIB_MAJOR == 0 && SND_LIB_MINOR >= 9) || SND_LIB_MAJOR >= 1)
#if
def HAVE_ALSA
case
DRVM_INIT
:
case
DRVM_EXIT
:
case
DRVM_ENABLE
:
...
...
include/config.h.in
View file @
151015fa
...
...
@@ -20,7 +20,7 @@
*/
#undef HAVE_ALLOCA_H
/* Define if you have ALSA including devel headers */
/* Define if you have ALSA
1.x
including devel headers */
#undef HAVE_ALSA
/* Define to 1 if you have the <alsa/asoundlib.h> header file. */
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment