为什么我的输入在通过管道发送到进程时会延迟?

时间:2013-10-06 13:21:16

标签: c audio pipe ipc

我正在编写一个操作系统项目的程序,它基本上是一个调制解调器键盘,就像我键入一个键,它输出一个FSK调制的音频信号,对应于该键的ASCII值。我如何设置我的程序是它分叉一个进程并执行一个名为minimodem(see here的程序来获取信息)。父级设置为非规范输入模式,并且一次为用户输入一个字符。然后通过管道将每个字符发送给孩子。我现在就粘贴代码:

#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <string.h>
#include <termios.h>

extern char* program_invocation_short_name;

static struct termios old, new;
void init_termios(int echo);
void reset_termios(void);
int main(int argc, char* argv[])
{
pid_t pid;
int my_pipe[2];

char* baud = "300";
if (argc == 2) {
    if(atoi(argv[1]) == 0) {
        printf("Use: %s [baud]\n",program_invocation_short_name);
        return EXIT_SUCCESS;
    }
    baud = argv[1];
}

if (argc > 2) {
    printf("Too many arguments.\nUsage: %s [baud]\n",program_invocation_short_name);
    return EXIT_SUCCESS;
}

if (pipe(my_pipe) == -1) {
    fprintf(stderr, "%s: %s",program_invocation_short_name,strerror(errno));
    return EXIT_FAILURE;
}

pid = fork();
if (pid < (pid_t) 0) {
    fprintf(stderr, "%s: %s",program_invocation_short_name,strerror(errno));
    return EXIT_FAILURE;
}else if (pid == (pid_t) 0) {
    /***************/
    /*CHILD PROCESS*/
    /***************/
    close(my_pipe[1]); /*Child doesn't write*/
    dup2(my_pipe[0], 0); /*Redirect stdin to read side of pipe*/
    close(my_pipe[0]); /*Close read end as it's dup'd*/
    execl("/usr/local/bin/minimodem","minimodem","--tx", baud,"-q","-A",NULL);

    fprintf(stderr, "%s: %s",program_invocation_short_name,strerror(errno));
}else if (pid > (pid_t) 0) {
    /****************/
    /*PARENT PROCESS*/
    /****************/
    char c;
    close(my_pipe[0]); /*Parent doesn't read*/
    init_termios(1);
    atexit(reset_termios);

    while(1) {
        c = getchar();
        if (c == 0x03)
            break;
        if (write(my_pipe[1], &c, 1) == -1) {
            fprintf(stderr, "%s: %s",
                    program_invocation_short_name, strerror(errno));
            return EXIT_FAILURE;
        }
    }
    close(my_pipe[1]);
}
return EXIT_SUCCESS;
}

void init_termios(int echo)
{
    tcgetattr(0, &old); /*get old terminal i/o settings*/
    new = old; /*make new  settings same as old settings */
    new.c_lflag &= ~ICANON;
    new.c_lflag &= echo ? ECHO : ~ECHO; /*set appropriate echo mode*/
    tcsetattr(0, TCSANOW, &new); /*use new terminal i/o settings*/
}

void reset_termios(void)
{
    tcsetattr(0, TCSANOW, &old);
}

我的问题在于用户输入。在键入时,似乎第一个字符被写入并生成音频然后有一个延迟,然后缓冲区中的其余字符会像他们想要的那样连续生成。如果在键入时有足够大的暂停,那么它将返回到开始,其中在中断之后键入的第一个字符生成,然后是延迟,然后是预期的功能。 我的手指交叉,这不是因为minimalodem程序没有写成以这种方式使用,并且可以克服这个问题。 如果有人能够对这件事情有所了解,那我就太棒了。 感谢。

注意:我已尝试将输入放入环形缓冲区,然后将该输入消耗并以单独的线程发送给子节点。没有更好的。甚至不确定是否注意到这是有效的。

1 个答案:

答案 0 :(得分:0)

当Minimodem检测到没有足够的输入数据来保持音频缓冲区满时,将写入0.5秒的静音。这是为了确保它所写的任何音频不断出现。通常情况下,音频驱动程序或服务器(如pulseaudio)音频仅以块的形式写入。当您将少于一大块数据写入音频缓冲区时,该卡不会发出声音,因为驱动程序或服务器正在等待足够的音频数据,以便它可以立即写入一个完整的块。由于minimodem写入的数据通常不会匹配完整的音频块,因此您将遇到无法写入音频数据的最后部分的情况。为了避免这个问题,minimodem写入足够的静音以保证音频的最后部分输出到卡。

例如,假设音频驱动程序或服务器以1000字节的块写入。现在让我们说你输入一个字符,然后产生2500字节的音频数据。音频驱动程序或服务器将播放2000字节的数据,并在缓冲区中保留500字节。由于调制解调器协议需要连续的音频,因此将这500字节的音频数据留在缓冲区中没有任何意义。接收者看不到完整的角色。接收器必须假设音频是乱码并丢弃该角色。

因此,为了避免这种情况,minimodem将写入0.5秒的静音,这可能是数千字节的音频数据。这将保证缓冲区中剩余的500字节音频数据被播放,因为肯定会有足够的音频数据来完成该块。

现在确实缓冲区中可能会有一些额外的沉默,并且不会立即播放,但这不是什么大问题,因为这不会导致任何损坏。接收器可以处理数据之间的不同数量的静音(请注意,这特定于minimalodem的工作原理,真正的模型总是会产生声音)。

通过手动设置块大小可以改善Minimodem。通过这种方式,它可以确切地知道需要写入多少静音才能刷新音频而不是写入任意数量,因此可以将延迟降低到最小。