多次写入和播放 Wav 文件
Wav file write and play multiplie time
我有一个需求,我需要多次写入一个 wav(带有 pcma 数据)文件。
假设我有一个文件 audio-g711a.wav。我想将它写入一个新文件说 audio-g711a-out.wav 2 次。当我播放 audio-g711a-out.wav 时,播放时间应该是原始文件播放时间的两倍。
我确实使用以下代码编写。然而,它的播放时间与原始文件的播放时间完全相同(我原以为它的播放时间是原来的两倍)。
代码如下
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/uio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int g_count = 0;
void init_config(int argc,char **argv);
void rewrite_file(int count);
int main(int argc, char **argv)
{
init_config(argc, argv);
printf("Count = %d\n", g_count);
rewrite_file(g_count);
return 0;
}
void rewrite_file(int count)
{
int index;
char arr[101];
FILE *fd_in;
FILE *fd_out;
size_t len;
unsigned long chunk_size;
index = 0;
fd_in = fopen("./audio-g711a.wav", "r");
fd_out = fopen("./audio-g711a-out.wav", "w+");
rw_again:
index++;
len = fread(arr, 1, 100, fd_in);
while(len == 100)
{
fwrite(arr, 1, 100, fd_out);
len = fread(arr, 1, 100, fd_in);
}
fwrite(arr, 1, len, fd_out);
if(count > index)
{
printf("Completed %d round of operation of %d total rounds.\n", index, count);
fclose(fd_in);
fd_in = fopen("./audio-g711a.wav", "r");
goto rw_again;
}
return;
}
void init_config(int argc,char **argv)
{
int ch;
while ((ch = getopt(argc, argv, "v:n:N:X")) != -1)
{
switch (ch)
{
case 'n':
case 'N':
{
g_count = atoi(optarg);
}
break;
default:
break;
}
}
return;
}
要执行这个,可以执行 ./a.out -n 2
经过一些研发,我意识到 wav 文件有某种 header。当我第二次写的时候,我又在写 header 了。这可能会导致文件无法继续播放。我在第二次写的时候停止写 header 部分(44 字节)。这并没有解决问题。
有人可以指导我如何实现至少写入 2 次 wav 文件。
更新
工作代码如下。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/uio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int g_count = 0;
void init_config(int argc,char **argv);
void rewrite_file(int count);
int main(int argc, char **argv)
{
init_config(argc, argv);
printf("Count = %d\n", g_count);
rewrite_file(g_count);
return 0;
}
void rewrite_file(int count)
{
int index;
char arr[101];
FILE *fd_in;
FILE *fd_out;
size_t len;
unsigned short data_index = 0;
index = 0;
fd_in = fopen("./audio-g711a.wav", "r");
fd_out = fopen("./audio-g711a-out.wav", "w+");
// copy header
len = fread(arr, 1, 40, fd_in);
fwrite(arr, 1, len, fd_out);
if( strncmp("data", (arr+36), 4) == 0 )
{
data_index = 40;
}
else
{
len = fread(arr, 1, 14, fd_in);
fwrite(arr, 1, len, fd_out);
if( strncmp("data", (arr+10), 4) == 0 )
{
data_index = 54;
}
}
// update header
uint32_t dl;
fseek(fd_in, data_index, SEEK_SET);
fseek(fd_out, data_index, SEEK_SET);
fread(&dl, sizeof(dl), 1, fd_in);
dl *= count;
fwrite(&dl, sizeof(dl), 1, fd_out);
// copy data
rw_again:
index++;
fseek(fd_in, (4 + data_index), SEEK_SET);
len = fread(arr, 1, 100, fd_in);
while(len > 0)
{
fwrite(arr, 1, len, fd_out);
len = fread(arr, 1, 100, fd_in);
}
fwrite(arr, 1, len, fd_out);
if(count > index)
{
printf("Completed %d round of operation of %d total rounds.\n", index, count);
fclose(fd_in);
fd_in = fopen("./audio-g711a.wav", "r");
goto rw_again;
}
fclose(fd_in);
fclose(fd_out);
return;
}
void init_config(int argc,char **argv)
{
int ch;
while ((ch = getopt(argc, argv, "v:n:N:X")) != -1)
{
switch (ch)
{
case 'n':
case 'N':
{
g_count = atoi(optarg);
}
break;
default:
break;
}
}
return;
}
wav 文件有一个 header 和一个数据部分:
[HEADER][DATA]
一个简单的副本,就像你做的那样,生成以下文件格式:
[HEADER][DATA][HEADER][DATA]
您需要的是:
[HEADER][DATADATA]
^
|
+--- chunksize at offset 40 updated
这是一个快速技巧:
void rewrite_file(int count)
{
int index;
char arr[101];
FILE *fd_in;
FILE *fd_out;
size_t len;
unsigned long chunk_size;
index = 0;
fd_in = fopen("./audio-g711a.wav", "r");
fd_out = fopen("./audio-g711a-out.wav", "w+");
// copy header
len = fread(arr, 1, 40, fd_in);
fwrite(arr, 1, len, fd_out);
// update header
uint32_t dl;
fseek(fd_in, 40, SEEK_SET);
fseek(fd_out, 40, SEEK_SET);
fread(&dl, sizeof(dl), 1, fd_in);
dl *= count;
fwrite(&dl, sizeof(dl), 1, fd_out);
// copy data
rw_again:
index++;
fseek(fd_in, 44, SEEK_SET);
len = fread(arr, 1, 100, fd_in);
while(len > 0)
{
fwrite(arr, 1, len, fd_out);
len = fread(arr, 1, 100, fd_in);
}
fwrite(arr, 1, len, fd_out);
if(count > index)
{
printf("Completed %d round of operation of %d total rounds.\n", index, count);
fclose(fd_in);
fd_in = fopen("./audio-g711a.wav", "r");
goto rw_again;
}
fclose(fd_in);
fclose(fd_out);
return;
}
更新:
如果 wav 文件包含 'fact' 块(non-PCM 格式),则数据块大小偏移量不是 40,而是 54。所以最好搜索 'data'-tag 来计算数据块偏移量,而不是使用 40 作为幻数。
您需要将 "data" 子块的 "data" 部分加倍。
读取总块大小(偏移量 4)。
读取数据子块大小(偏移量 40)。
这两个值应增加子块大小(如果子块大小值中包含该大小的 4 个字节,则可能为 -4)。
数据子块的数据部分(从偏移量 44 开始)应该加倍(写入两次)。
我有一个需求,我需要多次写入一个 wav(带有 pcma 数据)文件。
假设我有一个文件 audio-g711a.wav。我想将它写入一个新文件说 audio-g711a-out.wav 2 次。当我播放 audio-g711a-out.wav 时,播放时间应该是原始文件播放时间的两倍。
我确实使用以下代码编写。然而,它的播放时间与原始文件的播放时间完全相同(我原以为它的播放时间是原来的两倍)。
代码如下
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/uio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int g_count = 0;
void init_config(int argc,char **argv);
void rewrite_file(int count);
int main(int argc, char **argv)
{
init_config(argc, argv);
printf("Count = %d\n", g_count);
rewrite_file(g_count);
return 0;
}
void rewrite_file(int count)
{
int index;
char arr[101];
FILE *fd_in;
FILE *fd_out;
size_t len;
unsigned long chunk_size;
index = 0;
fd_in = fopen("./audio-g711a.wav", "r");
fd_out = fopen("./audio-g711a-out.wav", "w+");
rw_again:
index++;
len = fread(arr, 1, 100, fd_in);
while(len == 100)
{
fwrite(arr, 1, 100, fd_out);
len = fread(arr, 1, 100, fd_in);
}
fwrite(arr, 1, len, fd_out);
if(count > index)
{
printf("Completed %d round of operation of %d total rounds.\n", index, count);
fclose(fd_in);
fd_in = fopen("./audio-g711a.wav", "r");
goto rw_again;
}
return;
}
void init_config(int argc,char **argv)
{
int ch;
while ((ch = getopt(argc, argv, "v:n:N:X")) != -1)
{
switch (ch)
{
case 'n':
case 'N':
{
g_count = atoi(optarg);
}
break;
default:
break;
}
}
return;
}
要执行这个,可以执行 ./a.out -n 2
经过一些研发,我意识到 wav 文件有某种 header。当我第二次写的时候,我又在写 header 了。这可能会导致文件无法继续播放。我在第二次写的时候停止写 header 部分(44 字节)。这并没有解决问题。
有人可以指导我如何实现至少写入 2 次 wav 文件。
更新
工作代码如下。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/uio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int g_count = 0;
void init_config(int argc,char **argv);
void rewrite_file(int count);
int main(int argc, char **argv)
{
init_config(argc, argv);
printf("Count = %d\n", g_count);
rewrite_file(g_count);
return 0;
}
void rewrite_file(int count)
{
int index;
char arr[101];
FILE *fd_in;
FILE *fd_out;
size_t len;
unsigned short data_index = 0;
index = 0;
fd_in = fopen("./audio-g711a.wav", "r");
fd_out = fopen("./audio-g711a-out.wav", "w+");
// copy header
len = fread(arr, 1, 40, fd_in);
fwrite(arr, 1, len, fd_out);
if( strncmp("data", (arr+36), 4) == 0 )
{
data_index = 40;
}
else
{
len = fread(arr, 1, 14, fd_in);
fwrite(arr, 1, len, fd_out);
if( strncmp("data", (arr+10), 4) == 0 )
{
data_index = 54;
}
}
// update header
uint32_t dl;
fseek(fd_in, data_index, SEEK_SET);
fseek(fd_out, data_index, SEEK_SET);
fread(&dl, sizeof(dl), 1, fd_in);
dl *= count;
fwrite(&dl, sizeof(dl), 1, fd_out);
// copy data
rw_again:
index++;
fseek(fd_in, (4 + data_index), SEEK_SET);
len = fread(arr, 1, 100, fd_in);
while(len > 0)
{
fwrite(arr, 1, len, fd_out);
len = fread(arr, 1, 100, fd_in);
}
fwrite(arr, 1, len, fd_out);
if(count > index)
{
printf("Completed %d round of operation of %d total rounds.\n", index, count);
fclose(fd_in);
fd_in = fopen("./audio-g711a.wav", "r");
goto rw_again;
}
fclose(fd_in);
fclose(fd_out);
return;
}
void init_config(int argc,char **argv)
{
int ch;
while ((ch = getopt(argc, argv, "v:n:N:X")) != -1)
{
switch (ch)
{
case 'n':
case 'N':
{
g_count = atoi(optarg);
}
break;
default:
break;
}
}
return;
}
wav 文件有一个 header 和一个数据部分:
[HEADER][DATA]
一个简单的副本,就像你做的那样,生成以下文件格式:
[HEADER][DATA][HEADER][DATA]
您需要的是:
[HEADER][DATADATA]
^
|
+--- chunksize at offset 40 updated
这是一个快速技巧:
void rewrite_file(int count)
{
int index;
char arr[101];
FILE *fd_in;
FILE *fd_out;
size_t len;
unsigned long chunk_size;
index = 0;
fd_in = fopen("./audio-g711a.wav", "r");
fd_out = fopen("./audio-g711a-out.wav", "w+");
// copy header
len = fread(arr, 1, 40, fd_in);
fwrite(arr, 1, len, fd_out);
// update header
uint32_t dl;
fseek(fd_in, 40, SEEK_SET);
fseek(fd_out, 40, SEEK_SET);
fread(&dl, sizeof(dl), 1, fd_in);
dl *= count;
fwrite(&dl, sizeof(dl), 1, fd_out);
// copy data
rw_again:
index++;
fseek(fd_in, 44, SEEK_SET);
len = fread(arr, 1, 100, fd_in);
while(len > 0)
{
fwrite(arr, 1, len, fd_out);
len = fread(arr, 1, 100, fd_in);
}
fwrite(arr, 1, len, fd_out);
if(count > index)
{
printf("Completed %d round of operation of %d total rounds.\n", index, count);
fclose(fd_in);
fd_in = fopen("./audio-g711a.wav", "r");
goto rw_again;
}
fclose(fd_in);
fclose(fd_out);
return;
}
更新:
如果 wav 文件包含 'fact' 块(non-PCM 格式),则数据块大小偏移量不是 40,而是 54。所以最好搜索 'data'-tag 来计算数据块偏移量,而不是使用 40 作为幻数。
您需要将 "data" 子块的 "data" 部分加倍。
读取总块大小(偏移量 4)。
读取数据子块大小(偏移量 40)。
这两个值应增加子块大小(如果子块大小值中包含该大小的 4 个字节,则可能为 -4)。
数据子块的数据部分(从偏移量 44 开始)应该加倍(写入两次)。