2007-01-29 16:26:23 +01:00
/*
* Sample Wine Driver for Advanced Linux Sound System ( ALSA )
* Based on version < final > of the ALSA API
*
* Copyright 2002 Eric Pouech
* 2002 Marco Pietrobono
* 2003 Christian Costa : WaveIn support
* 2006 - 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
*/
/*======================================================================*
* Low level dsound output implementation *
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
# 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 <errno.h>
# include <limits.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 "alsa.h"
# include "wine/library.h"
# include "wine/unicode.h"
# include "wine/debug.h"
# ifdef HAVE_ALSA
2007-06-21 20:30:46 +02:00
WINE_DEFAULT_DEBUG_CHANNEL ( dsalsa ) ;
2007-01-29 16:26:23 +01:00
typedef struct IDsDriverImpl IDsDriverImpl ;
typedef struct IDsDriverBufferImpl IDsDriverBufferImpl ;
struct IDsDriverImpl
{
/* IUnknown fields */
const IDsDriverVtbl * lpVtbl ;
2007-06-02 17:03:24 +02:00
LONG ref ;
2007-01-29 16:26:23 +01:00
/* IDsDriverImpl fields */
2007-06-02 17:03:24 +02:00
IDsDriverBufferImpl * primary ;
UINT wDevID ;
2007-01-29 16:26:23 +01:00
} ;
struct IDsDriverBufferImpl
{
const IDsDriverBufferVtbl * lpVtbl ;
2007-06-21 20:30:46 +02:00
LONG ref ;
IDsDriverImpl * drv ;
CRITICAL_SECTION pcm_crst ;
LPVOID mmap_buffer ;
DWORD mmap_buflen_bytes ;
2007-01-29 16:26:23 +01:00
2007-06-02 17:03:24 +02:00
snd_pcm_t * pcm ;
snd_pcm_hw_params_t * hw_params ;
snd_pcm_sw_params_t * sw_params ;
2007-06-27 23:37:26 +02:00
snd_pcm_uframes_t mmap_buflen_frames , mmap_pos , mmap_commitahead , mmap_writeahead ;
2007-01-29 16:26:23 +01:00
} ;
2007-06-21 20:30:46 +02:00
/** Fill buffers, for starting and stopping
* Alsa won ' t start playing until everything is filled up
* This also updates mmap_pos
2007-06-27 23:37:26 +02:00
*
* Returns : Amount of periods in use so snd_pcm_avail_update
* doesn ' t have to be called up to 4 x in GetPosition ( )
2007-06-21 20:30:46 +02:00
*/
2007-06-27 23:37:26 +02:00
static snd_pcm_uframes_t CommitAll ( IDsDriverBufferImpl * This )
2007-01-29 16:26:23 +01:00
{
2007-06-21 20:30:46 +02:00
const snd_pcm_channel_area_t * areas ;
snd_pcm_uframes_t used ;
2007-06-27 23:37:26 +02:00
const snd_pcm_uframes_t commitahead = This - > mmap_commitahead ;
2007-06-21 20:30:46 +02:00
used = This - > mmap_buflen_frames - snd_pcm_avail_update ( This - > pcm ) ;
2007-06-27 23:37:26 +02:00
TRACE ( " %p needs to commit to %lu, used: %lu \n " , This , commitahead , used ) ;
if ( used < commitahead )
2007-06-21 20:30:46 +02:00
{
2007-06-27 23:37:26 +02:00
snd_pcm_uframes_t done , putin = commitahead - used ;
2007-06-21 20:30:46 +02:00
snd_pcm_mmap_begin ( This - > pcm , & areas , & This - > mmap_pos , & putin ) ;
2007-06-27 23:37:26 +02:00
done = snd_pcm_mmap_commit ( This - > pcm , This - > mmap_pos , putin ) ;
This - > mmap_pos + = done ;
used + = done ;
putin = commitahead - used ;
2007-06-21 20:30:46 +02:00
2007-06-27 23:37:26 +02:00
if ( This - > mmap_pos = = This - > mmap_buflen_frames & & ( snd_pcm_sframes_t ) putin > 0 )
2007-06-21 20:30:46 +02:00
{
2007-06-27 23:37:26 +02:00
snd_pcm_mmap_begin ( This - > pcm , & areas , & This - > mmap_pos , & putin ) ;
done = snd_pcm_mmap_commit ( This - > pcm , This - > mmap_pos , putin ) ;
This - > mmap_pos + = done ;
used + = done ;
2007-06-21 20:30:46 +02:00
}
}
2007-06-27 23:37:26 +02:00
if ( This - > mmap_pos = = This - > mmap_buflen_frames )
This - > mmap_pos = 0 ;
return used ;
2007-06-21 20:30:46 +02:00
}
static void CheckXRUN ( IDsDriverBufferImpl * This )
{
snd_pcm_state_t state = snd_pcm_state ( This - > pcm ) ;
snd_pcm_sframes_t delay ;
int err ;
snd_pcm_hwsync ( This - > pcm ) ;
snd_pcm_delay ( This - > pcm , & delay ) ;
2007-01-29 16:26:23 +01:00
if ( state = = SND_PCM_STATE_XRUN )
{
2007-06-21 20:30:46 +02:00
err = snd_pcm_prepare ( This - > pcm ) ;
CommitAll ( This ) ;
snd_pcm_start ( This - > pcm ) ;
WARN ( " xrun occurred \n " ) ;
if ( err < 0 )
2007-01-29 16:26:23 +01:00
ERR ( " recovery from xrun failed, prepare failed: %s \n " , snd_strerror ( err ) ) ;
}
else if ( state = = SND_PCM_STATE_SUSPENDED )
{
2007-06-21 20:30:46 +02:00
int err = snd_pcm_resume ( This - > pcm ) ;
TRACE ( " recovery from suspension occurred \n " ) ;
2007-01-29 16:26:23 +01:00
if ( err < 0 & & err ! = - EAGAIN ) {
2007-06-21 20:30:46 +02:00
err = snd_pcm_prepare ( This - > pcm ) ;
2007-01-29 16:26:23 +01:00
if ( err < 0 )
ERR ( " recovery from suspend failed, prepare failed: %s \n " , snd_strerror ( err ) ) ;
}
2007-01-29 16:33:24 +01:00
} else if ( state ! = SND_PCM_STATE_RUNNING ) {
2007-06-21 20:30:46 +02:00
WARN ( " Unhandled state: %d \n " , state ) ;
2007-01-29 16:26:23 +01:00
}
}
/**
* Allocate the memory - mapped buffer for direct sound , and set up the
* callback .
*/
static int DSDB_CreateMMAP ( IDsDriverBufferImpl * pdbi )
{
2007-06-02 17:03:24 +02:00
snd_pcm_t * pcm = pdbi - > pcm ;
snd_pcm_format_t format ;
2007-06-21 20:30:46 +02:00
snd_pcm_uframes_t frames , ofs , avail , psize , boundary ;
2007-06-02 17:03:24 +02:00
unsigned int channels , bits_per_sample , bits_per_frame ;
int err , mmap_mode ;
2007-06-21 20:30:46 +02:00
const snd_pcm_channel_area_t * areas ;
2007-06-02 17:03:24 +02:00
snd_pcm_hw_params_t * hw_params = pdbi - > hw_params ;
snd_pcm_sw_params_t * sw_params = pdbi - > sw_params ;
mmap_mode = snd_pcm_type ( pcm ) ;
2007-01-29 16:33:24 +01:00
2007-06-02 17:03:24 +02:00
if ( mmap_mode = = SND_PCM_TYPE_HW )
2007-06-27 23:37:26 +02:00
TRACE ( " mmap'd buffer is a direct hardware buffer. \n " ) ;
else if ( mmap_mode = = SND_PCM_TYPE_DMIX )
TRACE ( " mmap'd buffer is an ALSA dmix buffer \n " ) ;
2007-06-02 17:03:24 +02:00
else
2007-06-27 23:37:26 +02:00
TRACE ( " mmap'd buffer is an ALSA type %d buffer \n " , mmap_mode ) ;
2007-01-29 16:26:23 +01:00
2007-06-02 17:03:24 +02:00
err = snd_pcm_hw_params_get_period_size ( hw_params , & psize , NULL ) ;
err = snd_pcm_hw_params_get_format ( hw_params , & format ) ;
err = snd_pcm_hw_params_get_buffer_size ( hw_params , & frames ) ;
err = snd_pcm_hw_params_get_channels ( hw_params , & channels ) ;
2007-01-29 16:33:24 +01:00
bits_per_sample = snd_pcm_format_physical_width ( format ) ;
bits_per_frame = bits_per_sample * channels ;
2007-06-21 20:30:46 +02:00
if ( TRACE_ON ( dsalsa ) )
2007-06-02 17:03:24 +02:00
ALSA_TraceParameters ( hw_params , NULL , FALSE ) ;
2007-01-29 16:26:23 +01:00
TRACE ( " format=%s frames=%ld channels=%d bits_per_sample=%d bits_per_frame=%d \n " ,
snd_pcm_format_name ( format ) , frames , channels , bits_per_sample , bits_per_frame ) ;
pdbi - > mmap_buflen_frames = frames ;
2007-06-02 17:03:24 +02:00
pdbi - > mmap_buflen_bytes = snd_pcm_frames_to_bytes ( pcm , frames ) ;
2007-01-29 16:26:23 +01:00
2007-06-21 20:30:46 +02:00
snd_pcm_sw_params_current ( pcm , sw_params ) ;
snd_pcm_sw_params_set_start_threshold ( pcm , sw_params , 0 ) ;
snd_pcm_sw_params_get_boundary ( sw_params , & boundary ) ;
snd_pcm_sw_params_set_stop_threshold ( pcm , sw_params , boundary ) ;
snd_pcm_sw_params_set_silence_threshold ( pcm , sw_params , INT_MAX ) ;
snd_pcm_sw_params_set_silence_size ( pcm , sw_params , 0 ) ;
snd_pcm_sw_params_set_avail_min ( pcm , sw_params , 0 ) ;
snd_pcm_sw_params_set_xrun_mode ( pcm , sw_params , SND_PCM_XRUN_NONE ) ;
err = snd_pcm_sw_params ( pcm , sw_params ) ;
2007-06-02 17:03:24 +02:00
avail = snd_pcm_avail_update ( pcm ) ;
2007-01-29 16:26:23 +01:00
if ( avail < 0 )
{
2007-06-21 20:30:46 +02:00
ERR ( " No buffer is available: %s. \n " , snd_strerror ( avail ) ) ;
return DSERR_GENERIC ;
2007-01-29 16:26:23 +01:00
}
2007-06-21 20:30:46 +02:00
err = snd_pcm_mmap_begin ( pcm , & areas , & ofs , & avail ) ;
2007-01-29 16:26:23 +01:00
if ( err < 0 )
{
2007-06-21 20:30:46 +02:00
ERR ( " Can't map sound device for direct access: %s \n " , snd_strerror ( err ) ) ;
return DSERR_GENERIC ;
2007-01-29 16:26:23 +01:00
}
2007-06-28 17:34:01 +02:00
snd_pcm_format_set_silence ( format , areas - > addr , pdbi - > mmap_buflen_frames ) ;
2007-06-02 17:03:24 +02:00
err = snd_pcm_mmap_commit ( pcm , ofs , avail ) ;
2007-06-21 20:30:46 +02:00
pdbi - > mmap_buffer = areas - > addr ;
2007-01-29 16:26:23 +01:00
TRACE ( " created mmap buffer of %ld frames (%d bytes) at %p \n " ,
frames , pdbi - > mmap_buflen_bytes , pdbi - > mmap_buffer ) ;
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 ;
ULONG refCount = InterlockedIncrement ( & This - > ref ) ;
TRACE ( " (%p)->(ref before=%u) \n " , This , refCount - 1 ) ;
return refCount ;
}
static ULONG WINAPI IDsDriverBufferImpl_Release ( PIDSDRIVERBUFFER iface )
{
IDsDriverBufferImpl * This = ( IDsDriverBufferImpl * ) iface ;
ULONG refCount = InterlockedDecrement ( & This - > ref ) ;
TRACE ( " (%p)->(ref before=%u) \n " , This , refCount + 1 ) ;
if ( refCount )
2007-06-21 20:30:46 +02:00
return refCount ;
2007-01-29 16:33:24 +01:00
TRACE ( " mmap buffer %p destroyed \n " , This - > mmap_buffer ) ;
2007-01-29 16:26:23 +01:00
if ( This = = This - > drv - > primary )
2007-06-02 17:03:24 +02:00
This - > drv - > primary = NULL ;
2007-06-21 20:30:46 +02:00
This - > pcm_crst . DebugInfo - > Spare [ 0 ] = 0 ;
DeleteCriticalSection ( & This - > pcm_crst ) ;
snd_pcm_drop ( This - > pcm ) ;
2007-06-02 17:03:24 +02:00
snd_pcm_close ( This - > pcm ) ;
This - > pcm = NULL ;
HeapFree ( GetProcessHeap ( ) , 0 , This - > sw_params ) ;
HeapFree ( GetProcessHeap ( ) , 0 , This - > hw_params ) ;
2007-01-29 16:26:23 +01:00
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 )
{
2007-06-21 20:30:46 +02:00
FIXME ( " (%p) stub \n " , iface ) ;
2007-01-29 16:26:23 +01:00
return DSERR_UNSUPPORTED ;
}
static HRESULT WINAPI IDsDriverBufferImpl_Unlock ( PIDSDRIVERBUFFER iface ,
LPVOID pvAudio1 , DWORD dwLen1 ,
LPVOID pvAudio2 , DWORD dwLen2 )
{
2007-06-21 20:30:46 +02:00
FIXME ( " (%p) stub \n " , iface ) ;
2007-01-29 16:26:23 +01:00
return DSERR_UNSUPPORTED ;
}
2007-06-21 20:30:46 +02:00
static int warnonce ;
static HRESULT SetFormat ( IDsDriverBufferImpl * This , LPWAVEFORMATEX pwfx , BOOL forced )
2007-01-29 16:26:23 +01:00
{
2007-06-21 20:30:46 +02:00
snd_pcm_t * pcm = NULL ;
2007-06-02 17:03:24 +02:00
snd_pcm_hw_params_t * hw_params = This - > hw_params ;
unsigned int buffer_time = 500000 ;
snd_pcm_format_t format = - 1 ;
snd_pcm_uframes_t psize ;
DWORD rate = pwfx - > nSamplesPerSec ;
int err = 0 ;
switch ( pwfx - > wBitsPerSample )
{
case 8 : format = SND_PCM_FORMAT_U8 ; break ;
case 16 : format = SND_PCM_FORMAT_S16_LE ; break ;
case 24 : format = SND_PCM_FORMAT_S24_LE ; break ;
case 32 : format = SND_PCM_FORMAT_S32_LE ; break ;
2007-06-21 20:30:46 +02:00
default : FIXME ( " Unsupported bpp: %d \n " , pwfx - > wBitsPerSample ) ; return DSERR_GENERIC ;
2007-06-02 17:03:24 +02:00
}
2007-06-21 20:30:46 +02:00
/* **** */
EnterCriticalSection ( & This - > pcm_crst ) ;
2007-06-02 17:03:24 +02:00
err = snd_pcm_open ( & pcm , WOutDev [ This - > drv - > wDevID ] . pcmname , SND_PCM_STREAM_PLAYBACK , SND_PCM_NONBLOCK ) ;
2007-06-21 20:30:46 +02:00
2007-06-02 17:03:24 +02:00
if ( err < 0 )
{
2007-06-21 20:30:46 +02:00
if ( errno ! = EBUSY | | ! This - > pcm )
{
/* **** */
LeaveCriticalSection ( & This - > pcm_crst ) ;
WARN ( " Can not open sound device: %s \n " , snd_strerror ( err ) ) ;
return DSERR_GENERIC ;
}
snd_pcm_drop ( This - > pcm ) ;
snd_pcm_close ( This - > pcm ) ;
This - > pcm = NULL ;
err = snd_pcm_open ( & pcm , WOutDev [ This - > drv - > wDevID ] . pcmname , SND_PCM_STREAM_PLAYBACK , SND_PCM_NONBLOCK ) ;
if ( err < 0 )
{
/* **** */
LeaveCriticalSection ( & This - > pcm_crst ) ;
WARN ( " Can not open sound device: %s \n " , snd_strerror ( err ) ) ;
return DSERR_BUFFERLOST ;
}
2007-06-02 17:03:24 +02:00
}
/* Set some defaults */
snd_pcm_hw_params_any ( pcm , hw_params ) ;
err = snd_pcm_hw_params_set_channels ( pcm , hw_params , pwfx - > nChannels ) ;
if ( err < 0 ) { WARN ( " Could not set channels to %d \n " , pwfx - > nChannels ) ; goto err ; }
err = snd_pcm_hw_params_set_format ( pcm , hw_params , format ) ;
if ( err < 0 ) { WARN ( " Could not set format to %d bpp \n " , pwfx - > wBitsPerSample ) ; goto err ; }
/* Alsa's rate resampling is only used if the application specifically requests
* a buffer at a certain frequency , else it is better to disable due to unwanted
* side effects , which may include : Less granular pointer , changing buffer sizes , etc
*/
# if SND_LIB_VERSION >= 0x010009
2007-06-21 20:30:46 +02:00
snd_pcm_hw_params_set_rate_resample ( pcm , hw_params , 0 & & forced ) ;
2007-06-02 17:03:24 +02:00
# endif
err = snd_pcm_hw_params_set_rate_near ( pcm , hw_params , & rate , NULL ) ;
if ( err < 0 ) { rate = pwfx - > nSamplesPerSec ; WARN ( " Could not set rate \n " ) ; goto err ; }
if ( ! ALSA_NearMatch ( rate , pwfx - > nSamplesPerSec ) )
{
WARN ( " Could not set sound rate to %d, but instead to %d \n " , pwfx - > nSamplesPerSec , rate ) ;
pwfx - > nSamplesPerSec = rate ;
pwfx - > nAvgBytesPerSec = rate * pwfx - > nBlockAlign ;
/* Let DirectSound detect this */
}
snd_pcm_hw_params_set_periods_integer ( pcm , hw_params ) ;
snd_pcm_hw_params_set_buffer_time_near ( pcm , hw_params , & buffer_time , NULL ) ;
buffer_time = 10000 ;
snd_pcm_hw_params_set_period_time_near ( pcm , hw_params , & buffer_time , NULL ) ;
err = snd_pcm_hw_params ( pcm , hw_params ) ;
err = snd_pcm_sw_params ( pcm , This - > sw_params ) ;
2007-06-21 20:30:46 +02:00
snd_pcm_prepare ( pcm ) ;
2007-06-02 17:03:24 +02:00
err = snd_pcm_hw_params_get_period_size ( hw_params , & psize , NULL ) ;
2007-06-21 20:30:46 +02:00
TRACE ( " Period size is: %lu \n " , psize ) ;
2007-06-27 23:37:26 +02:00
/* If period size is 'high', try to commit less
* dmix needs at least 2 buffers to work succesfully but prefers 3
* however it seems to work ok if I just commit 2 1 / 2 buffers
*/
if ( psize > = 512 )
2007-06-21 20:30:46 +02:00
{
2007-06-27 23:37:26 +02:00
if ( psize > 512 & & + + warnonce = = 1 )
2007-06-21 20:30:46 +02:00
FIXME ( " Your alsa dmix period size is excessively high, unfortunately this is alsa default, try decreasing it to 512 or 256 (but double the amount of periods) if possible \n " ) ;
2007-06-27 23:37:26 +02:00
This - > mmap_commitahead = 2 * psize + psize / 2 ;
This - > mmap_writeahead = 2 * psize ;
2007-06-21 20:30:46 +02:00
}
2007-06-27 23:37:26 +02:00
else
2007-06-25 12:49:28 -05:00
{
2007-06-27 23:37:26 +02:00
This - > mmap_commitahead = 3 * psize ;
while ( This - > mmap_commitahead < = 512 )
This - > mmap_commitahead + = psize ;
This - > mmap_writeahead = This - > mmap_commitahead ;
2007-06-25 12:49:28 -05:00
}
2007-06-02 17:03:24 +02:00
if ( This - > pcm )
{
2007-06-21 20:30:46 +02:00
snd_pcm_drop ( This - > pcm ) ;
2007-06-02 17:03:24 +02:00
snd_pcm_close ( This - > pcm ) ;
}
This - > pcm = pcm ;
2007-06-21 20:30:46 +02:00
snd_pcm_prepare ( This - > pcm ) ;
2007-06-02 17:03:24 +02:00
DSDB_CreateMMAP ( This ) ;
2007-06-21 20:30:46 +02:00
/* **** */
LeaveCriticalSection ( & This - > pcm_crst ) ;
2007-06-02 17:03:24 +02:00
return S_OK ;
err :
if ( err < 0 )
WARN ( " Failed to apply changes: %s \n " , snd_strerror ( err ) ) ;
2007-06-27 23:37:26 +02:00
if ( ! This - > pcm )
This - > pcm = pcm ;
else
snd_pcm_close ( pcm ) ;
2007-06-02 17:03:24 +02:00
if ( This - > pcm )
snd_pcm_hw_params_current ( This - > pcm , This - > hw_params ) ;
2007-06-21 20:30:46 +02:00
/* **** */
LeaveCriticalSection ( & This - > pcm_crst ) ;
2007-06-02 17:03:24 +02:00
return DSERR_BADFORMAT ;
}
static HRESULT WINAPI IDsDriverBufferImpl_SetFormat ( PIDSDRIVERBUFFER iface , LPWAVEFORMATEX pwfx )
{
IDsDriverBufferImpl * This = ( IDsDriverBufferImpl * ) iface ;
2007-06-21 20:30:46 +02:00
HRESULT hr = S_OK ;
2007-06-02 17:03:24 +02:00
TRACE ( " (%p, %p) \n " , iface , pwfx ) ;
2007-06-21 20:30:46 +02:00
hr = SetFormat ( This , pwfx , TRUE ) ;
2007-06-02 17:03:24 +02:00
if ( hr = = S_OK )
/* Buffer size / Location changed, so tell dsound to recreate */
return DSERR_BUFFERLOST ;
return hr ;
2007-01-29 16:26:23 +01:00
}
static HRESULT WINAPI IDsDriverBufferImpl_SetFrequency ( PIDSDRIVERBUFFER iface , DWORD dwFreq )
{
/* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
2007-06-02 17:03:24 +02:00
FIXME ( " (%p,%d): stub \n " , iface , dwFreq ) ;
return S_OK ;
2007-01-29 16:26:23 +01:00
}
static HRESULT WINAPI IDsDriverBufferImpl_SetVolumePan ( PIDSDRIVERBUFFER iface , PDSVOLUMEPAN pVolPan )
{
IDsDriverBufferImpl * This = ( IDsDriverBufferImpl * ) iface ;
2007-06-02 17:03:24 +02:00
FIXME ( " (%p,%p): stub \n " , This , pVolPan ) ;
/* TODO: Bring volume control back */
2007-01-29 16:26:23 +01:00
return DS_OK ;
}
static HRESULT WINAPI IDsDriverBufferImpl_SetPosition ( PIDSDRIVERBUFFER iface , DWORD dwNewPos )
{
2007-06-02 17:03:24 +02:00
/* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
2007-06-21 20:30:46 +02:00
/* I don't even think alsa allows this */
2007-01-29 16:33:24 +01:00
FIXME ( " (%p,%d): stub \n " , iface , dwNewPos ) ;
2007-01-29 16:26:23 +01:00
return DSERR_UNSUPPORTED ;
}
static HRESULT WINAPI IDsDriverBufferImpl_GetPosition ( PIDSDRIVERBUFFER iface ,
LPDWORD lpdwPlay , LPDWORD lpdwWrite )
{
IDsDriverBufferImpl * This = ( IDsDriverBufferImpl * ) iface ;
2007-06-21 20:30:46 +02:00
snd_pcm_uframes_t hw_pptr = 0 , hw_wptr = 0 ;
2007-06-02 17:03:24 +02:00
snd_pcm_state_t state ;
2007-01-29 16:26:23 +01:00
2007-06-21 20:30:46 +02:00
EnterCriticalSection ( & This - > pcm_crst ) ;
if ( ! This - > pcm )
2007-06-02 17:03:24 +02:00
{
2007-06-21 20:30:46 +02:00
FIXME ( " Bad pointer for pcm: %p \n " , This - > pcm ) ;
2007-06-22 23:30:57 +02:00
LeaveCriticalSection ( & This - > pcm_crst ) ;
2007-06-02 17:03:24 +02:00
return DSERR_GENERIC ;
}
2007-01-29 16:26:23 +01:00
2007-06-21 20:30:46 +02:00
state = snd_pcm_state ( This - > pcm ) ;
2007-06-27 23:37:26 +02:00
if ( state ! = SND_PCM_STATE_RUNNING )
CheckXRUN ( This ) ;
else
2007-01-29 16:33:24 +01:00
{
2007-06-27 23:37:26 +02:00
snd_pcm_uframes_t used = CommitAll ( This ) ;
if ( This - > mmap_pos > used )
hw_pptr = This - > mmap_pos - used ;
else
hw_pptr = This - > mmap_buflen_frames - used + This - > mmap_pos ;
hw_wptr = hw_pptr + ( This - > mmap_writeahead > used ? used : This - > mmap_writeahead ) ;
2007-06-21 20:30:46 +02:00
hw_wptr % = This - > mmap_buflen_frames ;
2007-06-27 23:37:26 +02:00
hw_pptr % = This - > mmap_buflen_frames ;
2007-06-21 20:30:46 +02:00
2007-06-27 23:37:26 +02:00
TRACE ( " At position: %ld (%ld) - Used %ld \n " , hw_pptr , This - > mmap_pos , used ) ;
}
2007-06-21 20:30:46 +02:00
LeaveCriticalSection ( & This - > pcm_crst ) ;
2007-01-29 16:26:23 +01:00
if ( lpdwPlay )
2007-06-21 20:30:46 +02:00
* lpdwPlay = snd_pcm_frames_to_bytes ( This - > pcm , hw_pptr ) ;
2007-01-29 16:26:23 +01:00
if ( lpdwWrite )
2007-06-21 20:30:46 +02:00
* lpdwWrite = snd_pcm_frames_to_bytes ( This - > pcm , hw_wptr ) ;
2007-01-29 16:26:23 +01:00
2007-06-21 20:30:46 +02:00
TRACE ( " hw_pptr=0x%08x, hw_wptr=0x%08x playpos=%d, writepos=%d \n " , ( unsigned int ) hw_pptr , ( unsigned int ) hw_wptr , lpdwPlay ? * lpdwPlay : - 1 , lpdwWrite ? * lpdwWrite : - 1 ) ;
2007-01-29 16:26:23 +01:00
return DS_OK ;
}
static HRESULT WINAPI IDsDriverBufferImpl_Play ( PIDSDRIVERBUFFER iface , DWORD dwRes1 , DWORD dwRes2 , DWORD dwFlags )
{
IDsDriverBufferImpl * This = ( IDsDriverBufferImpl * ) iface ;
TRACE ( " (%p,%x,%x,%x) \n " , iface , dwRes1 , dwRes2 , dwFlags ) ;
2007-06-21 20:30:46 +02:00
/* **** */
EnterCriticalSection ( & This - > pcm_crst ) ;
snd_pcm_start ( This - > pcm ) ;
CommitAll ( This ) ;
/* **** */
LeaveCriticalSection ( & This - > pcm_crst ) ;
2007-01-29 16:26:23 +01:00
return DS_OK ;
}
static HRESULT WINAPI IDsDriverBufferImpl_Stop ( PIDSDRIVERBUFFER iface )
{
IDsDriverBufferImpl * This = ( IDsDriverBufferImpl * ) iface ;
TRACE ( " (%p) \n " , iface ) ;
2007-06-21 20:30:46 +02:00
/* **** */
EnterCriticalSection ( & This - > pcm_crst ) ;
snd_pcm_drop ( This - > pcm ) ;
snd_pcm_prepare ( This - > pcm ) ;
/* **** */
LeaveCriticalSection ( & This - > pcm_crst ) ;
2007-01-29 16:26:23 +01:00
return DS_OK ;
}
static const 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 ;
ULONG refCount = InterlockedIncrement ( & This - > ref ) ;
TRACE ( " (%p)->(ref before=%u) \n " , This , refCount - 1 ) ;
return refCount ;
}
static ULONG WINAPI IDsDriverImpl_Release ( PIDSDRIVER iface )
{
IDsDriverImpl * This = ( IDsDriverImpl * ) iface ;
ULONG refCount = InterlockedDecrement ( & This - > ref ) ;
TRACE ( " (%p)->(ref before=%u) \n " , This , refCount + 1 ) ;
if ( refCount )
2007-06-02 17:03:24 +02:00
return refCount ;
HeapFree ( GetProcessHeap ( ) , 0 , This ) ;
2007-01-29 16:26:23 +01:00
return 0 ;
}
static HRESULT WINAPI IDsDriverImpl_GetDriverDesc ( PIDSDRIVER iface , PDSDRIVERDESC pDesc )
{
IDsDriverImpl * This = ( IDsDriverImpl * ) iface ;
TRACE ( " (%p,%p) \n " , iface , pDesc ) ;
memcpy ( pDesc , & ( WOutDev [ This - > wDevID ] . ds_desc ) , sizeof ( DSDRIVERDESC ) ) ;
2007-06-23 20:40:35 +02:00
pDesc - > dwFlags = DSDDESC_DONTNEEDPRIMARYLOCK | DSDDESC_DONTNEEDSECONDARYLOCK | DSDDESC_DONTNEEDWRITELEAD ;
2007-01-29 16:26:23 +01:00
pDesc - > dnDevNode = WOutDev [ This - > wDevID ] . waveDesc . dnDevNode ;
pDesc - > wVxdId = 0 ;
pDesc - > wReserved = 0 ;
pDesc - > ulDeviceNum = This - > wDevID ;
pDesc - > dwHeapType = DSDHEAP_NOHEAP ;
pDesc - > pvDirectDrawHeap = NULL ;
2007-06-02 17:03:24 +02:00
pDesc - > dwMemStartAddress = 0xDEAD0000 ;
pDesc - > dwMemEndAddress = 0xDEAF0000 ;
2007-01-29 16:26:23 +01:00
pDesc - > dwMemAllocExtra = 0 ;
pDesc - > pvReserved1 = NULL ;
pDesc - > pvReserved2 = NULL ;
return DS_OK ;
}
static HRESULT WINAPI IDsDriverImpl_Open ( PIDSDRIVER iface )
{
2007-06-02 17:03:24 +02:00
HRESULT hr = S_OK ;
IDsDriverImpl * This = ( IDsDriverImpl * ) iface ;
int err = 0 ;
snd_pcm_t * pcm = NULL ;
snd_pcm_hw_params_t * hw_params ;
/* While this is not really needed, it is a good idea to do this,
* to see if sound can be initialized */
hw_params = HeapAlloc ( GetProcessHeap ( ) , HEAP_ZERO_MEMORY , snd_pcm_hw_params_sizeof ( ) ) ;
if ( ! hw_params )
{
hr = DSERR_OUTOFMEMORY ;
goto unalloc ;
}
err = snd_pcm_open ( & pcm , WOutDev [ This - > wDevID ] . pcmname , SND_PCM_STREAM_PLAYBACK , SND_PCM_NONBLOCK ) ;
if ( err < 0 ) goto err ;
err = snd_pcm_hw_params_any ( pcm , hw_params ) ;
if ( err < 0 ) goto err ;
err = snd_pcm_hw_params_set_access ( pcm , hw_params , SND_PCM_ACCESS_MMAP_INTERLEAVED ) ;
if ( err < 0 ) goto err ;
TRACE ( " Success \n " ) ;
snd_pcm_close ( pcm ) ;
goto unalloc ;
err :
hr = DSERR_GENERIC ;
FIXME ( " Failed to open device: %s \n " , snd_strerror ( err ) ) ;
if ( pcm )
snd_pcm_close ( pcm ) ;
unalloc :
HeapFree ( GetProcessHeap ( ) , 0 , hw_params ) ;
if ( hr ! = S_OK )
WARN ( " --> %08x \n " , hr ) ;
return hr ;
2007-01-29 16:26:23 +01:00
}
static HRESULT WINAPI IDsDriverImpl_Close ( PIDSDRIVER iface )
{
2007-06-02 17:03:24 +02:00
IDsDriverImpl * This = ( IDsDriverImpl * ) iface ;
TRACE ( " (%p) stub, harmless \n " , This ) ;
2007-01-29 16:26:23 +01:00
return DS_OK ;
}
static HRESULT WINAPI IDsDriverImpl_GetCaps ( PIDSDRIVER iface , PDSDRIVERCAPS pCaps )
{
IDsDriverImpl * This = ( IDsDriverImpl * ) iface ;
TRACE ( " (%p,%p) \n " , iface , pCaps ) ;
memcpy ( pCaps , & ( WOutDev [ This - > wDevID ] . ds_caps ) , sizeof ( DSDRIVERCAPS ) ) ;
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 ;
2007-06-02 17:03:24 +02:00
HRESULT err ;
2007-01-29 16:26:23 +01:00
TRACE ( " (%p,%p,%x,%x) \n " , iface , pwfx , dwFlags , dwCardAddress ) ;
2007-06-02 17:03:24 +02:00
/* we only support primary buffers... for now */
2007-01-29 16:26:23 +01:00
if ( ! ( dwFlags & DSBCAPS_PRIMARYBUFFER ) )
2007-06-02 17:03:24 +02:00
return DSERR_UNSUPPORTED ;
2007-01-29 16:26:23 +01:00
if ( This - > primary )
2007-06-02 17:03:24 +02:00
return DSERR_ALLOCATED ;
2007-01-29 16:26:23 +01:00
2007-06-02 17:03:24 +02:00
* ippdsdb = HeapAlloc ( GetProcessHeap ( ) , HEAP_ZERO_MEMORY , sizeof ( IDsDriverBufferImpl ) ) ;
2007-01-29 16:26:23 +01:00
if ( * ippdsdb = = NULL )
2007-06-02 17:03:24 +02:00
return DSERR_OUTOFMEMORY ;
( * ippdsdb ) - > hw_params = HeapAlloc ( GetProcessHeap ( ) , HEAP_ZERO_MEMORY , snd_pcm_hw_params_sizeof ( ) ) ;
( * ippdsdb ) - > sw_params = HeapAlloc ( GetProcessHeap ( ) , HEAP_ZERO_MEMORY , snd_pcm_sw_params_sizeof ( ) ) ;
if ( ! ( * ippdsdb ) - > hw_params | | ! ( * ippdsdb ) - > sw_params )
{
HeapFree ( GetProcessHeap ( ) , 0 , ( * ippdsdb ) - > sw_params ) ;
HeapFree ( GetProcessHeap ( ) , 0 , ( * ippdsdb ) - > hw_params ) ;
return DSERR_OUTOFMEMORY ;
}
2007-01-29 16:26:23 +01:00
( * ippdsdb ) - > lpVtbl = & dsdbvt ;
( * ippdsdb ) - > ref = 1 ;
( * ippdsdb ) - > drv = This ;
2007-06-21 20:30:46 +02:00
InitializeCriticalSection ( & ( * ippdsdb ) - > pcm_crst ) ;
( * ippdsdb ) - > pcm_crst . DebugInfo - > Spare [ 0 ] = ( DWORD_PTR ) ( __FILE__ " : ALSA_DSOUTPUT.pcm_crst " ) ;
2007-01-29 16:26:23 +01:00
2007-06-02 17:03:24 +02:00
/* SetFormat has to re-initialize pcm here anyway */
2007-06-21 20:30:46 +02:00
err = SetFormat ( * ippdsdb , pwfx , FALSE ) ;
2007-06-02 17:03:24 +02:00
if ( FAILED ( err ) )
{
WARN ( " Error occured: %08x \n " , err ) ;
goto err ;
}
2007-01-29 16:26:23 +01:00
2007-06-02 17:03:24 +02:00
if ( dwFlags & DSBCAPS_PRIMARYBUFFER )
This - > primary = * ippdsdb ;
2007-01-29 16:26:23 +01:00
2007-06-21 20:30:46 +02:00
* pdwcbBufferSize = ( * ippdsdb ) - > mmap_buflen_bytes ;
* ppbBuffer = ( * ippdsdb ) - > mmap_buffer ;
2007-01-29 16:26:23 +01:00
/* buffer is ready to go */
TRACE ( " buffer created at %p \n " , * ippdsdb ) ;
2007-06-02 17:03:24 +02:00
return err ;
err :
HeapFree ( GetProcessHeap ( ) , 0 , ( * ippdsdb ) - > sw_params ) ;
HeapFree ( GetProcessHeap ( ) , 0 , ( * ippdsdb ) - > hw_params ) ;
HeapFree ( GetProcessHeap ( ) , 0 , * ippdsdb ) ;
* ippdsdb = NULL ;
return err ;
2007-01-29 16:26:23 +01:00
}
static HRESULT WINAPI IDsDriverImpl_DuplicateSoundBuffer ( PIDSDRIVER iface ,
PIDSDRIVERBUFFER pBuffer ,
LPVOID * ppvObj )
{
2007-06-02 17:03:24 +02:00
IDsDriverImpl * This = ( IDsDriverImpl * ) iface ;
FIXME ( " (%p,%p): stub \n " , This , pBuffer ) ;
2007-01-29 16:26:23 +01:00
return DSERR_INVALIDCALL ;
}
static const IDsDriverVtbl dsdvt =
{
IDsDriverImpl_QueryInterface ,
IDsDriverImpl_AddRef ,
IDsDriverImpl_Release ,
IDsDriverImpl_GetDriverDesc ,
IDsDriverImpl_Open ,
IDsDriverImpl_Close ,
IDsDriverImpl_GetCaps ,
IDsDriverImpl_CreateSoundBuffer ,
IDsDriverImpl_DuplicateSoundBuffer
} ;
DWORD wodDsCreate ( UINT wDevID , PIDSDRIVER * drv )
{
IDsDriverImpl * * idrv = ( IDsDriverImpl * * ) drv ;
TRACE ( " driver created \n " ) ;
/* the HAL isn't much better than the HEL if we can't do mmap() */
2007-06-27 23:37:26 +02:00
if ( ! ( WOutDev [ wDevID ] . outcaps . dwSupport & WAVECAPS_DIRECTSOUND ) )
{
WARN ( " MMAP not supported for this device, falling back to waveout, should be harmless \n " ) ;
return MMSYSERR_NOTSUPPORTED ;
2007-01-29 16:26:23 +01:00
}
* idrv = HeapAlloc ( GetProcessHeap ( ) , 0 , sizeof ( IDsDriverImpl ) ) ;
if ( ! * idrv )
2007-06-27 23:37:26 +02:00
return MMSYSERR_NOMEM ;
2007-01-29 16:26:23 +01:00
( * idrv ) - > lpVtbl = & dsdvt ;
( * idrv ) - > ref = 1 ;
( * idrv ) - > wDevID = wDevID ;
( * idrv ) - > primary = NULL ;
return MMSYSERR_NOERROR ;
}
DWORD wodDsDesc ( UINT wDevID , PDSDRIVERDESC desc )
{
memcpy ( desc , & ( WOutDev [ wDevID ] . ds_desc ) , sizeof ( DSDRIVERDESC ) ) ;
return MMSYSERR_NOERROR ;
}
# endif /* HAVE_ALSA */