#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "wav.h"
bool check_format(WAVHEADER header);
int get_block_size(WAVHEADER header);
int main(int argc, char *argv[])
{
// Ensure proper usage
// TODO #1
if (argc != 3)
{
printf("Only put in the name of the original file, and the name of a new reversed file.\n");
return 1;
}
// Open input file for reading
// TODO #2
FILE *raw_file = fopen(argv[1], "r");
if (raw_file == NULL)
{
printf("Could not open original file.\n");
return 1;
}
// Read header
// TODO #3
WAVHEADER header;
//4 bytes
fread(header.chunkID, 1, 4, raw_file);
//1 dword
fread(&header.chunkSize, 4, 1, raw_file);
//4 bytes
fread(header.format, 1, 4, raw_file);
//4 bytes
fread(header.subchunk1ID, 1, 4, raw_file);
//1 dword
fread(&header.subchunk1Size, 4, 1, raw_file);
//1 word
fread(&header.audioFormat, 2, 1, raw_file);
//1 word
fread(&header.numChannels, 2, 1, raw_file);
//1 dword
fread(&header.sampleRate, 4, 1, raw_file);
//1 dword
fread(&header.byteRate, 4, 1, raw_file);
//1 word
fread(&header.blockAlign, 2, 1, raw_file);
//1 word
fread(&header.bitsPerSample, 2, 1, raw_file);
//4 bytes
fread(header.subchunk2ID, 1, 4, raw_file);
//1 dword
fread(&header.subchunkSize, 4, 1, raw_file);
// Use check_format to ensure WAV format
// TODO #4
if (check_format(header) == false)
{
printf("Only works with wav files. Sorry!\n");
}
// Open output file for writing
// TODO #5
FILE *new_file = fopen(argv[2], "a");
if (new_file == NULL)
{
printf("Could not open original file.\n");
return 1;
}
// Write header to file
// TODO #6
fwrite(header.chunkID, 1, 4, new_file);
//1 dword
fwrite(&header.chunkSize, 4, 1, new_file);
//4 bytes
fwrite(header.format, 1, 4, new_file);
//4 bytes
fwrite(header.subchunk1ID, 1, 4, new_file);
//1 dword
fwrite(&header.subchunk1Size, 4, 1, new_file);
//1 word
fwrite(&header.audioFormat, 2, 1, new_file);
//1 word
fwrite(&header.numChannels, 2, 1, new_file);
//1 dword
fwrite(&header.sampleRate, 4, 1, new_file);
//1 dword
fwrite(&header.byteRate, 4, 1, new_file);
//1 word
fwrite(&header.blockAlign, 2, 1, new_file);
//1 word
fwrite(&header.bitsPerSample, 2, 1, new_file);
//4 bytes
fwrite(header.subchunk2ID, 1, 4, new_file);
//1 dword
fwrite(&header.subchunk2Size, 4, 1, new_file);
// Use get_block_size to calculate size of block
// TODO #7
int block_size = get_block_size(header);
// Write reversed audio to file
// TODO #8
BYTE buffer[block_size];
while (fread(buffer, block_size, 1, raw_file) == 1)
{
fseek(new_file, 44, SEEK_SET);
fwrite(buffer, block_size, 1, new_file);
}
fclose(raw_file);
fclose(new_file);
}
bool check_format(WAVHEADER header)
{
// TODO #4
if (header.format[0] == 'W' && header.format[1] == 'A' && header.format[2] == 'V' && header.format[3] == 'E')
{
return true;
}
return false;
}
int get_block_size(WAVHEADER header)
{
// TODO #7
return header.numChannels * (header.bitsPerSample / 8);
}
当代码运行时,我留下的只是短语“打开音频文件时发生错误”。我也非常确定错误发生在 TODO 8,即实际反转发生的地方。我使用
fseek
将每个音频块放在标题后面,标题长度为 44 字节。然后我尝试从 block_size 大小的缓冲区写入。而且,在它告诉我这句话之后,一切基本上都停止了。它开始在我的所有文件上说:“由于意外错误,无法打开此编辑器:无法读取文件。解决此问题的唯一方法是重新加载页面。我不确定导致此问题的原因,但是也许它正在经历某种循环?此外,WAVHEADER
结构位于一个单独的文件中,如下所示:
#include <stdint.h>
typedef uint8_t BYTE;
typedef uint16_t WORD;
typedef uint32_t DWORD;
typedef struct
{
BYTE chunkID[4];
DWORD chunkSize;
BYTE format[4];
BYTE subchunk1ID[4];
DWORD subchunk1Size;
WORD audioFormat;
WORD numChannels;
DWORD sampleRate;
DWORD byteRate;
WORD blockAlign;
WORD bitsPerSample;
BYTE subchunk2ID[4];
DWORD subchunk2Size;
} __attribute__((__packed__))
WAVHEADER;
如果您想知道,这是 cs50x 的 Pset 4 的逆向。
之前的CS50作业是操作数据,解决方案是处理内存中的所有数据。为什么不在这里也这样做呢?
以下是一些“粗略”的代码选择,可能会指导您编写自己的解决方案。
一次操作加载整个标头(不是逐个字段):
WAVHEADER hdr;
if( fread( &hdr, sizeof hdr, 1, fp ) != 1 ) {
计算并分配一个工作缓冲区来保存所有的样本
参考网上关于WAV文件的doco来理解下面的计算
size_t dataSz = hdr.chunkSize + 8 - sizeof hdr;
uint8_t *p = malloc( dataSz );
加载文件的整个数据部分
if( fread( p, 1, dataSz, fp ) != dataSz ) {
现在可以关闭源文件了
计算一个样本的大小
int smpl_sz = hdr.numChannels * (hdr.bitsPerSample / 8);
使用 指针迭代交换第一个和最后一个样本,然后调整指针
for( uint8_t *sp = p, *rp = p + dataSz - smpl_sz; rp > sp; sp += smpl_sz, rp -= smpl_sz ) {
uint8_t tp[32]; // sufficient to handle 2, 4, ... up to (ridiculous) 16 bytes/sample
memcpy( tp, sp, smpl_sz );
memcpy( sp, rp, smpl_sz );
memcpy( rp, tp, smpl_sz );
}
打开目标文件。写入标头,然后写入处理后的数据
返回值的验证留给读者完成
fp = fopen( argv[2], "wb" );
fwrite( &hdr, sizeof hdr, 1, fp ); // != 1 ) {
fwrite( p, 1, dataSz, fp );
fclose( fp );
不要忘记
free()
分配的内存,然后...
return 0;
缺少一些琐碎的部分,例如验证文件是否为“.WAV”格式文件以及验证系统调用(如
malloc()
和 fwrite()
)的返回值。这些留作练习...