alsa snd_pcm_writei

#alsa

#alsa

Вопрос:

Я заметил, что генератор синусов в pcm.c и speaker-test.c генерируют новый буфер синусов в цикле. Таким образом, он постоянно воссоздает один и тот же буфер. Я хотел воспроизвести буфер, не создавая его заново каждый раз, чтобы сэкономить время процессора. Однако, когда я попытался запустить код, сначала создав буфер, а затем отправив тот же буфер в snd_pcm_writei, я получаю небольшой щелкающий звук в конце каждого буфера. Однако, когда он перестраивается каждый раз, а затем отправляется в snd_pcm_writei, в конце буфера нет маленького щелчка. Почему требуется перестраивать буфер sine каждый раз перед его воспроизведением, чтобы не возникал шум щелчка?

Буду признателен за любую помощь?

из pcm.c:

 while (1) {
    generate_sine(areas, 0, period_size, amp;phase);
    ptr = samples;
    cptr = period_size;
  

Ответ №1:

Вы предполагаете, что каждый раз генерируется одна и та же синусоидальная волна, но поскольку используется phase переменная и синусоидальная волна не всегда точно помещается в буфер, на каждой итерации генерируется другая синусоидальная волна, немного сдвинутая.

Не генерирование синусоидальной волны каждый раз приводит к «разрыву» синусоидальной волны.

Я попробую визуализировать с помощью пилообразной волны вместо синусоидальной. Представьте, что размер буфера равен 16, а значения волны варьируются от A до H.

 // Old way
  phase = 0        phase = 2        phase = 4
ABCDEFGHGFEDCBAB|CDEFGHGFEDCBABCD|EFGHGFEDCBABCDEF....

// New way
  phase = 0        phase = 0        phase = 0
ABCDEFGHGFEDCBAB|ABCDEFGHGFEDCBAB|ABCDEFGHGFEDCBAB....
  

Обратите внимание, что вокруг краев буфера есть только небольшие фрагменты, где звук «искажен» (например, AB|AB вместо AB|CD ). Вот почему большую часть времени это звучит правильно с некоторыми тревожащими короткими «щелчками» между ними.

В некоторых редких случаях, если длина буфера кратна длине волны или когда phase имеет то же значение, что и в предыдущей итерации, вы действительно можете пропустить генерацию буфера, но вы не можете делать это каждый раз.

РЕДАКТИРОВАТЬ: Посмотрите на функцию generate_sine, чтобы увидеть, как phase изменяется:

 static void generate_sine(const snd_pcm_channel_area_t *areas, 
                          snd_pcm_uframes_t offset,
                          int count, double *_phase)
{
    static double max_phase = 2. * M_PI;
    double phase = *_phase;
    double step = max_phase*freq/(double)rate;

    [...]

             phase  = step;
             if (phase >= max_phase)
                    phase -= max_phase;
     }
     *_phase = phase;
}
  

ПРАВКА 2: Это изображение может быть лучшей / более четкой визуализацией:

введите описание изображения здесь

Комментарии:

1. Вероятно, вы могли бы использовать codegolf.stackexchange.com/questions/951 /… для более наглядного примера 🙂

2. @ninjalj: Спасибо, но теперь я решил добавить изображение вместо этого. Еще лучше был бы интерактивный апплет, где вы можете изменять длину буфера и прослушивать звук, но я думаю, что это перебор 🙂

3. Спасибо вам обоим за вашу помощь. Я только начал понимать, что значение фазы в конце длины буфера было критическим фактором. Тогда, основываясь на вашем подтверждении, я должен быть в состоянии создать единый буфер, который может непрерывно воспроизводиться, гарантируя, что фаза всегда равна 0 в начале и конце буфера.