2015-09-04 37 views
8

Potrzebuję wysłać audio z radia do systemu wtórnego za pomocą wbudowanego systemu linux.Linux jak nagrywać dźwięk w buforze RAM i odtwarzanie dźwięku z niestandardowym opóźnieniem

System pomocniczy musi ustawić kanał komunikacyjny, który potrwa kilka sekund.

Więc jeśli nie chcę stracić początku dźwięku, potrzebuję sposobu na nagranie dźwięku i odtworzenie go z niestandardowym opóźnieniem (maksymalnie kilka sekund).

Powinno być możliwe uruchomienie arecord w celu nagrania dźwięku w pliku w systemie plików tmpfs, a gdy przychodzi komunikacja, uruchom aplay. Jednak w tym przypadku początek jest nadal tracony, ponieważ sygnał do nagrywania nadchodzi zbyt późno.

Czy istnieje program w systemie Linux, który nagrywa dźwięk w sposób ciągły w buforze pierścieniowym w pamięci RAM i może odtwarzać z niestandardowym opóźnieniem na żądanie?

Jeśli nie, jaka jest najlepsza biblioteka do kodowania takiego programu w systemie wbudowanym? alsa czy coś innego?

+0

W systemie Linux każda biblioteka audio kończy się na ALSA. Możesz jednak użyć dowolnej innej biblioteki, jeśli jest łatwiejsza w użyciu. –

+0

Czy to pytanie jest nie na temat, ponieważ prosi o narzędzie lub bibliotekę? –

+0

Alsa ma obsługę wtyczek LADSPA, powinna istnieć jedna z określonym opóźnieniem. – Phillip

Odpowiedz

6

Oto prosty program C, który będzie utrzymywał kolisty bufor między rurami do środka i na zewnątrz. Użyj jak in | buffer_program | out. Sprawdzanie błędów zostało pominięte. Solidność nie jest gwarantowana. Daje ogólny pomysł.

skrypt testowy (ale w rzeczywistości, ponieważ jego okrągła buforować dane Twój rurociągi w musi być tak, że jego spójne biorąc byle kawałek w strumieniu Albo po prostu zrobić bufor większy niż dane.):

cat some.wav | ./circular_buffer 100000 | (sleep 1 && aplay) 

circular_buffer.c:

/** 
* This program simply maintains a circular buffer of a given size indefinitely. 
*/ 
#include <stdio.h> 
#include <stddef.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <stdbool.h> /* C99 only */ 
#include <sys/select.h> 
#include <errno.h> 
#include <fcntl.h> 

int c_read(int fd, char * buf, unsigned int size, unsigned int * head_in, unsigned int * tail_in); 
int c_write(int fd, char * buf, unsigned int size, unsigned int * head_in, unsigned int * tail_in); 
bool empty_buf(unsigned int head, unsigned int tail); 
bool setblock(int fd, bool block); 
#define FD_SET_SET(set, fd, max) FD_SET(fd, &set); max = ((fd > max) ? fd : max); 
#define FD_SET_UNSET(set, fd, max) FD_CLR(fd, &set); max = ((fd == max) ? max - 1 : max); //not ideal. Do while ISFDSET... 

int main(int argc, char **argv) 
{ 
    char * buf; 
    unsigned int buf_size = 0; 
    unsigned int buf_head = 0; 
    unsigned int buf_tail = 0; 

    // Check args. 
    if(argc != 2) { 
    fprintf(stderr, "Usage: %s <buffer size in bytes>\n", __FILE__); 
    exit(EXIT_FAILURE); 
    } 
    sscanf(argv[1], "%d", &buf_size); 
    buf_size = (buf_size < 2) ? 2 : buf_size; 

    // Note the usable buffer space is buf_size-1. 
    fprintf(stderr, "Allocating %d\n", buf_size); 
    buf = (char*)malloc(buf_size); 

    bool done_reading = false; 
    int maxfd = 0; 
    fd_set r_set, w_set, r_tempset, w_tempset; 
    setblock(STDIN_FILENO, false); 
    setblock(STDOUT_FILENO, false); 
    FD_ZERO(&r_set); 
    FD_ZERO(&w_set); 
    FD_ZERO(&r_tempset); 
    FD_ZERO(&w_tempset); 
    FD_SET_SET(r_tempset, STDIN_FILENO, maxfd); 
    FD_SET_SET(w_tempset, STDOUT_FILENO, maxfd); 
    r_set = r_tempset; 
    while(true) { 
    select((maxfd + 1), &r_set, &w_set, NULL, NULL); 
    if(FD_ISSET(STDIN_FILENO, &r_set)) { 
     int c = c_read(STDIN_FILENO, buf, buf_size, &buf_head, &buf_tail); 
     if(c == -1) { // EOF, disable select on the input. 
     fprintf(stderr, "No more bytes to read\n"); 
     done_reading = true; 
     FD_ZERO(&r_set); 
     } 
    } 
    if(!done_reading) { 
     r_set = r_tempset; 
    } 
    if(FD_ISSET(STDOUT_FILENO, &w_set)) { 
     c_write(STDOUT_FILENO, buf, buf_size, &buf_head, &buf_tail); 
    } 
    if(!empty_buf(buf_head, buf_tail)) { // Enable select on write whenever there is bytes. 
     w_set = w_tempset; 
    } 
    else { 
     FD_ZERO(&w_set); 
     if(done_reading) { // Finish. 
     fprintf(stderr, "No more bytes to write\n"); 
     break; 
     } 
    } 
    } 
    fflush(stderr); 
    return 0; 
} 

