2009-01-26 15:06:44 +00:00
/*
* OSS audio output driver
*
* This file is part of MPlayer .
*
* MPlayer is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* MPlayer 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 General Public License for more details .
*
* You should have received a copy of the GNU General Public License along
* with MPlayer ; if not , write to the Free Software Foundation , Inc . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA .
*/
2001-06-02 23:25:43 +00:00
# include <stdio.h>
# include <stdlib.h>
# include <sys/ioctl.h>
# include <unistd.h>
# include <sys/time.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
2002-03-19 19:16:01 +00:00
# include <errno.h>
# include <string.h>
2001-06-02 23:25:43 +00:00
2004-12-07 02:24:15 +00:00
# include "config.h"
# include "mp_msg.h"
# include "mixer.h"
# include "help_mp.h"
2001-06-02 23:25:43 +00:00
2004-12-27 17:30:15 +00:00
# ifdef HAVE_SYS_SOUNDCARD_H
# include <sys/soundcard.h>
# else
# ifdef HAVE_SOUNDCARD_H
# include <soundcard.h>
# endif
# endif
2005-11-19 14:35:45 +00:00
# include "libaf/af_format.h"
2001-08-15 17:32:47 +00:00
2001-06-02 23:25:43 +00:00
# include "audio_out.h"
# include "audio_out_internal.h"
2009-05-13 02:58:57 +00:00
static const ao_info_t info =
2001-06-02 23:25:43 +00:00
{
" OSS/ioctl audio output " ,
2001-06-03 00:24:49 +00:00
" oss " ,
2001-06-02 23:25:43 +00:00
" A'rpi " ,
" "
} ;
2001-11-28 12:46:23 +00:00
/* Support for >2 output channels added 2001-11-25 - Steve Davies <steve@daviesfam.org> */
2001-06-02 23:25:43 +00:00
LIBAO_EXTERN ( oss )
2004-12-27 17:30:15 +00:00
static int format2oss ( int format )
{
switch ( format )
{
case AF_FORMAT_U8 : return AFMT_U8 ;
case AF_FORMAT_S8 : return AFMT_S8 ;
case AF_FORMAT_U16_LE : return AFMT_U16_LE ;
case AF_FORMAT_U16_BE : return AFMT_U16_BE ;
case AF_FORMAT_S16_LE : return AFMT_S16_LE ;
case AF_FORMAT_S16_BE : return AFMT_S16_BE ;
2009-07-24 10:35:22 +00:00
# ifdef AFMT_S24_PACKED
case AF_FORMAT_S24_LE : return AFMT_S24_PACKED ;
2004-12-27 17:30:15 +00:00
# endif
2005-01-06 13:15:53 +00:00
# ifdef AFMT_U32_LE
2004-12-27 17:30:15 +00:00
case AF_FORMAT_U32_LE : return AFMT_U32_LE ;
2005-01-06 13:15:53 +00:00
# endif
# ifdef AFMT_U32_BE
2004-12-27 17:30:15 +00:00
case AF_FORMAT_U32_BE : return AFMT_U32_BE ;
2005-01-06 13:15:53 +00:00
# endif
# ifdef AFMT_S32_LE
2004-12-27 17:30:15 +00:00
case AF_FORMAT_S32_LE : return AFMT_S32_LE ;
2005-01-06 13:15:53 +00:00
# endif
# ifdef AFMT_S32_BE
2004-12-27 17:30:15 +00:00
case AF_FORMAT_S32_BE : return AFMT_S32_BE ;
# endif
# ifdef AFMT_FLOAT
case AF_FORMAT_FLOAT_NE : return AFMT_FLOAT ;
# endif
// SPECIALS
case AF_FORMAT_MU_LAW : return AFMT_MU_LAW ;
case AF_FORMAT_A_LAW : return AFMT_A_LAW ;
case AF_FORMAT_IMA_ADPCM : return AFMT_IMA_ADPCM ;
# ifdef AFMT_MPEG
case AF_FORMAT_MPEG2 : return AFMT_MPEG ;
# endif
# ifdef AFMT_AC3
case AF_FORMAT_AC3 : return AFMT_AC3 ;
# endif
}
2005-01-29 12:58:16 +00:00
mp_msg ( MSGT_AO , MSGL_V , " OSS: Unknown/not supported internal format: %s \n " , af_fmt2str_short ( format ) ) ;
2004-12-27 17:30:15 +00:00
return - 1 ;
}
static int oss2format ( int format )
{
switch ( format )
{
case AFMT_U8 : return AF_FORMAT_U8 ;
case AFMT_S8 : return AF_FORMAT_S8 ;
case AFMT_U16_LE : return AF_FORMAT_U16_LE ;
case AFMT_U16_BE : return AF_FORMAT_U16_BE ;
case AFMT_S16_LE : return AF_FORMAT_S16_LE ;
case AFMT_S16_BE : return AF_FORMAT_S16_BE ;
2009-07-24 10:35:22 +00:00
# ifdef AFMT_S24_PACKED
case AFMT_S24_PACKED : return AF_FORMAT_S24_LE ;
2004-12-27 17:30:15 +00:00
# endif
2005-01-06 13:15:53 +00:00
# ifdef AFMT_U32_LE
2004-12-27 17:30:15 +00:00
case AFMT_U32_LE : return AF_FORMAT_U32_LE ;
2005-01-06 13:15:53 +00:00
# endif
# ifdef AFMT_U32_BE
2004-12-27 17:30:15 +00:00
case AFMT_U32_BE : return AF_FORMAT_U32_BE ;
2005-01-06 13:15:53 +00:00
# endif
# ifdef AFMT_S32_LE
2004-12-27 17:30:15 +00:00
case AFMT_S32_LE : return AF_FORMAT_S32_LE ;
2005-01-06 13:15:53 +00:00
# endif
# ifdef AFMT_S32_BE
2004-12-27 17:30:15 +00:00
case AFMT_S32_BE : return AF_FORMAT_S32_BE ;
# endif
# ifdef AFMT_FLOAT
case AFMT_FLOAT : return AF_FORMAT_FLOAT_NE ;
# endif
// SPECIALS
case AFMT_MU_LAW : return AF_FORMAT_MU_LAW ;
case AFMT_A_LAW : return AF_FORMAT_A_LAW ;
case AFMT_IMA_ADPCM : return AF_FORMAT_IMA_ADPCM ;
# ifdef AFMT_MPEG
case AFMT_MPEG : return AF_FORMAT_MPEG2 ;
# endif
# ifdef AFMT_AC3
case AFMT_AC3 : return AF_FORMAT_AC3 ;
# endif
}
2006-03-30 06:40:58 +00:00
mp_msg ( MSGT_GLOBAL , MSGL_ERR , MSGTR_AO_OSS_UnknownUnsupportedFormat , format ) ;
2004-12-27 17:30:15 +00:00
return - 1 ;
}
2002-02-22 14:20:15 +00:00
static char * dsp = PATH_DEV_DSP ;
2001-06-02 23:25:43 +00:00
static audio_buf_info zz ;
2001-06-05 02:26:56 +00:00
static int audio_fd = - 1 ;
2007-07-19 19:15:59 +00:00
static int prepause_space ;
2001-06-02 23:25:43 +00:00
2006-07-09 03:50:47 +00:00
static const char * oss_mixer_device = PATH_DEV_MIXER ;
static int oss_mixer_channel = SOUND_MIXER_PCM ;
2001-06-21 23:07:15 +00:00
2001-06-02 23:25:43 +00:00
// to set/get/query special features/parameters
2003-03-21 16:42:50 +00:00
static int control ( int cmd , void * arg ) {
2001-06-02 23:25:43 +00:00
switch ( cmd ) {
case AOCONTROL_SET_DEVICE :
dsp = ( char * ) arg ;
return CONTROL_OK ;
2002-07-25 20:28:47 +00:00
case AOCONTROL_GET_DEVICE :
2004-05-01 19:43:59 +00:00
* ( char * * ) arg = dsp ;
2002-07-25 20:28:47 +00:00
return CONTROL_OK ;
2005-07-03 11:38:35 +00:00
# ifdef SNDCTL_DSP_GETFMTS
2001-06-02 23:25:43 +00:00
case AOCONTROL_QUERY_FORMAT :
2005-07-03 11:38:35 +00:00
{
int format ;
if ( ! ioctl ( audio_fd , SNDCTL_DSP_GETFMTS , & format ) )
2008-08-24 13:52:54 +00:00
if ( ( unsigned int ) format & ( unsigned long ) arg )
2005-07-03 11:38:35 +00:00
return CONTROL_TRUE ;
return CONTROL_FALSE ;
}
# endif
2001-06-21 23:07:15 +00:00
case AOCONTROL_GET_VOLUME :
case AOCONTROL_SET_VOLUME :
{
ao_control_vol_t * vol = ( ao_control_vol_t * ) arg ;
2002-09-22 02:33:28 +00:00
int fd , v , devs ;
2001-08-15 11:50:55 +00:00
2004-12-27 17:30:15 +00:00
if ( ao_data . format = = AF_FORMAT_AC3 )
2001-08-15 11:50:55 +00:00
return CONTROL_TRUE ;
2009-05-13 02:58:57 +00:00
2002-02-21 16:02:26 +00:00
if ( ( fd = open ( oss_mixer_device , O_RDONLY ) ) > 0 )
2001-06-21 23:07:15 +00:00
{
ioctl ( fd , SOUND_MIXER_READ_DEVMASK , & devs ) ;
2004-01-24 11:53:52 +00:00
if ( devs & ( 1 < < oss_mixer_channel ) )
2002-02-21 16:02:26 +00:00
{
2001-06-21 23:07:15 +00:00
if ( cmd = = AOCONTROL_GET_VOLUME )
2002-02-21 16:02:26 +00:00
{
2004-01-24 11:53:52 +00:00
ioctl ( fd , MIXER_READ ( oss_mixer_channel ) , & v ) ;
2002-02-21 16:02:26 +00:00
vol - > right = ( v & 0xFF00 ) > > 8 ;
vol - > left = v & 0x00FF ;
}
2001-06-21 23:07:15 +00:00
else
2002-02-21 16:02:26 +00:00
{
v = ( ( int ) vol - > right < < 8 ) | ( int ) vol - > left ;
2004-01-24 11:53:52 +00:00
ioctl ( fd , MIXER_WRITE ( oss_mixer_channel ) , & v ) ;
2002-02-21 16:02:26 +00:00
}
}
2001-06-21 23:07:15 +00:00
else
{
close ( fd ) ;
return CONTROL_ERROR ;
}
close ( fd ) ;
return CONTROL_OK ;
}
}
return CONTROL_ERROR ;
2001-06-02 23:25:43 +00:00
}
return CONTROL_UNKNOWN ;
}
// open & setup audio device
// return: 1=success 0=fail
static int init ( int rate , int channels , int format , int flags ) {
2004-01-24 11:53:52 +00:00
char * mixer_channels [ SOUND_MIXER_NRDEVICES ] = SOUND_DEVICE_NAMES ;
2004-12-27 17:30:15 +00:00
int oss_format ;
2005-11-10 09:02:56 +00:00
char * mdev = mixer_device , * mchan = mixer_channel ;
2001-06-02 23:25:43 +00:00
2004-12-28 19:11:14 +00:00
mp_msg ( MSGT_AO , MSGL_V , " ao2: %d Hz %d chans %s \n " , rate , channels ,
af_fmt2str_short ( format ) ) ;
2001-06-02 23:25:43 +00:00
2005-11-10 09:02:56 +00:00
if ( ao_subdevice ) {
char * m , * c ;
m = strchr ( ao_subdevice , ' : ' ) ;
if ( m ) {
c = strchr ( m + 1 , ' : ' ) ;
if ( c ) {
mchan = c + 1 ;
c [ 0 ] = ' \0 ' ;
}
mdev = m + 1 ;
m [ 0 ] = ' \0 ' ;
}
2001-06-21 23:07:15 +00:00
dsp = ao_subdevice ;
2005-11-10 09:02:56 +00:00
}
2001-06-21 23:07:15 +00:00
2005-11-10 09:02:56 +00:00
if ( mdev )
oss_mixer_device = mdev ;
else
oss_mixer_device = PATH_DEV_MIXER ;
2009-05-13 02:58:57 +00:00
2005-11-10 09:02:56 +00:00
if ( mchan ) {
2004-01-24 11:53:52 +00:00
int fd , devs , i ;
2009-05-13 02:58:57 +00:00
2004-01-24 11:53:52 +00:00
if ( ( fd = open ( oss_mixer_device , O_RDONLY ) ) = = - 1 ) {
2004-09-18 20:31:28 +00:00
mp_msg ( MSGT_AO , MSGL_ERR , MSGTR_AO_OSS_CantOpenMixer ,
2004-01-24 11:53:52 +00:00
oss_mixer_device , strerror ( errno ) ) ;
} else {
ioctl ( fd , SOUND_MIXER_READ_DEVMASK , & devs ) ;
close ( fd ) ;
2009-05-13 02:58:57 +00:00
2004-01-24 11:53:52 +00:00
for ( i = 0 ; i < SOUND_MIXER_NRDEVICES ; i + + ) {
2005-11-10 09:02:56 +00:00
if ( ! strcasecmp ( mixer_channels [ i ] , mchan ) ) {
2004-01-24 11:53:52 +00:00
if ( ! ( devs & ( 1 < < i ) ) ) {
2005-11-10 09:02:56 +00:00
mp_msg ( MSGT_AO , MSGL_ERR , MSGTR_AO_OSS_ChanNotFound , mchan ) ;
2004-01-24 11:53:52 +00:00
i = SOUND_MIXER_NRDEVICES + 1 ;
break ;
}
oss_mixer_channel = i ;
break ;
}
}
if ( i = = SOUND_MIXER_NRDEVICES ) {
2005-11-10 09:02:56 +00:00
mp_msg ( MSGT_AO , MSGL_ERR , MSGTR_AO_OSS_ChanNotFound , mchan ) ;
2004-01-24 11:53:52 +00:00
}
}
2005-11-10 09:02:56 +00:00
} else
oss_mixer_channel = SOUND_MIXER_PCM ;
2004-01-24 11:53:52 +00:00
2002-06-03 23:23:03 +00:00
mp_msg ( MSGT_AO , MSGL_V , " audio_setup: using '%s' dsp device \n " , dsp ) ;
2004-01-24 11:53:52 +00:00
mp_msg ( MSGT_AO , MSGL_V , " audio_setup: using '%s' mixer device \n " , oss_mixer_device ) ;
mp_msg ( MSGT_AO , MSGL_V , " audio_setup: using '%s' mixer device \n " , mixer_channels [ oss_mixer_channel ] ) ;
2001-06-21 23:07:15 +00:00
2002-04-29 20:42:15 +00:00
# ifdef __linux__
2002-03-19 19:16:01 +00:00
audio_fd = open ( dsp , O_WRONLY | O_NONBLOCK ) ;
2002-04-29 20:42:15 +00:00
# else
audio_fd = open ( dsp , O_WRONLY ) ;
# endif
2001-06-02 23:25:43 +00:00
if ( audio_fd < 0 ) {
2004-09-18 20:31:28 +00:00
mp_msg ( MSGT_AO , MSGL_ERR , MSGTR_AO_OSS_CantOpenDev , dsp , strerror ( errno ) ) ;
2001-06-02 23:25:43 +00:00
return 0 ;
}
2002-04-29 20:42:15 +00:00
# ifdef __linux__
2002-03-19 19:16:01 +00:00
/* Remove the non-blocking flag */
if ( fcntl ( audio_fd , F_SETFL , 0 ) < 0 ) {
2004-09-18 20:31:28 +00:00
mp_msg ( MSGT_AO , MSGL_ERR , MSGTR_AO_OSS_CantMakeFd , strerror ( errno ) ) ;
2002-03-19 19:16:01 +00:00
return 0 ;
2009-05-13 02:58:57 +00:00
}
2002-04-29 20:42:15 +00:00
# endif
2002-11-28 16:14:08 +00:00
# if defined(FD_CLOEXEC) && defined(F_SETFD)
fcntl ( audio_fd , F_SETFD , FD_CLOEXEC ) ;
# endif
2009-05-13 02:58:57 +00:00
2004-12-27 17:30:15 +00:00
if ( format = = AF_FORMAT_AC3 ) {
2002-01-15 22:55:28 +00:00
ao_data . samplerate = rate ;
ioctl ( audio_fd , SNDCTL_DSP_SPEED , & ao_data . samplerate ) ;
}
2002-04-22 22:33:06 +00:00
2009-05-13 02:58:57 +00:00
ac3_retry :
2001-11-24 05:21:22 +00:00
ao_data . format = format ;
2004-12-27 17:30:15 +00:00
oss_format = format2oss ( format ) ;
2004-12-28 01:59:12 +00:00
if ( oss_format = = - 1 ) {
2009-07-26 19:53:00 +00:00
# if HAVE_BIGENDIAN
2004-12-28 01:59:12 +00:00
oss_format = AFMT_S16_BE ;
# else
oss_format = AFMT_S16_LE ;
# endif
2005-01-06 17:35:43 +00:00
format = AF_FORMAT_S16_NE ;
2004-12-28 01:59:12 +00:00
}
2004-12-27 17:30:15 +00:00
if ( ioctl ( audio_fd , SNDCTL_DSP_SETFMT , & oss_format ) < 0 | |
2005-02-26 23:03:53 +00:00
oss_format ! = format2oss ( format ) ) {
mp_msg ( MSGT_AO , MSGL_WARN , MSGTR_AO_OSS_CantSet , dsp ,
af_fmt2str_short ( format ) , af_fmt2str_short ( AF_FORMAT_S16_NE ) ) ;
2005-01-06 17:35:43 +00:00
format = AF_FORMAT_S16_NE ;
2002-04-22 22:33:06 +00:00
goto ac3_retry ;
2001-08-15 11:50:55 +00:00
}
2002-10-08 22:41:45 +00:00
#if 0
2004-12-27 17:30:15 +00:00
if ( oss_format ! = format2oss ( format ) )
2005-11-28 23:43:24 +00:00
mp_msg ( MSGT_AO , MSGL_WARN , " WARNING! Your soundcard does NOT support %s sample format! Broken audio or bad playback speed are possible! Try with '-af format' \n " , audio_out_format_name ( format ) ) ;
2002-10-08 22:41:45 +00:00
# endif
2004-12-27 17:30:15 +00:00
ao_data . format = oss2format ( oss_format ) ;
if ( ao_data . format = = - 1 ) return 0 ;
2004-12-28 19:11:14 +00:00
mp_msg ( MSGT_AO , MSGL_V , " audio_setup: sample format: %s (requested: %s) \n " ,
af_fmt2str_short ( ao_data . format ) , af_fmt2str_short ( format ) ) ;
2009-05-13 02:58:57 +00:00
2003-01-09 09:27:51 +00:00
ao_data . channels = channels ;
2004-12-27 17:30:15 +00:00
if ( format ! = AF_FORMAT_AC3 ) {
2001-11-28 12:46:23 +00:00
// We only use SNDCTL_DSP_CHANNELS for >2 channels, in case some drivers don't have it
if ( ao_data . channels > 2 ) {
2001-12-04 17:54:08 +00:00
if ( ioctl ( audio_fd , SNDCTL_DSP_CHANNELS , & ao_data . channels ) = = - 1 | |
ao_data . channels ! = channels ) {
2004-09-18 20:31:28 +00:00
mp_msg ( MSGT_AO , MSGL_ERR , MSGTR_AO_OSS_CantSetChans , channels ) ;
2001-11-28 12:46:23 +00:00
return 0 ;
}
}
else {
int c = ao_data . channels - 1 ;
if ( ioctl ( audio_fd , SNDCTL_DSP_STEREO , & c ) = = - 1 ) {
2004-09-18 20:31:28 +00:00
mp_msg ( MSGT_AO , MSGL_ERR , MSGTR_AO_OSS_CantSetChans , ao_data . channels ) ;
2001-11-28 12:46:23 +00:00
return 0 ;
}
2003-01-28 17:25:53 +00:00
ao_data . channels = c + 1 ;
2001-11-28 12:46:23 +00:00
}
2002-06-03 23:23:03 +00:00
mp_msg ( MSGT_AO , MSGL_V , " audio_setup: using %d channels (requested: %d) \n " , ao_data . channels , channels ) ;
2001-11-28 12:46:23 +00:00
// set rate
ao_data . samplerate = rate ;
ioctl ( audio_fd , SNDCTL_DSP_SPEED , & ao_data . samplerate ) ;
2002-06-03 23:23:03 +00:00
mp_msg ( MSGT_AO , MSGL_V , " audio_setup: using %d Hz samplerate (requested: %d) \n " , ao_data . samplerate , rate ) ;
2001-08-15 11:50:55 +00:00
}
2001-06-02 23:25:43 +00:00
if ( ioctl ( audio_fd , SNDCTL_DSP_GETOSPACE , & zz ) = = - 1 ) {
int r = 0 ;
2004-09-18 20:31:28 +00:00
mp_msg ( MSGT_AO , MSGL_WARN , MSGTR_AO_OSS_CantUseGetospace ) ;
2001-06-02 23:25:43 +00:00
if ( ioctl ( audio_fd , SNDCTL_DSP_GETBLKSIZE , & r ) = = - 1 ) {
2002-06-03 23:23:03 +00:00
mp_msg ( MSGT_AO , MSGL_V , " audio_setup: %d bytes/frag (config.h) \n " , ao_data . outburst ) ;
2001-06-02 23:25:43 +00:00
} else {
2001-11-24 05:21:22 +00:00
ao_data . outburst = r ;
2002-06-03 23:23:03 +00:00
mp_msg ( MSGT_AO , MSGL_V , " audio_setup: %d bytes/frag (GETBLKSIZE) \n " , ao_data . outburst ) ;
2001-06-02 23:25:43 +00:00
}
} else {
2002-06-03 23:23:03 +00:00
mp_msg ( MSGT_AO , MSGL_V , " audio_setup: frags: %3d/%d (%d bytes/frag) free: %6d \n " ,
2001-06-02 23:25:43 +00:00
zz . fragments , zz . fragstotal , zz . fragsize , zz . bytes ) ;
2001-11-24 05:21:22 +00:00
if ( ao_data . buffersize = = - 1 ) ao_data . buffersize = zz . bytes ;
ao_data . outburst = zz . fragsize ;
2001-06-02 23:25:43 +00:00
}
2001-11-24 05:21:22 +00:00
if ( ao_data . buffersize = = - 1 ) {
2001-06-02 23:25:43 +00:00
// Measuring buffer size:
void * data ;
2001-11-24 05:21:22 +00:00
ao_data . buffersize = 0 ;
2001-06-02 23:25:43 +00:00
# ifdef HAVE_AUDIO_SELECT
2001-11-24 05:21:22 +00:00
data = malloc ( ao_data . outburst ) ; memset ( data , 0 , ao_data . outburst ) ;
while ( ao_data . buffersize < 0x40000 ) {
2001-06-02 23:25:43 +00:00
fd_set rfds ;
struct timeval tv ;
FD_ZERO ( & rfds ) ; FD_SET ( audio_fd , & rfds ) ;
tv . tv_sec = 0 ; tv . tv_usec = 0 ;
if ( ! select ( audio_fd + 1 , NULL , & rfds , NULL , & tv ) ) break ;
2001-11-24 05:21:22 +00:00
write ( audio_fd , data , ao_data . outburst ) ;
ao_data . buffersize + = ao_data . outburst ;
2001-06-02 23:25:43 +00:00
}
free ( data ) ;
2001-11-24 05:21:22 +00:00
if ( ao_data . buffersize = = 0 ) {
2004-09-18 20:31:28 +00:00
mp_msg ( MSGT_AO , MSGL_ERR , MSGTR_AO_OSS_CantUseSelect ) ;
2001-06-02 23:25:43 +00:00
return 0 ;
}
# endif
}
2002-12-28 13:45:04 +00:00
ao_data . bps = ao_data . channels ;
2004-12-27 17:30:15 +00:00
if ( ao_data . format ! = AF_FORMAT_U8 & & ao_data . format ! = AF_FORMAT_S8 )
2002-12-28 13:45:04 +00:00
ao_data . bps * = 2 ;
2002-05-13 20:11:51 +00:00
ao_data . outburst - = ao_data . outburst % ao_data . bps ; // round down
2002-12-28 13:45:04 +00:00
ao_data . bps * = ao_data . samplerate ;
2002-05-13 20:11:51 +00:00
2001-06-02 23:25:43 +00:00
return 1 ;
}
// close audio device
2004-04-06 17:55:36 +00:00
static void uninit ( int immed ) {
2002-08-28 19:37:13 +00:00
if ( audio_fd = = - 1 ) return ;
2004-04-06 17:55:36 +00:00
# ifdef SNDCTL_DSP_SYNC
// to get the buffer played
if ( ! immed )
ioctl ( audio_fd , SNDCTL_DSP_SYNC , NULL ) ;
# endif
2001-06-05 02:26:56 +00:00
# ifdef SNDCTL_DSP_RESET
2004-04-06 17:55:36 +00:00
if ( immed )
ioctl ( audio_fd , SNDCTL_DSP_RESET , NULL ) ;
2001-06-05 02:26:56 +00:00
# endif
2001-06-02 23:25:43 +00:00
close ( audio_fd ) ;
2002-08-28 19:37:13 +00:00
audio_fd = - 1 ;
2001-06-02 23:25:43 +00:00
}
// stop playing and empty buffers (for seeking/pause)
2006-02-09 14:08:03 +00:00
static void reset ( void ) {
2004-12-27 17:30:15 +00:00
int oss_format ;
2004-04-06 17:55:36 +00:00
uninit ( 1 ) ;
2002-04-29 20:42:15 +00:00
audio_fd = open ( dsp , O_WRONLY ) ;
if ( audio_fd < 0 ) {
2004-09-18 20:31:28 +00:00
mp_msg ( MSGT_AO , MSGL_ERR , MSGTR_AO_OSS_CantReopen , strerror ( errno ) ) ;
2001-06-02 23:25:43 +00:00
return ;
}
2003-08-13 21:04:15 +00:00
# if defined(FD_CLOEXEC) && defined(F_SETFD)
fcntl ( audio_fd , F_SETFD , FD_CLOEXEC ) ;
# endif
2004-12-27 17:30:15 +00:00
oss_format = format2oss ( ao_data . format ) ;
2009-02-17 21:08:22 +00:00
if ( ao_data . format = = AF_FORMAT_AC3 )
ioctl ( audio_fd , SNDCTL_DSP_SPEED , & ao_data . samplerate ) ;
2004-12-27 17:30:15 +00:00
ioctl ( audio_fd , SNDCTL_DSP_SETFMT , & oss_format ) ;
if ( ao_data . format ! = AF_FORMAT_AC3 ) {
2001-11-28 12:46:23 +00:00
if ( ao_data . channels > 2 )
ioctl ( audio_fd , SNDCTL_DSP_CHANNELS , & ao_data . channels ) ;
else {
int c = ao_data . channels - 1 ;
ioctl ( audio_fd , SNDCTL_DSP_STEREO , & c ) ;
}
ioctl ( audio_fd , SNDCTL_DSP_SPEED , & ao_data . samplerate ) ;
2001-08-15 11:50:55 +00:00
}
2001-06-02 23:25:43 +00:00
}
2001-06-05 18:40:44 +00:00
// stop playing, keep buffers (for pause)
2006-02-09 14:08:03 +00:00
static void audio_pause ( void )
2001-06-05 18:40:44 +00:00
{
2007-07-19 19:15:59 +00:00
prepause_space = get_space ( ) ;
2004-04-06 17:55:36 +00:00
uninit ( 1 ) ;
2001-06-05 18:40:44 +00:00
}
// resume playing, after audio_pause()
2006-02-09 14:08:03 +00:00
static void audio_resume ( void )
2001-06-05 18:40:44 +00:00
{
2007-07-19 19:15:59 +00:00
int fillcnt ;
2002-08-28 19:37:13 +00:00
reset ( ) ;
2007-07-19 19:15:59 +00:00
fillcnt = get_space ( ) - prepause_space ;
2009-02-16 18:38:54 +00:00
if ( fillcnt > 0 & & ! ( ao_data . format & AF_FORMAT_SPECIAL_MASK ) ) {
2007-07-19 19:15:59 +00:00
void * silence = calloc ( fillcnt , 1 ) ;
play ( silence , fillcnt , 0 ) ;
free ( silence ) ;
}
2001-06-05 18:40:44 +00:00
}
2001-06-02 23:25:43 +00:00
// return: how many bytes can be played without blocking
2006-02-09 14:08:03 +00:00
static int get_space ( void ) {
2001-11-24 05:21:22 +00:00
int playsize = ao_data . outburst ;
2001-06-02 23:25:43 +00:00
2001-06-05 02:26:56 +00:00
# ifdef SNDCTL_DSP_GETOSPACE
2001-06-02 23:25:43 +00:00
if ( ioctl ( audio_fd , SNDCTL_DSP_GETOSPACE , & zz ) ! = - 1 ) {
// calculate exact buffer space:
2001-12-11 09:23:57 +00:00
playsize = zz . fragments * zz . fragsize ;
if ( playsize > MAX_OUTBURST )
playsize = ( MAX_OUTBURST / zz . fragsize ) * zz . fragsize ;
return playsize ;
2001-06-02 23:25:43 +00:00
}
2001-06-05 02:26:56 +00:00
# endif
2001-06-02 23:25:43 +00:00
// check buffer
# ifdef HAVE_AUDIO_SELECT
{ fd_set rfds ;
struct timeval tv ;
FD_ZERO ( & rfds ) ;
FD_SET ( audio_fd , & rfds ) ;
tv . tv_sec = 0 ;
tv . tv_usec = 0 ;
if ( ! select ( audio_fd + 1 , NULL , & rfds , NULL , & tv ) ) return 0 ; // not block!
}
# endif
2001-11-24 05:21:22 +00:00
return ao_data . outburst ;
2001-06-02 23:25:43 +00:00
}
// plays 'len' bytes of 'data'
// it should round it down to outburst*n
// return: number of bytes played
static int play ( void * data , int len , int flags ) {
2006-06-28 19:22:27 +00:00
if ( len = = 0 )
return len ;
if ( len > ao_data . outburst | | ! ( flags & AOPLAY_FINAL_CHUNK ) ) {
len / = ao_data . outburst ;
len * = ao_data . outburst ;
}
len = write ( audio_fd , data , len ) ;
2001-06-02 23:25:43 +00:00
return len ;
}
static int audio_delay_method = 2 ;
2001-11-24 05:21:22 +00:00
// return: delay in seconds between first and last sample in buffer
2006-02-09 14:08:03 +00:00
static float get_delay ( void ) {
2001-11-24 05:21:22 +00:00
/* Calculate how many bytes/second is sent out */
2001-06-02 23:25:43 +00:00
if ( audio_delay_method = = 2 ) {
2002-04-27 22:42:27 +00:00
# ifdef SNDCTL_DSP_GETODELAY
2001-06-02 23:25:43 +00:00
int r = 0 ;
if ( ioctl ( audio_fd , SNDCTL_DSP_GETODELAY , & r ) ! = - 1 )
2001-11-24 05:21:22 +00:00
return ( ( float ) r ) / ( float ) ao_data . bps ;
2002-04-27 22:42:27 +00:00
# endif
2001-06-02 23:25:43 +00:00
audio_delay_method = 1 ; // fallback if not supported
}
if ( audio_delay_method = = 1 ) {
// SNDCTL_DSP_GETOSPACE
if ( ioctl ( audio_fd , SNDCTL_DSP_GETOSPACE , & zz ) ! = - 1 )
2001-11-24 05:21:22 +00:00
return ( ( float ) ( ao_data . buffersize - zz . bytes ) ) / ( float ) ao_data . bps ;
2001-06-02 23:25:43 +00:00
audio_delay_method = 0 ; // fallback if not supported
}
2001-11-24 05:21:22 +00:00
return ( ( float ) ao_data . buffersize ) / ( float ) ao_data . bps ;
2001-06-02 23:25:43 +00:00
}