C++ RIFF WAVE reader 非常慢
C++ RIFF WAVE reader is painfully slow
所以我以前从未真正使用过二进制文件,而且我是 C++ 的新手。我想读取一个 wav 文件并将其数据部分输出到一个 txt 中(用逗号分隔每个样本的值)。我也设法阅读了 header 部分,但这段代码在这里并不重要,所以我不会包含它。
我的 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;
}
我让程序将进度打印到控制台。请注意,这仅适用于具有一个通道并以 IEEE 754 标准存储样本的 wav 文件。你可以找到我使用的文件here。我只是一个爱好程序员,请原谅我,我不知道是什么让我的程序这么慢……是矢量迭代吗?或者是 reinterpret_cast?
有点乱的变量声明
您的阅读方式完全错误。波形文件具有 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', ' '
块通常是第一个,应该包含 PCMWAVEFORMAT
描述 wav 数据的结构;
'd', 'a', 't', 'a'
块包含音频数据;
- 其他块可以跳过。
如果您已完成最后一个块的读取但尚未到达文件末尾,则很可能附加了另一个 RIFF 文件。由于 32 位长度限制,大文件是通过连接几个较小的 RIFF 文件来构建的。
也许进度打印减慢了速度?你印了很多。也许您可以尝试仅在百分比的整数值发生变化时打印,例如:
int lastPercent = -1;
loop {
...
float percent = ...;
int integralPercent = (int)percent;
if (integralPercent!=lastPercent) {
lastPercent = integralPercent;
// print percent here
}
}
所以我以前从未真正使用过二进制文件,而且我是 C++ 的新手。我想读取一个 wav 文件并将其数据部分输出到一个 txt 中(用逗号分隔每个样本的值)。我也设法阅读了 header 部分,但这段代码在这里并不重要,所以我不会包含它。
我的 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;
}
我让程序将进度打印到控制台。请注意,这仅适用于具有一个通道并以 IEEE 754 标准存储样本的 wav 文件。你可以找到我使用的文件here。我只是一个爱好程序员,请原谅我,我不知道是什么让我的程序这么慢……是矢量迭代吗?或者是 reinterpret_cast?
有点乱的变量声明您的阅读方式完全错误。波形文件具有 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', ' '
块通常是第一个,应该包含PCMWAVEFORMAT
描述 wav 数据的结构;'d', 'a', 't', 'a'
块包含音频数据;- 其他块可以跳过。
如果您已完成最后一个块的读取但尚未到达文件末尾,则很可能附加了另一个 RIFF 文件。由于 32 位长度限制,大文件是通过连接几个较小的 RIFF 文件来构建的。
也许进度打印减慢了速度?你印了很多。也许您可以尝试仅在百分比的整数值发生变化时打印,例如:
int lastPercent = -1;
loop {
...
float percent = ...;
int integralPercent = (int)percent;
if (integralPercent!=lastPercent) {
lastPercent = integralPercent;
// print percent here
}
}