Browse Source

platform/alsa: fix case when buffer size is POT, improve latency, calculate sample count

pull/2/head
mittorn 5 years ago
parent
commit
6723758edc
  1. 152
      engine/platform/linux/s_alsa.c

152
engine/platform/linux/s_alsa.c

@ -18,8 +18,7 @@ GNU General Public License for more details.
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#include "sound.h" #include "sound.h"
#define BUFFER_SAMPLES 4096 #define PERIOD_SIZE_DEFAULT 2048
#define SUBMISSION_CHUNK BUFFER_SAMPLES / 2
static struct s_alsa_t static struct s_alsa_t
{ {
@ -29,8 +28,27 @@ static struct s_alsa_t
struct sndinfo * si; struct sndinfo * si;
int period_size; int period_size;
qboolean period_npot;
qboolean paused;
} s_alsa; } s_alsa;
void SND_Pause_f( void )
{
s_alsa.paused = Q_atoi( Cmd_Argv( 1 ) ) ;
if( !s_alsa.paused )
{
snd_pcm_start( s_alsa.pcm_handle );
snd_pcm_prepare( s_alsa.pcm_handle );
}
else
{
snd_pcm_drain( s_alsa.pcm_handle );
snd_pcm_drop( s_alsa.pcm_handle );
}
}
/* /*
================== ==================
SNDDMA_Init SNDDMA_Init
@ -44,9 +62,12 @@ qboolean SNDDMA_Init( void )
unsigned int r; unsigned int r;
snd_pcm_uframes_t p = 0; snd_pcm_uframes_t p = 0;
string device = "default"; string device = "default";
int samples;
Sys_GetParmFromCmdLine( "-alsadev", device ); Sys_GetParmFromCmdLine( "-alsadev", device );
Cmd_AddCommand("pcm_pause", SND_Pause_f, "set pcm pause (debug)" );
if( ( err = snd_pcm_open( &s_alsa.pcm_handle, device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK ) ) < 0) 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 ) ); Con_Printf( "ALSA: cannot open device %s(%s)\n", device, snd_strerror( err ) );
@ -115,7 +136,7 @@ qboolean SNDDMA_Init( void )
return false; return false;
} }
p = BUFFER_SAMPLES / dma.format.channels; p = PERIOD_SIZE_DEFAULT;
if( ( err = snd_pcm_hw_params_set_period_size_near( s_alsa.pcm_handle, s_alsa.hw_params, &p, &dir ) ) < 0 ) 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)); Con_Printf("ALSA: cannot set period size (%s)\n", snd_strerror(err));
@ -129,11 +150,31 @@ qboolean SNDDMA_Init( void )
if( dir != 0 ) if( dir != 0 )
{ {
snd_pcm_hw_params_get_period_size(s_alsa.hw_params, &p, NULL); 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 ); Con_Printf( "ALSA: period %d not supported, using %lu\n", PERIOD_SIZE_DEFAULT, p );
dir = 0; dir = 0;
} }
} }
if( 0 ) //( p & (p - 1) ) == 0 ) // power of two
{
// normally, set samples to period * 2 to minimize latency
samples = p * 2;
}
else
{
// if period is NPOT it cannot be used as dma.samples in Xash3D
// and need more space to send buffer partially
samples = 1;
while( samples < p * 4 )
samples <<= 1;
s_alsa.period_npot = true;
}
s_alsa.period_size = p;
p = samples;
snd_pcm_hw_params_set_buffer_size_near( s_alsa.pcm_handle, s_alsa.hw_params, &p );
Con_Printf( "ALSA: buffer size %lu\n", p );
// set params // set params
if( ( err = snd_pcm_hw_params( s_alsa.pcm_handle, s_alsa.hw_params ) ) < 0 ) if( ( err = snd_pcm_hw_params( s_alsa.pcm_handle, s_alsa.hw_params ) ) < 0 )
{ {
@ -143,19 +184,16 @@ qboolean SNDDMA_Init( void )
return false; return false;
} }
// request period size again dma.buffer = Z_Malloc( samples * 2 ); //allocate pcm frame buffer
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.samplepos = 0;
dma.samples = BUFFER_SAMPLES; dma.samples = samples;
dma.format.width = 2; dma.format.width = 2;
dma.initialized = 1; dma.initialized = 1;
snd_pcm_prepare( s_alsa.pcm_handle ); snd_pcm_prepare( s_alsa.pcm_handle );
snd_pcm_writei( s_alsa.pcm_handle, dma.buffer, 2 * s_alsa.period_size );
snd_pcm_start( s_alsa.pcm_handle );
return true; return true;
} }
@ -242,29 +280,78 @@ Send sound to device if buffer isn't really the dma buffer
*/ */
void SNDDMA_Submit( void ) void SNDDMA_Submit( void )
{ {
int avail = snd_pcm_avail_update( s_alsa.pcm_handle ); if( s_alsa.paused )
return;
while( avail >= s_alsa.period_size ) if( s_alsa.period_npot )
{ {
int size = dma.samples << 1; int avail = snd_pcm_avail_update( s_alsa.pcm_handle );
int pos = dma.samplepos << 1;
unsigned long len = s_alsa.period_size;
int wrapped = pos + len - size;
if( wrapped < 0 ) if( avail < 0 )
snd_pcm_prepare( s_alsa.pcm_handle );
while( avail >= s_alsa.period_size )
{ {
snd_pcm_writei( s_alsa.pcm_handle, dma.buffer + pos, len / 4 ); int size = dma.samples << 1;
dma.samplepos += len >> 1; int pos = dma.samplepos << 1;
unsigned long len = s_alsa.period_size * 4;
int wrapped = pos + len - size;
int w;
if( wrapped < 0 )
{
w = snd_pcm_writei( s_alsa.pcm_handle, dma.buffer + pos, len / 4 );
if( w < 0 )
{
snd_pcm_prepare(s_alsa.pcm_handle);
return;
}
dma.samplepos += len >> 1;
}
else
{
int remaining = size - pos;
w = snd_pcm_writei( s_alsa.pcm_handle, dma.buffer + pos, remaining / 4 );
if( w < 0 )
{
snd_pcm_prepare(s_alsa.pcm_handle);
return;
}
w = snd_pcm_writei( s_alsa.pcm_handle, dma.buffer, wrapped / 4 );
if( w < 0 )
{
snd_pcm_prepare(s_alsa.pcm_handle);
return;
}
dma.samplepos = wrapped >> 1;
}
avail = snd_pcm_avail_update( s_alsa.pcm_handle );
} }
else }
else // period is 1/2 of samples
{
int s, w, frames;
void *start;
if( !dma.buffer )
return;
s = dma.samplepos * 2;
start = (void *)&dma.buffer[s];
frames = s_alsa.period_size / 2;
// write to card
if( ( w = snd_pcm_writei( s_alsa.pcm_handle, start, frames ) ) < 0)
{ {
int remaining = size - pos; // xrun occured
snd_pcm_writei( s_alsa.pcm_handle, dma.buffer + pos, remaining / 4 ); snd_pcm_prepare( s_alsa.pcm_handle );
snd_pcm_writei( s_alsa.pcm_handle, dma.buffer, wrapped / 4 ); return;
dma.samplepos = wrapped >> 1;
} }
avail = snd_pcm_avail_update( s_alsa.pcm_handle ); dma.samplepos += w * 2; // mark progress
if(dma.samplepos >= dma.samples)
dma.samplepos = 0; // wrap buffer
} }
} }
@ -289,7 +376,18 @@ between a deactivate and an activate.
*/ */
void SNDDMA_Activate( qboolean active ) void SNDDMA_Activate( qboolean active )
{ {
snd_pcm_pause( s_alsa.pcm_handle, active ); s_alsa.paused = !active;
if( !s_alsa.paused )
{
snd_pcm_start( s_alsa.pcm_handle );
snd_pcm_prepare( s_alsa.pcm_handle );
}
else
{
snd_pcm_drain( s_alsa.pcm_handle );
snd_pcm_drop( s_alsa.pcm_handle );
}
} }
#endif #endif

Loading…
Cancel
Save