Skocz do zawartości

Linux Audio W C - Alsa Api


sanji

Rekomendowane odpowiedzi

Witam wszystkich!

Od jakiegoś już czasu męczę się z API ALSy. Znalazłem wiele informacji, ale utknąłęm na podstawach. Ewentualne tutoriale są bardzo pobieżne, pisane na zasadzie:

"o tak to np. się robi: (...)", niektóre podstawy są wałkowane wiele razy, a inne traktowane jak oczywiste i pomijane milczeniem. Wiem coś niecoś o cyfrowym dźwięku.

Problemem jest dla mnie jednak kwestia komunikacji ze sprzętem. Posłuże się tu działającym przykładem:

 

#include <alsa/asoundlib.h>

static char *alsa_device = "default";
snd_pcm_t *h_in;
snd_pcm_t *h_out;
unsigned short buf[8*512];
int err;

int main(void) {
    // otwarcie urzadzenia
    err = snd_pcm_open(&h_in, alsa_device, SND_PCM_STREAM_CAPTURE, 0);
    if (err < 0) {
        printf("ERROR (1a): %s\n", snd_strerror(err));
        exit(EXIT_FAILURE);
    }

    err = snd_pcm_open(&h_out, alsa_device, SND_PCM_STREAM_PLAYBACK, 0);
    if (err < 0) {
        printf("ERROR (1b): %s\n", snd_strerror(err));
        exit(EXIT_FAILURE);
    }

    // ustawienie parametrow urzadzenia
    err = snd_pcm_set_params(h_in, SND_PCM_FORMAT_U16, SND_PCM_ACCESS_RW_INTERLEAVED, 1, 16000, 1, 300000); // ostatni parametr to "latency"
    if (err < 0) {
        printf("ERROR (2a): %s\n", snd_strerror(err));
        exit(EXIT_FAILURE);
    }

    err = snd_pcm_set_params(h_out, SND_PCM_FORMAT_U16, SND_PCM_ACCESS_RW_INTERLEAVED, 1, 16000, 1, 300000); // ostatni parametr to "latency"
    if (err < 0) {
        printf("ERROR (2b): %s\n", snd_strerror(err));
        exit(EXIT_FAILURE);
    }

    // pętla kopiująca
    while(1) {
        err = snd_pcm_readi (h_in, buf, 8*512);   // Kopiowanie z wejścia mikrofonowego
        if (err == -EPIPE) {
            puts("ERROR (3a): snd_pcm_prepare\n");
            // zapobiega wywalaniu się programu na błędach odczytu z urządzenia
            snd_pcm_prepare(h_in);
        } else if (err < 0) {
            printf("ERROR (3a): %s\n", snd_strerror(err));
        }
    
        err = snd_pcm_writei(h_out, buf, err);    // Zapis do wyjścia głośnikowego
        if (err < 0) {
            err = snd_pcm_recover(h_out, err, 0);
        }
        if (err < 0) {
            printf("ERROR (3b): %s\n", snd_strerror(err));
            break;
        }
    }

    snd_pcm_close(h_in);
    snd_pcm_close(h_out);
}

Teraz czas na pytania:

1) Czym jest to "latency" ustawiane jako ostatni parametr funkcji snd_pcm_set_params()? Na pewno jakieś opóźnienie, tylko pomiędzy czym, a czym?

2) Dlaczego dźwięk jest kopiowany z mikrofonu na głośniki w sposób ciągły, choć funkcje I/O wywoływane są jedna po drugiej, na zmianę, a nie jednocześnie?

 

Na pewno to jakieś banały. Wiem, że funkcje *writei()/*readi() to takie rozszerzone funkcje języka C write() i read(), ale w mojej książce o C tych drugich nie ma, a manual jest krótki i rzeczowy.

I ani słowa o tajemniczym latency nie widać :unsure: ...

 

Nie obrażę się też za jakiś pouczający link. Wszystko dobre, byleby na temat.

Odnośnik do komentarza
Udostępnij na innych stronach

Mi się raczej wydaje, że po zapisie/odczycie całego bufora jednorazowo, karta wysyła kolejne próbki (kierunek zależny od tego czy odczyt, czy zapis) tak długo,

jak pozwoli ilość dostępnych danych/wolnego miejsca w buforze.

Działa to pewno na zasadzie dorzucania do pieca - tak aby nie zgasł, ale i z uwzględnieniem ograniczonej ilości opału, jaki się w nim jednorazowo zmieści.

Problem w tym, że jka na razie nie znalazłem nic, co by potwierdzało na 100%, że tak jest. No i to przeklęte latency, które nie wiadomo, gdzie tu zmieścić.

Rozwiązanie które podajesz, bardzo by uzależniało przetwarzanie od dostępnej mocy obliczeniowej. Przy 44.1kHz daje to 23 us (!) na przeładowanie bufora.

To 44100 razy mniej, niż trwa cykl napełniania/opróżniania bufora. Po co miałby więc w ogóle istnieć takie coś jak bufor, skoro i tak, o tym, czy muzyka bądzie

się przycinać, czy nie, decyduje odstęp poszczególnych próbek od siebie? Zakładając ponadto, że zapis całego bufora trwa dłużej niż zapis jednej próbki,

wykorzystanie bufora zamiast zapisu "próbka po próbce", zdaje się dodatkowo jeszcze bardziej zwiększać ilość potrzebnej mocy obliczeniowej o tyle,

ile jest próbek w buforze, czyli np. 4096-cio krotnie.

Tak więc wydaje mi się, że nie jest tak, jak piszesz, bo to by było szalenie mało wydajne.

Odnośnik do komentarza
Udostępnij na innych stronach

Teraz jeszcze znaleźć, jaki to ma związek z buforem, liczbą cykli przypadającą na wielkość bufora sprzętu oraz liczbą ramek w cyklu i będzie grało.

Okazuje się, że to są ściśle związane ze sobą sprawy, a ich obecność zdaje się wynikać z architektury kart dźwiękowych i tego, czy sterownik i serwer dźwięku

potrafią to w pełni wykorzystać. Dziwne jest to, że samo latency, zdaje się wynikać bezpośrednio z wielkości bufora, jednak, czasami działa poprawnie

z wartością z palac wyssaną (teoretycznie nie powinno), a czasem wymaga dokładniejszej.

Ostatnio znalazłem conieco wyjaśniające linki:

 

http://www.suse.de/~mana/alsa090_howto.html

http://linux-muzyka.ixion.pl/tekst.php?id=25084#a -nie dotyczy ALSY, ale na temat.

Odnośnik do komentarza
Udostępnij na innych stronach

Jeśli chcesz dodać odpowiedź, zaloguj się lub zarejestruj nowe konto

Jedynie zarejestrowani użytkownicy mogą komentować zawartość tej strony.

Zarejestruj nowe konto

Załóż nowe konto. To bardzo proste!

Zarejestruj się

Zaloguj się

Posiadasz już konto? Zaloguj się poniżej.

Zaloguj się
×
×
  • Dodaj nową pozycję...