Странное поведение монитора PulseAudio

Столкнулся со странным поведением устройства контроля PulseAudio (то есть устройства ввода звука, которое воспроизводит звук, передаваемый на динамик). Я сократил код из моего реального проекта до простого примера на основе кода из документов PulseAudio https://freedesktop.org/software/pulseaudio/doxygen/parec-simple_8c-example.html, Я только добавил ограничение по времени и прочитал подсчет байтов. Это работает, например, 30 секунд и печатает считанные байты. Проблема в том, что количество байтов сильно отличается, если что-то воспроизводится во время выполнения программы. Я выполнил эту программу и параллельно выполнил Bash for петля состоящая aplay с коротким tada.wav файл. Разница составляет 9%. Чтобы проверить это больше, я попытался запустить 4 таких цикла параллельно с примером PulseAudio, а разница еще больше — 34%. Но если вместо нескольких aplay с короткой волной я бегу mplayer с длинным mp3-файлом — такой разницы нет, количество байтов аналогично случаю, когда звук не воспроизводится.

Такое поведение приводит к сбою кода обработки звука в моем реальном проекте, поэтому, если кто-то может подсказать, как его решить, я буду очень благодарен.

Аналогичный код на Windows, основанный на Qt и использующий устройство Stereo Mixer в качестве аналога монитора PulseAudio, работает без таких проблем.

Вот мой код, основанный на примере из документов PulseAudio:

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <iostream>
#include <chrono>

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#include <pulse/simple.h>
#include <pulse/error.h>

#define BUFSIZE 1024

using namespace std;
using namespace std::chrono;

int main(int argc, char *argv[])
{
int duration = 30000;
int64_t readBytesCounter = 0;
milliseconds msStart = duration_cast< milliseconds >(system_clock::now().time_since_epoch());

/* The sample type to use */
static const pa_sample_spec ss = {
.format = PA_SAMPLE_S16LE,
.rate = 44100,
.channels = 2
};
pa_simple *s = NULL;
int ret = 1;
int error;
/* Create the recording stream */
if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_RECORD, "alsa_output.pci-0000_00_1b.0.analog-stereo.monitor", "record", &ss, NULL, NULL, &error))) {
fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
goto finish;
}
for (;;) {
uint8_t buf[BUFSIZE];
/* Record some data ... */
if (pa_simple_read(s, buf, sizeof(buf), &error) < 0) {
fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error));
goto finish;
}
readBytesCounter += BUFSIZE;

milliseconds msCurrent = duration_cast< milliseconds >(system_clock::now().time_since_epoch());
int elapsed = msCurrent.count() - msStart.count();
if (elapsed > duration)
{
cerr << int(elapsed / 1000) << " seconds elapsed, terminating" << endl;
cerr << readBytesCounter << " bytes read" << endl;
goto finish;
}
}
ret = 0;
finish:
if (s)
pa_simple_free(s);
return ret;
}

Это может быть построено с помощью следующей команды:

g++ -o main main.cpp -lpulse -lpulse-simple -std=c++11

Пример WAV-файла, из которого я взял http://d0.waper.ru/f/462151/23/HgDwimvX37CwxWqW38eywg%2C1485353628/7d74/9/462151.wav/tada.wav

И вот результаты теста:

Тест 1. Нет звука в динамике

$ time ./main
30 seconds elapsed, terminating
5323776 bytes read

real    0m30.028s
user    0m0.168s
sys     0m0.388s

Тест 2. Баш for Цикл «для i в seq 1 22; сделать aplay tada.wav; сделано» с короткими файлами WAV в фоновом режиме. Увеличение количества байтов 5798912 / 5323776 = 1.089 раз.

$ time ./main
30 seconds elapsed, terminating
5798912 bytes read

real    0m30.023s
user    0m0.120s
sys     0m0.184s

Тест 3. 4 Bash for циклы с короткими файлами WAV в фоновом режиме. Увеличение количества байтов 7129088 / 5323776 = 1.339 раз.

$ time ./main
30 seconds elapsed, terminating
7129088 bytes read

real    0m30.019s
user    0m0.164s
sys     0m0.196s

Тест 4. mplayer с длинным mp3 в фоновом режиме. 5288960 / 5323776 = 0.993нет существенной разницы в количестве байтов.

$ time ./main
30 seconds elapsed, terminating
5288960 bytes read

real    0m30.024s
user    0m0.096s
sys     0m0.204s

Пытался выполнить набор каждого теста, усредненное количество байтов — схожая разница.

П.С .: Конфигурация моей системы:

  • ОС Ubuntu 16.04.1 amd64
  • pulseaudio 1: 8.0-0ubuntu3.2
  • alsa-base 1.0.25 + dfsg-0ubuntu5

0

Решение

Вот ответ Тану Каскинен из списка рассылки PulseAudio https://lists.freedesktop.org/archives/pulseaudio-discuss/2017-January/027412.html . Не полный ответ, но небольшое объяснение и обходной путь:

Вы, вероятно, столкнулись с известной ошибкой, которую мы имеем с перемоткой в
контролировать источники (в данном случае «известные» означает, что симптомы
известная, но не точная причина). Когда поток начинает играть на
отслеживаемый приемник, приемник перезапишет содержимое буфера воспроизведения
(это называется «перемотка»), и это вызывает сбой в мониторе
источник (некоторый дополнительный звук, очевидно, подталкивается к записи
стрим, судя по вашим экспериментам). Я надеюсь исправить это когда-нибудь,
но не похоже, что у меня будет время на это в ближайшем будущем. Если
у вас есть время и мотивация, чтобы исследовать и исправить ошибку,
было бы здорово

В качестве обходного пути вы, вероятно, можете уменьшить величину ошибки, уменьшив размер буфера воспроизведения. Для этого нужно пройти
tsched_buffer_size = X для обнаружения модуля udev в /etc/pulse/default.pa
(замените X размером буфера в байтах). «

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]