2015-09-04 9 views
8

組み込みLinuxシステムを使用してラジオからセカンダリシステムにオーディオを送信する必要があります。Linux RAMバッファに音を録音してカスタム遅延を再生する方法

セカンダリシステムでは、数秒かかる通信チャネルを設定する必要があります。

私はオーディオの始めを失いたくない場合は、サウンドを録音してカスタム遅延(最大数秒)で再生する方法が必要です。

arecordを起動して、tmpfsファイルシステムのファイルにオーディオを記録し、通信が着信したら、aplayを開始する必要があります。 しかし、この場合、録音する信号が遅すぎるため、最初はまだ失われています。

RAM上のリングバッファにサウンドを連続的に録音し、必要に応じてカスタム遅延で再生できるプログラムはありますか?

もしそうでなければ、組み込みシステムでそのようなプログラムをコーディングするのに最適なライブラリは何ですか? alsaまたは何か他の?

+0

Linuxでは、すべてのオーディオライブラリは最終的にALSAを使用して終了します。ただし、他のライブラリを使用すると簡単に使用できます。 –

+0

ツールやライブラリを尋ねるので、この質問は話題になりませんか? –

+0

AlsaはLADSPAプラグインをサポートしていますが、固定遅延を備えたプラグインが必要です。 – Phillip

答えて

6

ここでは、パイプの入出力間に循環バッファを維持する単純なCプログラムを示します。 in | buffer_program | outのように使用してください。エラーチェックが省略されました。ロバストネスは保証されません。一般的な考え方を示します。

テストスクリプト(実際にその循環バッファ以来、ニーズのデータ​​を、あなたの配管はそのコヒーレントをストリームにただの塊を取っていることなどであること、または単にデータよりもバッファを大きくする。):

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

あなたが必要とするすべては、こののバリエーションが動作するはずですが消費される準備が整うまで、音の出力を維持するためのバッファを持つことがある場合:

スタート記録:

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

スタートあなた、演奏

aplay /tmp/f 

必要に応じて出力バッファサイズを調整します。

EDIT(演奏はいつでも始めることができることを考えると):あなたがに必要がある場合は

連続レコードを、いつでもあなたがsplitコマンドを使用して小さなファイルに出力を分割し、古いファイルを削除することができの再生を開始ヘルパープロセス。

ような何か:

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

そして再生する:

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

このソリューションはかなり汚いですが、かもしれない(好ましくは既に述べたようtm​​pfsの上)仕事に...

+0

私は循環バッファ(8000Hz 8bitオーディオの60秒では240Kしかありません)ですべての時間を記録したいと思っています。再生するときは、ddを使ってバッファ内の正しい位置から再生します。あなたのケースでは、バッファがいっぱいになるとstdbufがブロックされます。 –

+0

あなたの質問を誤解しました。私は別のアイデアで編集します(これは_完全なソリューションではありませんが、_ワーキングソリューション_かもしれません)... – vlp

関連する問題