|
a/src/alsadirect.cpp |
|
b/src/alsadirect.cpp |
|
... |
|
... |
185 |
} else {
|
185 |
} else {
|
186 |
return 0;
|
186 |
return 0;
|
187 |
}
|
187 |
}
|
188 |
}
|
188 |
}
|
189 |
|
189 |
|
|
|
190 |
class Filter {
|
|
|
191 |
public:
|
|
|
192 |
#define FNS 128
|
|
|
193 |
Filter() : old(0.0), sum(0.0), idx(0) {
|
|
|
194 |
for (int i = 0; i < FNS; i++) {
|
|
|
195 |
buf[i] = 1.0;
|
|
|
196 |
sum += buf[i];
|
|
|
197 |
}
|
|
|
198 |
}
|
|
|
199 |
float operator()(float ns) {
|
|
|
200 |
#if 0
|
|
|
201 |
if (old == 0.0) {
|
|
|
202 |
old = ns;
|
|
|
203 |
} else {
|
|
|
204 |
old = (old + ns) / 2;
|
|
|
205 |
}
|
|
|
206 |
return old;
|
|
|
207 |
#endif
|
|
|
208 |
#if 1
|
|
|
209 |
buf[idx++] = ns;
|
|
|
210 |
sum += ns;
|
|
|
211 |
if (idx == FNS)
|
|
|
212 |
idx = 0;
|
|
|
213 |
sum -= buf[idx];
|
|
|
214 |
return sum/FNS;
|
|
|
215 |
#endif
|
|
|
216 |
}
|
|
|
217 |
float old;
|
|
|
218 |
float buf[FNS];
|
|
|
219 |
float sum;
|
|
|
220 |
int idx;
|
|
|
221 |
};
|
|
|
222 |
|
190 |
static void *audioEater(void *cls)
|
223 |
static void *audioEater(void *cls)
|
191 |
{
|
224 |
{
|
192 |
LOGDEB("audioEater: alsadirect\n");
|
225 |
LOGDEB("audioEater: alsadirect\n");
|
193 |
AudioEater::Context *ctxt = (AudioEater::Context*)cls;
|
226 |
AudioEater::Context *ctxt = (AudioEater::Context*)cls;
|
194 |
|
227 |
|
|
... |
|
... |
199 |
ctxt = 0;
|
232 |
ctxt = 0;
|
200 |
|
233 |
|
201 |
qinit = false;
|
234 |
qinit = false;
|
202 |
|
235 |
|
203 |
float samplerate_ratio = 1.0;
|
236 |
float samplerate_ratio = 1.0;
|
|
|
237 |
Filter filter;
|
204 |
|
238 |
|
205 |
int src_error = 0;
|
239 |
int src_error = 0;
|
206 |
SRC_STATE *src_state = 0;
|
240 |
SRC_STATE *src_state = 0;
|
207 |
SRC_DATA src_data;
|
241 |
SRC_DATA src_data;
|
208 |
memset(&src_data, 0, sizeof(src_data));
|
242 |
memset(&src_data, 0, sizeof(src_data));
|
|
... |
|
... |
231 |
queue->workerExit();
|
265 |
queue->workerExit();
|
232 |
return (void *)1;
|
266 |
return (void *)1;
|
233 |
}
|
267 |
}
|
234 |
// BEST_QUALITY yields approx 25% cpu on a core i7
|
268 |
// BEST_QUALITY yields approx 25% cpu on a core i7
|
235 |
// 4770T. Obviously too much, actually might not be
|
269 |
// 4770T. Obviously too much, actually might not be
|
236 |
// sustainable (it's quite 100% of 1 cpu)
|
270 |
// sustainable (it's almost 100% of 1 cpu)
|
237 |
// MEDIUM_QUALITY is around 10%
|
271 |
// MEDIUM_QUALITY is around 10%
|
238 |
// FASTEST is 4-5%. Given that this is process-wide, probably
|
272 |
// FASTEST is 4-5%. Given that this measured for the full
|
239 |
// a couple % in fact.
|
273 |
// process, probably a couple % for the conversion in fact.
|
240 |
// To be re-evaluated on the pi... FASTEST is 30% CPU on a Pi2
|
274 |
// Rpi: FASTEST is 30% CPU on a Pi2 with USB
|
241 |
// with USB audio. Curiously it's 25-30% on a Pi1 with i2s audio.
|
275 |
// audio. Curiously it's 25-30% on a Pi1 with i2s audio.
|
242 |
src_state = src_new(SRC_SINC_FASTEST, tsk->m_chans, &src_error);
|
276 |
src_state = src_new(SRC_SINC_FASTEST, tsk->m_chans, &src_error);
|
243 |
|
277 |
|
244 |
// Number of frames per buffer. This is constant for a
|
278 |
// Number of frames per buffer. This is constant for a
|
245 |
// given stream (depends on fe, Songcast buffers are 10mS)
|
279 |
// given stream (depends on fe, Songcast buffers are 10mS)
|
246 |
bufframes = tsk->m_bytes / (tsk->m_chans * (tsk->m_bits/8));
|
280 |
bufframes = tsk->m_bytes / (tsk->m_chans * (tsk->m_bits/8));
|
|
... |
|
... |
249 |
// Computing the samplerate conversion factor. We want to keep
|
283 |
// Computing the samplerate conversion factor. We want to keep
|
250 |
// the queue at its target size to control the delay. The
|
284 |
// the queue at its target size to control the delay. The
|
251 |
// present hack sort of works but has a tendancy to keep
|
285 |
// present hack sort of works but has a tendancy to keep
|
252 |
// oscillating (with a very small amplitude). It should be
|
286 |
// oscillating (with a very small amplitude). It should be
|
253 |
// replaced by a proper filter
|
287 |
// replaced by a proper filter
|
254 |
float qs = alsaqueue.qsize();
|
288 |
float qs;
|
255 |
if (qinit) {
|
289 |
if (qinit) {
|
256 |
qs = alsaqueue.qsize() + alsadelay() / bufframes;
|
290 |
qs = alsaqueue.qsize() + alsadelay() / bufframes;
|
257 |
float t = ((qstarg - qs) / qstarg);
|
291 |
float t = ((qstarg - qs) / qstarg);
|
258 |
float adj = t * t;
|
292 |
float adj = t * t;
|
259 |
if (qs < qstarg) {
|
293 |
if (qs < qstarg) {
|
|
... |
|
... |
264 |
samplerate_ratio = 1.0 - adj;
|
298 |
samplerate_ratio = 1.0 - adj;
|
265 |
if (samplerate_ratio < 0.9)
|
299 |
if (samplerate_ratio < 0.9)
|
266 |
samplerate_ratio = 0.9;
|
300 |
samplerate_ratio = 0.9;
|
267 |
}
|
301 |
}
|
268 |
} else {
|
302 |
} else {
|
|
|
303 |
qs = alsaqueue.qsize();
|
269 |
samplerate_ratio = 1.0;
|
304 |
samplerate_ratio = 1.0;
|
270 |
}
|
305 |
}
|
|
|
306 |
samplerate_ratio = filter(samplerate_ratio);
|
271 |
|
307 |
|
272 |
unsigned int tot_samples = tsk->m_bytes / (tsk->m_bits/8);
|
308 |
unsigned int tot_samples = tsk->m_bytes / (tsk->m_bits/8);
|
273 |
if ((unsigned int)src_data.input_frames < tot_samples / tsk->m_chans) {
|
309 |
if ((unsigned int)src_data.input_frames < tot_samples / tsk->m_chans) {
|
274 |
int bytes = tot_samples * sizeof(float);
|
310 |
int bytes = tot_samples * sizeof(float);
|
275 |
src_data.data_in = (float *)realloc(src_data.data_in, bytes);
|
311 |
src_data.data_in = (float *)realloc(src_data.data_in, bytes);
|
|
... |
|
... |
279 |
src_data.output_frames = 2 * src_data.input_frames;
|
315 |
src_data.output_frames = 2 * src_data.input_frames;
|
280 |
}
|
316 |
}
|
281 |
src_data.src_ratio = samplerate_ratio;
|
317 |
src_data.src_ratio = samplerate_ratio;
|
282 |
src_data.end_of_input = 0;
|
318 |
src_data.end_of_input = 0;
|
283 |
|
319 |
|
|
|
320 |
// Data always comes in host order, because this is what we
|
|
|
321 |
// request from upstream. 24 and 32 bits are untested.
|
284 |
switch (tsk->m_bits) {
|
322 |
switch (tsk->m_bits) {
|
285 |
case 16: {
|
323 |
case 16:
|
|
|
324 |
{
|
286 |
const short *sp = (const short *)tsk->m_buf;
|
325 |
const short *sp = (const short *)tsk->m_buf;
|
287 |
for (unsigned int i = 0; i < tot_samples; i++) {
|
326 |
for (unsigned int i = 0; i < tot_samples; i++) {
|
288 |
src_data.data_in[i] = *sp++;
|
327 |
src_data.data_in[i] = *sp++;
|
289 |
}
|
328 |
}
|
|
|
329 |
}
|
290 |
break;
|
330 |
break;
|
291 |
}
|
|
|
292 |
case 24:
|
331 |
case 24:
|
|
|
332 |
{
|
|
|
333 |
const unsigned char *icp = (const unsigned char *)tsk->m_buf;
|
|
|
334 |
unsigned int o;
|
|
|
335 |
unsigned char *ocp = (unsigned char *)&o;
|
|
|
336 |
ocp[3] = 0;
|
|
|
337 |
for (unsigned int i = 0; i < tot_samples; i++) {
|
|
|
338 |
ocp[0] = *icp++;
|
|
|
339 |
ocp[1] = *icp++;
|
|
|
340 |
ocp[2] = *icp++;
|
|
|
341 |
src_data.data_in[i] = o;
|
|
|
342 |
}
|
|
|
343 |
}
|
|
|
344 |
break;
|
293 |
case 32:
|
345 |
case 32:
|
|
|
346 |
{
|
|
|
347 |
const int *ip = (const int *)tsk->m_buf;
|
|
|
348 |
for (unsigned int i = 0; i < tot_samples; i++) {
|
|
|
349 |
src_data.data_in[i] = *ip++;
|
|
|
350 |
}
|
|
|
351 |
}
|
|
|
352 |
break;
|
294 |
default:
|
353 |
default:
|
295 |
abort();
|
354 |
LOGERR("audioEater:alsa: bad m_bits: " << tsk->m_bits << endl);
|
|
|
355 |
alsaqueue.setTerminateAndWait();
|
|
|
356 |
queue->workerExit();
|
|
|
357 |
return (void *)1;
|
296 |
}
|
358 |
}
|
|
|
359 |
|
297 |
int ret = src_process(src_state, &src_data);
|
360 |
int ret = src_process(src_state, &src_data);
|
298 |
if (ret) {
|
361 |
if (ret) {
|
299 |
LOGERR("src_process: " << src_strerror(ret) << endl);
|
362 |
LOGERR("src_process: " << src_strerror(ret) << endl);
|
300 |
continue;
|
363 |
continue;
|
301 |
}
|
364 |
}
|
|
|
365 |
|
302 |
{
|
366 |
{
|
303 |
static int cnt;
|
367 |
static int cnt;
|
304 |
if (cnt++ == 100) {
|
368 |
if (cnt++ == 103) {
|
305 |
LOGDEB("samplerate: "
|
369 |
LOGDEB("audioEater:alsa: "
|
306 |
" qstarg " << qstarg <<
|
370 |
" qstarg " << qstarg <<
|
307 |
" iqsz " << alsaqueue.qsize() <<
|
371 |
" iqsz " << alsaqueue.qsize() <<
|
308 |
" qsize " << int(qs) <<
|
372 |
" qsize " << int(qs) <<
|
309 |
" ratio " << samplerate_ratio <<
|
373 |
" ratio " << samplerate_ratio <<
|
310 |
" in " << src_data.input_frames <<
|
374 |
" in " << src_data.input_frames <<
|
311 |
" consumed " << src_data.input_frames_used <<
|
375 |
" consumed " << src_data.input_frames_used <<
|
312 |
" out " << src_data.output_frames_gen << endl);
|
376 |
" out " << src_data.output_frames_gen << endl);
|
313 |
cnt = 0;
|
377 |
cnt = 0;
|
314 |
}
|
378 |
}
|
315 |
}
|
379 |
}
|
|
|
380 |
|
316 |
tot_samples = src_data.output_frames_gen * tsk->m_chans;
|
381 |
tot_samples = src_data.output_frames_gen * tsk->m_chans;
|
317 |
if (src_data.output_frames_gen > src_data.input_frames) {
|
382 |
if (src_data.output_frames_gen > src_data.input_frames) {
|
318 |
tsk->m_bytes = tot_samples * (tsk->m_bits / 8);
|
383 |
tsk->m_bytes = tot_samples * (tsk->m_bits / 8);
|
319 |
tsk->m_buf = (char *)realloc(tsk->m_buf, tsk->m_bytes);
|
384 |
tsk->m_buf = (char *)realloc(tsk->m_buf, tsk->m_bytes);
|
320 |
if (!tsk->m_buf)
|
385 |
if (!tsk->m_buf) {
|
321 |
abort();
|
386 |
LOGERR("audioEater:alsa: out of memory\n");
|
|
|
387 |
alsaqueue.setTerminateAndWait();
|
|
|
388 |
queue->workerExit();
|
|
|
389 |
return (void *)1;
|
322 |
}
|
390 |
}
|
|
|
391 |
}
|
323 |
|
392 |
|
324 |
// Output is always 16 bits lsb first for now. We should
|
393 |
// Convert floats buffer into output which is always 16LE for
|
325 |
// probably dither the lsb ?
|
394 |
// now. We should probably dither the lsb ?
|
326 |
tsk->m_bits = 16;
|
395 |
tsk->m_bits = 16;
|
327 |
{
|
396 |
{
|
328 |
#ifdef WORDS_BIGENDIAN
|
397 |
#ifdef WORDS_BIGENDIAN
|
329 |
unsigned char *ocp = (unsigned char *)tsk->m_buf;
|
398 |
unsigned char *ocp = (unsigned char *)tsk->m_buf;
|
330 |
short val;
|
399 |
short val;
|