--- a/sc2src/alsadirect.cpp
+++ b/sc2src/alsadirect.cpp
@@ -105,17 +105,20 @@
// there is one period belonging to the hardware and normally
// others that the software can fill up. The minimum reasonable is
// 2 periods (one for us, one for the hardware), which we try to
-// use as this gives minimum latency while being workable. But we
-// have to accept that the driver may have other constraints. Not
-// too sure why we bother actually, because we don't use the
-// resulting config at all while writing...
-// In any case, if we get what we ask for, we have an in-driver
-// latency of between 16KBytes and 32KBytes, 4K to 8K frames (16:2),
-// 200mS at 44.1 Khz
+// use as this gives minimum latency while being workable.
//
-// These may be changed depending on local alsa caps:
-static snd_pcm_uframes_t periodsize = 16384; /* Periodsize (bytes) */
-static unsigned int periods = 2; /* Number of periods */
+// We try to control this so that the delay is constant in all
+// instances, independantly of the local hardware defaults. But we
+// have to accept that the driver may have other constraints.
+//
+// It appears that the min buffer time on common hardware is about 200
+// mS, so that it does not work to ask for much less.
+//
+static unsigned int buffer_time = 200000;
+static unsigned int period_time = 50000;
+// Set after initializing the driver
+static snd_pcm_uframes_t periodframes;
+static snd_pcm_uframes_t bufferframes;
static void *alsawriter(void *p)
{
@@ -176,7 +179,6 @@
snd_pcm_hw_params_t *hwparams;
int err;
const char *cmd = "";
- int dir=0;
unsigned int actual_rate = tsk->m_freq;
if ((err = snd_pcm_open(&pcm, dev.c_str(),
@@ -204,19 +206,18 @@
}
cmd = "snd_pcm_hw_params_set_format";
- if ((err =
- snd_pcm_hw_params_set_format(pcm, hwparams,
- SND_PCM_FORMAT_S16_LE)) < 0) {
+ if ((err = snd_pcm_hw_params_set_format(pcm, hwparams,
+ SND_PCM_FORMAT_S16_LE)) < 0) {
goto error;
}
cmd = "snd_pcm_hw_params_set_channels";
- if ((err = snd_pcm_hw_params_set_channels(pcm, hwparams,
+ if ((err = snd_pcm_hw_params_set_channels(pcm, hwparams,
tsk->m_chans)) < 0) {
goto error;
}
cmd = "snd_pcm_hw_params_set_rate_near";
if ((err = snd_pcm_hw_params_set_rate_near(pcm, hwparams,
- &actual_rate, &dir)) < 0) {
+ &actual_rate, 0)) < 0) {
goto error;
}
if (actual_rate != tsk->m_freq) {
@@ -225,40 +226,75 @@
goto error;
}
unsigned int periodsmin, periodsmax;
- snd_pcm_hw_params_get_periods_min(hwparams, &periodsmin, &dir);
- snd_pcm_hw_params_get_periods_max(hwparams, &periodsmax, &dir);
- LOGDEB("Alsa: periods min " << periodsmin <<
- " max " << periodsmax << endl);
- periods = 2;
- if (periods < periodsmin || periods > periodsmax)
- periods = periodsmin;
- cmd = "snd_pcm_hw_params_set_periods";
- if ((err = snd_pcm_hw_params_set_periods(pcm, hwparams, periods, 0)) < 0) {
- goto error;
- }
- snd_pcm_uframes_t bsmax, bsmin;
+ snd_pcm_hw_params_get_periods_min(hwparams, &periodsmin, 0);
+ snd_pcm_hw_params_get_periods_max(hwparams, &periodsmax, 0);
+ snd_pcm_uframes_t bsmax, bsmin, prmin, prmax;
snd_pcm_hw_params_get_buffer_size_min(hwparams, &bsmin);
snd_pcm_hw_params_get_buffer_size_max(hwparams, &bsmax);
- unsigned int bufferframes;
- bufferframes = periodsize * periods / (2*tsk->m_chans);
- if (bufferframes < bsmin || bufferframes > bsmax) {
- bufferframes = bsmin;
- periodsize = bufferframes / periods * (2 * tsk->m_chans);
- }
- cmd = "snd_pcm_hw_params_set_buffer_size";
- LOGDEB("Alsa: set buffer_size: min " << bsmin << " max " << bsmax <<
- " val " << bufferframes << endl);
- if ((err = snd_pcm_hw_params_set_buffer_size(pcm, hwparams, bufferframes))
+ snd_pcm_hw_params_get_period_size_min(hwparams, &prmin, 0);
+ snd_pcm_hw_params_get_period_size_max(hwparams, &prmax, 0);
+ LOGDEB("Alsa: periodsmin " << periodsmin << " periodsmax " << periodsmax <<
+ " bsminsz " << bsmin << " bsmaxsz " << bsmax <<
+ " prminsz " << prmin << " prmaxsz " << prmax << endl);
+
+ cmd = "snd_pcm_hw_params_set_buffer_time_near";
+ unsigned int buftimereq;
+ buftimereq = buffer_time;
+ if ((err = snd_pcm_hw_params_set_buffer_time_near(pcm, hwparams,
+ &buffer_time, 0))
< 0) {
goto error;
}
-
+ LOGDEB("Alsa: set buffer_time_near: asked " <<buftimereq << " got " <<
+ buffer_time << endl);
+
+ cmd = "snd_pcm_hw_params_set_period_time_near";
+ buftimereq = period_time;
+ if ((err = snd_pcm_hw_params_set_period_time_near(pcm, hwparams,
+ &period_time, 0))
+ < 0) {
+ goto error;
+ }
+ LOGDEB("Alsa: set_period_time_near: asked " << buftimereq << " got " <<
+ period_time << endl);
+
+ snd_pcm_hw_params_get_period_size(hwparams, &periodframes, 0);
+ snd_pcm_hw_params_get_buffer_size(hwparams, &bufferframes);
+ LOGDEB("Alsa: bufferframes " << bufferframes << " periodframes " <<
+ periodframes << endl);
+
cmd = "snd_pcm_hw_params";
if ((err = snd_pcm_hw_params(pcm, hwparams)) < 0) {
goto error;
}
snd_pcm_hw_params_free(hwparams);
+
+ /* configure SW params */
+ snd_pcm_sw_params_t *swparams;
+ snd_pcm_sw_params_alloca(&swparams);
+
+ cmd = "snd_pcm_sw_params_current";
+ err = snd_pcm_sw_params_current(pcm, swparams);
+ if (err < 0)
+ goto error;
+
+ cmd = "snd_pcm_sw_params_set_start_threshold";
+ err = snd_pcm_sw_params_set_start_threshold(pcm, swparams,
+ bufferframes - periodframes);
+ if (err < 0)
+ goto error;
+
+ cmd = "snd_pcm_sw_params_set_avail_min";
+ err = snd_pcm_sw_params_set_avail_min(pcm, swparams, periodframes);
+ if (err < 0)
+ goto error;
+
+ cmd = "snd_pcm_sw_params";
+ err = snd_pcm_sw_params(pcm, swparams);
+ if (err < 0)
+ goto error;
+
return true;
error: