前言
最近研究了下wav格式,为了巩固下对格式的认识,写了个小工具来修改音频通道数,原理很简单,看两张图就大概懂了
wav格式
信息头结构
数据排列方式
废话不多说,直接上代码
转换代码
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>
#include <endian.h>
#include <unistd.h>
#define ID_RIFF 0x46464952
#define ID_WAVE 0x45564157
#define ID_FMT 0x20746d66
#define ID_DATA 0x61746164
struct wav_header {
uint32_t riff_id;
uint32_t riff_sz;
uint32_t riff_fmt;
uint32_t fmt_id;
uint32_t fmt_sz;
uint16_t audio_format;
uint16_t num_channels;
uint32_t sample_rate;
uint32_t byte_rate;
uint16_t block_align;
uint16_t bits_per_sample;
uint32_t data_id;
uint32_t data_sz;
};
int main(int argc, char **argv) {
FILE *file;
FILE *out;
struct wav_header in_header;
unsigned int channels = 2;
char *filename;
char *out_filename;
int more_chunks = 1;
if (argc < 3) {
fprintf(stderr, "Usage: %s in.wav out.wav [-c channels] \n", argv[0]);
return 1;
}
filename = argv[1];
file = fopen(filename, "rb");
if (!file) {
fprintf(stderr, "Unable to open file '%s'\n", filename);
return 1;
}
out_filename = argv[2];
out = fopen(out_filename, "wb");
if (!out) {
fprintf(stderr, "Unable to open file '%s'\n", out_filename);
return 1;
}
fread(&in_header, sizeof(in_header), 1, file);
if ((in_header.riff_id != ID_RIFF) ||
(in_header.riff_fmt != ID_WAVE)) {
fprintf(stderr, "Error: '%s' is not a riff/wave file\n", filename);
fclose(file);
return 1;
}
/* parse command line arguments */
argv += 3;
while (*argv) {
if (strcmp(*argv, "-c") == 0) {
argv++;
if (*argv)
channels = atoi(*argv);
}
if (*argv)
argv++;
}
struct wav_header out_header;
memcpy(&out_header, &in_header, sizeof(out_header));
out_header.num_channels = channels;
out_header.block_align = out_header.num_channels * (out_header.bits_per_sample / 8);
out_header.data_sz = out_header.block_align * (in_header.data_sz / in_header.block_align);
out_header.riff_sz = out_header.data_sz + sizeof(out_header) - 8;
fwrite(&out_header, sizeof(struct wav_header), 1, out);
fseek(file, sizeof(struct wav_header), SEEK_SET);
fseek(out, sizeof(struct wav_header), SEEK_SET);
char *buffer = malloc(in_header.block_align);
for (int i = 0; i < in_header.data_sz; i += in_header.block_align) {
if (fread(buffer, 1, in_header.block_align, file) != in_header.block_align) {
fprintf(stderr, "Error read file\n");
break;
}
if (in_header.num_channels > out_header.num_channels) {
// remove channel
if (fwrite(buffer, 1, out_header.block_align, out) != out_header.block_align) {
fprintf(stderr, "Error write file\n");
break;
}
} else if (in_header.num_channels < out_header.num_channels) {
// add channel
if (fwrite(buffer, 1, in_header.block_align, out) != in_header.block_align) {
fprintf(stderr, "Error write file\n");
break;
}
for (int j = 0; j < out_header.num_channels - in_header.num_channels; j++) {
// add channel with first channel
if (fwrite(buffer, 1, in_header.block_align / in_header.num_channels, out) !=
in_header.block_align / in_header.num_channels) {
fprintf(stderr, "Error write file\n");
break;
}
}
}
}
fclose(file);
fclose(out);
return 0;
}