0

我正在为一个操作系统项目编写一个程序,它基本上是一个调制解调器键盘,因为我键入一个键,它输出一个与该键的 ASCII 值相对应的 FSK 调制音频信号。我如何设置我的程序是它派生一个进程并执行一个名为 minimodem 的程序(有关信息,请参见此处)。父级设置为非规范输入模式,并让用户一次输入一个字符。然后通过管道将每个字符发送给孩子。我现在就粘贴代码:

#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);
}

我的问题是用户输入。打字时,似乎第一个字符被写入并生成了音频,然后有一个延迟,然后缓冲区中的其余字符会像预期的那样连续生成。如果输入中有足够大的停顿,那么它会回到开始,在中断后输入的第一个字符生成,然后是延迟,然后是预期的功能。我的手指交叉,这不是因为 minimodem 程序不是为了以这种方式使用而编写的,而且这个问题是可以克服的。如果有人能对此事有所了解,我将非常感激。谢谢。

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

4

1 回答 1

0

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

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

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

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

可以通过手动设置块大小来改进 Minimodem。这样,它就可以准确地知道需要写入多少静音来刷新音频,而不是写入任意数量,因此可以将延迟减少到最低限度。

于 2013-10-06T16:43:26.627 回答