从 wav 文件头读取采样率

Reading samplerate from a wavfile header

我正在编写代码以读取 wave 文件。

我使用 this document 作为我的指南。

指定header的byte 22为wave文件的声道数,header的byte 24为采样率。

我正在使用由 Ableton 输出的测试文件作为 2 通道 16 位 44100hz。我已经确认测试波形文件的格式是大胆的,以确保它确实是 44100hz 采样率。

当我读入波形文件时,我得到的采样率值为 -21436。我很确定我读取小端整数的代码是正确的。而且我确定我的测试 wavfile 是正确的。所以现在我不知道为什么读取采样率不正确....

我的int读取代码如下

int ReadInt(char* bytes , int start) { return (bytes[start+3] << 24) + (bytes[start+2] << 16) + (bytes[start+1] << 8) + bytes[start]; }

读取wave文件的函数如下...

WavFile::WavFile(std::string filename)
{
    std::ifstream ifs;      
    ifs.open( filename, std::ios::binary | std::ios::in );

    LogStream(LOG_DEBUG) << "WavFile::WavFile - - - - BEGIN READING WAV - - - -";

    if(ifs.fail())
        throw std::invalid_argument("WavFile::WavFile : Failed to open wavFile "+filename);

    char hbytes[HEADER_SIZE];

    ifs.read(hbytes , HEADER_SIZE);

    // check that this is actually a wave file
    bool valid_riff = hbytes[0]=='R' && hbytes[1]=='I' && hbytes[2]=='F' && hbytes[3]=='F';
    bool valid_wave = hbytes[8]=='W' && hbytes[9]=='A' && hbytes[10]=='V' && hbytes[11]=='E';
    bool valid_ftm = (hbytes[12]=='f' && hbytes[13]=='m' && hbytes[14]=='t' && hbytes[15]==' ');
    bool valid_data = (hbytes[36]=='d' && hbytes[37]=='a' && hbytes[38]=='t' && hbytes[39]=='a');

    LogStream(LOG_DEBUG) << "WavFile::WavFile - valid_riff="<<valid_riff<<" valid_wave="<<valid_wave<<" valid_ftm="<<valid_ftm<<" valid_data="<<valid_data;
    if(!(valid_data && valid_ftm && valid_riff))
        throw std::invalid_argument("WavFile::WavFile : Invalid argument - unable to open wavfile "+filename);

    int audioFormat = ReadShort(hbytes , 20);
    int SubChunk1Size = ReadInt(hbytes , 16);

    if(audioFormat != 1 || SubChunk1Size != 16)
        throw std::invalid_argument("WavFile::WavFile : Only uncompressed PCM wave format supported."+filename);    

    int subChunk2size = ReadInt(hbytes , 40);
    m_header.num_channels = ReadShort(hbytes , 22);
    m_header.sample_rate = ReadInt(hbytes , 24);
    m_header.bits_per_sample = ReadShort(hbytes , 34);

    LogStream(LOG_DEBUG) << "WavFile::WavFile num_channels="<<m_header.num_channels << " sample_rate="<<m_header.sample_rate<<" bits_per_sample="<<m_header.bits_per_sample;

    m_pcm_data.resize( subChunk2size / sizeof(int16_t) );

    LogStream(LOG_DEBUG) << "WavFile::WavFile - subChunk2size = "<<subChunk2size;
    LogStream(LOG_DEBUG) << "WavFile::WavFile - m_pcm_data.size() = "<<m_pcm_data.size();
    ifs.read((char*)m_pcm_data.data() , subChunk2size);

    LogStream(LOG_DEBUG) << "WavFile: ifstream failbit="<<ifs.fail()<<" badbit="<<ifs.bad()<<" goodbit="<<ifs.good();

    ifs.close();

    LogStream(LOG_DEBUG) << "WavFile::WavFile - - - - END READING WAV - - - -\n";
    LogStream(LOG_DEBUG) << "WavFile::WavFile";
}

44100 的十六进制值为 44ac(无符号整数 16),-21436 也有十六进制值 44ac(有符号整数 16)——问题在于编译器在移位之前隐式地将每个有符号字符转换为有符号整数的方式。您可以通过如下方式进行转换(输出 44100)来避免这种情况:

int main()
{
    char bytes[4] = { 0x44, 0xac, 0x00, 0x00 };

    printf("%i\n", (((unsigned char)bytes[3]) << 24) | (((unsigned char)bytes[2]) << 16) | (((unsigned char)bytes[1]) << 8) | ((unsigned char)bytes[0]));
    return 0;
}

或者简单地读取无符号字节——这将避免其他字段出现同样的问题:

int main()
{
    unsigned char bytes[4] = { 0x44, 0xac, 0x00, 0x00 };

    printf("%i\n", (bytes[3] << 24) | (bytes[2] << 16) | (bytes[1] << 8) | bytes[0]);
    return 0;
}