--- a/src/alsadirect.cpp
+++ b/src/alsadirect.cpp
@@ -34,24 +34,29 @@
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#endif
+static const snd_pcm_uframes_t periodsize = 32768; /* Periodsize (bytes) */
+
// The queue for audio blocks ready for alsa
static const unsigned int qs = 200;
static const unsigned int qt = qs/2;
// the 40 value should be computed from the alsa buffer size. It's
// there becausee we have a jump on the first alsa write (alsa buffer
// is empty).
-static const unsigned int qit = qs/2 + 40;
+static const unsigned int qit = qs/2 + periodsize/1024;
static WorkQueue<AudioMessage*> alsaqueue("alsaqueue", qs);
static snd_pcm_t *pcm;
+static bool qinit = false;
static void *alsawriter(void *p)
{
- if (!alsaqueue.waitminsz(qit)) {
- LOGERR("alsawriter: waitminsz failed\n");
- return (void *)1;
- }
while (true) {
+ if (!qinit) {
+ if (!alsaqueue.waitminsz(qit)) {
+ LOGERR("alsawriter: waitminsz failed\n");
+ return (void *)1;
+ }
+ }
AudioMessage *tsk = 0;
size_t qsz;
if (!alsaqueue.take(&tsk, &qsz)) {
@@ -59,7 +64,6 @@
alsaqueue.workerExit();
return (void*)1;
}
-
// Bufs
snd_pcm_uframes_t frames =
tsk->m_bytes / (tsk->m_chans * (tsk->m_bits/8));
@@ -67,22 +71,24 @@
if (ret != int(frames)) {
LOGERR("snd-cm_writei(" << frames <<" frames) failed: ret: " <<
ret << endl);
- if (ret < 0)
+ if (ret < 0) {
+ qinit = false;
snd_pcm_prepare(pcm);
-// return (void *)1;
+ }
+ } else {
+ qinit = true;
}
}
}
-static bool alsa_init(AudioMessage *tsk)
+static bool alsa_init(const string& dev, AudioMessage *tsk)
{
snd_pcm_hw_params_t *hw_params;
int err;
-// static const string dev("plughw:CARD=PCH,DEV=0");
- static const string dev("hw:2,0");
const char *cmd = "";
unsigned int actual_rate = tsk->m_freq;
int dir=0;
+ int periods = 2; /* Number of periods */
if ((err = snd_pcm_open(&pcm, dev.c_str(),
SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
@@ -127,7 +133,20 @@
tsk->m_chans)) < 0) {
goto error;
}
-
+ /* Set number of periods. Periods used to be called fragments. */
+ cmd = "snd_pcm_hw_params_set_periods";
+ if (snd_pcm_hw_params_set_periods(pcm, hw_params, periods, 0) < 0) {
+ goto error;
+ }
+
+ /* Set buffer size (in frames). The resulting latency is given by */
+ /* latency = periodsize * periods / (rate * bytes_per_frame) */
+ cmd = "snd_pcm_hw_params_set_buffer_size";
+ if (snd_pcm_hw_params_set_buffer_size(pcm, hw_params,
+ (periodsize * periods)>>2) < 0) {
+ goto error;
+ }
+
cmd = "snd_pcm_hw_params";
if ((err = snd_pcm_hw_params(pcm, hw_params)) < 0) {
goto error;
@@ -149,9 +168,11 @@
LOGDEB("alsaEater: queue " << ctxt->queue << endl);
WorkQueue<AudioMessage*> *queue = ctxt->queue;
+ string alsadevice = ctxt->alsadevice;
+
delete ctxt;
- bool qinit = false;
+ qinit = false;
int src_error = 0;
SRC_STATE *src_state = 0;
SRC_DATA src_data;
@@ -169,7 +190,7 @@
}
if (src_state == 0) {
- if (!alsa_init(tsk)) {
+ if (!alsa_init(alsadevice, tsk)) {
queue->workerExit();
return (void *)1;
}
@@ -179,7 +200,8 @@
// MEDIUM_QUALITY is around 10%
// FASTEST is 4-5%. Given that this is process-wide, probably
// a couple % in fact.
- // To be re-evaluated on the pi...
+ // To be re-evaluated on the pi... FASTEST is 30% CPU on a Pi 2
+ // with USB audio. Curiously it's 25-30% on a Pi1 with i2s audio.
src_state = src_new(SRC_SINC_FASTEST, tsk->m_chans, &src_error);
}
@@ -192,10 +214,12 @@
if (samplerate_ratio > 1.1)
samplerate_ratio = 1.1;
} else {
- samplerate_ratio -= adj;
+ samplerate_ratio = 1.0 - adj;
if (samplerate_ratio < 0.9)
samplerate_ratio = 0.9;
}
+ } else {
+ samplerate_ratio = 1.0;
}
unsigned int tot_samples = tsk->m_bytes / (tsk->m_bits/8);
@@ -275,8 +299,6 @@
LOGERR("alsaEater: queue put failed\n");
return (void *)1;
}
- if (alsaqueue.qsize() >= qit)
- qinit = true;
}
}