反转WAV文件,“打开音频文件时发生错误”

问题描述 投票:0回答:1
#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 的逆向。

c audio reverse cs50 wav
1个回答
0
投票

之前的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()
)的返回值。这些留作练习...

© www.soinside.com 2019 - 2024. All rights reserved.