更改 wav 文件的采样率和比特率而不会遇到打开文件的问题?

Posted

技术标签:

【中文标题】更改 wav 文件的采样率和比特率而不会遇到打开文件的问题?【英文标题】:Changing samplerate and byterate of a wav file without encountering problems opening the file? 【发布时间】:2015-07-12 21:34:33 【问题描述】:

我正在尝试将 503,196 字节的 wav 文件的内容写入另一个文件,并将采样率/字节率加倍以使速度加倍。但是,我无法从输入文件中读取数据并将其写入输出文件。这是我的代码:

#include <stdio.h>

int main() 
    struct WAVE 
           char ChunkID[4];
           unsigned int ChunkSize;
           char Format[4];
           char Subchunk1ID[4];
           unsigned int Subchunk1Size;
           unsigned short int AudioFormat;
           unsigned short int NumChannels;
           unsigned int SampleRate;
           unsigned int ByteRate;
           unsigned short int BlockAlign;
           unsigned short int BitsPerSample;
           char Subchunk2ID[4];
           unsigned int Subchunk2Size;
     wav;
    FILE *original, *fast;
    original = fopen("Alejandro_project.wav", "rb");
    fast = fopen("Alejandro_fast.wav", "wb");
    if (original == NULL) 
                 printf("File does not exist.\n");
                 return 0;
    
    fread(&wav, 1, 44, original);
    unsigned short int data[12 * wav.SampleRate]
    int i;
    for (i = 0; i < Subchunk2Size / 2; i++)
        fread(&data[i], 1, 2, original);
    fwrite(&wav, 1, 44, fast);
    unsigned int fastSampleRate = wav.SampleRate * 2;
    unsigned int fastByteRate = 2 * fastSampleRate;
    fseek(fast, 24, SEEK_SET);
    fwrite(&fastSampleRate, 4, 1, fast);
    fseek(fast, 28, SEEK_SET);
    fwrite(&fastByteRate, 4, 1, fast);
    fwrite(data, 1, 2, original);
    fclose(fast);
    fclose(original);

【问题讨论】:

