C ++ RIFF WAVE阅读器非常慢

时间:2017-07-09 18:46:57

标签: c++ audio wav binaryfiles

所以我之前从未真正使用二进制文件,而且我是C ++的新手。我想读取一个wav文件并将其数据部分输出到一个txt(用逗号分隔每个样本的值)。我还设法在标题部分阅读,但这段代码在这里并不重要,所以我不会包含它。

我的wav文件以32bps的速率存储IEEE 754标准(浮点数)中的数据。我首先将整个wav文件读入char矢量,然后尝试使用它。该程序的输出是我所期望的,我可以通过在没有问题的情况下读取txt在Python中播放声音。该程序速度非常慢(对于几秒钟的wav文件需要几分钟)。

这是wavReader.cpp

#include "stdafx.h"
#include "wavFile.h"
#include <fstream>
#include <iostream>
#include <vector>

int main()
{
    std::ifstream file("file.wav", std::ios::binary);
    std::vector<char> buffer((
        std::istreambuf_iterator<char>(file)),
        (std::istreambuf_iterator<char>()));
    std::cout << "Loading complete!\n";

    WavFile wavFile = setWavFile(buffer);

    return 0;
}

这是wavFile.h

#pragma once
#include <iostream>
#include <vector>

struct WavFile
{
    uint32_t dataSize;
};

WavFile setWavFile(std::vector<char> buffer);
uint32_t getUint32(std::vector<char> buffer, std::vector<char>::iterator it);

这是wavFile.cpp

#include "stdafx.h"
#include "WavFile.h"
#include <fstream>

WavFile setWavFile(std::vector<char> buffer) {

    WavFile wavFile;
    std::vector<char>::iterator it = buffer.begin();

    // Beginning of data chunk is marked with "data"
    it += 4;
    while (*(it - 4) != 'd' ||
        *(it - 3) != 'a' ||
        *(it - 2) != 't' ||
        *(it - 1) != 'a')
        it++;

    wavFile.dataSize = getUint32(buffer, it), it += 4;
    std::ofstream output("data.txt");

    while (it != buffer.end())
    {
        char outputChar[4];
        for (int i = 0; i < 4; (i++, it++))
            outputChar[i] = *it;
        char* outputStr = outputChar;
        char** outputStrPtr = &outputStr;
        float** outputPtr = reinterpret_cast<float**>(outputStrPtr);
        output << **outputPtr << ", ";
        std::cout << static_cast<double>(std::distance(buffer.begin(), it)) * 100 / wavFile.dataSize << "\%\n";
    }

    return wavFile;
}

uint32_t getUint32(std::vector<char> buffer, std::vector<char>::iterator it)
{
    char outputChar[4];
    for (int i = 0; i < 4; (i++, it++))
        outputChar[i] = *it;
    char* outputStr = outputChar;
    char** outputStrPtr = &outputStr;
    uint32_t** outputPtr = reinterpret_cast<uint32_t**>(outputStrPtr);
    return **outputPtr;
}

我让程序将进度打印到控制台。请注意,这仅适用于具有一个通道的wav文件,并以IEEE 754标准存储样本。您可以找到我使用的文件here。我只是一个爱好程序员,所以请原谅我,我不知道是什么让我的程序变得缓慢......是矢量迭代吗?或者它是reinterpret_cast的那种凌乱的变量声明?

2 个答案:

答案 0 :(得分:0)

你正在以完全错误的方式阅读它。 Wave文件具有RIFF格式。每个文件都包含RIFF文件头和一系列块。

#include <Windows.h> // for DWORD
#include <MMReg.h> // for PCMWAVEFORMATPCMWAVEFORMAT and FORCC

struct t_RiffFileHeader
{
    ::FOURCC m_riff;      // must be 'R', 'I', 'F', 'F'
    ::DWORD  m_file_size; // should be less than or equal to the total file zize
    ::FOURCC m_formtype;  // must be 'W', 'A', 'V', 'E'
};
static_assert(12 == sizeof(t_RiffFileHeader), "");

所以你首先读取这个riff头的12个字节并验证它是否正确。

size_t remaining_bytes_count(buffer.size());
const char * p_cursor(buffer.data());
if(remaining_bytes_count <= sizeof(t_RiffFileHeader))
{
    exit(1);
}
const t_RiffFileHeader & riff_header(*reinterpret_cast< const t_RiffFileHeader * >(reinterpret_cast< uintptr_t >(p_cursor)));
if(static_cast< size_t >(riff_header.m_file_size) < sizeof(t_RiffChunkHeader))
{
    exit(1);
}
p_cursor += sizeof(t_RiffFileHeader);
remaining_bytes_count -= sizeof(t_RiffFileHeader);

然后你继续阅读块。

struct t_RiffChunkHeader
{
    ::FOURCC m_id;        
    ::DWORD  m_chunk_content_size;
};
static_assert(8 == sizeof(t_RiffFileHeader), "");

您读取块头,然后根据块ID读取m_chunk_content_size个字节的数据:

  • 'f', 'm', 't', ' ' chunk通常是第一个,应该包含描述wav数据的PCMWAVEFORMAT结构;
  • 'd', 'a', 't', 'a' chunk包含音频数据;
  • 可以跳过其他块。

如果您已完成读取最后一个块但尚未到达文件结束,那么很可能还会附加另一个RIFF文件。由于32位长度限制,大文件是通过连接几个较小的RIFF文件构建的。

答案 1 :(得分:0)

打印进度可能会降低速度吗?你打印很多。也许只有在百分比的整数值发生变化时才可以尝试打印,例如:

int lastPercent = -1;

loop {
  ...
  float percent = ...;
  int integralPercent = (int)percent;
  if (integralPercent!=lastPercent) {
    lastPercent = integralPercent;
    // print percent here
  }
}