|
a/sc2src/alsadirect.cpp |
|
b/sc2src/alsadirect.cpp |
|
... |
|
... |
31 |
#define BSWAP16(X) (X)
|
31 |
#define BSWAP16(X) (X)
|
32 |
#endif
|
32 |
#endif
|
33 |
|
33 |
|
34 |
#include <iostream>
|
34 |
#include <iostream>
|
35 |
#include <queue>
|
35 |
#include <queue>
|
|
|
36 |
#include <mutex>
|
36 |
#include <alsa/asoundlib.h>
|
37 |
#include <alsa/asoundlib.h>
|
37 |
|
38 |
|
38 |
#include <samplerate.h>
|
39 |
#include <samplerate.h>
|
39 |
|
40 |
|
40 |
#include "log.h"
|
41 |
#include "log.h"
|
|
... |
|
... |
60 |
|
61 |
|
61 |
/* This is used to disable sample rate conversion until playing is actually
|
62 |
/* This is used to disable sample rate conversion until playing is actually
|
62 |
started */
|
63 |
started */
|
63 |
static bool qinit = false;
|
64 |
static bool qinit = false;
|
64 |
|
65 |
|
|
|
66 |
/* Alsa. Locked while we work with it */
|
|
|
67 |
static std::mutex alsa_mutex;
|
65 |
static snd_pcm_t *pcm = nullptr;
|
68 |
static snd_pcm_t *pcm;
|
66 |
static string alsadevice("default");
|
69 |
static string alsadevice("default");
|
|
|
70 |
static snd_pcm_sframes_t alsa_delay;
|
67 |
|
71 |
|
68 |
static bool alsa_init(const string& dev, AudioMessage *tsk);
|
|
|
69 |
static void alsa_close();
|
|
|
70 |
|
|
|
71 |
// From MPD recovery code
|
72 |
// From MPD recovery code.
|
|
|
73 |
// Note: no locking: we're called from alsawriter holding the lock.
|
72 |
static int alsa_recover(snd_pcm_t *pcm, int err)
|
74 |
static int alsa_recover(snd_pcm_t *pcm, int err)
|
73 |
{
|
75 |
{
|
74 |
if (err == -EPIPE) {
|
76 |
if (err == -EPIPE) {
|
75 |
LOGDEB("Underrun on ALSA device\n");
|
77 |
LOGDEB("Underrun on ALSA device\n");
|
76 |
} else if (err == -ESTRPIPE) {
|
78 |
} else if (err == -ESTRPIPE) {
|
|
... |
|
... |
122 |
static unsigned int period_time = 50000;
|
124 |
static unsigned int period_time = 50000;
|
123 |
// Set after initializing the driver
|
125 |
// Set after initializing the driver
|
124 |
static snd_pcm_uframes_t periodframes;
|
126 |
static snd_pcm_uframes_t periodframes;
|
125 |
static snd_pcm_uframes_t bufferframes;
|
127 |
static snd_pcm_uframes_t bufferframes;
|
126 |
|
128 |
|
|
|
129 |
static bool alsa_init(const string& dev, AudioMessage *tsk);
|
|
|
130 |
|
127 |
static void *alsawriter(void *p)
|
131 |
static void *alsawriter(void *p)
|
128 |
{
|
132 |
{
|
129 |
while (true) {
|
133 |
while (true) {
|
130 |
if (!qinit) {
|
134 |
if (!qinit) {
|
131 |
if (!alsaqueue.waitminsz(qstarg)) {
|
135 |
if (!alsaqueue.waitminsz(qstarg)) {
|
|
... |
|
... |
140 |
// TBD: reset alsa?
|
144 |
// TBD: reset alsa?
|
141 |
alsaqueue.workerExit();
|
145 |
alsaqueue.workerExit();
|
142 |
return (void*)1;
|
146 |
return (void*)1;
|
143 |
}
|
147 |
}
|
144 |
|
148 |
|
|
|
149 |
std::unique_lock<std::mutex> lock(alsa_mutex);
|
145 |
if (pcm == nullptr) {
|
150 |
if (pcm == nullptr) {
|
146 |
if (!alsa_init(alsadevice, tsk)) {
|
151 |
if (!alsa_init(alsadevice, tsk)) {
|
147 |
LOGERR("alsawriter: error alsa init\n");
|
|
|
148 |
alsaqueue.workerExit();
|
152 |
alsaqueue.workerExit();
|
149 |
return (void*)1;
|
153 |
return (void*)1;
|
150 |
}
|
154 |
}
|
151 |
}
|
155 |
}
|
152 |
|
156 |
|
|
... |
|
... |
154 |
char *buf = tsk->m_buf;
|
158 |
char *buf = tsk->m_buf;
|
155 |
// This loop is copied from the alsa sample, but it should not
|
159 |
// This loop is copied from the alsa sample, but it should not
|
156 |
// be necessary, in synchronous mode, alsa is supposed to
|
160 |
// be necessary, in synchronous mode, alsa is supposed to
|
157 |
// perform complete writes except for errors or interrupts
|
161 |
// perform complete writes except for errors or interrupts
|
158 |
while (frames > 0) {
|
162 |
while (frames > 0) {
|
|
|
163 |
if (g_quitrequest) {
|
|
|
164 |
break;
|
|
|
165 |
}
|
|
|
166 |
if (snd_pcm_delay(pcm, &alsa_delay) < 0) {
|
|
|
167 |
alsa_delay = 0;
|
|
|
168 |
}
|
159 |
// LOGDEB("alsawriter: avail frames " << snd_pcm_avail(pcm) <<
|
169 |
// LOGDEB("alsawriter: avail frames " << snd_pcm_avail(pcm) <<
|
160 |
// " writing " << frames << endl);
|
170 |
// " writing " << frames << endl);
|
161 |
snd_pcm_sframes_t ret = snd_pcm_writei(pcm, tsk->m_buf, frames);
|
171 |
snd_pcm_sframes_t ret = snd_pcm_writei(pcm, tsk->m_buf, frames);
|
162 |
if (ret != int(frames)) {
|
172 |
if (ret != int(frames)) {
|
163 |
LOGERR("snd_pcm_writei(" << frames <<" frames) failed: ret: " <<
|
173 |
LOGERR("snd_pcm_writei(" << frames <<" frames) failed: ret: " <<
|
|
... |
|
... |
311 |
|
321 |
|
312 |
return true;
|
322 |
return true;
|
313 |
|
323 |
|
314 |
error:
|
324 |
error:
|
315 |
LOGERR("alsa_init: " << cmd << " error:" << snd_strerror(err) << endl);
|
325 |
LOGERR("alsa_init: " << cmd << " error:" << snd_strerror(err) << endl);
|
|
|
326 |
alsa_close();
|
316 |
return false;
|
327 |
return false;
|
317 |
}
|
328 |
}
|
318 |
|
329 |
|
319 |
static void alsa_close()
|
330 |
void alsa_close()
|
320 |
{
|
331 |
{
|
321 |
LOGDEB("alsawriter: alsa close\n");
|
332 |
LOGDEB("alsawriter: alsa close\n");
|
|
|
333 |
std::unique_lock<std::mutex> lock(alsa_mutex);
|
|
|
334 |
alsa_delay = 0;
|
322 |
if (pcm != nullptr) {
|
335 |
if (pcm != nullptr) {
|
323 |
snd_pcm_close(pcm);
|
336 |
snd_pcm_close(pcm);
|
324 |
pcm = nullptr;
|
337 |
pcm = nullptr;
|
325 |
}
|
338 |
}
|
326 |
}
|
|
|
327 |
|
|
|
328 |
// Current in-driver delay in samples
|
|
|
329 |
static int alsadelay()
|
|
|
330 |
{
|
|
|
331 |
snd_pcm_sframes_t delay;
|
|
|
332 |
if (pcm != nullptr) {
|
|
|
333 |
if (snd_pcm_delay(pcm, &delay) >= 0) {
|
|
|
334 |
return delay;
|
|
|
335 |
}
|
|
|
336 |
}
|
|
|
337 |
return 0;
|
|
|
338 |
}
|
339 |
}
|
339 |
|
340 |
|
340 |
class Filter {
|
341 |
class Filter {
|
341 |
public:
|
342 |
public:
|
342 |
#define FNS 128
|
343 |
#define FNS 128
|
|
... |
|
... |
413 |
|
414 |
|
414 |
double qs = 0.0;
|
415 |
double qs = 0.0;
|
415 |
|
416 |
|
416 |
if (qinit) {
|
417 |
if (qinit) {
|
417 |
// Qsize in frames. This is the variable to control
|
418 |
// Qsize in frames. This is the variable to control
|
418 |
qs = alsaqueue.qsize() * bufframes + alsadelay();
|
419 |
qs = alsaqueue.qsize() * bufframes + alsa_delay;
|
419 |
// Error term
|
420 |
// Error term
|
420 |
double qstargframes = qstarg * bufframes;
|
421 |
double qstargframes = qstarg * bufframes;
|
421 |
double et = ((qstargframes - qs) / qstargframes);
|
422 |
double et = ((qstargframes - qs) / qstargframes);
|
422 |
|
423 |
|
423 |
// Integral. Not used, made it worse each time I tried.
|
424 |
// Integral. Not used, made it worse each time I tried.
|
|
... |
|
... |
721 |
size_t src_input_bytes = 0;
|
722 |
size_t src_input_bytes = 0;
|
722 |
|
723 |
|
723 |
alsaqueue.start(1, alsawriter, 0);
|
724 |
alsaqueue.start(1, alsawriter, 0);
|
724 |
|
725 |
|
725 |
while (true) {
|
726 |
while (true) {
|
|
|
727 |
if (g_quitrequest) {
|
|
|
728 |
goto done;
|
|
|
729 |
}
|
726 |
AudioMessage *tsk = 0;
|
730 |
AudioMessage *tsk = 0;
|
727 |
// Get new data
|
731 |
// Get new data
|
728 |
if (!queue->take(&tsk)) {
|
732 |
if (!queue->take(&tsk)) {
|
729 |
LOGDEB("audioEater: alsadirect: queue take failed\n");
|
733 |
LOGDEB("audioEater: alsadirect: queue take failed\n");
|
730 |
goto done;
|
734 |
goto done;
|
731 |
}
|
735 |
}
|
|
|
736 |
alsaAudioEater.pktcounter++;
|
|
|
737 |
|
732 |
if (tsk->m_bytes == 0 || tsk->m_chans == 0 || tsk->m_bits == 0) {
|
738 |
if (tsk->m_bytes == 0 || tsk->m_chans == 0 || tsk->m_bits == 0) {
|
733 |
LOGDEB("Zero buf\n");
|
739 |
LOGDEB("Zero buf\n");
|
734 |
continue;
|
740 |
continue;
|
735 |
}
|
741 |
}
|
736 |
|
742 |
|