您可能应该打包您的 WAVE 结构(#pragma pack(1)__attribute__((__packed__)) 或其他)...它可能有不需要的填充。 程序启动时 Alejandro_fast.wav 是否已经包含该文件的副本?看起来您没有将标头之后的其余文件写入目标文件 @samgak 实际上,既然你提到了它,即使 Alejandro_fast.wav 确实 (最初)包含一个副本,它也会被丢弃,因为文件是用 " wb"...也许"r+b"会更好。 【参考方案1】:

有几个问题.. 对于初学者,您需要确保 WAVE 标头的结构已打包,以免结构内出现意外填充,从而导致某些字段未对齐并增加结构的大小.否则,您可能会在每个 unsigned short int 成员之后获得两个字节的额外填充。

#pragma pack(push,1) /* for VC, may also work for GCC */
struct WAVE 
     /* ...struct contents... */
 wav;
#pragma pack(pop)

其次,您似乎正试图简单地覆盖新文件中的 SampleRate 和 ByteRate 成员,而其余部分保持不变。这仅在新文件已经是旧文件的副本时才有效。如果是这样,您还需要将打开新文件的模式更改为“r+b”(读取模式+写入)而不是“wb”(写入模式),这样现有文件内容就不会被丢弃。

当您尝试写入标头时也会出错。这一行:

fread(original, 1, 44, fast);

应该是:

fwrite(&wav, 44, 1, fast);

将 44 个字节从 wav 写入新文件,而不是从新文件中读取并使用旧文件的 FILE * 作为目标。


如果新文件不是已经是旧文件的副本,您还需要复制文件的其余部分(包括标题的其他部分)。在这种情况下,这可能是最简单的(一旦打开文件):

    将旧文件的标头读入wav

    fread(&wav, sizeof(wav), 1, original);
    

    修改SampleRateByteRate 字段

    wav.SampleRate *= 2;
    wav.ByteRate *= 2;
    

    将整个修改后的wav标头写入新文件

    fwrite(&wav, sizeof(wav), 1, fast);
    

    循环复制旧文件的剩余部分到新文件中

    char buf[1024];
    int count;
    while ((count = fread(buf, 1, sizeof(buf), original)) > 0)
        fwrite(buf, 1, count, fast);
    

..然后关闭文件并返回。

【讨论】:

【参考方案2】:

我想通了。我删除了结构只是为了看看我在做什么。完成最终项目后,我将重新合并该结构。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() 
    FILE *original, *fast, *slow, *slowdown;
    unsigned int ChunkSize, Subchunk1Size, Subchunk2Size, RIFFSize, fmtSize, dataSize, SampleRate, ByteRate;
    unsigned short int AudioFormat, NumChannels, BlockAlign, BitsPerSample;
    char ChunkID[5], Format[5], Subchunk1ID[5], Subchunk2ID[5];
    ChunkID[4] = '\0';
    Format[4] = '\0';
    Subchunk1ID[4] = '\0';
    Subchunk2ID[4] = '\0';
    char path[FILENAME_MAX], dfast[FILENAME_MAX], dslow[FILENAME_MAX], dslowdown[FILENAME_MAX];
    printf("Enter path to Alejandro_project.wav file: ");
    scanf("%s", path);
    strcpy(dfast, path);
    dfast[strlen(path) - 21] = '\0';
    strcpy(dslow, dfast);
    strcpy(dslowdown, dslow);
    strcat(dfast, "Alejandro_fast.wav");
    strcat(dslow, "Alejandro_slow.wav");
    strcat(dslowdown, "Alejandro_slowdown.wav");
    original = fopen(path, "rb");
    if (!original) 
        printf("Error: file does not exist.\n");
        return EXIT_FAILURE;
    
    fread(ChunkID, 4, 1, original);
    fread(&ChunkSize, 4, 1, original);
    fread(Format, 4, 1, original);
    fread(Subchunk1ID, 4, 1, original);
    fread(&Subchunk1Size, 4, 1, original);
    fread(&AudioFormat, 2, 1, original);
    fread(&NumChannels, 2, 1, original);
    fread(&SampleRate, 4, 1, original);
    fread(&ByteRate, 4, 1, original);
    fread(&BlockAlign, 2, 1, original);
    fread(&BitsPerSample, 2, 1, original);
    fread(Subchunk2ID, 4, 1, original);
    fread(&Subchunk2Size, 4, 1, original);
    fmtSize = Subchunk1Size + 8;
    dataSize = Subchunk2Size + 8;
    RIFFSize = ChunkSize + 8 - (fmtSize + dataSize);
    printf("RIFF Size:     %d\n", RIFFSize);
    printf("fmt Size:      %d\n", fmtSize);
    printf("data Size:     %d\n\n", dataSize);
    printf("ChunkID:       %s\n", ChunkID);
    printf("ChunkSize:     %d\n", ChunkSize);
    printf("Format:        %s\n\n", Format);
    printf("Subchunk1ID:   %s\n", Subchunk1ID);
    printf("Subchunk1Size: %d\n", Subchunk1Size);
    printf("AudioFormat:   %d\n", AudioFormat);
    printf("NumChannels:   %d\n", NumChannels);
    printf("SampleRate:    %d\n", SampleRate);
    printf("ByteRate:      %d\n", ByteRate);
    printf("BlockAlign:    %d\n", BlockAlign);
    printf("BitsPerSample: %d\n\n", BitsPerSample);
    printf("Subchunk2ID:   %s\n", Subchunk2ID);
    printf("Subchunk2Size: %d\n", Subchunk2Size);
    fseek(original, 0, SEEK_SET);
    fast = fopen(dfast, "wb");
    unsigned int fastSampleRate = SampleRate * 2;
    unsigned int fastByteRate = 2 * fastSampleRate;
    fwrite(ChunkID, 4, 1, fast);
    fwrite(&ChunkSize, 4, 1, fast);
    fwrite(Format, 4, 1, fast);
    fwrite(Subchunk1ID, 4, 1, fast);
    fwrite(&Subchunk1Size, 4, 1, fast);
    fwrite(&AudioFormat, 2, 1, fast);
    fwrite(&NumChannels, 2, 1, fast);
    fwrite(&fastSampleRate, 4, 1, fast);
    fwrite(&fastByteRate, 4, 1, fast);
    fwrite(&BlockAlign, 2, 1, fast);
    fwrite(&BitsPerSample, 2, 1, fast);
    fwrite(Subchunk2ID, 4, 1, fast);
    fwrite(&Subchunk2Size, 4, 1, fast);
    short int data;
    unsigned int i;
    for (i = 0; i < Subchunk2Size / 2; i++) 
        fread(&data, 2, 1, original);
        fwrite(&data, 2, 1, fast);
    
    fclose(fast);
    fseek(original, 0, SEEK_SET);
    slow = fopen(dslow, "wb");
    unsigned int slowSampleRate = SampleRate / 2;
    unsigned int slowByteRate = 2 * slowSampleRate;
    fwrite(ChunkID, 4, 1, slow);
    fwrite(&ChunkSize, 4, 1, slow);
    fwrite(Format, 4, 1, slow);
    fwrite(Subchunk1ID, 4, 1, slow);
    fwrite(&Subchunk1Size, 4, 1, slow);
    fwrite(&AudioFormat, 2, 1, slow);
    fwrite(&NumChannels, 2, 1, slow);
    fwrite(&slowSampleRate, 4, 1, slow);
    fwrite(&slowByteRate, 4, 1, slow);
    fwrite(&BlockAlign, 2, 1, slow);
    fwrite(&BitsPerSample, 2, 1, slow);
    fwrite(Subchunk2ID, 4, 1, slow);
    fwrite(&Subchunk2Size, 4, 1, slow);
    for (i = 0; i < Subchunk2Size / 2; i++) 
        fread(&data, 2, 1, original);
        fwrite(&data, 2, 1, slow);
    
    fclose(slow);    
    fclose(original);
    system("pause");
    return EXIT_SUCCESS;

【讨论】:

【参考方案3】:

要使速度加倍,您不能只更改 WAV 标头中的 sample ratebyte-rate。如果这样做,它会播放得很快,但由于音高错误而听起来很奇怪。相反,您需要使用适当的采样率转换 (SRC) 库将输入 WAV 文件转换为具有更快采样率的输出 WAV 文件。

【讨论】:

以上是关于更改 wav 文件的采样率和比特率而不会遇到打开文件的问题?的主要内容,如果未能解决你的问题,请参考以下文章

ffmpeg 从 flac 转换为 wav

使用 afconvert 对 wav (LEI16) 文件进行下采样

当我尝试更改采样率时,为啥 sox 会损坏我的 wav 文件?

在 .wav 比特率之间转换时降低音量

这是读取音频文件 FFT 的正确方法吗? (python + wav)

将 wav 文件从 GSM 转换为 PCM