Как минимизировать задержку при чтении аудио с помощью ALSA? - PullRequest
0 голосов
/ 02 марта 2020

Когда я пытался получить некоторые сигналы в частотной области, я столкнулся с проблемой того, что snd_pcm_readi () может занимать очень много времени. Это вызывает проблемы в разделе logi c моего кода, который зависит от времени.

У меня есть то, что большую часть времени snd_pcm_readi () возвращается через приблизительно от 0,00003 до 0,00006 секунд. Однако каждый 4-5 вызов snd_pcm_readi () требует приблизительно 0,028 секунд. Это огромная разница, и это приводит к сбою части моего кода в logi c.

Как получить согласованное время для каждого вызова snd_pcm_readi ()?

пытался поэкспериментировать с размером периода, но мне неясно, что именно он делает, даже после перечитывания документации несколько раз. Я не использую дизайн, управляемый прерываниями, я просто вызываю snd_pcm_readi () и он блокируется до тех пор, пока не вернется - с данными.

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

Какой цели служит «размер периода», когда я не используете дизайн, управляемый прерываниями? Может ли моя проблема быть решена путем манипулирования размером периода, или я должен сделать что-то еще?

Я хочу добиться, чтобы каждый вызов snd_pcm_readi () занимал примерно одинаковое количество времени. Я не прошу API-интерфейс, совместимый с реальным временем, который, как я полагаю, ALSA даже не пытается из-за разницы во времени вызова функций, порядка 500 раз длиннее (что что я вижу!) тогда это реальная проблема.

Что с этим можно сделать и что мне с этим делать?

Я бы привел минимальный воспроизводимый пример, но это не легко в моем случае.

1 Ответ

0 голосов
/ 27 марта 2020

Обычно при чтении и записи аудио размер периода указывает, сколько данных ALSA зарезервировало в кремнии DMA. Обычно размер периода определяет вашу задержку. Так, например, когда вы заполняете буфер для записи через DMA в кремний I2S, один буфер DMA уже записывается.

Если у вас слишком маленький размер периода, то у процессора нет времени записать аудио в предусмотренном слоте исполнения. Обычно люди стремятся к минимальной задержке 500 мкс или 1 мс. Если вы выполняете тяжелые формы вычислений, вы можете выбрать 5 мс или 10 мс задержки. Вы можете выбрать еще большую задержку, если вы используете не мощную встроенную систему.

Если вы хотите увеличить sh ограничение системы, вы можете запросить увеличение приоритета потока обработки аудио. , Увеличивая приоритет вашего потока, вы просите планировщик обработать ваш аудио поток перед всеми другими потоками с более низким приоритетом.

Один метод увеличения приоритета взят из классов gtkIOStream ALSA C ++ OO выглядит так (взято из метода changeThreadPriority ):

/** Set the current thread's priority
\param priority <0 implies maximum priority, otherwise must be between sched_get_priority_max and sched_get_priority_min
\return 0 on success, error code otherwise
*/
static int changeThreadPriority(int priority){
  int ret;
  pthread_t thisThread = pthread_self(); // get the current thread
  struct sched_param origParams, params;
  int origPolicy, policy = SCHED_FIFO, newPolicy=0;

  if ((ret = pthread_getschedparam(thisThread, &origPolicy, &origParams))!=0)
    return ALSA::ALSADebug().evaluateError(ret, "when trying to pthread_getschedparam\n");
  printf("ALSA::Stream::changeThreadPriority : Current thread policy %d and priority %d\n", origPolicy, origParams.sched_priority);

  if (priority<0) //maximum priority
    params.sched_priority = sched_get_priority_max(policy);
  else
    params.sched_priority = priority;

  if (params.sched_priority>sched_get_priority_max(policy))
    return ALSA::ALSADebug().evaluateError(ALSA_SCHED_PRIORITY_ERROR, "requested priority is too high\n");
  if (params.sched_priority<sched_get_priority_min(policy))
    return ALSA::ALSADebug().evaluateError(ALSA_SCHED_PRIORITY_ERROR, "requested priority is too low\n");

  if ((ret = pthread_setschedparam(thisThread, policy, &params))!=0)
    return ALSA::ALSADebug().evaluateError(ret, "when trying to pthread_setschedparam - are you su or do you have permission to set this priority?\n");
  if ((ret = pthread_getschedparam(thisThread, &newPolicy, &params))!=0)
    return ALSA::ALSADebug().evaluateError(ret, "when trying to pthread_getschedparam\n");
  if(policy != newPolicy)
    return ALSA::ALSADebug().evaluateError(ALSA_SCHED_POLICY_ERROR, "requested scheduler policy is not correctly set\n");
  printf("ALSA::Stream::changeThreadPriority : New thread priority changed to %d\n", params.sched_priority);

  return 0;
}
...