mirror of
https://github.com/FWGS/xash3d-fwgs
synced 2025-01-01 05:35:49 +01:00
platform: alsa support
This commit is contained in:
parent
93a43ae9a5
commit
23956a0b82
@ -27,6 +27,7 @@ GNU General Public License for more details.
|
||||
#define SOUND_NULL 0
|
||||
#define SOUND_SDL 1
|
||||
#define SOUND_OPENSLES 2
|
||||
#define SOUND_ALSA 3
|
||||
|
||||
// crash handler (XASH_CRASHHANDLER)
|
||||
#define CRASHHANDLER_NULL 0
|
||||
|
@ -81,7 +81,7 @@ SETUP BACKENDS DEFINITIONS
|
||||
#endif
|
||||
|
||||
#ifndef XASH_SOUND
|
||||
#define XASH_SOUND SOUND_NULL
|
||||
#define XASH_SOUND SOUND_ALSA
|
||||
#endif
|
||||
#define XASH_USE_EVDEV
|
||||
#endif // android case
|
||||
|
295
engine/platform/linux/s_alsa.c
Normal file
295
engine/platform/linux/s_alsa.c
Normal file
@ -0,0 +1,295 @@
|
||||
/*
|
||||
s_alsa.c - alsa sound hardware output
|
||||
Copyright (C) 2019 mittorn
|
||||
|
||||
This program 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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.
|
||||
*/
|
||||
|
||||
#include "platform/platform.h"
|
||||
#if XASH_SOUND == SOUND_ALSA
|
||||
#include <alsa/asoundlib.h>
|
||||
#include "sound.h"
|
||||
|
||||
#define BUFFER_SAMPLES 4096
|
||||
#define SUBMISSION_CHUNK BUFFER_SAMPLES / 2
|
||||
|
||||
static struct s_alsa_t
|
||||
{
|
||||
snd_pcm_t *pcm_handle;
|
||||
snd_pcm_hw_params_t *hw_params;
|
||||
|
||||
struct sndinfo * si;
|
||||
|
||||
int period_size;
|
||||
} s_alsa;
|
||||
|
||||
/*
|
||||
==================
|
||||
SNDDMA_Init
|
||||
|
||||
Initialize ALSA pcm device, and bind it to sndinfo.
|
||||
==================
|
||||
*/
|
||||
qboolean SNDDMA_Init( void )
|
||||
{
|
||||
int err, dir = 0;
|
||||
unsigned int r;
|
||||
snd_pcm_uframes_t p = 0;
|
||||
string device = "default";
|
||||
|
||||
Sys_GetParmFromCmdLine( "-alsadev", device );
|
||||
|
||||
if( ( err = snd_pcm_open( &s_alsa.pcm_handle, device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK ) ) < 0)
|
||||
{
|
||||
Con_Printf( "ALSA: cannot open device %s(%s)\n", device, snd_strerror( err ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( ( err = snd_pcm_hw_params_malloc( &s_alsa.hw_params )) < 0 )
|
||||
{
|
||||
Con_Printf( "ALSA: cannot allocate hw params(%s)\n", snd_strerror( err ) );
|
||||
snd_pcm_close(s_alsa.pcm_handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
if( ( err = snd_pcm_hw_params_any( s_alsa.pcm_handle, s_alsa.hw_params ) ) < 0 )
|
||||
{
|
||||
Con_Printf( "ALSA: cannot init hw params(%s)\n", snd_strerror( err ) );
|
||||
snd_pcm_hw_params_free( s_alsa.hw_params );
|
||||
snd_pcm_close( s_alsa.pcm_handle );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( ( err = snd_pcm_hw_params_set_access( s_alsa.pcm_handle, s_alsa.hw_params, SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 )
|
||||
{
|
||||
Con_Printf( "ALSA: cannot set access(%s)\n", snd_strerror( err ) );
|
||||
snd_pcm_hw_params_free( s_alsa.hw_params );
|
||||
snd_pcm_close( s_alsa.pcm_handle );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( ( err = snd_pcm_hw_params_set_format( s_alsa.pcm_handle, s_alsa.hw_params, SND_PCM_FORMAT_S16 ) ) < 0 )
|
||||
{
|
||||
Con_Printf("ALSA: 16 bit not supported\n");
|
||||
snd_pcm_hw_params_free( s_alsa.hw_params );
|
||||
snd_pcm_close( s_alsa.pcm_handle );
|
||||
return false;
|
||||
}
|
||||
|
||||
dma.format.speed = SOUND_DMA_SPEED;
|
||||
r = dma.format.speed;
|
||||
|
||||
if( ( err = snd_pcm_hw_params_set_rate_near( s_alsa.pcm_handle, s_alsa.hw_params, &r, &dir ) ) < 0 )
|
||||
{
|
||||
Con_Printf( "ALSA: cannot set rate %d(%s)\n", r, snd_strerror( err ) );
|
||||
snd_pcm_hw_params_free(s_alsa.hw_params);
|
||||
snd_pcm_close( s_alsa.pcm_handle );
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// rate succeeded, but is perhaps slightly different
|
||||
if( dir != 0 )
|
||||
{
|
||||
Con_Printf( "ALSA: rate %d not supported, using %d\n", SOUND_DMA_SPEED, r );
|
||||
dma.format.speed = r;
|
||||
dir = 0;
|
||||
}
|
||||
}
|
||||
|
||||
dma.format.channels = 2;
|
||||
|
||||
if( ( err = snd_pcm_hw_params_set_channels(s_alsa.pcm_handle, s_alsa.hw_params, dma.format.channels ) ) < 0 )
|
||||
{
|
||||
Con_Printf( "ALSA: cannot set channels %d(%s)\n", 2, snd_strerror( err ) );
|
||||
snd_pcm_hw_params_free( s_alsa.hw_params );
|
||||
snd_pcm_close( s_alsa.pcm_handle );
|
||||
return false;
|
||||
}
|
||||
|
||||
p = BUFFER_SAMPLES / dma.format.channels;
|
||||
if( ( err = snd_pcm_hw_params_set_period_size_near( s_alsa.pcm_handle, s_alsa.hw_params, &p, &dir ) ) < 0 )
|
||||
{
|
||||
Con_Printf("ALSA: cannot set period size (%s)\n", snd_strerror(err));
|
||||
snd_pcm_hw_params_free(s_alsa.hw_params);
|
||||
snd_pcm_close( s_alsa.pcm_handle );
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// period succeeded, but is perhaps slightly different
|
||||
if( dir != 0 )
|
||||
{
|
||||
snd_pcm_hw_params_get_period_size(s_alsa.hw_params, &p, NULL);
|
||||
Con_Printf( "ALSA: period %d not supported, using %lu\n", ( BUFFER_SAMPLES / dma.format.channels ), p );
|
||||
dir = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// set params
|
||||
if( ( err = snd_pcm_hw_params( s_alsa.pcm_handle, s_alsa.hw_params ) ) < 0 )
|
||||
{
|
||||
Con_Printf( "ALSA: cannot set params(%s)\n", snd_strerror( err ) );
|
||||
snd_pcm_hw_params_free(s_alsa.hw_params);
|
||||
snd_pcm_close( s_alsa.pcm_handle );
|
||||
return false;
|
||||
}
|
||||
|
||||
// request period size again
|
||||
snd_pcm_hw_params_get_period_size( s_alsa.hw_params, &p, NULL );
|
||||
s_alsa.period_size = p;
|
||||
Con_Printf( "ALSA: period size %lu\n", p );
|
||||
|
||||
dma.buffer = Z_Malloc( BUFFER_SAMPLES * 2 ); //allocate pcm frame buffer
|
||||
|
||||
dma.samplepos = 0;
|
||||
|
||||
dma.samples = BUFFER_SAMPLES;
|
||||
dma.format.width = 2;
|
||||
dma.initialized = 1;
|
||||
snd_pcm_prepare( s_alsa.pcm_handle );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
SNDDMA_GetDMAPos
|
||||
|
||||
return the current sample position (in mono samples read)
|
||||
inside the recirculating dma buffer, so the mixing code will know
|
||||
how many sample are required to fill it up.
|
||||
===============
|
||||
*/
|
||||
int SNDDMA_GetDMAPos( void )
|
||||
{
|
||||
return dma.samplepos;
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
SNDDMA_GetSoundtime
|
||||
|
||||
update global soundtime
|
||||
===============
|
||||
*/
|
||||
int SNDDMA_GetSoundtime( void )
|
||||
{
|
||||
static int buffers, oldsamplepos;
|
||||
int samplepos, fullsamples;
|
||||
|
||||
fullsamples = dma.samples / 2;
|
||||
|
||||
// it is possible to miscount buffers
|
||||
// if it has wrapped twice between
|
||||
// calls to S_Update. Oh well.
|
||||
samplepos = SNDDMA_GetDMAPos( );
|
||||
|
||||
if( samplepos < oldsamplepos )
|
||||
{
|
||||
buffers++; // buffer wrapped
|
||||
|
||||
if( paintedtime > 0x40000000 )
|
||||
{
|
||||
// time to chop things off to avoid 32 bit limits
|
||||
buffers = 0;
|
||||
paintedtime = fullsamples;
|
||||
S_StopAllSounds( true );
|
||||
}
|
||||
}
|
||||
|
||||
oldsamplepos = samplepos;
|
||||
|
||||
return ( buffers * fullsamples + samplepos / 2 );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==============
|
||||
SNDDMA_Shutdown
|
||||
|
||||
Closes the ALSA pcm device and frees the dma buffer.
|
||||
===============
|
||||
*/
|
||||
void SNDDMA_Shutdown(void)
|
||||
{
|
||||
Con_Printf( "Shutting down audio.\n" );
|
||||
dma.initialized = false;
|
||||
|
||||
if( dma.buffer )
|
||||
{
|
||||
snd_pcm_drop( s_alsa.pcm_handle );
|
||||
snd_pcm_close( s_alsa.pcm_handle );
|
||||
Mem_Free( dma.buffer );
|
||||
dma.buffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
SNDDMA_Submit
|
||||
|
||||
Send sound to device if buffer isn't really the dma buffer
|
||||
===============
|
||||
*/
|
||||
void SNDDMA_Submit( void )
|
||||
{
|
||||
int avail = snd_pcm_avail_update( s_alsa.pcm_handle );
|
||||
|
||||
while( avail >= s_alsa.period_size )
|
||||
{
|
||||
int size = dma.samples << 1;
|
||||
int pos = dma.samplepos << 1;
|
||||
unsigned long len = s_alsa.period_size;
|
||||
int wrapped = pos + len - size;
|
||||
|
||||
if( wrapped < 0 )
|
||||
{
|
||||
snd_pcm_writei( s_alsa.pcm_handle, dma.buffer + pos, len / 4 );
|
||||
dma.samplepos += len >> 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int remaining = size - pos;
|
||||
snd_pcm_writei( s_alsa.pcm_handle, dma.buffer + pos, remaining / 4 );
|
||||
snd_pcm_writei( s_alsa.pcm_handle, dma.buffer, wrapped / 4 );
|
||||
dma.samplepos = wrapped >> 1;
|
||||
}
|
||||
|
||||
avail = snd_pcm_avail_update( s_alsa.pcm_handle );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
SNDDMA_BeginPainting
|
||||
|
||||
Callback provided by the engine in case we need it. We don't.
|
||||
==============
|
||||
*/
|
||||
void SNDDMA_BeginPainting(void)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
SNDDMA_Activate
|
||||
Called when the main window gains or loses focus.
|
||||
The window have been destroyed and recreated
|
||||
between a deactivate and an activate.
|
||||
===========
|
||||
*/
|
||||
void SNDDMA_Activate( qboolean active )
|
||||
{
|
||||
snd_pcm_pause( s_alsa.pcm_handle, active );
|
||||
}
|
||||
|
||||
#endif
|
@ -28,7 +28,7 @@ def configure(conf):
|
||||
conf.check_cc(lib = i)
|
||||
elif conf.options.FBDEV_SW:
|
||||
conf.define('XASH_FBDEV', 1)
|
||||
conf.env.FBDEV
|
||||
conf.check_cc(lib = 'asound')
|
||||
else:
|
||||
conf.load('sdl2')
|
||||
if not conf.env.HAVE_SDL2:
|
||||
@ -57,7 +57,7 @@ def build(bld):
|
||||
|
||||
# basic build: dedicated only, no dependencies
|
||||
if bld.env.DEST_OS != 'win32':
|
||||
libs += [ 'DL' , 'M', 'PTHREAD' ]
|
||||
libs += [ 'DL' , 'M' , 'RT', 'PTHREAD']
|
||||
source += bld.path.ant_glob(['platform/posix/*.c'])
|
||||
else:
|
||||
libs += ['USER32', 'SHELL32', 'GDI32', 'ADVAPI32', 'DBGHELP', 'PSAPI', 'WS2_32' ]
|
||||
@ -82,9 +82,12 @@ def build(bld):
|
||||
'client/*.c',
|
||||
'client/vgui/*.c',
|
||||
'client/avi/*.c'])
|
||||
else:
|
||||
if bld.env.DEST_OS == 'linux':
|
||||
libs.append('RT')
|
||||
|
||||
if bld.env.DEST_OS == 'linux' and not bld.env.HAVE_SDL2:
|
||||
libs.append('RT')
|
||||
if not bld.env.DEDICATED:
|
||||
libs.append('ASOUND')
|
||||
|
||||
|
||||
# HACK: public headers must be put before common, so we don't get wrong mathlib included
|
||||
includes = ['common', 'server', 'client', 'client/vgui', '.', '../public', '../common', '../pm_shared' ]
|
||||
|
Loading…
Reference in New Issue
Block a user