2015-09-04 19 views
8

Ho bisogno di inviare l'audio da una radio a un sistema secondario utilizzando un sistema Linux incorporato.Linux come registrare il suono nel buffer RAM e riprodurre l'audio con il ritardo personalizzato

Il sistema secondario deve impostare un canale di comunicazione che richiede alcuni secondi.

Quindi, se non voglio perdere l'inizio dell'audio, ho bisogno di un modo per registrare il suono e riprodurlo con un ritardo personalizzato (massimo di pochi secondi).

Dovrebbe essere possibile avviare arecord per registrare l'audio in un file in un filesystem tmpfs e, quando è in arrivo una comunicazione, avviare aplay. Ma in questo caso l'inizio è ancora perso perché il segnale da registrare arriva troppo tardi.

Esiste un programma su Linux che registra continuamente il suono in un buffer circolare nella RAM e può essere riprodotto con un ritardo personalizzato su richiesta?

In caso contrario, qual è la migliore libreria per codificare tale programma su un sistema embedded? alsa o qualcos'altro?

+0

Su Linux, ogni libreria audio finisce con l'utilizzo di ALSA. Tuttavia, puoi usare qualsiasi altra libreria se è più facile da usare. –

+0

Questa domanda è off-topic in quanto richiede uno strumento o una libreria? –

+0

Alsa ha il supporto per i plugin LADSPA, ce ne dovrebbe essere uno con un ritardo fisso. – Phillip

risposta

6

Ecco un semplice programma C che manterrà un buffer circolare tra il tubo dentro e fuori. Utilizzare come in | buffer_program | out. Controllo errori omesso. Robustezza non garantita Dà l'idea generale

script di test (ma in realtà fin dalla sua circolare buffer dei dati di tuo tubazioni in deve essere tale che la sua coerente prendere un qualsiasi pezzo nel flusso o semplicemente fare il buffer più grande di dati.):

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

Se tutto ciò che serve è quello di avere un buffer per mantenere uscita audio fino a quando è pronto per essere consumato una variante di questo dovrebbe funzionare: registrazione

Inizio:

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

Inizia a giocare, quando la vostra dispositivo di uscita è pronto:

aplay /tmp/f 

modificare la dimensione del buffer in base alle proprie esigenze.

EDIT (dato che il gioco può iniziare in qualsiasi momento):

Se è necessario continuamente registrazione e iniziare a giocare qualsiasi momento si potrebbe dividere l'output in file più piccoli con il comando split e cancellare i vecchi file in un processo di aiuto.

Qualcosa di simile:

# 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' 

E si gioca:

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

Questa soluzione è abbastanza sporca, ma potenza lavoro (preferibilmente su tmpfs come si è già accennato) ...

+0

Mi piacerebbe registrare tutto il tempo con un buffer circolare (può essere solo 240K per 60s di audio 8bit a 8000Hz) e poi quando dovrebbe suonare, userebbe dd per riprodurre dalla posizione corretta nel buffer. Nel tuo caso stdbuf bloccherà quando il buffer è pieno. –

+0

Poi ho frainteso la tua domanda. Modificherò con un'altra idea (che non è una soluzione _perfetta_, ma potrebbe essere una soluzione _working _) ... – vlp

Problemi correlati