bool empty_buf(unsigned int head, unsigned int tail) { 
    return head == tail; 
} 

/** 
* Keep reading until we can read no more. Keep on pushing the tail forward as we overflow. 
* Expects fd to be non blocking. 
* @returns number of byte read, 0 on non stopping error, or -1 on error or EOF. 
*/ 
int c_read(int fd, char * buf, unsigned int size, unsigned int * head_in, unsigned int * tail_in) { 
    fprintf(stderr, "In c_read()\n"); 
    unsigned int head = *head_in; 
    unsigned int tail = *tail_in; 
    bool more_bytes = true; 
    int n = 0; 
    int c = 0; 

    while(more_bytes) { 
    bool in_front = tail > head; 
    fprintf(stderr, "Read %d %d %d\n", size, head, tail); 

    n = read(fd, buf+head, size - head); 
    if(n == -1) { 
     more_bytes = false; 
     if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { // Not EOF but the read would block. 
     c = 0; 
     } 
     else { 
     c = -1; 
     } 
    } 
    else if(n == 0) { // EOF. No more bytes possible. 
     more_bytes = false; 
     c = -1; 
    } 
    else if(n != (size - head)) { // if not full read adjust pointers and break. 
     more_bytes = false; 
     c += n; 
     head = (head+n)%size; 
     if(in_front && (head >= tail || head == 0)) { 
     tail = (head+1)%size; 
     } 
    } 
    else { 
     c = 0; 
     head = 0; 
     tail = (tail == 0) ? 1 : tail; 
    } 
    } 
    *head_in = head; 
    *tail_in = tail; 
    return c; 
} 

/** 
* Try flush the buffer to fd. fd should be non blocking. 
*/ 
int c_write(int fd, char * buf, unsigned int size, unsigned int * head_in, unsigned int * tail_in) { 
    fprintf(stderr, "In c_write()\n"); 
    unsigned int head = *head_in; 
    unsigned int tail = *tail_in; 
    int n = 0; 
    fprintf(stderr, "Write %d %d %d\n", size, head, tail); 

    if(tail < head) { 
    n = write(fd, buf+tail, head-tail); 
    tail += n; 
    } 
    else if(head < tail) { 
    n = write(fd, buf+tail, size-tail); 
    if(n == size-tail) { 
     n = write(fd, buf, head); 
     tail = n; 
    } 
    } 
    *head_in = head; 
    *tail_in = tail; 
    return n; 
} 

bool setblock(int fd, bool block) 
{ 
    int flags; 
    flags = fcntl(fd, F_GETFL); 
    if (block) 
     flags &= ~O_NONBLOCK; 
    else 
     flags |= O_NONBLOCK; 
    fcntl(fd, F_SETFL, flags); 
    return true; 
} 
1

Jeśli wszystko, co potrzebne jest, aby mieć bufor zachować wyjście dźwięku, dopóki nie jest gotowy do spożycia wariant to powinno działać: nagrywanie

start:

mkfifo /tmp/f 
stdbuf -o256M arecord -i | cat > /tmp/f 

Włącz się do gry, gdy twoje urządzenie wyjściowe jest gotowe:

aplay /tmp/f 

Dostosuj rozmiar bufora wyjściowego do swoich potrzeb.

EDIT (biorąc pod uwagę, że gra może rozpocząć się w każdej chwili):

Jeśli trzeba ciągły rekordu i rozpocząć odtwarzanie każdej chwili można podzielić wyjście na mniejsze pliki używając komendy split i usuwać stare pliki w proces pomocniczy.

Coś jak:

# Garbage collector 
(while sleep 1 ; do rm $(ls *.blb 2>/dev/null | sort | head -n-3) > /dev/null 2>&1 ; done) & 
# Actual recording 
arecord -i | split -a 10 -u -b 24576 --additional-suffix '.blb' 

I grać:

{ while true ; do for f in $(find . -name '*.blb' -size 24576c | sort) ; do cat $f ; rm $f ; done ; done } | aplay 

To rozwiązanie jest bardzo brudna, ale potęga pracy (najlepiej na tmpfs jak już wspomniano) ...

+0

Chciałbym nagrywać cały czas z okrągłym buforem (może być tylko 240K dla 60s z 8000Hz 8bit audio), a następnie, kiedy powinien grać, użyłoby dd do odtworzenia z właściwej pozycji w buforze. W twoim przypadku stdbuf będzie blokował, gdy bufor jest pełny. –

+0

Potem źle zrozumiałem twoje pytanie. Będę edytować z innym pomysłem (który nie jest idealnym rozwiązaniem_, ale może być _pracującym rozwiązaniem _) ... – vlp