我正在为一个操作系统项目编写一个程序,该程序基本上是一个调制解调器键盘,就像我键入一个键一样,它会输出一个与该键的 ASCII 值相对应的 FSK 调制音频信号。我如何设置我的程序是它 fork 一个进程并执行一个名为 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);
}
我的问题是用户输入。打字时,似乎第一个字符被写入并生成音频,然后有一个延迟,然后缓冲区中的其余字符按照预期连续生成。如果输入中有足够大的停顿,那么它会回到开头,生成中断后输入的第一个字符,然后是延迟,然后是预期的功能。 我祈祷这不是因为迷你调制解调器程序不是为了以这种方式使用而编写的,并且这个问题可以克服。 如果有人能对此事有所了解,我将非常感激。 谢谢。
注意:我尝试将输入放入环形缓冲区,然后在单独的线程中使用该输入并将其发送给子进程。没有更好。甚至不确定注意到这一点是否有效。
最佳答案
当 Minimodem 检测到没有足够的输入数据来保持音频缓冲区满时,它将写入 0.5 秒的静音。这是为了确保它写入的任何音频都能连续输出。音频驱动程序或服务器(如pulseaudio)的典型情况是音频仅以 block 的形式写入。当您向音频缓冲区写入少于完整数据 block 时,卡不会发出声音,因为驱动程序或服务器正在等待足够的音频数据,以便可以立即写入完整数据 block 。由于小型调制解调器写入的数据通常不会与完整的音频 block 匹配,因此会出现音频数据的最后部分不会被写入的情况。为了避免这个问题,minimodem 写入足够的静音以保证音频的最后部分输出到卡。
例如,假设音频驱动程序或服务器以 1000 字节 block 写入。现在假设您输入一个字符,就会产生 2500 字节的音频数据。音频驱动程序或服务器将导致播放 2000 字节的数据,并在缓冲区中保留 500 字节。由于调制解调器协议(protocol)需要连续的音频,因此将这 500 字节的音频数据留在缓冲区中没有任何意义。接收者看不到完整的字符。然后接收器必须假设音频是乱码并丢弃该字符。
因此,为了避免这种情况,minimodem 将写入 0.5 秒的静音,这可能是数千字节的音频数据。这将保证缓冲区中剩余的 500 字节音频数据得到播放,因为肯定有足够的音频数据来完成该 block 。
现在缓冲区中确实可能会有一些额外的静音,并且不会立即播放,但这并不是什么大问题,因为这不会导致任何损坏。接收器可以处理数据之间不同数量的静音(请注意,这特定于迷你调制解调器的工作方式,真实的模型将始终生成声音)。
Minimodem 可以通过手动设置 block 大小来改进。这样,它就可以准确地知道需要写入多少静音来刷新音频,而不是写入任意数量,因此可以将延迟降至最低。
关于c - 为什么我的输入通过管道发送到进程时会延迟?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19209313/